Files
koreader/plugins/batterystat.koplugin/main.lua
poire-z 850be52177 Keep some menus open when Tap or Hold (#4189)
TouchMenu: added options to menu items with the following defaults:
    keep_menu_open = false
    hold_keep_menu_open = true
So, default for Tap callback is to close menu, and for Hold callback
to keep menu open.
In both cases, provide the TouchMenu instance as the 1st argument to
the callback functions (instead of a refresh_menu_func I added in #3941)
so the callback can do more things, like closing, refreshing,
changing menu items text and re-ordering...

ReaderZooming: show symbol for default (like it was done for
ReaderFont, ReaderHyphenation...)
TextEditor plugin: update the previously opened files list in real
time, so the menu can be kept open and used as the TextEditor main
interface.
SSH plugin: keep menu open and update the Start/Stop state in real time
ReadTimer plugin: tried to do what feels right (but I don't use it)

Also remove forgotten cp in the move/paste file code
2018-09-04 23:55:58 +02:00

329 lines
9.0 KiB
Lua

local DataStorage = require("datastorage")
local KeyValuePage = require("ui/widget/keyvaluepage")
local LuaSettings = require("luasettings")
local PowerD = require("device"):getPowerDevice()
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local T = require("ffi/util").template
local dbg = require("dbg")
local logger = require("logger")
local util = require("ffi/util")
local _ = require("gettext")
local State = {}
function State:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
if o.percentage == nil or o.timestamp == nil then
o.percentage = PowerD:getCapacityHW()
o.timestamp = os.time()
end
return o
end
function State:toString()
return string.format("{%d @ %s}", self.percentage, os.date("%c", self.timestamp))
end
local Usage = {}
function Usage:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
if o.percentage == nil or o.time == nil then
o.percentage = 0
o.time = 0
end
return o
end
function Usage:append(state)
local curr = State:new()
self.percentage = self.percentage + (state.percentage - curr.percentage)
self.time = self.time + os.difftime(curr.timestamp - state.timestamp)
end
function Usage:minutes()
return self.time / 60
end
function Usage:hours()
return self:minutes() / 60
end
function Usage:percentagePerHour()
if self.time == 0 then
return 0
else
return self.percentage / self:hours()
end
end
function Usage:remainingHours()
local curr = State:new()
return curr.percentage / self:percentagePerHour()
end
function Usage:chargingHours()
local curr = State:new()
return (curr.percentage - 100) / self:percentagePerHour()
end
local function shorten(number)
return string.format("%.2f", number);
end
function Usage:dump(kv_pairs)
table.insert(kv_pairs, {_(" Consumed %"), shorten(self.percentage)})
table.insert(kv_pairs, {_(" Total minutes"), shorten(self:minutes())})
table.insert(kv_pairs, {_(" % per hour"), shorten(self:percentagePerHour())})
end
function Usage:dumpRemaining(kv_pairs)
table.insert(kv_pairs, {_(" Estimated remaining hours"), shorten(self:remainingHours())})
end
function Usage:dumpCharging(kv_pairs)
table.insert(kv_pairs, {_(" Estimated hours for charging"), shorten(self:chargingHours())})
end
local BatteryStat = {
settings = LuaSettings:open(DataStorage:getSettingsDir() .. "/batterstat.lua"),
dump_file = util.realpath(DataStorage:getDataDir()) .. "/batterystat.log",
debugging = false,
kv_page = nil,
}
function BatteryStat:init()
self.charging = Usage:new(self.settings:readSetting("charging"))
self.discharging = Usage:new(self.settings:readSetting("discharging"))
self.awake = Usage:new(self.settings:readSetting("awake"))
self.sleeping = Usage:new(self.settings:readSetting("sleeping"))
-- Note: these fields are not the "real" timestamp and battery usage, but
-- the unaccumulated values.
self.charging_state = State:new(self.settings:readSetting("charging_state"))
self.awake_state = State:new(self.settings:readSetting("awake_state"))
-- Whether the device was suspending before current timestamp.
self.was_suspending = false
-- Whether the device was charging before current timestamp.
self.was_charging = PowerD:isCharging()
if self.debugging then
self.debugOutput = self._debugOutput
else
self.debugOutput = function() end
end
end
function BatteryStat:onFlushSettings()
self.settings:reset({
charging = self.charging,
discharging = self.discharging,
awake = self.awake,
sleeping = self.sleeping,
charging_state = self.charging_state,
awake_state = self.awake_state,
})
self.settings:flush()
end
function BatteryStat:accumulate()
if self.was_suspending then
-- Suspending to awake.
self.sleeping:append(self.awake_state)
else
-- Awake to suspending, time between self.awake_state and now should belong to awake.
self.awake:append(self.awake_state)
end
if self.was_charging then
-- Decharging to charging.
self.charging:append(self.charging_state)
else
self.discharging:append(self.charging_state)
end
self.awake_state = State:new()
self.charging_state = State:new()
end
function BatteryStat:dumpOrLog(content)
local file = io.open(self.dump_file, "a")
if file then
file:write(content .. "\n")
file:close()
else
logger.warn("Failed to dump output ", content, " into ", self.dump_file )
end
end
function BatteryStat:_debugOutput(event)
self:dumpOrLog(event .. " @ " .. State:new():toString() ..
", awake_state " .. self.awake_state:toString() ..
", charging_state " .. self.charging_state:toString())
end
function BatteryStat:onSuspend()
self:debugOutput("onSuspend")
if not self.was_suspending then
self:accumulate()
end
self.was_suspending = true
end
function BatteryStat:onResume()
self:debugOutput("onResume")
if self.was_suspending then
self:accumulate()
end
self.was_suspending = false
end
function BatteryStat:onCharging()
self:debugOutput("onCharging")
if not self.was_charging then
self:reset(true, false)
self:accumulate()
end
self.was_charging = true
end
function BatteryStat:onNotCharging()
self:debugOutput("onNotCharging")
if self.was_charging then
self:reset(false, true)
self:accumulate()
end
self.was_charging = false
end
function BatteryStat:showStatistics()
self:accumulate()
local kv_pairs = self:dump()
table.insert(kv_pairs, "----------")
table.insert(kv_pairs, {_("Historical records are dumped to"), ""})
table.insert(kv_pairs, {self.dump_file, ""})
table.insert(kv_pairs, "----------")
table.insert(kv_pairs, {_("If you would like to reset the data,"), "",
callback = function()
self:resetAll()
self:restart()
end})
table.insert(kv_pairs, {_("please tap here."), "",
callback = function()
self:resetAll()
self:restart()
end})
self.kv_page = KeyValuePage:new{
title = _("Battery statistics"),
kv_pairs = kv_pairs,
}
UIManager:show(self.kv_page)
end
function BatteryStat:reset(withCharging, withDischarging)
self:dumpToText()
self.awake = Usage:new()
self.sleeping = Usage:new()
if withCharging then
self.charging = Usage:new()
end
if withDischarging then
self.discharging = Usage:new()
end
end
function BatteryStat:resetAll()
self:reset(true, true)
self.charging_state = State:new()
self.awake_state = State:new()
end
function BatteryStat:restart()
dbg.dassert(self.kv_page ~= nil)
UIManager:close(self.kv_page)
self:showStatistics()
end
function BatteryStat:dumpToText()
local kv_pairs = self:dump()
local content = T(_("Dump at %1"), os.date("%c"))
for _, pair in ipairs(kv_pairs) do
content = content .. "\n" .. pair[1]
if pair[2] ~= nil and pair[2] ~= "" then
content = content .. "\t" .. pair[2]
end
end
self:dumpOrLog(content .. "\n-=-=-=-=-=-\n")
end
function BatteryStat:dump()
local kv_pairs = {}
table.insert(kv_pairs, {_("Awake since last charge"), ""})
self.awake:dump(kv_pairs)
self.awake:dumpRemaining(kv_pairs)
table.insert(kv_pairs, {_("Sleeping since last charge"), ""})
self.sleeping:dump(kv_pairs)
self.sleeping:dumpRemaining(kv_pairs)
table.insert(kv_pairs, {_("During last charge"), ""})
self.charging:dump(kv_pairs)
self.charging:dumpCharging(kv_pairs)
table.insert(kv_pairs, {_("Since last charge"), ""})
self.discharging:dump(kv_pairs)
self.discharging:dumpRemaining(kv_pairs)
return kv_pairs
end
BatteryStat:init()
local BatteryStatWidget = WidgetContainer:new{
name = "batterystat",
}
function BatteryStatWidget:init()
-- self.ui is nil in test cases.
if not self.ui or not self.ui.menu then return end
self.ui.menu:registerToMainMenu(self)
end
function BatteryStatWidget:addToMainMenu(menu_items)
menu_items.battery_statistics = {
text = _("Battery statistics"),
keep_menu_open = true,
callback = function()
BatteryStat:showStatistics()
end,
}
end
function BatteryStatWidget:onFlushSettings()
BatteryStat:onFlushSettings()
end
function BatteryStatWidget:onSuspend()
BatteryStat:onSuspend()
end
function BatteryStatWidget:onResume()
BatteryStat:onResume()
end
function BatteryStatWidget:onCharging()
BatteryStat:onCharging()
end
function BatteryStatWidget:onNotCharging()
BatteryStat:onNotCharging()
end
-- Test only
function BatteryStatWidget:stat()
return BatteryStat
end
return BatteryStatWidget