Files
bookhoard.koplugin/main.lua
T

975 lines
32 KiB
Lua

local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device")
local Event = require("ui/event")
local InfoMessage = require("ui/widget/infomessage")
local InputDialog = require("ui/widget/inputdialog")
local Math = require("optmath")
local NetworkMgr = require("ui/network/manager")
local SpinWidget = require("ui/widget/spinwidget")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local json = require("json")
local logger = require("logger")
local sha2 = require("ffi/sha2")
local time = require("ui/time")
local util = require("util")
local T = require("ffi/util").template
local _ = require("gettext")
local BookhoardAPI = require("BookhoardAPI")
local SYNC_STRATEGY = {
PROMPT = 1,
SILENT = 2,
DISABLE = 3,
}
local SYNC_MODE = {
IMMEDIATE = "immediate",
CHECKPOINT = "checkpoint",
}
local API_CALL_DEBOUNCE_DELAY = time.s(25)
local PERIODIC_PUSH_DELAY = 10
local sha256hex = sha2.sha256hex or sha2.sha256
local Bookhoard = WidgetContainer:extend({
name = "bookhoard",
is_doc_only = false,
title = _("Bookhoard Server"),
settings_key = "bookhoard",
push_timestamp = nil,
pull_timestamp = nil,
page_update_counter = nil,
last_page = nil,
periodic_push_task = nil,
periodic_push_scheduled = nil,
registration_poll_scheduled = nil,
settings = nil,
})
Bookhoard.default_settings = {
server_url = nil,
auth_token = nil,
device_id = nil,
auto_sync = false,
pages_before_update = 50,
sync_forward = SYNC_STRATEGY.PROMPT,
sync_backward = SYNC_STRATEGY.DISABLE,
sync_progress = true,
sync_bookmarks = true,
sync_highlights = true,
sync_notes = true,
sync_mode = SYNC_MODE.IMMEDIATE,
sync_endpoints = nil,
}
function Bookhoard:init()
self.push_timestamp = 0
self.pull_timestamp = 0
self.page_update_counter = 0
self.last_page = -1
self.periodic_push_scheduled = false
self.registration_poll_scheduled = false
self.periodic_push_task = function()
self.periodic_push_scheduled = false
self.page_update_counter = 0
self:updateProgress(false, false)
end
self.settings = G_reader_settings:readSetting(self.settings_key, self.default_settings)
if self.settings.auto_sync
and Device:hasSeamlessWifiToggle()
and G_reader_settings:readSetting("wifi_enable_action") ~= "turn_on" then
self.settings.auto_sync = false
logger.warn("Bookhoard: auto-sync disabled because wifi_enable_action is not turn_on")
end
self.ui.menu:registerToMainMenu(self)
end
function Bookhoard:onReaderReady()
if self.settings.auto_sync then
UIManager:nextTick(function()
self:getProgress(true, false)
end)
end
self:registerEvents()
self.last_page = self.ui:getCurrentPage()
end
function Bookhoard:registerEvents()
if self.settings.auto_sync then
self.onCloseDocument = self._onCloseDocument
self.onPageUpdate = self._onPageUpdate
self.onResume = self._onResume
self.onSuspend = self._onSuspend
self.onNetworkConnected = self._onNetworkConnected
self.onNetworkDisconnecting = self._onNetworkDisconnecting
else
self.onCloseDocument = nil
self.onPageUpdate = nil
self.onResume = nil
self.onSuspend = nil
self.onNetworkConnected = nil
self.onNetworkDisconnecting = nil
end
end
function Bookhoard:getAPI()
return BookhoardAPI:new({
server_url = self.settings.server_url,
auth_token = self.settings.auth_token,
})
end
function Bookhoard:isConfigured()
return self.settings.server_url and self.settings.auth_token
end
function Bookhoard:getSyncPeriod()
if not self.settings.auto_sync then
return _("Not available")
end
local period = self.settings.pages_before_update
if period and period > 0 then
return period
end
return _("Never")
end
function Bookhoard:addToMainMenu(menu_items)
menu_items.bookhoard_sync = {
text = _("Bookhoard sync"),
sorting_hint = "tools",
sub_item_table = self:buildMainMenu(),
}
end
function Bookhoard:buildMainMenu()
local items = {}
table.insert(items, {
text = _("Server URL"),
keep_menu_open = true,
tap_input_func = function()
return {
title = _("Bookhoard server URL"),
input = self.settings.server_url or "http://",
callback = function(input)
self.settings.server_url = input ~= "" and input or nil
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
}
end,
})
if self:isConfigured() then
table.insert(items, {
text = _("Device info"),
keep_menu_open = true,
callback = function()
UIManager:show(InfoMessage:new{
text = T(_("Device ID: %1\nServer: %2"),
self.settings.device_id or _("unknown"),
self.settings.server_url),
})
end,
})
table.insert(items, {
text = _("Disconnect"),
keep_menu_open = true,
callback = function()
UIManager:show(ConfirmBox:new{
text = _("Disconnect from Bookhoard server?"),
ok_text = _("Disconnect"),
ok_callback = function()
self.settings.auth_token = nil
self.settings.device_id = nil
self.settings.sync_endpoints = nil
self.settings.auto_sync = false
G_reader_settings:saveSetting(self.settings_key, self.settings)
self:registerEvents()
UIManager:askForRestart()
end,
})
end,
separator = true,
})
else
table.insert(items, {
text = _("Register device"),
keep_menu_open = true,
callback = function()
self:startRegistration()
end,
separator = true,
})
end
table.insert(items, {
text = _("Automatically keep documents in sync"),
checked_func = function() return self.settings.auto_sync end,
help_text = _([[This may lead to prompts about toggling WiFi on document close and suspend/resume, depending on your device's connectivity.]]),
callback = function()
self:toggleAutoSync()
end,
})
table.insert(items, {
text_func = function()
return T(_("Periodically sync every # pages (%1)"), self:getSyncPeriod())
end,
enabled_func = function() return self.settings.auto_sync end,
keep_menu_open = true,
callback = function(touchmenu_instance)
local spin = SpinWidget:new{
text = _([[Number of page turns between progress updates. Set to 0 to disable.]]),
value = self.settings.pages_before_update or 0,
value_min = 0,
value_max = 999,
value_step = 1,
value_hold_step = 10,
ok_text = _("Set"),
title_text = _("Pages before update"),
default_value = 50,
callback = function(spin)
self.settings.pages_before_update = spin.value > 0 and spin.value or nil
G_reader_settings:saveSetting(self.settings_key, self.settings)
if touchmenu_instance then touchmenu_instance:updateItems() end
end,
}
UIManager:show(spin)
end,
})
table.insert(items, {
text_func = function()
return T(_("Sync mode (%1)"),
self.settings.sync_mode == SYNC_MODE.IMMEDIATE and _("immediate") or _("checkpoint"))
end,
sub_item_table = {
{
text = _("Immediate"),
checked_func = function()
return self.settings.sync_mode == SYNC_MODE.IMMEDIATE
end,
callback = function()
self.settings.sync_mode = SYNC_MODE.IMMEDIATE
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Checkpoint"),
checked_func = function()
return self.settings.sync_mode == SYNC_MODE.CHECKPOINT
end,
callback = function()
self.settings.sync_mode = SYNC_MODE.CHECKPOINT
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
},
separator = true,
})
table.insert(items, {
text = _("Sync behavior"),
sub_item_table = {
{
text_func = function()
return T(_("Sync to a newer state (%1)"),
self:getStrategyName(self.settings.sync_forward))
end,
sub_item_table = {
{
text = _("Silently"),
checked_func = function()
return self.settings.sync_forward == SYNC_STRATEGY.SILENT
end,
callback = function()
self.settings.sync_forward = SYNC_STRATEGY.SILENT
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Prompt"),
checked_func = function()
return self.settings.sync_forward == SYNC_STRATEGY.PROMPT
end,
callback = function()
self.settings.sync_forward = SYNC_STRATEGY.PROMPT
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Never"),
checked_func = function()
return self.settings.sync_forward == SYNC_STRATEGY.DISABLE
end,
callback = function()
self.settings.sync_forward = SYNC_STRATEGY.DISABLE
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
},
},
{
text_func = function()
return T(_("Sync to an older state (%1)"),
self:getStrategyName(self.settings.sync_backward))
end,
sub_item_table = {
{
text = _("Silently"),
checked_func = function()
return self.settings.sync_backward == SYNC_STRATEGY.SILENT
end,
callback = function()
self.settings.sync_backward = SYNC_STRATEGY.SILENT
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Prompt"),
checked_func = function()
return self.settings.sync_backward == SYNC_STRATEGY.PROMPT
end,
callback = function()
self.settings.sync_backward = SYNC_STRATEGY.PROMPT
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Never"),
checked_func = function()
return self.settings.sync_backward == SYNC_STRATEGY.DISABLE
end,
callback = function()
self.settings.sync_backward = SYNC_STRATEGY.DISABLE
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
},
},
},
separator = true,
})
table.insert(items, {
text = _("What to sync"),
sub_item_table = {
{
text = _("Reading progress"),
checked_func = function() return self.settings.sync_progress end,
callback = function()
self.settings.sync_progress = not self.settings.sync_progress
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Bookmarks"),
checked_func = function() return self.settings.sync_bookmarks end,
callback = function()
self.settings.sync_bookmarks = not self.settings.sync_bookmarks
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Highlights"),
checked_func = function() return self.settings.sync_highlights end,
callback = function()
self.settings.sync_highlights = not self.settings.sync_highlights
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
{
text = _("Notes"),
checked_func = function() return self.settings.sync_notes end,
callback = function()
self.settings.sync_notes = not self.settings.sync_notes
G_reader_settings:saveSetting(self.settings_key, self.settings)
end,
},
},
separator = true,
})
table.insert(items, {
text = _("Sync now"),
enabled_func = function() return self:isConfigured() end,
callback = function()
self:updateProgress(true, true)
self:getProgress(true, true)
end,
})
table.insert(items, {
text = _("Push progress from this device"),
enabled_func = function() return self:isConfigured() end,
callback = function()
self:updateProgress(true, true)
end,
})
table.insert(items, {
text = _("Pull progress from server"),
enabled_func = function() return self:isConfigured() end,
callback = function()
self:getProgress(true, true)
end,
separator = true,
})
table.insert(items, {
text = _("Setup OPDS catalog"),
keep_menu_open = true,
callback = function()
if not self.settings.server_url or not self.settings.device_id then
UIManager:show(InfoMessage:new{
text = _("Please configure and register your device first."),
timeout = 3,
})
return
end
local opds_url = self.settings.server_url
.. "/opds/devices/" .. self.settings.device_id .. "/catalog"
UIManager:show(InfoMessage:new{
text = T(_("Add this URL as an OPDS catalog in KOReader:\n\n%1\n\nGo to Home → + → OPDS Catalog to add it."), opds_url),
})
end,
})
return items
end
function Bookhoard:getStrategyName(strategy)
if strategy == SYNC_STRATEGY.PROMPT then
return _("Prompt")
elseif strategy == SYNC_STRATEGY.SILENT then
return _("Auto")
else
return _("Disable")
end
end
function Bookhoard:toggleAutoSync()
if not self.settings.auto_sync
and Device:hasSeamlessWifiToggle()
and G_reader_settings:readSetting("wifi_enable_action") ~= "turn_on" then
UIManager:show(InfoMessage:new{
text = _("Set 'Action when Wi-Fi is off' to 'turn on' in Network settings to enable auto sync."),
})
return
end
self.settings.auto_sync = not self.settings.auto_sync
self:registerEvents()
G_reader_settings:saveSetting(self.settings_key, self.settings)
if self.settings.auto_sync and self.ui.doc_settings then
self:getProgress(true, true)
end
end
function Bookhoard:startRegistration()
if not self.settings.server_url or self.settings.server_url == "" then
UIManager:show(InfoMessage:new{
text = _("Please set your server URL first."),
timeout = 3,
})
return
end
if NetworkMgr:willRerunWhenOnline(function() self:startRegistration() end) then
return
end
local device_name = Device.model or "KOReader Device"
local device_identifier = Device:info() or device_name
UIManager:show(InfoMessage:new{
text = _("Registering device…"),
timeout = 1,
})
UIManager:scheduleIn(0.5, function()
local api = BookhoardAPI:new({ server_url = self.settings.server_url })
local ok, result = api:registerDevice(device_name, device_identifier)
if not ok then
UIManager:show(InfoMessage:new{
text = T(_("Registration failed: %1"),
result and result.error or _("unknown error")),
})
return
end
self.registration_id = result.registration_id
UIManager:show(InfoMessage:new{
text = T(_("Device registered on server.\n\nOpen your Bookhoard web UI and go to:\n%1/devices\n\nApprove this device in the \"Pending Device Registrations\" section.\n\nWaiting for approval…"), self.settings.server_url),
})
self:startRegistrationPoll()
end)
end
function Bookhoard:startRegistrationPoll()
if self.registration_poll_scheduled then return end
self.registration_poll_scheduled = true
local function poll()
self.registration_poll_scheduled = false
if not self.registration_id then return end
local api = BookhoardAPI:new({ server_url = self.settings.server_url })
local ok, result = api:checkRegistrationStatus(self.registration_id)
if not ok then
if result and result.status == 410 then
self.registration_id = nil
UIManager:show(InfoMessage:new{
text = _("Registration expired. Please try again."),
})
return
end
self.registration_poll_scheduled = true
UIManager:scheduleIn(3, poll)
return
end
if result.status == "approved" then
self.registration_id = nil
self.settings.auth_token = result.auth_token
self.settings.device_id = result.device_id and tostring(result.device_id) or nil
self.settings.sync_endpoints = result.sync_endpoints
G_reader_settings:saveSetting(self.settings_key, self.settings)
self:registerEvents()
UIManager:closeAllDialogs()
UIManager:show(InfoMessage:new{
text = _("Device registered successfully!"),
timeout = 3,
})
elseif result.status == "pending" then
self.registration_poll_scheduled = true
UIManager:scheduleIn(3, poll)
else
self.registration_id = nil
UIManager:show(InfoMessage:new{
text = T(_("Registration %1"), result.status or _("failed")),
})
end
end
UIManager:scheduleIn(3, poll)
end
function Bookhoard:getLastPercent()
if self.ui.document.info.has_pages then
return Math.roundPercent(self.ui.paging:getLastPercent())
else
return Math.roundPercent(self.ui.rolling:getLastPercent())
end
end
function Bookhoard:getLastProgress()
if self.ui.document.info.has_pages then
return self.ui.paging:getLastProgress()
else
return self.ui.rolling:getLastProgress()
end
end
function Bookhoard:getFileSHA256()
local cached = self.ui.doc_settings:readSetting("bookhoard_sha256")
if cached then return cached end
local file = io.open(self.ui.document.file, "rb")
if not file then return nil end
local data = file:read("*a")
file:close()
local hash = sha256hex(data)
if hash then
self.ui.doc_settings:saveSetting("bookhoard_sha256", hash)
end
return hash
end
function Bookhoard:getBookhoardUUID()
if not self.ui.doc_settings then return nil end
return self.ui.doc_settings:readSetting("bookhoard_uuid")
end
function Bookhoard:collectBookData()
local props = self.ui.doc_props
local file_path = self.ui.document.file
local title = props.display_title or ""
local authors = ""
if props.authors then
authors = props.authors
end
local file_sha256 = self:getFileSHA256()
local book_uuid = self:getBookhoardUUID()
local percentage = self:getLastPercent()
local progress = self:getLastProgress()
local page = self.ui:getCurrentPage()
local total_pages = self.ui.document:getPageCount()
local chapter = ""
if self.ui.toc and self.ui.toc.getTocTitleOfCurrentPage then
chapter = self.ui.toc:getTocTitleOfCurrentPage() or ""
end
local book_data = {
uuid = book_uuid,
sha256 = file_sha256,
title = title,
authors = authors,
percentage = percentage,
chapter = chapter,
epubcfi = progress,
page = page,
total_pages = total_pages,
file_path = file_path,
device_info = {
koreader_version = require("version"):getCurrentRevision(),
device_model = Device.model,
},
}
return book_data
end
function Bookhoard:collectAnnotations()
local bookmarks = {}
local highlights = {}
local notes = {}
if not self.ui.bookmark then
return bookmarks, highlights, notes
end
local all_bookmarks = self.ui.bookmark.bookmarks
if not all_bookmarks then
return bookmarks, highlights, notes
end
local file_sha256 = self:getFileSHA256()
local total_pages = self.ui.document:getPageCount()
for _, bm in ipairs(all_bookmarks) do
local page_num = bm.page
if type(page_num) == "string" and self.ui.document.info.has_pages == false then
page_num = self.ui.document:getPageFromXPointer(page_num)
end
page_num = tonumber(page_num) or 0
local percentage = total_pages > 0 and (page_num / total_pages) or 0
local chapter = bm.chapter or ""
local entry = {
chapter = chapter,
datetime = bm.datetime or "",
notes = bm.notes or "",
pos0 = bm.pos0 or "",
pos1 = bm.pos1 or "",
page = tostring(bm.page or ""),
text = bm.text or "",
percentage = Math.roundPercent(percentage),
book_sha256 = file_sha256,
}
local has_text = bm.text and bm.text ~= ""
local has_notes = bm.notes and bm.notes ~= ""
if has_text and has_notes then
entry.type = "note"
table.insert(notes, entry)
elseif has_text then
entry.type = "highlight"
if bm.color then
entry.color = bm.color
end
table.insert(highlights, entry)
else
entry.type = "bookmark"
table.insert(bookmarks, entry)
end
end
return bookmarks, highlights, notes
end
function Bookhoard:syncToProgress(progress, percentage)
logger.dbg("Bookhoard: sync to progress", progress, percentage)
if self.ui.document.info.has_pages then
self.ui:handleEvent(Event:new("GotoPage", tonumber(progress)))
else
self.ui:handleEvent(Event:new("GotoXPointer", progress))
end
end
function Bookhoard:updateProgress(ensure_networking, interactive)
if not self:isConfigured() then
if interactive then
UIManager:show(InfoMessage:new{
text = _("Please configure and register your device first."),
timeout = 3,
})
end
return
end
if not self.settings.sync_progress then return end
local now = UIManager:getElapsedTimeSinceBoot()
if not interactive and now - self.push_timestamp <= API_CALL_DEBOUNCE_DELAY then
return
end
if ensure_networking
and NetworkMgr:willRerunWhenOnline(function() self:updateProgress(ensure_networking, interactive) end) then
return
end
UIManager:scheduleIn(0.5, function()
self:_doUpdateProgress(interactive)
end)
self.push_timestamp = now
end
function Bookhoard:_doUpdateProgress(interactive)
local book_data = self:collectBookData()
if not book_data then return end
if self.settings.sync_bookmarks or self.settings.sync_highlights or self.settings.sync_notes then
local bm, hl, nt = self:collectAnnotations()
book_data.bookmarks = self.settings.sync_bookmarks and bm or {}
book_data.highlights = self.settings.sync_highlights and hl or {}
book_data.notes = self.settings.sync_notes and nt or {}
else
book_data.bookmarks = {}
book_data.highlights = {}
book_data.notes = {}
end
local api = self:getAPI()
local ok, result = api:syncProgress(book_data, self.settings.sync_mode)
UIManager:nextTick(function()
if ok then
logger.dbg("Bookhoard: progress pushed successfully")
if result and result.device_updated and result.device_updated.uuid then
self.ui.doc_settings:saveSetting("bookhoard_uuid", result.device_updated.uuid)
self.ui.doc_settings:flush()
end
if interactive then
UIManager:show(InfoMessage:new{
text = _("Progress has been pushed."),
timeout = 3,
})
end
else
logger.warn("Bookhoard: failed to push progress")
if interactive then
UIManager:show(InfoMessage:new{
text = _("Failed to push progress. Check your network connection."),
timeout = 3,
})
end
end
end)
end
function Bookhoard:getProgress(ensure_networking, interactive)
if not self:isConfigured() then
if interactive then
UIManager:show(InfoMessage:new{
text = _("Please configure and register your device first."),
timeout = 3,
})
end
return
end
if not self.settings.sync_progress then return end
local now = UIManager:getElapsedTimeSinceBoot()
if not interactive and now - self.pull_timestamp <= API_CALL_DEBOUNCE_DELAY then
return
end
if ensure_networking
and NetworkMgr:willRerunWhenOnline(function() self:getProgress(ensure_networking, interactive) end) then
return
end
local book_uuid = self:getBookhoardUUID()
if not book_uuid then
if interactive then
UIManager:show(InfoMessage:new{
text = _("No Bookhoard UUID for this document. Push progress first."),
timeout = 3,
})
end
return
end
UIManager:scheduleIn(0.5, function()
self:_doGetProgress(interactive)
end)
self.pull_timestamp = now
end
function Bookhoard:_doGetProgress(interactive)
local book_uuid = self:getBookhoardUUID()
if not book_uuid then return end
local api = self:getAPI()
local ok, result = api:getMetadata(book_uuid)
UIManager:nextTick(function()
if not ok or not result then
if interactive then
UIManager:show(InfoMessage:new{
text = _("Failed to pull progress."),
timeout = 3,
})
end
return
end
if not result.progress then
if interactive then
UIManager:show(InfoMessage:new{
text = _("No progress found for this document."),
timeout = 3,
})
end
return
end
local progress = result.progress
local percentage = self:getLastPercent()
local server_percentage = progress.percentage or 0
if percentage == server_percentage then
if interactive then
UIManager:show(InfoMessage:new{
text = _("Progress is already synchronized."),
timeout = 3,
})
end
return
end
local self_older = server_percentage > percentage
if self_older then
if self.settings.sync_forward == SYNC_STRATEGY.SILENT then
self:syncToProgress(progress.epubcfi or progress.page, server_percentage)
self:_showSyncedMessage()
elseif self.settings.sync_forward == SYNC_STRATEGY.PROMPT then
UIManager:show(ConfirmBox:new{
text = T(_("Sync to newer location %1%% from server?"),
Math.round(server_percentage * 100)),
ok_callback = function()
self:syncToProgress(progress.epubcfi or progress.page, server_percentage)
end,
})
end
else
if self.settings.sync_backward == SYNC_STRATEGY.SILENT then
self:syncToProgress(progress.epubcfi or progress.page, server_percentage)
self:_showSyncedMessage()
elseif self.settings.sync_backward == SYNC_STRATEGY.PROMPT then
UIManager:show(ConfirmBox:new{
text = T(_("Sync to previous location %1%% from server?"),
Math.round(server_percentage * 100)),
ok_callback = function()
self:syncToProgress(progress.epubcfi or progress.page, server_percentage)
end,
})
end
end
end)
end
function Bookhoard:_showSyncedMessage()
UIManager:show(InfoMessage:new{
text = _("Progress has been synchronized."),
timeout = 3,
})
end
function Bookhoard:_onCloseDocument()
self.onResume = nil
self.onSuspend = nil
NetworkMgr:goOnlineToRun(function()
self:updateProgress(false, false)
end)
end
function Bookhoard:schedulePeriodicPush()
UIManager:unschedule(self.periodic_push_task)
UIManager:scheduleIn(PERIODIC_PUSH_DELAY, self.periodic_push_task)
self.periodic_push_scheduled = true
end
function Bookhoard:_onPageUpdate(page)
if page == nil then return end
if self.last_page ~= page then
self.last_page = page
self.page_update_counter = self.page_update_counter + 1
if self.periodic_push_scheduled
or (self.settings.pages_before_update
and self.page_update_counter >= self.settings.pages_before_update) then
self:schedulePeriodicPush()
end
end
end
function Bookhoard:_onResume()
if Device:hasWifiRestore() and NetworkMgr.wifi_was_on
and G_reader_settings:isTrue("auto_restore_wifi") then
return
end
UIManager:scheduleIn(1, function()
self:getProgress(true, false)
end)
end
function Bookhoard:_onSuspend()
self:updateProgress(true, false)
end
function Bookhoard:_onNetworkConnected()
UIManager:scheduleIn(0.5, function()
self:getProgress(false, false)
end)
end
function Bookhoard:_onNetworkDisconnecting()
self:updateProgress(false, false)
end
function Bookhoard:onCloseWidget()
UIManager:unschedule(self.periodic_push_task)
self.periodic_push_task = nil
end
return Bookhoard