diff --git a/frontend/apps/reader/modules/readercoptlistener.lua b/frontend/apps/reader/modules/readercoptlistener.lua index a0ac59a1f..9a8dee001 100644 --- a/frontend/apps/reader/modules/readercoptlistener.lua +++ b/frontend/apps/reader/modules/readercoptlistener.lua @@ -84,10 +84,7 @@ end function ReaderCoptListener:onCharging() self:headerRefresh() end - -function ReaderCoptListener:onNotCharging() - self:headerRefresh() -end +ReaderCoptListener.onNotCharging = ReaderCoptListener.onCharging function ReaderCoptListener:onTimeFormatChanged() self.ui.document._document:setIntProperty("window.status.clock.12hours", G_reader_settings:isTrue("twelve_hour_clock") and 1 or 0) diff --git a/frontend/apps/reader/modules/readerdictionary.lua b/frontend/apps/reader/modules/readerdictionary.lua index 54179cafb..a0f36e8a7 100644 --- a/frontend/apps/reader/modules/readerdictionary.lua +++ b/frontend/apps/reader/modules/readerdictionary.lua @@ -275,7 +275,7 @@ function ReaderDictionary:addToMainMenu(menu_items) { text = _("Enable fuzzy search"), checked_func = function() - return not self.disable_fuzzy_search == true + return self.disable_fuzzy_search ~= true end, callback = function() self.disable_fuzzy_search = not self.disable_fuzzy_search diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index a025b8202..ffb477f3b 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -2485,35 +2485,24 @@ function ReaderFooter:maybeUpdateFooter() self:onUpdateFooter(self:shouldBeRepainted()) end --- is the same as maybeUpdateFooter function ReaderFooter:onFrontlightStateChanged() - self:onUpdateFooter(self:shouldBeRepainted()) + self:maybeUpdateFooter() end +ReaderFooter.onCharging = ReaderFooter.onFrontlightStateChanged +ReaderFooter.onNotCharging = ReaderFooter.onFrontlightStateChanged function ReaderFooter:onNetworkConnected() if self.settings.wifi_status then self:maybeUpdateFooter() end end - -function ReaderFooter:onNetworkDisconnected() - if self.settings.wifi_status then - self:onUpdateFooter(self.view.footer_visible) - end -end - -function ReaderFooter:onCharging() - self:maybeUpdateFooter() -end - -function ReaderFooter:onNotCharging() - self:maybeUpdateFooter() -end +ReaderFooter.onNetworkDisconnected = ReaderFooter.onNetworkConnected function ReaderFooter:onSetRotationMode() self:updateFooterContainer() self:resetLayout(true) end +ReaderFooter.onScreenResize = ReaderFooter.onSetRotationMode function ReaderFooter:onSetPageHorizMargins(h_margins) self.book_margins_footer_width = math.floor((h_margins[1] + h_margins[2])/2) @@ -2523,11 +2512,6 @@ function ReaderFooter:onSetPageHorizMargins(h_margins) end end -function ReaderFooter:onScreenResize() - self:updateFooterContainer() - self:resetLayout(true) -end - function ReaderFooter:onTimeFormatChanged() self:refreshFooter(true, true) end diff --git a/frontend/apps/reader/modules/readerwikipedia.lua b/frontend/apps/reader/modules/readerwikipedia.lua index 4c2fa0c75..5c50ea7e5 100644 --- a/frontend/apps/reader/modules/readerwikipedia.lua +++ b/frontend/apps/reader/modules/readerwikipedia.lua @@ -244,7 +244,7 @@ function ReaderWikipedia:addToMainMenu(menu_items) -- home_dir/Wikipedia/ if not G_reader_settings:readSetting("wikipedia_save_dir") then local home_dir = G_reader_settings:readSetting("home_dir") - if not home_dir or not lfs.attributes(home_dir, "mode") == "directory" then + if not home_dir or lfs.attributes(home_dir, "mode") ~= "directory" then home_dir = require("apps/filemanager/filemanagerutil").getDefaultDir() end home_dir = home_dir:gsub("^(.-)/*$", "%1") -- remove trailing slash diff --git a/frontend/device/generic/device.lua b/frontend/device/generic/device.lua index dcef3b658..05907b888 100644 --- a/frontend/device/generic/device.lua +++ b/frontend/device/generic/device.lua @@ -312,7 +312,7 @@ function Device:onPowerEvent(ev) if self:hasWifiToggle() then local network_manager = require("ui/network/manager") if network_manager:isWifiOn() then - network_manager:releaseIP() + UIManager:broadcastEvent(Event:new("NetworkDisconnecting")) network_manager:turnOffWifi() end end @@ -346,7 +346,7 @@ function Device:onPowerEvent(ev) -- because suspend will at best fail, and at worst deadlock the system if Wi-Fi is on, -- regardless of who enabled it! if network_manager:isWifiOn() then - network_manager:releaseIP() + UIManager:broadcastEvent(Event:new("NetworkDisconnecting")) network_manager:turnOffWifi() end end diff --git a/frontend/device/gesturedetector.lua b/frontend/device/gesturedetector.lua index 5eb42f565..a8f5936c8 100644 --- a/frontend/device/gesturedetector.lua +++ b/frontend/device/gesturedetector.lua @@ -436,7 +436,7 @@ function GestureDetector:probeClockSource(timev) -- Finally, BOOTTIME local boottime = time.boottime() -- NOTE: It was implemented in Linux 2.6.39, so, reject 0, which would mean it's unsupported... - if not boottime == 0 and timev >= boottime - threshold and timev <= boottime + threshold then + if boottime ~= 0 and timev >= boottime - threshold and timev <= boottime + threshold then self.clock_id = C.CLOCK_BOOTTIME logger.dbg("GestureDetector:probeClockSource: Touch event timestamps appear to use CLOCK_BOOTTIME") return diff --git a/frontend/ui/data/onetime_migration.lua b/frontend/ui/data/onetime_migration.lua index 73f83cd4e..a0865f16c 100644 --- a/frontend/ui/data/onetime_migration.lua +++ b/frontend/ui/data/onetime_migration.lua @@ -7,7 +7,7 @@ local lfs = require("libs/libkoreader-lfs") local logger = require("logger") -- Date at which the last migration snippet was added -local CURRENT_MIGRATION_DATE = 20230531 +local CURRENT_MIGRATION_DATE = 20230627 -- Retrieve the date of the previous migration, if any local last_migration_date = G_reader_settings:readSetting("last_migration_date", 0) @@ -145,14 +145,16 @@ if last_migration_date < 20210330 then if not ok or not ReaderStatistics then logger.warn("Error when loading plugins/statistics.koplugin/main.lua:", ReaderStatistics) else - local settings = G_reader_settings:readSetting("statistics", ReaderStatistics.default_settings) - -- Handle a snafu in 2021.03 that could lead to an empty settings table on fresh installs. - for k, v in pairs(ReaderStatistics.default_settings) do - if settings[k] == nil then - settings[k] = v + local settings = G_reader_settings:readSetting("statistics") + if settings then + -- Handle a snafu in 2021.03 that could lead to an empty settings table on fresh installs. + for k, v in pairs(ReaderStatistics.default_settings) do + if settings[k] == nil then + settings[k] = v + end end + G_reader_settings:saveSetting("statistics", settings) end - G_reader_settings:saveSetting("statistics", settings) end end @@ -514,5 +516,46 @@ if last_migration_date < 20230531 then end end +-- 20230627, Migrate to a full settings table, and disable KOSync's auto sync mode if wifi_enable_action is not turn_on +if last_migration_date < 20230627 then + logger.info("Performing one-time migration for 20230627") + + -- c.f., PluginLoader + local package_path = package.path + package.path = string.format("%s/?.lua;%s", "plugins/kosync.koplugin", package_path) + local ok, KOSync = pcall(dofile, "plugins/kosync.koplugin/main.lua") + package.path = package_path + if not ok or not KOSync then + logger.warn("Error when loading plugins/kosync.koplugin/main.lua:", KOSync) + else + local settings = G_reader_settings:readSetting("kosync") + if settings then + -- Make sure the table is complete + for k, v in pairs(KOSync.default_settings) do + if settings[k] == nil then + settings[k] = v + end + end + + -- Migrate the whisper_* keys + settings.sync_forward = settings.whisper_forward or KOSync.default_settings.sync_forward + settings.whisper_forward = nil + settings.sync_backward = settings.whisper_backward or KOSync.default_settings.sync_backward + settings.whisper_backward = nil + + G_reader_settings:saveSetting("kosync", settings) + end + end + + local Device = require("device") + if Device:hasWifiManager() and G_reader_settings:readSetting("wifi_enable_action") ~= "turn_on" then + local kosync = G_reader_settings:readSetting("kosync") + if kosync and kosync.auto_sync then + kosync.auto_sync = false + G_reader_settings:saveSetting("kosync", kosync) + end + end +end + -- We're done, store the current migration date G_reader_settings:saveSetting("last_migration_date", CURRENT_MIGRATION_DATE) diff --git a/frontend/ui/network/manager.lua b/frontend/ui/network/manager.lua index c77cc423c..4369c9057 100644 --- a/frontend/ui/network/manager.lua +++ b/frontend/ui/network/manager.lua @@ -32,14 +32,15 @@ end -- as quite a few things rely on it (KOSync, c.f. #5109; the network activity check, c.f., #6424). function NetworkMgr:connectivityCheck(iter, callback, widget) -- Give up after a while (restoreWifiAsync can take over 45s, so, try to cover that)... - if iter > 25 then - logger.info("Failed to restore Wi-Fi (after", iter, "iterations)!") + if iter > 180 then + logger.info("Failed to restore Wi-Fi (after", iter * 0.25, "seconds)!") self.wifi_was_on = false G_reader_settings:makeFalse("wifi_was_on") -- If we abort, murder Wi-Fi and the async script first... if Device:hasWifiManager() then os.execute("pkill -TERM restore-wifi-async.sh 2>/dev/null") end + -- We were never connected to begin with, so, no disconnecting broadcast required self:turnOffWifi() -- Handle the UI warning if it's from a beforeWifiAction... @@ -54,8 +55,8 @@ function NetworkMgr:connectivityCheck(iter, callback, widget) if self.is_wifi_on and self.is_connected then self.wifi_was_on = true G_reader_settings:makeTrue("wifi_was_on") + logger.info("Wi-Fi successfully restored (after", iter * 0.25, "seconds)!") UIManager:broadcastEvent(Event:new("NetworkConnected")) - logger.info("Wi-Fi successfully restored (after", iter, "iterations)!") -- Handle the UI & callback if it's from a beforeWifiAction... if widget then @@ -74,12 +75,12 @@ function NetworkMgr:connectivityCheck(iter, callback, widget) end end else - UIManager:scheduleIn(2, self.connectivityCheck, self, iter + 1, callback, widget) + UIManager:scheduleIn(0.25, self.connectivityCheck, self, iter + 1, callback, widget) end end function NetworkMgr:scheduleConnectivityCheck(callback, widget) - UIManager:scheduleIn(2, self.connectivityCheck, self, 1, callback, widget) + UIManager:scheduleIn(0.5, self.connectivityCheck, self, 1, callback, widget) end function NetworkMgr:init() @@ -298,6 +299,8 @@ function NetworkMgr:turnOnWifiAndWaitForConnection(callback) -- This will handle sending the proper Event, manage wifi_was_on, as well as tearing down Wi-Fi in case of failures, -- (i.e., much like getWifiToggleMenuTable). self:scheduleConnectivityCheck(callback, info) + + return info end --- This quirky internal flag is used for the rare beforeWifiAction -> afterWifiAction brackets. @@ -322,9 +325,9 @@ function NetworkMgr:beforeWifiAction(callback) local wifi_enable_action = G_reader_settings:readSetting("wifi_enable_action") if wifi_enable_action == "turn_on" then - self:turnOnWifiAndWaitForConnection(callback) + return self:turnOnWifiAndWaitForConnection(callback) else - self:promptWifiOn(callback) + return self:promptWifiOn(callback) end end @@ -344,6 +347,7 @@ function NetworkMgr:afterWifiAction(callback) callback() end elseif wifi_disable_action == "turn_off" then + UIManager:broadcastEvent(Event:new("NetworkDisconnecting")) self:turnOffWifi(callback) else self:promptWifiOff(callback) @@ -461,6 +465,53 @@ function NetworkMgr:willRerunWhenConnected(callback) return false end +-- And this one is for when you absolutely *need* to block until we're online to run something (e.g., because it runs in a finalizer). +function NetworkMgr:goOnlineToRun(callback) + if self:isOnline() then + callback() + return true + end + + -- In case we abort before the beforeWifiAction, we won't pass it the callback, but run it ourselves, + -- to avoid it firing too late (or at the very least being pinned for too long). + local info = self:beforeWifiAction() + -- We'll basically do the same but in a blocking manner... + UIManager:unschedule(self.connectivityCheck) + + local iter = 0 + while not self.is_connected do + iter = iter + 1 + if iter >= 120 then + logger.info("Failed to connect to Wi-Fi after 30s, giving up!") + self.wifi_was_on = false + G_reader_settings:makeFalse("wifi_was_on") + if info then + UIManager:close(info) + end + UIManager:show(InfoMessage:new{ text = _("Error connecting to the network") }) + self:turnOffWifi() + return false + end + ffiutil.usleep(250000) + self:queryNetworkState() + end + + -- Close the initial "Connecting..." InfoMessage from turnOnWifiAndWaitForConnection via beforeWifiAction + if info then + UIManager:close(info) + end + -- We're finally connected! + self.wifi_was_on = true + G_reader_settings:makeTrue("wifi_was_on") + callback() + -- Delay this so it won't fire for dead/dying instances in case we're called by a finalizer... + UIManager:scheduleIn(2, function() + UIManager:broadcastEvent(Event:new("NetworkConnected")) + end) + return true +end + + function NetworkMgr:getWifiMenuTable() if Device:isAndroid() then @@ -701,89 +752,90 @@ end function NetworkMgr:reconnectOrShowNetworkMenu(complete_callback) local info = InfoMessage:new{text = _("Scanning for networks…")} UIManager:show(info) - UIManager:nextTick(function() - local network_list, err = self:getNetworkList() - UIManager:close(info) + UIManager:forceRePaint() + + local network_list, err = self:getNetworkList() + UIManager:close(info) + if network_list == nil then + UIManager:show(InfoMessage:new{text = err}) + return + end + -- NOTE: Fairly hackish workaround for #4387, + -- rescan if the first scan appeared to yield an empty list. + --- @fixme This *might* be an issue better handled in lj-wpaclient... + if #network_list == 0 then + logger.warn("Initial Wi-Fi scan yielded no results, rescanning") + network_list, err = self:getNetworkList() if network_list == nil then UIManager:show(InfoMessage:new{text = err}) return end - -- NOTE: Fairly hackish workaround for #4387, - -- rescan if the first scan appeared to yield an empty list. - --- @fixme This *might* be an issue better handled in lj-wpaclient... - if #network_list == 0 then - logger.warn("Initial Wi-Fi scan yielded no results, rescanning") - network_list, err = self:getNetworkList() - if network_list == nil then - UIManager:show(InfoMessage:new{text = err}) - return - end - end + end - table.sort(network_list, - function(l, r) return l.signal_quality > r.signal_quality end) + table.sort(network_list, + function(l, r) return l.signal_quality > r.signal_quality end) - local success = false - if self.wifi_toggle_long_press then - self.wifi_toggle_long_press = nil - else - local ssid - -- We need to do two passes, as we may have *both* an already connected network (from the global wpa config), - -- *and* preferred networks, and if the prferred networks have a better signal quality, - -- they'll be sorted *earlier*, which would cause us to try to associate to a different AP than - -- what wpa_supplicant is already trying to do... - for dummy, network in ipairs(network_list) do - if network.connected then - -- On platforms where we use wpa_supplicant (if we're calling this, we are), - -- the invocation will check its global config, and if an AP configured there is reachable, - -- it'll already have connected to it on its own. - success = true - ssid = network.ssid - break - end + local success = false + if self.wifi_toggle_long_press then + self.wifi_toggle_long_press = nil + else + local ssid + -- We need to do two passes, as we may have *both* an already connected network (from the global wpa config), + -- *and* preferred networks, and if the prferred networks have a better signal quality, + -- they'll be sorted *earlier*, which would cause us to try to associate to a different AP than + -- what wpa_supplicant is already trying to do... + for dummy, network in ipairs(network_list) do + if network.connected then + -- On platforms where we use wpa_supplicant (if we're calling this, we are), + -- the invocation will check its global config, and if an AP configured there is reachable, + -- it'll already have connected to it on its own. + success = true + ssid = network.ssid + break end + end - -- Next, look for our own prferred networks... - local err_msg = _("Connection failed") - if not success then - for dummy, network in ipairs(network_list) do - if network.password then - -- If we hit a preferred network and we're not already connected, - -- attempt to connect to said preferred network.... - success, err_msg = self:authenticateNetwork(network) - if success then - ssid = network.ssid - break - end + -- Next, look for our own prferred networks... + local err_msg = _("Connection failed") + if not success then + for dummy, network in ipairs(network_list) do + if network.password then + -- If we hit a preferred network and we're not already connected, + -- attempt to connect to said preferred network.... + success, err_msg = self:authenticateNetwork(network) + if success then + ssid = network.ssid + break end end end + end - if success then - self:obtainIP() - if complete_callback then - complete_callback() - end - UIManager:show(InfoMessage:new{ - text = T(_("Connected to network %1"), BD.wrap(ssid)), - timeout = 3, - }) - else - UIManager:show(InfoMessage:new{ - text = err_msg, - timeout = 3, - }) + if success then + self:obtainIP() + if complete_callback then + complete_callback() end - end - if not success then - -- NOTE: Also supports a disconnect_callback, should we use it for something? - -- Tearing down Wi-Fi completely when tapping "disconnect" would feel a bit harsh, though... - UIManager:show(require("ui/widget/networksetting"):new{ - network_list = network_list, - connect_callback = complete_callback, + UIManager:show(InfoMessage:new{ + tag = "NetworkMgr", -- for crazy KOSync purposes + text = T(_("Connected to network %1"), BD.wrap(ssid)), + timeout = 3, + }) + else + UIManager:show(InfoMessage:new{ + text = err_msg, + timeout = 3, }) end - end) + end + if not success then + -- NOTE: Also supports a disconnect_callback, should we use it for something? + -- Tearing down Wi-Fi completely when tapping "disconnect" would feel a bit harsh, though... + UIManager:show(require("ui/widget/networksetting"):new{ + network_list = network_list, + connect_callback = complete_callback, + }) + end end function NetworkMgr:saveNetwork(setting) diff --git a/frontend/ui/network/networklistener.lua b/frontend/ui/network/networklistener.lua index 522980fc3..5376d8ac2 100644 --- a/frontend/ui/network/networklistener.lua +++ b/frontend/ui/network/networklistener.lua @@ -39,6 +39,7 @@ function NetworkListener:onToggleWifi() UIManager:show(toggle_im) UIManager:forceRePaint() + UIManager:broadcastEvent(Event:new("NetworkDisconnecting")) NetworkMgr:turnOffWifi(complete_callback) UIManager:close(toggle_im) @@ -60,6 +61,7 @@ function NetworkListener:onInfoWifiOff() UIManager:show(toggle_im) UIManager:forceRePaint() + UIManager:broadcastEvent(Event:new("NetworkDisconnecting")) NetworkMgr:turnOffWifi(complete_callback) UIManager:close(toggle_im) @@ -166,6 +168,7 @@ function NetworkListener:_scheduleActivityCheck() local complete_callback = function() UIManager:broadcastEvent(Event:new("NetworkDisconnected")) end + UIManager:broadcastEvent(Event:new("NetworkDisconnecting")) NetworkMgr:turnOffWifi(complete_callback) -- NOTE: We leave wifi_was_on as-is on purpose, we wouldn't want to break auto_restore_wifi workflows on the next start... else diff --git a/frontend/ui/widget/infomessage.lua b/frontend/ui/widget/infomessage.lua index 0c4aaec75..6acdd0e31 100644 --- a/frontend/ui/widget/infomessage.lua +++ b/frontend/ui/widget/infomessage.lua @@ -240,8 +240,7 @@ function InfoMessage:onShow() -- schedule us to close ourself if timeout provided if self.timeout then UIManager:scheduleIn(self.timeout, function() - -- In case we're provided with dismiss_callback, also call it - -- on timeout + -- In case we're provided with dismiss_callback, also call it on timeout if self.dismiss_callback then self.dismiss_callback() self.dismiss_callback = nil diff --git a/plugins/kosync.koplugin/main.lua b/plugins/kosync.koplugin/main.lua index efac058cd..14b54aaeb 100644 --- a/plugins/kosync.koplugin/main.lua +++ b/plugins/kosync.koplugin/main.lua @@ -11,6 +11,7 @@ local WidgetContainer = require("ui/widget/container/widgetcontainer") local logger = require("logger") local md5 = require("ffi/sha2").md5 local random = require("random") +local time = require("ui/time") local util = require("util") local T = require("ffi/util").template local _ = require("gettext") @@ -24,20 +25,21 @@ local KOSync = WidgetContainer:extend{ is_doc_only = true, title = _("Register/login to KOReader server"), - page_update_times = 0, - last_page = -1, - last_page_turn_ticks = 0, + push_timestamp = nil, + pull_timestamp = nil, + page_update_counter = nil, + last_page = nil, + last_page_turn_timestamp = nil, + periodic_push_task = nil, + periodic_push_scheduled = nil, + + settings = nil, } local SYNC_STRATEGY = { - -- Forward and backward whisper sync settings are using different - -- default value, so none of following opinions should be zero. - PROMPT = 1, - WHISPER = 2, + PROMPT = 1, + SILENT = 2, DISABLE = 3, - - DEFAULT_FORWARD = 1, - DEFAULT_BACKWARD = 3, } local CHECKSUM_METHOD = { @@ -45,6 +47,64 @@ local CHECKSUM_METHOD = { FILENAME = 1 } +-- Debounce push/pull attempts +local API_CALL_DEBOUNCE_DELAY = time.s(25) + +-- NOTE: This is used in a migration script by ui/data/onetime_migration, +-- which is why it's public. +KOSync.default_settings = { + custom_server = nil, + username = nil, + userkey = nil, + -- Do *not* default to auto-sync on devices w/ NetworkManager support, as wifi is unlikely to be on at all times there, and the nagging enabling this may cause requires careful consideration. + auto_sync = not Device:hasWifiManager(), + pages_before_update = nil, + sync_forward = SYNC_STRATEGY.PROMPT, + sync_backward = SYNC_STRATEGY.DISABLE, + checksum_method = CHECKSUM_METHOD.BINARY, +} + +function KOSync:init() + self.push_timestamp = 0 + self.pull_timestamp = 0 + self.page_update_counter = 0 + self.last_page = -1 + self.last_page_turn_timestamp = 0 + self.periodic_push_scheduled = false + + -- Like AutoSuspend, we need an instance-specific task for scheduling/resource management reasons. + self.periodic_push_task = function() + self.periodic_push_scheduled = false + self.page_update_counter = 0 + -- We do *NOT* want to make sure networking is up here, as the nagging would be extremely annoying; we're leaving that to the network activity check... + self:updateProgress(false, false) + end + + self.settings = G_reader_settings:readSetting("kosync", self.default_settings) + self.device_id = G_reader_settings:readSetting("device_id") + + -- Disable auto-sync if beforeWifiAction was reset to "prompt" behind our back... + if self.settings.auto_sync and Device:hasWifiManager() and G_reader_settings:readSetting("wifi_enable_action") ~= "turn_on" then + self.settings.auto_sync = false + logger.warn("KOSync: Automatic sync has been disabled because wifi_enable_action is *not* turn_on") + end + + self.ui.menu:registerToMainMenu(self) +end + +function KOSync:getSyncPeriod() + if not self.settings.auto_sync then + return _("Unavailable") + end + + local period = self.settings.pages_before_update + if period and period > 0 then + return period + else + return _("Never") + end +end + local function getNameStrategy(type) if type == 1 then return _("Prompt") @@ -109,218 +169,224 @@ function KOSync:onDispatcherRegisterActions() end function KOSync:onReaderReady() - --- @todo: Viable candidate for a port to the new readSetting API - local settings = G_reader_settings:readSetting("kosync") or {} - self.kosync_custom_server = settings.custom_server - self.kosync_username = settings.username - self.kosync_userkey = settings.userkey - self.kosync_auto_sync = settings.auto_sync ~= false - self.kosync_pages_before_update = settings.pages_before_update - self.kosync_whisper_forward = settings.whisper_forward or SYNC_STRATEGY.DEFAULT_FORWARD - self.kosync_whisper_backward = settings.whisper_backward or SYNC_STRATEGY.DEFAULT_BACKWARD - self.kosync_checksum_method = settings.checksum_method or CHECKSUM_METHOD.BINARY - self.kosync_device_id = G_reader_settings:readSetting("device_id") - --assert(self.kosync_device_id) - if self.kosync_auto_sync then - self:_onResume() + -- Make sure checksum has been calculated before we ever query it, + -- to prevent document saving features from affecting the checksum, + -- and eventually affecting the document identity for the progress sync feature. + self.view.document:fastDigest(self.ui.doc_settings) + + if self.settings.auto_sync then + UIManager:nextTick(function() + self:getProgress(true, false) + end) end self:registerEvents() self:onDispatcherRegisterActions() - self.ui.menu:registerToMainMenu(self) - -- Make sure checksum has been calculated at the very first time a document has been opened, to - -- avoid document saving feature to impact the checksum, and eventually impact the document - -- identity in the progress sync feature. - self.view.document:fastDigest(self.ui.doc_settings) + + self.last_page = self.ui:getCurrentPage() end function KOSync:addToMainMenu(menu_items) menu_items.progress_sync = { text = _("Progress sync"), sub_item_table = { + { + text = _("Custom sync server"), + keep_menu_open = true, + tap_input_func = function() + return { + -- @translators Server address defined by user for progress sync. + title = _("Custom progress sync server address"), + input = self.settings.custom_server or "https://", + type = "text", + callback = function(input) + self:setCustomServer(input) + end, + } + end, + }, { text_func = function() - return self.kosync_userkey and (_("Logout")) + return self.settings.userkey and (_("Logout")) or _("Register") .. " / " .. _("Login") end, keep_menu_open = true, callback_func = function() - if self.kosync_userkey then + if self.settings.userkey then return function(menu) - self._menu_to_update = menu - self:logout() + self:logout(menu) end else return function(menu) - self._menu_to_update = menu - self:login() + self:login(menu) end end end, + separator = true, }, { - text = _("Auto sync now and future"), - checked_func = function() return self.kosync_auto_sync end, + text = _("Automatically keep documents in sync"), + checked_func = function() return self.settings.auto_sync end, + help_text = _([[This may lead to nagging about toggling WiFi on document close and suspend/resume, depending on the device's connectivity.]]), callback = function() - self.kosync_auto_sync = not self.kosync_auto_sync + -- Actively recommend switching the before wifi action to "turn_on" instead of prompt, as prompt will just not be practical (or even plain usable) here. + if Device:hasWifiManager() and G_reader_settings:readSetting("wifi_enable_action") ~= "turn_on" then + UIManager:show(InfoMessage:new{ text = _("You will have to switch the 'Action when Wi-Fi is off' Network setting to 'turn on' to be able to enable this feature!") }) + return + end + + self.settings.auto_sync = not self.settings.auto_sync self:registerEvents() - if self.kosync_auto_sync then - -- since we will update the progress when closing document, we should pull - -- current progress now to avoid to overwrite it silently. - self:getProgress(true) + if self.settings.auto_sync then + -- Since we will update the progress when closing the document, + -- pull the current progress now so as not to silently overwrite it. + self:getProgress(true, true) else - -- since we won't update the progress when closing document, we should push - -- current progress now to avoid to lose it silently. - self:updateProgress(true) + -- Since we won't update the progress when closing the document, + -- push the current progress now so as not to lose it. + self:updateProgress(true, true) end - self:saveSettings() end, }, { - text = _("Whisper sync"), - enabled_func = function() return self.kosync_auto_sync end, + text_func = function() + return T(_("Periodically sync every # pages (%1)"), self:getSyncPeriod()) + end, + enabled_func = function() return self.settings.auto_sync end, + -- This is the condition that allows enabling auto_disable_wifi in NetworkManager ;). + help_text = NetworkMgr:getNetworkInterfaceName() and _([[Unlike the automatic sync above, this will *not* attempt to setup a network connection, but instead relies on it being already up, and may trigger enough network activity to passively keep WiFi enabled!]]), + keep_menu_open = true, + callback = function(touchmenu_instance) + local SpinWidget = require("ui/widget/spinwidget") + local items = SpinWidget:new{ + text = _([[This value determines how many page turns it takes to update book progress. +If set to 0, updating progress based on page turns will be disabled.]]), + 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 = _("Number of pages before update"), + default_value = 0, + callback = function(spin) + self:setPagesBeforeUpdate(spin.value) + if touchmenu_instance then touchmenu_instance:updateItems() end + end + } + UIManager:show(items) + end, + separator = true, + }, + { + text = _("Sync behavior"), sub_item_table = { { text_func = function() - return T(_("Sync to latest record (%1)"), getNameStrategy(self.kosync_whisper_forward)) + -- NOTE: With an up-to-date Sync server, "forward" means *newer*, not necessarily ahead in the document. + return T(_("Sync to a newer state (%1)"), getNameStrategy(self.settings.sync_forward)) end, sub_item_table = { { - text = _("Auto"), + text = _("Silently"), checked_func = function() - return self.kosync_whisper_forward == SYNC_STRATEGY.WHISPER + return self.settings.sync_forward == SYNC_STRATEGY.SILENT end, callback = function() - self:setWhisperForward(SYNC_STRATEGY.WHISPER) + self:setSyncForward(SYNC_STRATEGY.SILENT) end, }, { text = _("Prompt"), checked_func = function() - return self.kosync_whisper_forward == SYNC_STRATEGY.PROMPT + return self.settings.sync_forward == SYNC_STRATEGY.PROMPT end, callback = function() - self:setWhisperForward(SYNC_STRATEGY.PROMPT) + self:setSyncForward(SYNC_STRATEGY.PROMPT) end, }, { - text = _("Disable"), + text = _("Never"), checked_func = function() - return self.kosync_whisper_forward == SYNC_STRATEGY.DISABLE + return self.settings.sync_forward == SYNC_STRATEGY.DISABLE end, callback = function() - self:setWhisperForward(SYNC_STRATEGY.DISABLE) + self:setSyncForward(SYNC_STRATEGY.DISABLE) end, }, } }, { text_func = function() - return T(_("Sync to a previous record (%1)"), getNameStrategy(self.kosync_whisper_backward)) + return T(_("Sync to an older state (%1)"), getNameStrategy(self.settings.sync_backward)) end, sub_item_table = { { - text = _("Auto"), + text = _("Silently"), checked_func = function() - return self.kosync_whisper_backward == SYNC_STRATEGY.WHISPER + return self.settings.sync_backward == SYNC_STRATEGY.SILENT end, callback = function() - self:setWhisperBackward(SYNC_STRATEGY.WHISPER) + self:setSyncBackward(SYNC_STRATEGY.SILENT) end, }, { text = _("Prompt"), checked_func = function() - return self.kosync_whisper_backward == SYNC_STRATEGY.PROMPT + return self.settings.sync_backward == SYNC_STRATEGY.PROMPT end, callback = function() - self:setWhisperBackward(SYNC_STRATEGY.PROMPT) + self:setSyncBackward(SYNC_STRATEGY.PROMPT) end, }, { - text = _("Disable"), + text = _("Never"), checked_func = function() - return self.kosync_whisper_backward == SYNC_STRATEGY.DISABLE + return self.settings.sync_backward == SYNC_STRATEGY.DISABLE end, callback = function() - self:setWhisperBackward(SYNC_STRATEGY.DISABLE) + self:setSyncBackward(SYNC_STRATEGY.DISABLE) end, }, } }, }, + separator = true, }, { - text = _("Push progress from this device"), + text = _("Push progress from this device now"), enabled_func = function() - return self.kosync_userkey ~= nil + return self.settings.userkey ~= nil end, callback = function() - self:updateProgress(true) + self:updateProgress(true, true) end, }, { - text = _("Pull progress from other devices"), + text = _("Pull progress from other devices now"), enabled_func = function() - return self.kosync_userkey ~= nil + return self.settings.userkey ~= nil end, callback = function() - self:getProgress(true) - end, - }, - { - text = _("Custom sync server"), - keep_menu_open = true, - tap_input_func = function() - return { - -- @translators Server address defined by user for progress sync. - title = _("Custom progress sync server address"), - input = self.kosync_custom_server or "https://", - type = "text", - callback = function(input) - self:setCustomServer(input) - end, - } - end, - }, - { - text = _("Sync every # pages"), - keep_menu_open = true, - callback = function() - local SpinWidget = require("ui/widget/spinwidget") - local items = SpinWidget:new{ - text = _([[This value determines how many page turns it takes to update book progress. -If set to 0, updating progress based on page turns will be disabled.]]), - value = self.kosync_pages_before_update or 0, - value_min = 0, - value_max = 999, - value_step = 1, - value_hold_step = 10, - ok_text = _("Set"), - title_text = _("Number of pages before update"), - default_value = 0, - callback = function(spin) - self:setPagesBeforeUpdate(spin.value) - end - } - UIManager:show(items) + self:getProgress(true, true) end, + separator = true, }, { text = _("Document matching method"), sub_item_table = { { - text = _("Binary. Only identical files will sync progress."), + text = _("Binary. Only identical files will be kept in sync."), checked_func = function() - return self.kosync_checksum_method == CHECKSUM_METHOD.BINARY + return self.settings.checksum_method == CHECKSUM_METHOD.BINARY end, callback = function() self:setChecksumMethod(CHECKSUM_METHOD.BINARY) end, }, { - text = _("Filename. Files with the same name will sync progress."), + text = _("Filename. Files with matching names will be kept in sync."), checked_func = function() - return self.kosync_checksum_method == CHECKSUM_METHOD.FILENAME + return self.settings.checksum_method == CHECKSUM_METHOD.FILENAME end, callback = function() self:setChecksumMethod(CHECKSUM_METHOD.FILENAME) @@ -333,33 +399,28 @@ If set to 0, updating progress based on page turns will be disabled.]]), end function KOSync:setPagesBeforeUpdate(pages_before_update) - self.kosync_pages_before_update = pages_before_update > 0 and pages_before_update or nil - self:saveSettings() + self.settings.pages_before_update = pages_before_update > 0 and pages_before_update or nil end function KOSync:setCustomServer(server) - logger.dbg("set custom server", server) - self.kosync_custom_server = server ~= "" and server or nil - self:saveSettings() + logger.dbg("KOSync: Setting custom server to:", server) + self.settings.custom_server = server ~= "" and server or nil end -function KOSync:setWhisperForward(strategy) - self.kosync_whisper_forward = strategy - self:saveSettings() +function KOSync:setSyncForward(strategy) + self.settings.sync_forward = strategy end -function KOSync:setWhisperBackward(strategy) - self.kosync_whisper_backward = strategy - self:saveSettings() +function KOSync:setSyncBackward(strategy) + self.settings.sync_backward = strategy end function KOSync:setChecksumMethod(method) - self.kosync_checksum_method = method - self:saveSettings() + self.settings.checksum_method = method end -function KOSync:login() - if NetworkMgr:willRerunWhenOnline(function() self:login() end) then +function KOSync:login(menu) + if NetworkMgr:willRerunWhenOnline(function() self:login(menu) end) then return end @@ -368,7 +429,7 @@ function KOSync:login() title = self.title, fields = { { - text = self.kosync_username, + text = self.settings.username, hint = "username", }, { @@ -398,7 +459,7 @@ function KOSync:login() else UIManager:close(dialog) UIManager:scheduleIn(0.5, function() - self:doLogin(username, password) + self:doLogin(username, password, menu) end) UIManager:show(InfoMessage:new{ text = _("Logging in. Please wait…"), @@ -420,7 +481,7 @@ function KOSync:login() else UIManager:close(dialog) UIManager:scheduleIn(0.5, function() - self:doRegister(username, password) + self:doRegister(username, password, menu) end) UIManager:show(InfoMessage:new{ text = _("Registering. Please wait…"), @@ -436,10 +497,10 @@ function KOSync:login() dialog:onShowKeyboard() end -function KOSync:doRegister(username, password) +function KOSync:doRegister(username, password, menu) local KOSyncClient = require("KOSyncClient") local client = KOSyncClient:new{ - custom_url = self.kosync_custom_server, + custom_url = self.settings.custom_server, service_spec = self.path .. "/api.json" } -- on Android to avoid ANR (no-op on other platforms) @@ -458,9 +519,11 @@ function KOSync:doRegister(username, password) }) end elseif status then - self.kosync_username = username - self.kosync_userkey = userkey - self._menu_to_update:updateItems() + self.settings.username = username + self.settings.userkey = userkey + if menu then + menu:updateItems() + end UIManager:show(InfoMessage:new{ text = _("Registered to KOReader server."), }) @@ -470,13 +533,12 @@ function KOSync:doRegister(username, password) }) end Device:setIgnoreInput(false) - self:saveSettings() end -function KOSync:doLogin(username, password) +function KOSync:doLogin(username, password, menu) local KOSyncClient = require("KOSyncClient") local client = KOSyncClient:new{ - custom_url = self.kosync_custom_server, + custom_url = self.settings.custom_server, service_spec = self.path .. "/api.json" } Device:setIgnoreInput(true) @@ -496,9 +558,11 @@ function KOSync:doLogin(username, password) Device:setIgnoreInput(false) return elseif status then - self.kosync_username = username - self.kosync_userkey = userkey - self._menu_to_update:updateItems() + self.settings.username = username + self.settings.userkey = userkey + if menu then + menu:updateItems() + end UIManager:show(InfoMessage:new{ text = _("Logged in to KOReader server."), }) @@ -508,14 +572,14 @@ function KOSync:doLogin(username, password) }) end Device:setIgnoreInput(false) - self:saveSettings() end -function KOSync:logout() - self.kosync_userkey = nil - self.kosync_auto_sync = true - self._menu_to_update:updateItems() - self:saveSettings() +function KOSync:logout(menu) + self.settings.userkey = nil + self.settings.auto_sync = true + if menu then + menu:updateItems() + end end function KOSync:getLastPercent() @@ -535,7 +599,7 @@ function KOSync:getLastProgress() end function KOSync:getDocumentDigest() - if self.kosync_checksum_method == CHECKSUM_METHOD.FILENAME then + if self.settings.checksum_method == CHECKSUM_METHOD.FILENAME then return self:getFileNameDigest() else return self:getFileDigest() @@ -543,7 +607,7 @@ function KOSync:getDocumentDigest() end function KOSync:getFileDigest() - return self.view.document:fastDigest() + return self.ui.doc_settings:readSetting("partial_md5_checksum") end function KOSync:getFileNameDigest() @@ -557,7 +621,7 @@ function KOSync:getFileNameDigest() end function KOSync:syncToProgress(progress) - logger.dbg("sync to", progress) + logger.dbg("KOSync: [Sync] progress to", progress) if self.ui.document.info.has_pages then self.ui:handleEvent(Event:new("GotoPage", tonumber(progress))) else @@ -565,21 +629,27 @@ function KOSync:syncToProgress(progress) end end -function KOSync:updateProgress(manual) - if not self.kosync_username or not self.kosync_userkey then - if manual then +function KOSync:updateProgress(ensure_networking, interactive, refresh_on_success) + if not self.settings.username or not self.settings.userkey then + if interactive then promptLogin() end return end - if manual and NetworkMgr:willRerunWhenOnline(function() self:updateProgress(manual) end) then + local now = UIManager:getElapsedTimeSinceBoot() + if not interactive and now - self.push_timestamp <= API_CALL_DEBOUNCE_DELAY then + logger.dbg("KOSync: We've already pushed progress less than 25s ago!") + return + end + + if ensure_networking and NetworkMgr:willRerunWhenOnline(function() self:updateProgress(ensure_networking, interactive, refresh_on_success) end) then return end local KOSyncClient = require("KOSyncClient") local client = KOSyncClient:new{ - custom_url = self.kosync_custom_server, + custom_url = self.settings.custom_server, service_spec = self.path .. "/api.json" } local doc_digest = self:getDocumentDigest() @@ -587,16 +657,17 @@ function KOSync:updateProgress(manual) local percentage = self:getLastPercent() local ok, err = pcall(client.update_progress, client, - self.kosync_username, - self.kosync_userkey, + self.settings.username, + self.settings.userkey, doc_digest, progress, percentage, Device.model, - self.kosync_device_id, + self.device_id, function(ok, body) - logger.dbg("update progress for", self.view.document.file, ok) - if manual then + logger.dbg("KOSync: [Push] progress to", percentage * 100, "% =>", progress, "for", self.view.document.file) + logger.dbg("KOSync: ok:", ok, "body:", body) + if interactive then if ok then UIManager:show(InfoMessage:new{ text = _("Progress has been pushed."), @@ -608,45 +679,67 @@ function KOSync:updateProgress(manual) end end) if not ok then - if manual then showSyncError() end + if interactive then showSyncError() end if err then logger.dbg("err:", err) end + else + -- This is solely for onSuspend's sake, to clear the ghosting left by the the "Connected" InfoMessage + if refresh_on_success then + -- Our top-level widget should be the "Connected to network" InfoMessage from NetworkMgr's reconnectOrShowNetworkMenu + local widget = UIManager:getTopmostVisibleWidget() + if widget and widget.modal and widget.tag == "NetworkMgr" and not widget.dismiss_callback then + -- We want a full-screen flash on dismiss + widget.dismiss_callback = function() + -- Enqueued, because we run before the InfoMessage's close + UIManager:setDirty(nil, "full") + end + end + end end + + self.push_timestamp = now end -function KOSync:getProgress(manual) - if not self.kosync_username or not self.kosync_userkey then - if manual then +function KOSync:getProgress(ensure_networking, interactive) + if not self.settings.username or not self.settings.userkey then + if interactive then promptLogin() end return end - if manual and NetworkMgr:willRerunWhenOnline(function() self:getProgress(manual) end) then + local now = UIManager:getElapsedTimeSinceBoot() + if not interactive and now - self.pull_timestamp <= API_CALL_DEBOUNCE_DELAY then + logger.dbg("KOSync: We've already pulled progress less than 25s ago!") + return + end + + if ensure_networking and NetworkMgr:willRerunWhenOnline(function() self:getProgress(ensure_networking, interactive) end) then return end local KOSyncClient = require("KOSyncClient") local client = KOSyncClient:new{ - custom_url = self.kosync_custom_server, + custom_url = self.settings.custom_server, service_spec = self.path .. "/api.json" } local doc_digest = self:getDocumentDigest() local ok, err = pcall(client.get_progress, client, - self.kosync_username, - self.kosync_userkey, + self.settings.username, + self.settings.userkey, doc_digest, function(ok, body) - logger.dbg("get progress for", self.view.document.file, ok, body) + logger.dbg("KOSync: [Pull] progress for", self.view.document.file) + logger.dbg("KOSync: ok:", ok, "body:", body) if not ok or not body then - if manual then + if interactive then showSyncError() end return end if not body.percentage then - if manual then + if interactive then UIManager:show(InfoMessage:new{ text = _("No progress found for this document."), timeout = 3, @@ -656,8 +749,8 @@ function KOSync:getProgress(manual) end if body.device == Device.model - and body.device_id == self.kosync_device_id then - if manual then + and body.device_id == self.device_id then + if interactive then UIManager:show(InfoMessage:new{ text = _("Latest progress is coming from this device."), timeout = 3, @@ -669,11 +762,11 @@ function KOSync:getProgress(manual) body.percentage = Math.roundPercent(body.percentage) local progress = self:getLastProgress() local percentage = self:getLastPercent() - logger.dbg("current progress", percentage) + logger.dbg("KOSync: Current progress:", percentage * 100, "% =>", progress) if percentage == body.percentage or body.progress == progress then - if manual then + if interactive then UIManager:show(InfoMessage:new{ text = _("The progress has already been synchronized."), timeout = 3, @@ -683,9 +776,9 @@ function KOSync:getProgress(manual) end -- The progress needs to be updated. - if manual then - -- If user actively pulls progress from other devices, we always update the - -- progress without further confirmation. + if interactive then + -- If user actively pulls progress from other devices, + -- we always update the progress without further confirmation. self:syncToProgress(body.progress) showSyncedMessage() return @@ -693,17 +786,16 @@ function KOSync:getProgress(manual) local self_older if body.timestamp ~= nil then - self_older = (body.timestamp > self.last_page_turn_ticks) + self_older = (body.timestamp > self.last_page_turn_timestamp) else - -- If we are working with old sync server, we can only use - -- percentage field. + -- If we are working with an old sync server, we can only use the percentage field. self_older = (body.percentage > percentage) end if self_older then - if self.kosync_whisper_forward == SYNC_STRATEGY.WHISPER then + if self.settings.sync_forward == SYNC_STRATEGY.SILENT then self:syncToProgress(body.progress) showSyncedMessage() - elseif self.kosync_whisper_forward == SYNC_STRATEGY.PROMPT then + elseif self.settings.sync_forward == SYNC_STRATEGY.PROMPT then UIManager:show(ConfirmBox:new{ text = T(_("Sync to latest location %1% from device '%2'?"), Math.round(body.percentage * 100), @@ -714,10 +806,10 @@ function KOSync:getProgress(manual) }) end else -- if not self_older then - if self.kosync_whisper_backward == SYNC_STRATEGY.WHISPER then + if self.settings.sync_backward == SYNC_STRATEGY.SILENT then self:syncToProgress(body.progress) showSyncedMessage() - elseif self.kosync_whisper_backward == SYNC_STRATEGY.PROMPT then + elseif self.settings.sync_backward == SYNC_STRATEGY.PROMPT then UIManager:show(ConfirmBox:new{ text = T(_("Sync to previous location %1% from device '%2'?"), Math.round(body.percentage * 100), @@ -730,36 +822,30 @@ function KOSync:getProgress(manual) end end) if not ok then - if manual then showSyncError() end + if interactive then showSyncError() end if err then logger.dbg("err:", err) end end + + self.pull_timestamp = now end -function KOSync:saveSettings() - local settings = { - custom_server = self.kosync_custom_server, - username = self.kosync_username, - userkey = self.kosync_userkey, - auto_sync = self.kosync_auto_sync, - pages_before_update = self.kosync_pages_before_update, - whisper_forward = - (self.kosync_whisper_forward ~= SYNC_STRATEGY.DEFAULT_FORWARD - and self.kosync_whisper_forward - or nil), - whisper_backward = - (self.kosync_whisper_backward ~= SYNC_STRATEGY.DEFAULT_BACKWARD - and self.kosync_whisper_backward - or nil), - checksum_method = self.kosync_checksum_method, - } - G_reader_settings:saveSetting("kosync", settings) +function KOSync:_onCloseDocument() + logger.dbg("KOSync: onCloseDocument") + -- NOTE: Because we'll lose the document instance on return, we need to *block* until the connection is actually up here, + -- we cannot rely on willRerunWhenOnline, because if we're not currently online, + -- it *will* return early, and that means the actual callback *will* run *after* teardown of the document instance + -- (and quite likely ours, too). + NetworkMgr:goOnlineToRun(function() + -- Drop the inner willRerunWhenOnline ;). + self:updateProgress(false, false) + end) end -function KOSync:onCloseDocument() - logger.dbg("on close document") - if self.kosync_auto_sync then - self:updateProgress() - end +function KOSync:schedulePeriodicPush() + UIManager:unschedule(self.periodic_push_task) + -- Use a sizable delay to make debouncing this on skim feasible... + UIManager:scheduleIn(10, self.periodic_push_task) + self.periodic_push_scheduled = true end function KOSync:_onPageUpdate(page) @@ -767,54 +853,81 @@ function KOSync:_onPageUpdate(page) return end - if self.last_page == -1 then - self.last_page = page - elseif self.last_page ~= page then + if self.last_page ~= page then self.last_page = page - self.last_page_turn_ticks = os.time() - self.page_update_times = self.page_update_times + 1 - if self.kosync_pages_before_update and self.page_update_times == self.kosync_pages_before_update then - self.page_update_times = 0 - UIManager:scheduleIn(1, function() self:updateProgress() end) + self.last_page_turn_timestamp = os.time() + self.page_update_counter = self.page_update_counter + 1 + -- If we've already scheduled a push, regardless of the counter's state, delay it until we're *actually* idle + 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 KOSync:_onResume() - UIManager:scheduleIn(1, function() self:getProgress() end) + logger.dbg("KOSync: onResume") + -- If we have auto_restore_wifi enabled, skip this to prevent both the "Connecting..." UI to pop-up, + -- *and* a duplicate NetworkConnected event from firing... + if Device:hasWifiManager() and NetworkMgr.wifi_was_on and G_reader_settings:isTrue("auto_restore_wifi") then + return + end + + -- And if we don't, this *will* (attempt to) trigger a connection and as such a NetworkConnected event, + -- but only a single pull will happen, since getProgress debounces itself. + UIManager:scheduleIn(1, function() + self:getProgress(true, false) + end) end -function KOSync:_onFlushSettings() - if self.ui == nil or self.ui.document == nil then return end - self:updateProgress() +function KOSync:_onSuspend() + logger.dbg("KOSync: onSuspend") + -- We request an extra flashing refresh on success, to deal with potential ghosting left by the NetworkMgr UI + self:updateProgress(true, false, true) end function KOSync:_onNetworkConnected() - self:_onResume() + logger.dbg("KOSync: onNetworkConnected") + UIManager:scheduleIn(0.5, function() + -- Network is supposed to be on already, don't wrap this in willRerunWhenOnline + self:getProgress(false, false) + end) +end + +function KOSync:_onNetworkDisconnecting() + logger.dbg("KOSync: onNetworkDisconnecting") + -- Network is supposed to be on already, don't wrap this in willRerunWhenOnline + self:updateProgress(false, false) end function KOSync:onKOSyncPushProgress() - if not self.kosync_userkey then return end - self:updateProgress(true) + self:updateProgress(true, true) end function KOSync:onKOSyncPullProgress() - if not self.kosync_userkey then return end - self:getProgress(true) + self:getProgress(true, true) end function KOSync:registerEvents() - if self.kosync_auto_sync then + if self.settings.auto_sync then + self.onCloseDocument = self._onCloseDocument self.onPageUpdate = self._onPageUpdate self.onResume = self._onResume - self.onFlushSettings = self._onFlushSettings + self.onSuspend = self._onSuspend self.onNetworkConnected = self._onNetworkConnected + self.onNetworkDisconnecting = self._onNetworkDisconnecting else + self.onCloseDocument = nil self.onPageUpdate = nil self.onResume = nil - self.onFlushSettings = nil + self.onSuspend = nil self.onNetworkConnected = nil + self.onNetworkDisconnecting = nil end end +function KOSync:onCloseWidget() + UIManager:unschedule(self.periodic_push_task) + self.periodic_push_task = nil +end + return KOSync