mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
AutoFrontlight plugin (#2941)
* Add AutoFrontlight plugin * Add configuration to control autofrontlight feature
This commit is contained in:
@@ -43,10 +43,8 @@ local footerTextGeneratorMap = {
|
||||
frontlight = function()
|
||||
if not Device:hasFrontlight() then return "L: NA" end
|
||||
local powerd = Device:getPowerDevice()
|
||||
if powerd.is_fl_on ~= nil and powerd.is_fl_on == true then
|
||||
if powerd.fl_intensity ~= nil then
|
||||
return ("L: %d%%"):format(powerd.fl_intensity)
|
||||
end
|
||||
if powerd:isFrontlightOn() then
|
||||
return ("L: %d%%"):format(powerd:frontlightIntensity())
|
||||
else
|
||||
return "L: Off"
|
||||
end
|
||||
|
||||
@@ -38,47 +38,45 @@ function ReaderFrontLight:init()
|
||||
end
|
||||
|
||||
function ReaderFrontLight:onAdjust(arg, ges)
|
||||
if not Device.hasFrontlight() then return true end
|
||||
local powerd = Device:getPowerDevice()
|
||||
if powerd.fl_intensity ~= nil then
|
||||
logger.dbg("frontlight intensity", powerd.fl_intensity)
|
||||
local step = math.ceil(#self.steps * ges.distance / self.gestureScale)
|
||||
logger.dbg("step = ", step)
|
||||
local delta_int = self.steps[step] or self.steps[#self.steps]
|
||||
logger.dbg("delta_int = ", delta_int)
|
||||
local new_intensity
|
||||
if ges.direction == "north" then
|
||||
new_intensity = powerd.fl_intensity + delta_int
|
||||
elseif ges.direction == "south" then
|
||||
new_intensity = powerd.fl_intensity - delta_int
|
||||
end
|
||||
if new_intensity ~= nil then
|
||||
-- when new_intensity <=0, toggle light off
|
||||
if new_intensity <= 0 then
|
||||
powerd:toggleFrontlight()
|
||||
end
|
||||
powerd:setIntensity(new_intensity)
|
||||
if self.view.footer_visible and self.view.footer.settings.frontlight then
|
||||
self.view.footer:updateFooter()
|
||||
end
|
||||
end
|
||||
logger.dbg("frontlight intensity", powerd:frontlightIntensity())
|
||||
local step = math.ceil(#self.steps * ges.distance / self.gestureScale)
|
||||
logger.dbg("step = ", step)
|
||||
local delta_int = self.steps[step] or self.steps[#self.steps]
|
||||
logger.dbg("delta_int = ", delta_int)
|
||||
local new_intensity
|
||||
if ges.direction == "north" then
|
||||
new_intensity = powerd:frontlightIntensity() + delta_int
|
||||
elseif ges.direction == "south" then
|
||||
new_intensity = powerd:frontlightIntensity() - delta_int
|
||||
end
|
||||
if new_intensity == nil then return true end
|
||||
-- when new_intensity <=0, toggle light off
|
||||
if new_intensity <= 0 then
|
||||
powerd:turnOffFrontlight()
|
||||
else
|
||||
powerd:setIntensity(new_intensity)
|
||||
end
|
||||
if self.view.footer_visible and self.view.footer.settings.frontlight then
|
||||
self.view.footer:updateFooter()
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function ReaderFrontLight:onShowIntensity()
|
||||
if not Device.hasFrontlight() then return true end
|
||||
local powerd = Device:getPowerDevice()
|
||||
if powerd.fl_intensity ~= nil then
|
||||
local new_text
|
||||
if powerd.fl_intensity == 0 then
|
||||
new_text = _("Frontlight is off.")
|
||||
else
|
||||
new_text = T(_("Frontlight intensity is set to %1."), powerd.fl_intensity)
|
||||
end
|
||||
UIManager:show(Notification:new{
|
||||
text = new_text,
|
||||
timeout = 1.0,
|
||||
})
|
||||
local new_text
|
||||
if powerd:isFrontlightOff() then
|
||||
new_text = _("Frontlight is off.")
|
||||
else
|
||||
new_text = T(_("Frontlight intensity is set to %1."), powerd:frontlightIntensity())
|
||||
end
|
||||
UIManager:show(Notification:new{
|
||||
text = new_text,
|
||||
timeout = 1.0,
|
||||
})
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ function Device:new(o)
|
||||
end
|
||||
|
||||
function Device:init()
|
||||
assert(self ~= nil)
|
||||
if not self.screen then
|
||||
error("screen/framebuffer must be implemented")
|
||||
end
|
||||
@@ -204,4 +205,14 @@ function Device:retrieveNetworkInfo()
|
||||
end
|
||||
end
|
||||
|
||||
-- Return an integer value to indicate the brightness of the environment. The value should be in
|
||||
-- range [0, 3].
|
||||
-- 0: dark.
|
||||
-- 1: dim, frontlight is needed.
|
||||
-- 2: bright, frontlight is not needed.
|
||||
-- 3: dazzling.
|
||||
function Device:ambientBrightnessLevel()
|
||||
return 0
|
||||
end
|
||||
|
||||
return Device
|
||||
|
||||
@@ -8,21 +8,35 @@ local BasePowerD = {
|
||||
device = nil, -- device object
|
||||
|
||||
last_capacity_pull_time = 0, -- timestamp of last pull
|
||||
|
||||
is_fl_on = false, -- whether the frontlight is on
|
||||
}
|
||||
|
||||
function BasePowerD:new(o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
assert(o.fl_min < o.fl_max)
|
||||
if o.init then o:init() end
|
||||
if o.device and o.device.hasFrontlight() then
|
||||
o.fl_intensity = o:frontlightIntensityHW()
|
||||
o:_decideFrontlightState()
|
||||
if o:isFrontlightOn() then
|
||||
o:turnOnFrontlightHW()
|
||||
else
|
||||
o:turnOffFrontlightHW()
|
||||
end
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
function BasePowerD:init() end
|
||||
function BasePowerD:toggleFrontlight() end
|
||||
function BasePowerD:setIntensityHW() end
|
||||
function BasePowerD:setIntensityHW(intensity) end
|
||||
function BasePowerD:getCapacityHW() return 0 end
|
||||
function BasePowerD:isChargingHW() return false end
|
||||
function BasePowerD:frontlightIntensityHW() return 0 end
|
||||
function BasePowerD:turnOffFrontlightHW() self:setIntensityHW(self.fl_min) end
|
||||
function BasePowerD:turnOnFrontlightHW() self:setIntensityHW(self.fl_intensity) end
|
||||
-- Anything needs to be done before do a real hardware suspend. Such as turn off
|
||||
-- front light.
|
||||
function BasePowerD:beforeSuspend() end
|
||||
@@ -30,6 +44,57 @@ function BasePowerD:beforeSuspend() end
|
||||
-- front light state.
|
||||
function BasePowerD:afterResume() end
|
||||
|
||||
function BasePowerD:isFrontlightOn()
|
||||
assert(self ~= nil)
|
||||
return self.is_fl_on
|
||||
end
|
||||
|
||||
function BasePowerD:_decideFrontlightState()
|
||||
assert(self ~= nil)
|
||||
assert(self.device.hasFrontlight())
|
||||
self.is_fl_on = (self.fl_intensity > self.fl_min)
|
||||
end
|
||||
|
||||
function BasePowerD:isFrontlightOff()
|
||||
return not self:isFrontlightOn()
|
||||
end
|
||||
|
||||
function BasePowerD:frontlightIntensity()
|
||||
assert(self ~= nil)
|
||||
if not self.device.hasFrontlight() then return 0 end
|
||||
if self:isFrontlightOff() then return 0 end
|
||||
return self.fl_intensity
|
||||
end
|
||||
|
||||
function BasePowerD:toggleFrontlight()
|
||||
assert(self ~= nil)
|
||||
if not self.device.hasFrontlight() then return false end
|
||||
if self:isFrontlightOn() then
|
||||
return self:turnOffFrontlight()
|
||||
else
|
||||
return self:turnOnFrontlight()
|
||||
end
|
||||
end
|
||||
|
||||
function BasePowerD:turnOffFrontlight()
|
||||
assert(self ~= nil)
|
||||
if not self.device.hasFrontlight() then return end
|
||||
if self:isFrontlightOff() then return false end
|
||||
self:turnOffFrontlightHW()
|
||||
self.is_fl_on = false
|
||||
return true
|
||||
end
|
||||
|
||||
function BasePowerD:turnOnFrontlight()
|
||||
assert(self ~= nil)
|
||||
if not self.device.hasFrontlight() then return end
|
||||
if self:isFrontlightOn() then return false end
|
||||
if self.fl_intensity == self.fl_min then return false end
|
||||
self:turnOnFrontlightHW()
|
||||
self.is_fl_on = true
|
||||
return true
|
||||
end
|
||||
|
||||
function BasePowerD:read_int_file(file)
|
||||
local fd = io.open(file, "r")
|
||||
if fd then
|
||||
@@ -58,10 +123,13 @@ function BasePowerD:normalizeIntensity(intensity)
|
||||
end
|
||||
|
||||
function BasePowerD:setIntensity(intensity)
|
||||
if intensity == self.fl_intensity then return end
|
||||
if not self.device.hasFrontlight() then return false end
|
||||
if intensity == self.fl_intensity then return false end
|
||||
self.fl_intensity = self:normalizeIntensity(intensity)
|
||||
self:_decideFrontlightState()
|
||||
logger.dbg("set light intensity", self.fl_intensity)
|
||||
self:setIntensityHW()
|
||||
self:setIntensityHW(self.fl_intensity)
|
||||
return true
|
||||
end
|
||||
|
||||
function BasePowerD:getCapacity()
|
||||
|
||||
@@ -133,6 +133,20 @@ function Kindle:usbPlugOut()
|
||||
self.charging_mode = false
|
||||
end
|
||||
|
||||
function Kindle:ambientBrightnessLevel()
|
||||
local haslipc, lipc = pcall(require, "liblipclua")
|
||||
if not haslipc or lipc == nil then return 0 end
|
||||
local lipc_handle = lipc.init("com.github.koreader.ambientbrightness")
|
||||
if not lipc_handle then return 0 end
|
||||
local value = lipc_handle:get_int_property("com.lab126.powerd", "alsLux")
|
||||
lipc_handle:close()
|
||||
if type(value) ~= "number" then return 0 end
|
||||
if value < 10 then return 0 end
|
||||
if value < 256 then return 1 end
|
||||
if value < 32768 then return 2 end
|
||||
return 3
|
||||
end
|
||||
|
||||
|
||||
local Kindle2 = Kindle:new{
|
||||
model = "Kindle2",
|
||||
|
||||
@@ -4,10 +4,7 @@ local BasePowerD = require("device/generic/powerd")
|
||||
local KindlePowerD = BasePowerD:new{
|
||||
fl_min = 0, fl_max = 24,
|
||||
|
||||
fl_intensity = nil,
|
||||
lipc_handle = nil,
|
||||
|
||||
is_fl_on = false,
|
||||
}
|
||||
|
||||
function KindlePowerD:init()
|
||||
@@ -15,40 +12,29 @@ function KindlePowerD:init()
|
||||
if haslipc and lipc then
|
||||
self.lipc_handle = lipc.init("com.github.koreader.kindlepowerd")
|
||||
end
|
||||
if self.device.hasFrontlight() then
|
||||
-- Kindle stock software does not use intensity file directly, so we need to read from its
|
||||
-- lipc property first.
|
||||
if self.lipc_handle ~= nil then
|
||||
self.fl_intensity = self.lipc_handle:get_int_property("com.lab126.powerd", "flIntensity")
|
||||
else
|
||||
self.fl_intensity = self:_readFLIntensity()
|
||||
end
|
||||
self:_set_fl_on()
|
||||
end
|
||||
end
|
||||
|
||||
function KindlePowerD:toggleFrontlight()
|
||||
if not self.device.hasFrontlight() then
|
||||
return
|
||||
end
|
||||
|
||||
if self:_readFLIntensity() == 0 then
|
||||
self:setIntensityHW()
|
||||
function KindlePowerD:frontlightIntensityHW()
|
||||
if not self.device.hasFrontlight() then return 0 end
|
||||
-- Kindle stock software does not use intensity file directly, so we need to read from its
|
||||
-- lipc property first.
|
||||
if self.lipc_handle ~= nil then
|
||||
return self.lipc_handle:get_int_property("com.lab126.powerd", "flIntensity")
|
||||
else
|
||||
self:_turnOffFL()
|
||||
return self:_readFLIntensity()
|
||||
end
|
||||
end
|
||||
|
||||
function KindlePowerD:setIntensityHW()
|
||||
if self.lipc_handle ~= nil and self.fl_intensity > 0 then
|
||||
function KindlePowerD:setIntensityHW(intensity)
|
||||
if self.lipc_handle ~= nil and intensity > 0 then
|
||||
-- NOTE: We want to bypass setIntensity's shenanigans and simply restore the light as-is
|
||||
self.lipc_handle:set_int_property("com.lab126.powerd", "flIntensity", self.fl_intensity)
|
||||
self.lipc_handle:set_int_property(
|
||||
"com.lab126.powerd", "flIntensity", self.fronglightIntensity())
|
||||
else
|
||||
-- NOTE: when fl_intensity is 0, We want to really kill the light, so do it manually
|
||||
-- NOTE: when intensity is 0, We want to really kill the light, so do it manually
|
||||
-- (asking lipc to set it to 0 would in fact set it to 1)...
|
||||
os.execute("echo -n ".. self.fl_intensity .." > " .. self.fl_intensity_file)
|
||||
os.execute("echo -n ".. intensity .." > " .. self.fl_intensity_file)
|
||||
end
|
||||
self:_set_fl_on()
|
||||
end
|
||||
|
||||
function KindlePowerD:getCapacityHW()
|
||||
@@ -76,30 +62,20 @@ function KindlePowerD:__gc()
|
||||
end
|
||||
end
|
||||
|
||||
function KindlePowerD:_turnOffFL()
|
||||
-- NOTE: We want to really kill the light, so do it manually (asking lipc to set it to 0 would in fact set it to 1)...
|
||||
os.execute("echo -n 0 > " .. self.fl_intensity_file)
|
||||
self.is_fl_on = false
|
||||
end
|
||||
|
||||
function KindlePowerD:_readFLIntensity()
|
||||
return self:read_int_file(self.fl_intensity_file)
|
||||
end
|
||||
|
||||
function KindlePowerD:_set_fl_on()
|
||||
self.is_fl_on = (self.fl_intensity > 0)
|
||||
end
|
||||
|
||||
function KindlePowerD:afterResume()
|
||||
if not self.device.hasFrontlight() then
|
||||
return
|
||||
end
|
||||
if self.is_fl_on then
|
||||
if self:isFrontlightOn() then
|
||||
-- Kindle stock software should turn on the front light automatically. The follow statement
|
||||
-- ensure the consistency of intensity.
|
||||
self:setIntensityHW()
|
||||
else
|
||||
self:_turnOffFL()
|
||||
self:turnOffFrontlightHW()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -53,9 +53,7 @@ function NickelConf.frontLightLevel.get()
|
||||
-- FrontLightState config, so don't normalize the value here yet.
|
||||
return tonumber(new_intensity)
|
||||
else
|
||||
-- In NickelConfSpec, require("device") won't return KoboDevice
|
||||
local powerd = require("device/kobo/powerd")
|
||||
local fallback_fl_level = powerd.fl_intensity or 1
|
||||
local fallback_fl_level = 1
|
||||
assert(NickelConf.frontLightLevel.set(fallback_fl_level))
|
||||
return fallback_fl_level
|
||||
end
|
||||
|
||||
@@ -8,15 +8,8 @@ local KoboPowerD = BasePowerD:new{
|
||||
-- Do not actively set front light to 0, it may confuse users -- pressing
|
||||
-- hardware button won't take any effect.
|
||||
fl_min = 1, fl_max = 100,
|
||||
fl_intensity = 20,
|
||||
restore_settings = true,
|
||||
fl = nil,
|
||||
|
||||
-- We will set this value for all kobo models. but it will only be synced
|
||||
-- with nickel's FrontLightState config if the current nickel firmware
|
||||
-- supports this config.
|
||||
is_fl_on = false,
|
||||
|
||||
batt_capacity_file = batt_state_folder .. "capacity",
|
||||
is_charging_file = batt_state_folder .. "status",
|
||||
}
|
||||
@@ -36,42 +29,24 @@ function KoboPowerD:init()
|
||||
end
|
||||
end
|
||||
|
||||
function KoboPowerD:toggleFrontlight()
|
||||
if self.fl ~= nil then
|
||||
if self.is_fl_on then
|
||||
self.fl:setBrightness(0)
|
||||
else
|
||||
self.fl:setBrightness(self.fl_intensity)
|
||||
end
|
||||
self.is_fl_on = not self.is_fl_on
|
||||
if self.has_fl_state_cfg and KOBO_SYNC_BRIGHTNESS_WITH_NICKEL then
|
||||
NickelConf.frontLightState.set(self.is_fl_on)
|
||||
end
|
||||
function KoboPowerD:_syncNickelConf()
|
||||
if self.has_fl_state_cfg and KOBO_SYNC_BRIGHTNESS_WITH_NICKEL then
|
||||
NickelConf.frontLightState.set(self:isFrontlightOn())
|
||||
NickelConf.frontLightLevel.set(self:frontlightIntensity())
|
||||
end
|
||||
end
|
||||
|
||||
function KoboPowerD:setIntensityHW()
|
||||
if self.fl ~= nil then
|
||||
self.fl:setBrightness(self.fl_intensity)
|
||||
-- Make sure we persist intensity config in reader setting
|
||||
G_reader_settings:saveSetting("frontlight_intensity", self.fl_intensity)
|
||||
if KOBO_SYNC_BRIGHTNESS_WITH_NICKEL then
|
||||
NickelConf.frontLightLevel.set(self.fl_intensity)
|
||||
end
|
||||
-- also keep self.is_fl_on in sync with intensity if needed
|
||||
local is_fl_on
|
||||
if self.fl_intensity > 0 then
|
||||
is_fl_on = true
|
||||
else
|
||||
is_fl_on = false
|
||||
end
|
||||
if self.is_fl_on ~= is_fl_on then
|
||||
self.is_fl_on = is_fl_on
|
||||
if self.has_fl_state_cfg and KOBO_SYNC_BRIGHTNESS_WITH_NICKEL then
|
||||
NickelConf.frontLightState.set(self.is_fl_on)
|
||||
end
|
||||
end
|
||||
function KoboPowerD:frontlightIntensityHW()
|
||||
if self.has_fl_state_cfg then
|
||||
return NickelConf.frontLightLevel.get()
|
||||
end
|
||||
return 20
|
||||
end
|
||||
|
||||
function KoboPowerD:setIntensityHW(intensity)
|
||||
if self.fl == nil then return end
|
||||
self.fl:setBrightness(self.frontlightIntensity())
|
||||
self:_syncNickelConf()
|
||||
end
|
||||
|
||||
function KoboPowerD:getCapacityHW()
|
||||
@@ -84,19 +59,15 @@ end
|
||||
|
||||
-- Turn off front light before suspend.
|
||||
function KoboPowerD:beforeSuspend()
|
||||
if self.fl ~= nil then
|
||||
self.fl:setBrightness(0)
|
||||
end
|
||||
self:turnOffFrontlightHW()
|
||||
end
|
||||
|
||||
-- Restore front light state after resume.
|
||||
function KoboPowerD:afterResume()
|
||||
if self.fl ~= nil then
|
||||
if KOBO_LIGHT_ON_START and tonumber(KOBO_LIGHT_ON_START) > -1 then
|
||||
self:setIntensity(math.min(KOBO_LIGHT_ON_START, 100))
|
||||
elseif self.is_fl_on then
|
||||
self.fl:setBrightness(self.fl_intensity)
|
||||
end
|
||||
if KOBO_LIGHT_ON_START and tonumber(KOBO_LIGHT_ON_START) > -1 then
|
||||
self:setIntensity(math.min(KOBO_LIGHT_ON_START, 100))
|
||||
elseif self:isFrontlightOn() then
|
||||
self:turnOnFrontlightHW()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -54,11 +54,13 @@ end
|
||||
--- Saves a setting.
|
||||
function LuaSettings:saveSetting(key, value)
|
||||
self.data[key] = value
|
||||
return self
|
||||
end
|
||||
|
||||
--- Deletes a setting.
|
||||
function LuaSettings:delSetting(key)
|
||||
self.data[key] = nil
|
||||
return self
|
||||
end
|
||||
|
||||
--- Checks if setting exists.
|
||||
@@ -98,6 +100,7 @@ function LuaSettings:flipNilOrTrue(key)
|
||||
else
|
||||
self:delSetting(key)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Flips `nil` or `false` to `true`.
|
||||
@@ -107,6 +110,7 @@ function LuaSettings:flipNilOrFalse(key)
|
||||
else
|
||||
self:delSetting(key)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Flips setting to `true`.
|
||||
@@ -116,6 +120,7 @@ function LuaSettings:flipTrue(key)
|
||||
else
|
||||
self:saveSetting(key, true)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Flips setting to `false`.
|
||||
@@ -125,11 +130,13 @@ function LuaSettings:flipFalse(key)
|
||||
else
|
||||
self:saveSetting(key, true)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Replaces existing settings with table.
|
||||
function LuaSettings:reset(table)
|
||||
self.data = table
|
||||
return self
|
||||
end
|
||||
|
||||
--- Writes settings to disk.
|
||||
@@ -143,6 +150,7 @@ function LuaSettings:flush()
|
||||
f_out:write("\n")
|
||||
f_out:close()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Closes settings file.
|
||||
@@ -155,6 +163,7 @@ function LuaSettings:purge()
|
||||
if self.file then
|
||||
os.remove(self.file)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
return LuaSettings
|
||||
|
||||
@@ -299,7 +299,7 @@ function UIManager:scheduleIn(seconds, action)
|
||||
local usecs = (seconds - s) * MILLION
|
||||
when[1] = when[1] + s
|
||||
when[2] = when[2] + usecs
|
||||
if when[2] > MILLION then
|
||||
if when[2] >= MILLION then
|
||||
when[1] = when[1] + 1
|
||||
when[2] = when[2] - MILLION
|
||||
end
|
||||
|
||||
@@ -38,10 +38,7 @@ function FrontLightWidget:init()
|
||||
local powerd = Device:getPowerDevice()
|
||||
self.fl_min = powerd.fl_min
|
||||
self.fl_max = powerd.fl_max
|
||||
self.fl_cur = powerd.fl_intensity
|
||||
if self.fl_cur == nil then
|
||||
self.fl_cur = self.fl_min
|
||||
end
|
||||
self.fl_cur = powerd:frontlightIntensity()
|
||||
local steps_fl = self.fl_max - self.fl_min + 1
|
||||
self.one_step = math.ceil(steps_fl / 25)
|
||||
self.steps = math.ceil(steps_fl / self.one_step)
|
||||
|
||||
64
plugins/autofrontlight.koplugin/main.lua
Normal file
64
plugins/autofrontlight.koplugin/main.lua
Normal file
@@ -0,0 +1,64 @@
|
||||
local Device = require("device")
|
||||
|
||||
if not Device:isKindle() or
|
||||
(Device.model ~= "KindleVoyage" and Device.model ~= "KindleOasis") then
|
||||
return { disabled = true, }
|
||||
end
|
||||
|
||||
local DataStorage = require("datastorage")
|
||||
local LuaSettings = require("luasettings")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local logger = require("logger")
|
||||
|
||||
local AutoFrontlight = {
|
||||
settings = LuaSettings:open(DataStorage:getSettingsDir() .. "/autofrontlight.lua"),
|
||||
settings_id = 0,
|
||||
enabled = false,
|
||||
}
|
||||
|
||||
function AutoFrontlight:_schedule()
|
||||
if not self.enabled then
|
||||
logger.dbg("AutoFrontlight:_schedule() is disabled")
|
||||
return
|
||||
end
|
||||
|
||||
local settings_id = self.settings_id
|
||||
logger.dbg("AutoFrontlight:_schedule() @ ", os.time(), ", it should be executed at ", os.time() + 1)
|
||||
UIManager:scheduleIn(1, function()
|
||||
self:_action(settings_id)
|
||||
self:_schedule(self.settings_id)
|
||||
end)
|
||||
end
|
||||
|
||||
function AutoFrontlight:_action(settings_id)
|
||||
if settings_id ~= self.settings_id then
|
||||
logger.dbg("AutoFrontlight:_action(): registered settings_id ",
|
||||
settings_id,
|
||||
" does not equal to current one ",
|
||||
self.settings_id)
|
||||
return
|
||||
end
|
||||
logger.dbg("AutoFrontlight:_action() @ ", os.time())
|
||||
if Device:ambientBrightnessLevel() <= 1 then
|
||||
logger.dbg("AutoFrontlight: going to turn on frontlight")
|
||||
Device:getPowerDevice():turnOnFrontlight()
|
||||
else
|
||||
logger.dbg("AutoFrontlight: going to turn off frontlight")
|
||||
Device:getPowerDevice():turnOffFrontlight()
|
||||
end
|
||||
end
|
||||
|
||||
function AutoFrontlight:init()
|
||||
self.enabled = not self.settings:nilOrFalse("enable")
|
||||
logger.dbg("AutoFrontlight:init() self.enabled: ", self.enabled)
|
||||
self:_schedule()
|
||||
end
|
||||
|
||||
AutoFrontlight:init()
|
||||
|
||||
local AutoFrontlightWidget = WidgetContainer:new{
|
||||
name = "AutoFrontlight",
|
||||
}
|
||||
|
||||
return AutoFrontlightWidget
|
||||
48
spec/autofrontlight_spec.lua
Normal file
48
spec/autofrontlight_spec.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
describe("AutoFrontlight widget tests", function()
|
||||
local Device, MockTime
|
||||
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
package.unloadAll()
|
||||
|
||||
Device = require("device/generic/device"):new()
|
||||
Device.brightness = 0
|
||||
Device.hasFrontlight = function() return true end
|
||||
Device.powerd = require("device/generic/powerd"):new{
|
||||
frontlight = 0,
|
||||
}
|
||||
Device.powerd.frontlightIntensityHW = function()
|
||||
return 2
|
||||
end
|
||||
Device.powerd.setIntensityHW = function(self, intensity)
|
||||
self.frontlight = intensity
|
||||
end
|
||||
Device.ambientBrightnessLevel = function(self)
|
||||
return self.brightness
|
||||
end
|
||||
|
||||
MockTime = require("mock_time")
|
||||
MockTime:install()
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
MockTime:uninstall()
|
||||
package.unloadAll()
|
||||
end)
|
||||
|
||||
it("should automatically turn on or off frontlight", function()
|
||||
local UIManager = require("ui/uimanager")
|
||||
Device.brightness = 0
|
||||
MockTime:increase(2)
|
||||
assert.are.equal(Device:getPowerDevice().frontlight, 2)
|
||||
Device.brightness = 1
|
||||
MockTime:increase(2)
|
||||
assert.are.equal(Device:getPowerDevice().frontlight, 2)
|
||||
Device.brightness = 2
|
||||
MockTime:increase(2)
|
||||
assert.are.equal(Device:getPowerDevice().frontlight, 0)
|
||||
Device.brightness = 3
|
||||
MockTime:increase(2)
|
||||
assert.are.equal(Device:getPowerDevice().frontlight, 0)
|
||||
end)
|
||||
end)
|
||||
73
spec/unit/autofrontlight_spec.lua
Normal file
73
spec/unit/autofrontlight_spec.lua
Normal file
@@ -0,0 +1,73 @@
|
||||
describe("AutoFrontlight widget tests", function()
|
||||
local Device, PowerD, MockTime
|
||||
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
package.unloadAll()
|
||||
|
||||
MockTime = require("mock_time")
|
||||
MockTime:install()
|
||||
|
||||
PowerD = require("device/generic/powerd"):new{
|
||||
frontlight = 0,
|
||||
}
|
||||
PowerD.frontlightIntensityHW = function()
|
||||
return 2
|
||||
end
|
||||
PowerD.setIntensityHW = function(self, intensity)
|
||||
self.frontlight = intensity
|
||||
end
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
MockTime:uninstall()
|
||||
package.unloadAll()
|
||||
end)
|
||||
|
||||
before_each(function()
|
||||
Device = require("device")
|
||||
Device.isKindle = function() return true end
|
||||
Device.model = "KindleVoyage"
|
||||
Device.brightness = 0
|
||||
Device.hasFrontlight = function() return true end
|
||||
Device.powerd = PowerD:new{
|
||||
device = Device,
|
||||
}
|
||||
Device.ambientBrightnessLevel = function(self)
|
||||
return self.brightness
|
||||
end
|
||||
Device.input.waitEvent = function() end
|
||||
end)
|
||||
|
||||
it("should automatically turn on or off frontlight", function()
|
||||
local UIManager = require("ui/uimanager")
|
||||
UIManager._run_forever = true
|
||||
require("luasettings"):
|
||||
open(require("datastorage"):getSettingsDir() .. "/autofrontlight.lua"):
|
||||
saveSetting("enable", "true"):
|
||||
close()
|
||||
local class = dofile("plugins/autofrontlight.koplugin/main.lua")
|
||||
local AutoFrontlight = class:new()
|
||||
AutoFrontlight:init()
|
||||
Device.brightness = 3
|
||||
MockTime:increase(1)
|
||||
UIManager:handleInput()
|
||||
assert.are.equal(0, Device:getPowerDevice().frontlight)
|
||||
Device.brightness = 0
|
||||
MockTime:increase(1)
|
||||
UIManager:handleInput()
|
||||
assert.are.equal(2, Device:getPowerDevice().frontlight)
|
||||
Device.brightness = 1
|
||||
MockTime:increase(1)
|
||||
UIManager:handleInput()
|
||||
assert.are.equal(2, Device:getPowerDevice().frontlight)
|
||||
Device.brightness = 2
|
||||
MockTime:increase(1)
|
||||
UIManager:handleInput()
|
||||
assert.are.equal(0, Device:getPowerDevice().frontlight)
|
||||
Device.brightness = 3
|
||||
MockTime:increase(1)
|
||||
UIManager:handleInput()
|
||||
assert.are.equal(0, Device:getPowerDevice().frontlight)
|
||||
end)
|
||||
end)
|
||||
@@ -50,3 +50,50 @@ function assertNotAlmostEquals(expected, actual, margin)
|
||||
.. ', received: ' .. actual
|
||||
)
|
||||
end
|
||||
|
||||
package.unload = function(module)
|
||||
if type(module) ~= "string" then return false end
|
||||
package.loaded[module] = nil
|
||||
_G[module] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
package.replace = function(name, module)
|
||||
if type(name) ~= "string" then return false end
|
||||
assert(package.unload(name))
|
||||
package.loaded[name] = module
|
||||
return true
|
||||
end
|
||||
|
||||
package.reload = function(name)
|
||||
if type(name) ~= "string" then return false end
|
||||
assert(package.unload(name))
|
||||
return require(name)
|
||||
end
|
||||
|
||||
package.unloadAll = function()
|
||||
local candidates = {
|
||||
"spec/",
|
||||
"frontend/",
|
||||
"plugins/",
|
||||
"datastorage.lua",
|
||||
"defaults.lua",
|
||||
}
|
||||
local pending = {}
|
||||
for name, _ in pairs(package.loaded) do
|
||||
local path = package.searchpath(name, package.path)
|
||||
if path ~= nil then
|
||||
for _, candidate in ipairs(candidates) do
|
||||
if path:find(candidate) == 1 then
|
||||
table.insert(pending, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, name in ipairs(pending) do
|
||||
if name ~= "commonrequire" then
|
||||
assert(package.unload(name))
|
||||
end
|
||||
end
|
||||
return #pending
|
||||
end
|
||||
|
||||
185
spec/unit/frontlight_spec.lua
Normal file
185
spec/unit/frontlight_spec.lua
Normal file
@@ -0,0 +1,185 @@
|
||||
describe("Frontlight function in PowerD", function()
|
||||
local PowerD, device
|
||||
setup(function()
|
||||
require("commonrequire")
|
||||
|
||||
PowerD = require("device/generic/powerd"):new{
|
||||
frontlight = 0,
|
||||
}
|
||||
|
||||
param = {
|
||||
fl_min = 1,
|
||||
fl_max = 5,
|
||||
device = {
|
||||
hasFrontlight = function() return true end
|
||||
},
|
||||
}
|
||||
end)
|
||||
|
||||
before_each(function()
|
||||
frontlight = param.fl_min
|
||||
stub(PowerD, "init")
|
||||
stub(PowerD, "frontlightIntensityHW")
|
||||
stub(PowerD, "setIntensityHW")
|
||||
PowerD.setIntensityHW = function(self, intensity)
|
||||
self.frontlight = intensity
|
||||
end
|
||||
spy.on(PowerD, "setIntensityHW")
|
||||
spy.on(PowerD, "turnOnFrontlightHW")
|
||||
spy.on(PowerD, "turnOffFrontlightHW")
|
||||
end)
|
||||
|
||||
it("should read frontlight intensity during initialization", function()
|
||||
PowerD.frontlightIntensityHW.returns(2)
|
||||
local p = PowerD:new(param)
|
||||
assert.are.equal(2, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOn())
|
||||
assert.stub(p.init).is_called(1)
|
||||
assert.stub(p.frontlightIntensityHW).is_called(1)
|
||||
end)
|
||||
|
||||
test_when_off = function(fl_min)
|
||||
param.fl_min = fl_min
|
||||
PowerD.frontlightIntensityHW.returns(fl_min)
|
||||
local p = PowerD:new(param)
|
||||
assert.are.equal(0, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOff())
|
||||
assert.stub(p.init).is_called(1)
|
||||
assert.stub(p.setIntensityHW).is_called(1)
|
||||
assert.are.equal(param.fl_min, p.frontlight)
|
||||
assert.stub(p.frontlightIntensityHW).is_called(1)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(0)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(1)
|
||||
|
||||
-- The intensity is param.fl_min, turnOnFrontlight() should take no effect.
|
||||
assert.is.falsy(p:turnOnFrontlight())
|
||||
assert.are.equal(0, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOff())
|
||||
assert.stub(p.setIntensityHW).is_called(1)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(0)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(1)
|
||||
|
||||
-- Same as the above one, toggleFrontlight() should also take no effect.
|
||||
assert.is.falsy(p:toggleFrontlight())
|
||||
assert.are.equal(0, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOff())
|
||||
assert.stub(p.setIntensityHW).is_called(1)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(0)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(1)
|
||||
|
||||
assert.is.truthy(p:setIntensity(2))
|
||||
assert.are.equal(2, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOn())
|
||||
assert.stub(p.setIntensityHW).is_called(2)
|
||||
assert.are.equal(2, p.frontlight)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(0)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(1)
|
||||
|
||||
assert.is.falsy(p:turnOnFrontlight())
|
||||
assert.are.equal(2, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOn())
|
||||
assert.stub(p.setIntensityHW).is_called(2)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(0)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(1)
|
||||
|
||||
assert.is.truthy(p:turnOffFrontlight())
|
||||
assert.are.equal(0, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOff())
|
||||
assert.stub(p.setIntensityHW).is_called(3)
|
||||
assert.are.equal(param.fl_min, p.frontlight)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(0)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(2)
|
||||
|
||||
assert.is.truthy(p:turnOnFrontlight())
|
||||
assert.are.equal(2, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOn())
|
||||
assert.stub(p.setIntensityHW).is_called(4)
|
||||
assert.are.equal(2, p.frontlight)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(1)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(2)
|
||||
|
||||
assert.is.truthy(p:toggleFrontlight())
|
||||
assert.are.equal(0, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOff())
|
||||
assert.stub(p.setIntensityHW).is_called(5)
|
||||
assert.are.equal(param.fl_min, p.frontlight)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(1)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(3)
|
||||
|
||||
assert.is.truthy(p:toggleFrontlight())
|
||||
assert.are.equal(2, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOn())
|
||||
assert.stub(p.setIntensityHW).is_called(6)
|
||||
assert.are.equal(2, p.frontlight)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(2)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(3)
|
||||
end
|
||||
|
||||
test_when_on = function(fl_min)
|
||||
assert(fl_min < 2)
|
||||
param.fl_min = fl_min
|
||||
PowerD.frontlightIntensityHW.returns(2)
|
||||
local p = PowerD:new(param)
|
||||
assert.are.equal(2, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOn())
|
||||
assert.stub(p.init).is_called(1)
|
||||
assert.stub(p.setIntensityHW).is_called(1)
|
||||
assert.are.equal(2, p.frontlight)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(1)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(0)
|
||||
|
||||
assert.is.falsy(p:setIntensity(2))
|
||||
assert.are.equal(2, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOn())
|
||||
assert.stub(p.setIntensityHW).is_called(1)
|
||||
assert.are.equal(2, p.frontlight)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(1)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(0)
|
||||
|
||||
assert.is.falsy(p:turnOnFrontlight())
|
||||
assert.are.equal(2, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOn())
|
||||
assert.stub(p.setIntensityHW).is_called(1)
|
||||
assert.are.equal(2, p.frontlight)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(1)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(0)
|
||||
|
||||
assert.is.truthy(p:turnOffFrontlight())
|
||||
assert.are.equal(0, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOff())
|
||||
assert.stub(p.setIntensityHW).is_called(2)
|
||||
assert.are.equal(param.fl_min, p.frontlight)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(1)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(1)
|
||||
|
||||
assert.is.truthy(p:toggleFrontlight())
|
||||
assert.are.equal(2, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOn())
|
||||
assert.stub(p.setIntensityHW).is_called(3)
|
||||
assert.are.equal(2, p.frontlight)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(2)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(1)
|
||||
|
||||
assert.is.truthy(p:toggleFrontlight())
|
||||
assert.are.equal(0, p:frontlightIntensity())
|
||||
assert.is.truthy(p:isFrontlightOff())
|
||||
assert.stub(p.setIntensityHW).is_called(4)
|
||||
assert.are.equal(param.fl_min, p.frontlight)
|
||||
assert.spy(p.turnOnFrontlightHW).is_called(2)
|
||||
assert.spy(p.turnOffFrontlightHW).is_called(2)
|
||||
end
|
||||
|
||||
it("should turn on and off frontlight when the frontlight was off", function()
|
||||
test_when_off(0)
|
||||
end)
|
||||
|
||||
it("should turn on and off frontlight when the minimum level is 1 and frontlight was off",
|
||||
function() test_when_off(1) end)
|
||||
|
||||
it("should turn on and off frontlight when the frontlight was on", function()
|
||||
test_when_on(0)
|
||||
end)
|
||||
|
||||
it("should turn on and off frontlight when the minimum level is 1 and frontlight was on",
|
||||
function() test_when_on(1) end)
|
||||
end)
|
||||
45
spec/unit/mock_time.lua
Normal file
45
spec/unit/mock_time.lua
Normal file
@@ -0,0 +1,45 @@
|
||||
local MockTime = {
|
||||
original_os_time = os.time,
|
||||
original_util_time = nil,
|
||||
value = os.time(),
|
||||
}
|
||||
|
||||
function MockTime:install()
|
||||
assert(self ~= nil)
|
||||
local util = require("ffi/util")
|
||||
if self.original_util_time == nil then
|
||||
self.original_util_time = util.gettime
|
||||
assert(self.original_util_time ~= nil)
|
||||
end
|
||||
os.time = function() return self.value end
|
||||
util.gettime = function() return self.value, 0 end
|
||||
end
|
||||
|
||||
function MockTime:uninstall()
|
||||
assert(self ~= nil)
|
||||
local util = require("ffi/util")
|
||||
os.time = self.original_os_time
|
||||
if self.original_util_time ~= nil then
|
||||
util.gettime = self.original_util_time
|
||||
end
|
||||
end
|
||||
|
||||
function MockTime:set(value)
|
||||
assert(self ~= nil)
|
||||
if type(value) ~= "number" then
|
||||
return false
|
||||
end
|
||||
self.value = math.floor(value)
|
||||
return true
|
||||
end
|
||||
|
||||
function MockTime:increase(value)
|
||||
assert(self ~= nil)
|
||||
if type(value) ~= "number" then
|
||||
return false
|
||||
end
|
||||
self.value = math.floor(self.value + value)
|
||||
return true
|
||||
end
|
||||
|
||||
return MockTime
|
||||
@@ -61,7 +61,7 @@ bar=baz
|
||||
fd:close()
|
||||
|
||||
NickelConf._set_kobo_conf_path(fn)
|
||||
assert.Equals(NickelConf.frontLightLevel.get(), 20)
|
||||
assert.Equals(NickelConf.frontLightLevel.get(), 1)
|
||||
assert.Equals(NickelConf.frontLightState.get(), nil)
|
||||
|
||||
os.remove(fn)
|
||||
|
||||
Reference in New Issue
Block a user