mirror of
https://github.com/koreader/koreader.git
synced 2025-12-13 20:36:53 +01:00
AutoDim: Allow to dim the frontlight on idle (#9028)
This commit is contained in:
@@ -90,6 +90,7 @@ local order = {
|
||||
"----------------------------",
|
||||
"screen_dpi",
|
||||
"screen_eink_opt",
|
||||
"autodim",
|
||||
"autowarmth",
|
||||
"color_rendering",
|
||||
"----------------------------",
|
||||
|
||||
@@ -135,6 +135,7 @@ local order = {
|
||||
"----------------------------",
|
||||
"screen_dpi",
|
||||
"screen_eink_opt",
|
||||
"autodim",
|
||||
"autowarmth",
|
||||
"color_rendering",
|
||||
"----------------------------",
|
||||
|
||||
6
plugins/autodim.koplugin/_meta.lua
Normal file
6
plugins/autodim.koplugin/_meta.lua
Normal file
@@ -0,0 +1,6 @@
|
||||
local _ = require("gettext")
|
||||
return {
|
||||
name = "autodim",
|
||||
fullname = _("Automatic dimmer"),
|
||||
description = _("This plugin allows dimming the frontlight after a period of inactivity."),
|
||||
}
|
||||
274
plugins/autodim.koplugin/main.lua
Normal file
274
plugins/autodim.koplugin/main.lua
Normal file
@@ -0,0 +1,274 @@
|
||||
--[[--
|
||||
Plugin for automatic dimming of the frontlight after an idle period.
|
||||
|
||||
@module koplugin.autodim
|
||||
--]]--
|
||||
|
||||
local Device = require("device")
|
||||
local Event = require("ui/event")
|
||||
local FFIUtil = require("ffi/util")
|
||||
local SpinWidget = require("ui/widget/spinwidget")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local TrapWidget = require("ui/widget/trapwidget")
|
||||
local time = require("ui/time")
|
||||
local util = require("util")
|
||||
local _ = require("gettext")
|
||||
local C_ = _.pgettext
|
||||
local T = FFIUtil.template
|
||||
|
||||
local DEFAULT_AUTODIM_STARTTIME_M = 5
|
||||
local DEFAULT_AUTODIM_DURATION_S = 5
|
||||
local DEFAULT_AUTODIM_FRACTION = 20
|
||||
local AUTODIM_EVENT_FREQUENCY = 2 -- in Hz; Frequenzy for FrontlightChangedEvent on E-Ink devices
|
||||
|
||||
local AutoDim = WidgetContainer:new{ name = "autodim" }
|
||||
|
||||
function AutoDim:init()
|
||||
self.autodim_starttime_m = G_reader_settings:readSetting("autodim_starttime_minutes", -1)
|
||||
self.autodim_duration_s = G_reader_settings:readSetting("autodim_duration_seconds", DEFAULT_AUTODIM_DURATION_S)
|
||||
self.autodim_fraction = G_reader_settings:readSetting("autodim_fraction", DEFAULT_AUTODIM_FRACTION)
|
||||
|
||||
self.last_action_time = UIManager:getElapsedTimeSinceBoot()
|
||||
|
||||
self.ui.menu:registerToMainMenu(self)
|
||||
UIManager.event_hook:registerWidget("InputEvent", self)
|
||||
|
||||
self:_schedule_autodim_task()
|
||||
self.isCurrentlyDimming = false -- true during or after the dimming ramp
|
||||
self.trap_widget = nil
|
||||
end
|
||||
|
||||
function AutoDim:addToMainMenu(menu_items)
|
||||
menu_items.autodim = self:getAutodimMenu()
|
||||
end
|
||||
|
||||
function AutoDim:getAutodimMenu()
|
||||
return {
|
||||
text = _("Automatic dimmer"),
|
||||
checked_func = function() return self.autodim_starttime_m > 0 end,
|
||||
sub_item_table = {
|
||||
{
|
||||
text_func = function()
|
||||
return self.autodim_starttime_m <= 0 and _("Idle time for dimmer") or
|
||||
T(_("Idle time for dimmer: %1"),
|
||||
util.secondsToClockDuration("modern", self.autodim_starttime_m * 60, false, true, false, true))
|
||||
end,
|
||||
checked_func = function() return self.autodim_starttime_m > 0 end,
|
||||
callback = function(touchmenu_instance)
|
||||
local idle_dialog = SpinWidget:new{
|
||||
title_text = _("Automatic dimmer idle time"),
|
||||
info_text = _("Start the dimmer after the designated period of inactivity."),
|
||||
value = self.autodim_starttime_m >=0 and self.autodim_starttime_m or 0.5,
|
||||
default_value = DEFAULT_AUTODIM_STARTTIME_M,
|
||||
value_min = 0.5,
|
||||
value_max = 60,
|
||||
value_step = 0.5,
|
||||
value_hold_step = 5,
|
||||
unit = C_("Time", "min"),
|
||||
precision = "%0.1f",
|
||||
ok_always_enabled = true,
|
||||
callback = function(spin)
|
||||
if not spin then return end
|
||||
self.autodim_starttime_m = spin.value
|
||||
G_reader_settings:saveSetting("autodim_starttime_minutes", spin.value)
|
||||
self:_schedule_autodim_task()
|
||||
if touchmenu_instance then touchmenu_instance:updateItems() end
|
||||
end,
|
||||
extra_text = _("Disable"),
|
||||
extra_callback = function()
|
||||
self.autodim_starttime_m = -1
|
||||
G_reader_settings:saveSetting("autodim_starttime_minutes", -1)
|
||||
self:_schedule_autodim_task()
|
||||
if touchmenu_instance then touchmenu_instance:updateItems() end
|
||||
end,
|
||||
}
|
||||
UIManager:show(idle_dialog)
|
||||
if touchmenu_instance then touchmenu_instance:updateItems() end
|
||||
end,
|
||||
keep_menu_open = true,
|
||||
},
|
||||
{
|
||||
text_func = function()
|
||||
return T(_("Dimmer duration: %1"),
|
||||
util.secondsToClockDuration("modern", self.autodim_duration_s, false, true, false, true))
|
||||
end,
|
||||
enabled_func = function() return self.autodim_starttime_m > 0 end,
|
||||
callback = function(touchmenu_instance)
|
||||
local dimmer_dialog = SpinWidget:new{
|
||||
title_text = _("Automatic dimmer duration"),
|
||||
info_text = _("Delay to reach the lowest brightness."),
|
||||
value = self.autodim_duration_s,
|
||||
default_value = DEFAULT_AUTODIM_DURATION_S,
|
||||
value_min = 0,
|
||||
value_max = 300,
|
||||
value_step = 1,
|
||||
value_hold_step = 10,
|
||||
precision = "%1d",
|
||||
unit = C_("Time", "s"),
|
||||
callback = function(spin)
|
||||
if not spin then return end
|
||||
self.autodim_duration_s = spin.value
|
||||
G_reader_settings:saveSetting("autodim_duration_seconds", spin.value)
|
||||
self:_schedule_autodim_task()
|
||||
if touchmenu_instance then touchmenu_instance:updateItems() end
|
||||
end,
|
||||
}
|
||||
UIManager:show(dimmer_dialog)
|
||||
if touchmenu_instance then touchmenu_instance:updateItems() end
|
||||
end,
|
||||
keep_menu_open = true,
|
||||
},
|
||||
{
|
||||
text_func = function()
|
||||
return T(_("Dim to %1 % of the regular brightness"), self.autodim_fraction)
|
||||
end,
|
||||
enabled_func = function() return self.autodim_starttime_m > 0 end,
|
||||
callback = function(touchmenu_instance)
|
||||
local percentage_dialog = SpinWidget:new{
|
||||
title_text = _("Dim to percentage"),
|
||||
info_text = _("The lowest brightness as a percentage of the regular brightness."),
|
||||
value = self.autodim_fraction,
|
||||
value_default = DEFAULT_AUTODIM_FRACTION,
|
||||
value_min = 0,
|
||||
value_max = 100,
|
||||
value_hold_step = 10,
|
||||
unit = "%",
|
||||
callback = function(spin)
|
||||
self.autodim_fraction = spin.value
|
||||
G_reader_settings:saveSetting("autodim_fraction", spin.value)
|
||||
self:_schedule_autodim_task()
|
||||
if touchmenu_instance then touchmenu_instance:updateItems() end
|
||||
end,
|
||||
}
|
||||
UIManager:show(percentage_dialog)
|
||||
if touchmenu_instance then touchmenu_instance:updateItems() end
|
||||
end,
|
||||
keep_menu_open = true,
|
||||
},
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
-- Schedules the first idle task, the consecutive ones are scheduled by the `autodim_task` itself.
|
||||
-- `seconds` the initial scheduling delay of the first task
|
||||
function AutoDim:_schedule_autodim_task(seconds)
|
||||
UIManager:unschedule(self.autodim_task)
|
||||
if self.autodim_starttime_m < 0 then
|
||||
return
|
||||
end
|
||||
|
||||
seconds = seconds or self.autodim_starttime_m * 60
|
||||
UIManager:scheduleIn(seconds, self.autodim_task, self)
|
||||
end
|
||||
|
||||
function AutoDim:restoreFrontlight()
|
||||
Device.powerd:setIntensity(self.autodim_save_fl)
|
||||
UIManager:broadcastEvent(Event:new("UpdateFooter", true, true))
|
||||
self:_unschedule_ramp_task()
|
||||
self:_schedule_autodim_task()
|
||||
end
|
||||
|
||||
function AutoDim:onInputEvent()
|
||||
self.last_action_time = UIManager:getElapsedTimeSinceBoot()
|
||||
end
|
||||
|
||||
function AutoDim:_unschedule_autodim_task()
|
||||
if self.isCurrentlyDimming then
|
||||
UIManager:unschedule(self.ramp_task)
|
||||
self.isCurrentlyDimming = false
|
||||
end
|
||||
end
|
||||
|
||||
function AutoDim:onResume()
|
||||
self.last_action_time = UIManager:getElapsedTimeSinceBoot()
|
||||
if self.isCurrentlyDimming then
|
||||
if self.trap_widget then
|
||||
UIManager:close(self.trap_widget)
|
||||
self.trap_widget = nil
|
||||
end
|
||||
UIManager:scheduleIn(1, function()
|
||||
Device.powerd:setIntensity(self.autodim_save_fl)
|
||||
UIManager:broadcastEvent(Event:new("UpdateFooter", true, true))
|
||||
end)
|
||||
self.isCurrentlyDimming = false
|
||||
end
|
||||
self:_schedule_autodim_task()
|
||||
end
|
||||
|
||||
function AutoDim:onSuspend()
|
||||
if self.isCurrentlyDimming then
|
||||
self:_unschedule_autodim_task()
|
||||
self:_unschedule_ramp_task()
|
||||
self.isCurrentlyDimming = true -- message to self:onResume to go on with restoring
|
||||
end
|
||||
end
|
||||
|
||||
function AutoDim:autodim_task()
|
||||
if self.isCurrentlyDimming then return end
|
||||
|
||||
local now = UIManager:getElapsedTimeSinceBoot()
|
||||
local idle_duration = now - self.last_action_time
|
||||
local check_delay = time.s(self.autodim_starttime_m * 60) - idle_duration
|
||||
if check_delay <= 0 then
|
||||
self.trap_widget = TrapWidget:new{
|
||||
dismiss_callback = function()
|
||||
self:restoreFrontlight()
|
||||
self.trap_widget = nil
|
||||
end
|
||||
}
|
||||
|
||||
UIManager:show(self.trap_widget) -- suppress taps during dimming
|
||||
|
||||
self.autodim_save_fl = Device.powerd:frontlightIntensity()
|
||||
self.autodim_end_fl = math.floor(self.autodim_save_fl * self.autodim_fraction / 100 + 0.5)
|
||||
-- Clamp `self.autodim_end_fl` to 1 if `self.autodim_fraction` ~= 0
|
||||
if self.autodim_fraction ~= 0 and self.autodim_end_fl == 0 then
|
||||
self.autodim_end_fl = 1
|
||||
end
|
||||
local fl_diff = self.autodim_save_fl - self.autodim_end_fl
|
||||
-- calculate time until the next decrease step
|
||||
self.autodim_step_time_s = math.max(self.autodim_duration_s / fl_diff, 0.001)
|
||||
self.ramp_event_countdown_startvalue = Device:hasEinkScreen() and
|
||||
math.floor((1/AUTODIM_EVENT_FREQUENCY) / self.autodim_step_time_s + 0.5) or 0
|
||||
self.ramp_event_countdown = self.ramp_event_countdown_startvalue
|
||||
|
||||
self:ramp_task() -- which schedules itself
|
||||
-- Don't schedule `autodim_task` here, as this is done in `trap_widget.dismiss_callback` or in `onResume`
|
||||
else
|
||||
self:_schedule_autodim_task(time.to_s(check_delay))
|
||||
end
|
||||
end
|
||||
|
||||
function AutoDim:ramp_task()
|
||||
self.isCurrentlyDimming = true -- this will disable rescheduling of the `autodim_task`
|
||||
local fl_level = Device.powerd:frontlightIntensity()
|
||||
if fl_level > self.autodim_end_fl then
|
||||
Device.powerd:setIntensity(fl_level - 1)
|
||||
self.ramp_event_countdown = self.ramp_event_countdown - 1
|
||||
if self.ramp_event_countdown <= 0 then
|
||||
-- Update footer on every self.ramp_event_countdown call
|
||||
UIManager:broadcastEvent(Event:new("UpdateFooter", true, true))
|
||||
self.ramp_event_countdown = self.ramp_event_countdown_startvalue
|
||||
end
|
||||
self:_schedule_ramp_task() -- Reschedule only if not ready
|
||||
-- `isCurrentlyDimming` stays true, to flag we have a dimmed FL.
|
||||
end
|
||||
if fl_level == self.autodim_end_fl and self.ramp_event_countdown_startvalue > 0 then
|
||||
-- Update footer at the end of the ramp.
|
||||
UIManager:broadcastEvent(Event:new("UpdateFooter", true, true))
|
||||
end
|
||||
end
|
||||
|
||||
function AutoDim:_schedule_ramp_task()
|
||||
UIManager:scheduleIn(self.autodim_step_time_s, self.ramp_task, self)
|
||||
end
|
||||
|
||||
function AutoDim:_unschedule_ramp_task()
|
||||
if self.isCurrentlyDimming then
|
||||
UIManager:unschedule(self.ramp_task)
|
||||
self.isCurrentlyDimming = false
|
||||
end
|
||||
end
|
||||
|
||||
return AutoDim
|
||||
@@ -2,5 +2,5 @@ local _ = require("gettext")
|
||||
return {
|
||||
name = "autowarmth",
|
||||
fullname = require("device"):hasNaturalLight() and _("Auto warmth and night mode") or _("Auto night mode"),
|
||||
description = _([[This plugin allows set the frontlight warmth automagically.]]),
|
||||
description = _([[This plugin allows to set the frontlight warmth automagically.]]),
|
||||
}
|
||||
|
||||
@@ -65,9 +65,9 @@ function AutoWarmth:init()
|
||||
self.longitude = G_reader_settings:readSetting("autowarmth_longitude") or -20.30
|
||||
self.altitude = G_reader_settings:readSetting("autowarmth_altitude") or 200
|
||||
self.timezone = G_reader_settings:readSetting("autowarmth_timezone") or 0
|
||||
self.scheduler_times = G_reader_settings:readSetting("autowarmth_scheduler_times") or
|
||||
{0.0, 5.5, 6.0, 6.5, 7.0, 13.0, 21.5, 22.0, 22.5, 23.0, 24.0}
|
||||
self.warmth = G_reader_settings:readSetting("autowarmth_warmth")
|
||||
self.scheduler_times = G_reader_settings:readSetting("autowarmth_scheduler_times")
|
||||
or {0.0, 5.5, 6.0, 6.5, 7.0, 13.0, 21.5, 22.0, 22.5, 23.0, 24.0}
|
||||
self.warmth = G_reader_settings:readSetting("autowarmth_warmth")
|
||||
or { 90, 90, 80, 60, 20, 20, 20, 60, 80, 90, 90}
|
||||
|
||||
-- schedule recalculation shortly afer midnight
|
||||
@@ -178,7 +178,7 @@ function AutoWarmth:scheduleMidnightUpdate()
|
||||
if warmth_diff ~= 0 then
|
||||
local time_diff = SunTime:getTimeInSec(time2) - time
|
||||
local delta_t = time_diff / math.abs(warmth_diff) -- can be inf, no problem
|
||||
local delta_w = warmth_diff > 0 and 1 or -1
|
||||
local delta_w = warmth_diff > 0 and 1 or -1
|
||||
for i = 1, math.abs(warmth_diff)-1 do
|
||||
local next_warmth = math.min(self.warmth[index1], 100) + delta_w * i
|
||||
-- only apply warmth for steps the hardware has (e.g. Tolino has 0-10 hw steps
|
||||
@@ -354,7 +354,7 @@ function AutoWarmth:getSubMenuItems()
|
||||
callback = function()
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = about_text,
|
||||
width = math.floor(Screen:getWidth() * 0.9),
|
||||
width = math.floor(Screen:getWidth() * 0.9),
|
||||
})
|
||||
end,
|
||||
keep_menu_open = true,
|
||||
@@ -475,8 +475,9 @@ function AutoWarmth:getLocationMenu()
|
||||
if touchmenu_instance then touchmenu_instance:updateItems() end
|
||||
end,
|
||||
},
|
||||
}}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
UIManager:show(location_name_dialog)
|
||||
location_name_dialog:onShowKeyboard()
|
||||
end,
|
||||
@@ -560,8 +561,7 @@ function AutoWarmth:getScheduleMenu()
|
||||
self.scheduler_times[num] = new_time
|
||||
if num == 1 then
|
||||
if new_time then
|
||||
self.scheduler_times[midnight_index]
|
||||
= new_time + 24 -- next day
|
||||
self.scheduler_times[midnight_index] = new_time + 24 -- next day
|
||||
else
|
||||
self.scheduler_times[midnight_index] = nil
|
||||
end
|
||||
@@ -609,7 +609,7 @@ function AutoWarmth:getScheduleMenu()
|
||||
end
|
||||
if num > 1 and new_time < get_valid_time(num, -1) then
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = _("This time is before the previous time.\nAdjust the previous time?"),
|
||||
text = _("This time is before the previous time.\nAdjust the previous time?"),
|
||||
ok_callback = function()
|
||||
for i = num-1, 1, -1 do
|
||||
if self.scheduler_times[i] then
|
||||
@@ -625,9 +625,9 @@ function AutoWarmth:getScheduleMenu()
|
||||
})
|
||||
elseif num < 10 and new_time > get_valid_time(num, 1) then
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = _("This time is after the subsequent time.\nAdjust the subsequent time?"),
|
||||
text = _("This time is after the subsequent time.\nAdjust the subsequent time?"),
|
||||
ok_callback = function()
|
||||
for i = num + 1, midnight_index - 1 do
|
||||
for i = num + 1, midnight_index - 1 do
|
||||
if self.scheduler_times[i] then
|
||||
if new_time > self.scheduler_times[i] then
|
||||
self.scheduler_times[i] = new_time
|
||||
@@ -744,7 +744,6 @@ function AutoWarmth:getWarmthMenu()
|
||||
text = _("Cancel"),
|
||||
}
|
||||
}},
|
||||
|
||||
})
|
||||
end
|
||||
end,
|
||||
@@ -859,7 +858,7 @@ function AutoWarmth:showTimesInfo(title, location, activator, request_easy)
|
||||
UIManager:show(InfoMessage:new{
|
||||
face = face,
|
||||
width = math.floor(Screen:getWidth() * (self.easy_mode and 0.75 or 0.90)),
|
||||
text = title .. location_string .. ":\n\n" ..
|
||||
text = title .. location_string .. ":\n\n" ..
|
||||
info_line(0, _("Solar midnight:"), times, 1, face, request_easy) ..
|
||||
add_line(_("Dawn"), request_easy) ..
|
||||
info_line(4, _("Astronomic:"), times, 2, face, request_easy) ..
|
||||
|
||||
Reference in New Issue
Block a user