2015-03-09 12:20:32 +00:00
local ConfirmBox = require ( " ui/widget/confirmbox " )
2020-06-13 11:13:23 +00:00
local Device = require ( " device " )
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
local Dispatcher = require ( " dispatcher " )
2015-03-09 12:20:32 +00:00
local Event = require ( " ui/event " )
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
local InfoMessage = require ( " ui/widget/infomessage " )
2015-03-10 07:09:42 +00:00
local Math = require ( " optmath " )
2022-12-30 16:38:42 +00:00
local MultiInputDialog = require ( " ui/widget/multiinputdialog " )
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
local NetworkMgr = require ( " ui/network/manager " )
local UIManager = require ( " ui/uimanager " )
local WidgetContainer = require ( " ui/widget/container/widgetcontainer " )
2020-06-13 11:47:07 +00:00
local logger = require ( " logger " )
2020-07-21 21:25:46 +00:00
local md5 = require ( " ffi/sha2 " ) . md5
2016-08-11 09:12:55 +00:00
local random = require ( " random " )
2021-06-21 16:53:06 +00:00
local util = require ( " util " )
2020-06-13 11:47:07 +00:00
local T = require ( " ffi/util " ) . template
local _ = require ( " gettext " )
2016-08-11 09:12:55 +00:00
2021-03-06 21:44:18 +00:00
if G_reader_settings : hasNot ( " device_id " ) then
2016-08-11 09:12:55 +00:00
G_reader_settings : saveSetting ( " device_id " , random.uuid ( ) )
end
2015-03-09 12:20:32 +00:00
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
local KOSync = WidgetContainer : extend {
2015-03-09 12:20:32 +00:00
name = " kosync " ,
2016-12-20 07:19:54 +00:00
is_doc_only = true ,
2016-03-28 01:50:23 +00:00
title = _ ( " Register/login to KOReader server " ) ,
2016-09-24 08:20:28 +00:00
page_update_times = 0 ,
last_page = - 1 ,
last_page_turn_ticks = 0 ,
}
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 ,
DISABLE = 3 ,
DEFAULT_FORWARD = 1 ,
DEFAULT_BACKWARD = 3 ,
2015-03-09 12:20:32 +00:00
}
2021-06-21 16:53:06 +00:00
local CHECKSUM_METHOD = {
BINARY = 0 ,
FILENAME = 1
}
2020-04-25 07:04:44 +00:00
local function getNameStrategy ( type )
if type == 1 then
return _ ( " Prompt " )
elseif type == 2 then
return _ ( " Auto " )
else
return _ ( " Disable " )
end
end
2016-09-24 08:20:28 +00:00
local function showSyncedMessage ( )
UIManager : show ( InfoMessage : new {
text = _ ( " Progress has been synchronized. " ) ,
timeout = 3 ,
} )
end
local function promptLogin ( )
UIManager : show ( InfoMessage : new {
text = _ ( " Please register or login before using the progress synchronization feature. " ) ,
timeout = 3 ,
} )
end
local function showSyncError ( )
UIManager : show ( InfoMessage : new {
text = _ ( " Something went wrong when syncing progress, please check your network connection and try again later. " ) ,
timeout = 3 ,
} )
end
2020-02-25 19:40:50 +00:00
local function validate ( entry )
if not entry then return false end
if type ( entry ) == " string " then
if entry == " " or not entry : match ( " %S " ) then return false end
end
return true
end
local function validateUser ( user , pass )
local error_message = nil
local user_ok = validate ( user )
local pass_ok = validate ( pass )
if not user_ok and not pass_ok then
error_message = _ ( " invalid username and password " )
elseif not user_ok then
error_message = _ ( " invalid username " )
elseif not pass_ok then
error_message = _ ( " invalid password " )
end
if not error_message then
return user_ok and pass_ok
else
return user_ok and pass_ok , error_message
end
end
2020-10-12 03:06:04 +00:00
function KOSync : onDispatcherRegisterActions ( )
2021-09-10 20:11:24 +00:00
Dispatcher : registerAction ( " kosync_push_progress " , { category = " none " , event = " KOSyncPushProgress " , title = _ ( " Push progress from this device " ) , reader = true , } )
Dispatcher : registerAction ( " kosync_pull_progress " , { category = " none " , event = " KOSyncPullProgress " , title = _ ( " Pull progress from other devices " ) , reader = true , separator = true , } )
2020-10-12 03:06:04 +00:00
end
2016-09-24 08:20:28 +00:00
function KOSync : onReaderReady ( )
2021-03-06 21:44:18 +00:00
--- @todo: Viable candidate for a port to the new readSetting API
2015-03-09 12:20:32 +00:00
local settings = G_reader_settings : readSetting ( " kosync " ) or { }
2015-09-17 15:27:19 +00:00
self.kosync_custom_server = settings.custom_server
2015-03-10 07:49:33 +00:00
self.kosync_username = settings.username
2015-03-09 12:20:32 +00:00
self.kosync_userkey = settings.userkey
2022-06-11 17:06:06 +00:00
self.kosync_auto_sync = settings.auto_sync ~= false
2020-10-02 06:53:24 +00:00
self.kosync_pages_before_update = settings.pages_before_update
2016-09-24 08:20:28 +00:00
self.kosync_whisper_forward = settings.whisper_forward or SYNC_STRATEGY.DEFAULT_FORWARD
self.kosync_whisper_backward = settings.whisper_backward or SYNC_STRATEGY.DEFAULT_BACKWARD
2021-06-21 16:53:06 +00:00
self.kosync_checksum_method = settings.checksum_method or CHECKSUM_METHOD.BINARY
2016-07-30 00:38:02 +00:00
self.kosync_device_id = G_reader_settings : readSetting ( " device_id " )
2016-08-11 09:12:55 +00:00
--assert(self.kosync_device_id)
2016-09-24 08:20:28 +00:00
if self.kosync_auto_sync then
self : _onResume ( )
end
self : registerEvents ( )
2020-10-12 03:06:04 +00:00
self : onDispatcherRegisterActions ( )
2015-03-09 12:20:32 +00:00
self.ui . menu : registerToMainMenu ( self )
2016-07-30 00:38:02 +00:00
-- 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.
2017-10-13 17:18:36 +00:00
self.view . document : fastDigest ( self.ui . doc_settings )
2015-03-09 12:20:32 +00:00
end
2017-03-04 13:46:38 +00:00
function KOSync : addToMainMenu ( menu_items )
menu_items.progress_sync = {
2015-03-10 07:12:44 +00:00
text = _ ( " Progress sync " ) ,
2015-03-09 12:20:32 +00:00
sub_item_table = {
{
text_func = function ( )
return self.kosync_userkey and ( _ ( " Logout " ) )
or _ ( " Register " ) .. " / " .. _ ( " Login " )
end ,
2018-09-04 21:55:58 +00:00
keep_menu_open = true ,
2015-03-09 12:20:32 +00:00
callback_func = function ( )
2019-08-29 08:05:49 +00:00
if self.kosync_userkey then
return function ( menu )
self._menu_to_update = menu
self : logout ( )
end
else
return function ( menu )
self._menu_to_update = menu
self : login ( )
end
end
2015-03-09 12:20:32 +00:00
end ,
} ,
2015-03-10 07:09:42 +00:00
{
2016-07-30 00:38:02 +00:00
text = _ ( " Auto sync now and future " ) ,
2015-03-10 07:09:42 +00:00
checked_func = function ( ) return self.kosync_auto_sync end ,
callback = function ( )
self.kosync_auto_sync = not self.kosync_auto_sync
2016-09-24 08:20:28 +00:00
self : registerEvents ( )
2016-07-30 00:38:02 +00:00
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 )
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 )
end
2020-08-09 09:19:50 +00:00
self : saveSettings ( )
2015-03-10 07:09:42 +00:00
end ,
} ,
2016-09-24 08:20:28 +00:00
{
text = _ ( " Whisper sync " ) ,
enabled_func = function ( ) return self.kosync_auto_sync end ,
sub_item_table = {
{
2020-04-25 07:04:44 +00:00
text_func = function ( )
return T ( _ ( " Sync to latest record (%1) " ) , getNameStrategy ( self.kosync_whisper_forward ) )
2016-09-24 08:20:28 +00:00
end ,
2020-04-25 07:04:44 +00:00
sub_item_table = {
{
text = _ ( " Auto " ) ,
checked_func = function ( )
return self.kosync_whisper_forward == SYNC_STRATEGY.WHISPER
end ,
callback = function ( )
self : setWhisperForward ( SYNC_STRATEGY.WHISPER )
end ,
} ,
{
text = _ ( " Prompt " ) ,
checked_func = function ( )
return self.kosync_whisper_forward == SYNC_STRATEGY.PROMPT
end ,
callback = function ( )
self : setWhisperForward ( SYNC_STRATEGY.PROMPT )
end ,
} ,
{
text = _ ( " Disable " ) ,
checked_func = function ( )
return self.kosync_whisper_forward == SYNC_STRATEGY.DISABLE
end ,
callback = function ( )
self : setWhisperForward ( SYNC_STRATEGY.DISABLE )
end ,
} ,
}
2016-09-24 08:20:28 +00:00
} ,
{
2020-04-25 07:04:44 +00:00
text_func = function ( )
return T ( _ ( " Sync to a previous record (%1) " ) , getNameStrategy ( self.kosync_whisper_backward ) )
2016-09-24 08:20:28 +00:00
end ,
2020-04-25 07:04:44 +00:00
sub_item_table = {
{
text = _ ( " Auto " ) ,
checked_func = function ( )
return self.kosync_whisper_backward == SYNC_STRATEGY.WHISPER
end ,
callback = function ( )
self : setWhisperBackward ( SYNC_STRATEGY.WHISPER )
end ,
} ,
{
text = _ ( " Prompt " ) ,
checked_func = function ( )
return self.kosync_whisper_backward == SYNC_STRATEGY.PROMPT
end ,
callback = function ( )
self : setWhisperBackward ( SYNC_STRATEGY.PROMPT )
end ,
} ,
{
text = _ ( " Disable " ) ,
checked_func = function ( )
return self.kosync_whisper_backward == SYNC_STRATEGY.DISABLE
end ,
callback = function ( )
self : setWhisperBackward ( SYNC_STRATEGY.DISABLE )
end ,
} ,
}
2016-09-24 08:20:28 +00:00
} ,
} ,
} ,
2015-03-10 07:09:42 +00:00
{
2016-07-30 00:38:02 +00:00
text = _ ( " Push progress from this device " ) ,
enabled_func = function ( )
return self.kosync_userkey ~= nil
end ,
callback = function ( )
self : updateProgress ( true )
end ,
} ,
{
text = _ ( " Pull progress from other devices " ) ,
2015-03-10 07:09:42 +00:00
enabled_func = function ( )
return self.kosync_userkey ~= nil
end ,
callback = function ( )
self : getProgress ( true )
end ,
2015-09-17 15:27:19 +00:00
} ,
{
text = _ ( " Custom sync server " ) ,
2018-09-04 21:55:58 +00:00
keep_menu_open = true ,
2017-03-28 22:47:18 +00:00
tap_input_func = function ( )
return {
2019-08-24 07:25:38 +00:00
-- @translators Server address defined by user for progress sync.
2017-03-28 22:47:18 +00:00
title = _ ( " Custom progress sync server address " ) ,
input = self.kosync_custom_server or " https:// " ,
type = " text " ,
callback = function ( input )
self : setCustomServer ( input )
end ,
}
end ,
2015-09-17 15:27:19 +00:00
} ,
2020-10-02 06:53:24 +00:00
{
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 )
end ,
} ,
2021-06-21 16:53:06 +00:00
{
text = _ ( " Document matching method " ) ,
sub_item_table = {
{
text = _ ( " Binary. Only identical files will sync progress. " ) ,
checked_func = function ( )
return self.kosync_checksum_method == CHECKSUM_METHOD.BINARY
end ,
callback = function ( )
self : setChecksumMethod ( CHECKSUM_METHOD.BINARY )
end ,
} ,
{
text = _ ( " Filename. Files with the same name will sync progress. " ) ,
checked_func = function ( )
return self.kosync_checksum_method == CHECKSUM_METHOD.FILENAME
end ,
callback = function ( )
self : setChecksumMethod ( CHECKSUM_METHOD.FILENAME )
end ,
} ,
}
} ,
2015-03-09 12:20:32 +00:00
}
2017-02-28 21:46:32 +00:00
}
2015-03-09 12:20:32 +00:00
end
2020-10-02 06:53:24 +00:00
function KOSync : setPagesBeforeUpdate ( pages_before_update )
self.kosync_pages_before_update = pages_before_update > 0 and pages_before_update or nil
self : saveSettings ( )
end
2015-09-17 15:27:19 +00:00
function KOSync : setCustomServer ( server )
2020-06-13 11:47:07 +00:00
logger.dbg ( " set custom server " , server )
2015-09-17 15:27:19 +00:00
self.kosync_custom_server = server ~= " " and server or nil
2017-05-16 09:11:11 +00:00
self : saveSettings ( )
2015-09-17 15:27:19 +00:00
end
2017-08-08 06:29:57 +00:00
function KOSync : setWhisperForward ( strategy )
self.kosync_whisper_forward = strategy
self : saveSettings ( )
end
function KOSync : setWhisperBackward ( strategy )
self.kosync_whisper_backward = strategy
self : saveSettings ( )
end
2021-06-21 16:53:06 +00:00
function KOSync : setChecksumMethod ( method )
self.kosync_checksum_method = method
self : saveSettings ( )
end
2015-03-09 12:20:32 +00:00
function KOSync : login ( )
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
if NetworkMgr : willRerunWhenOnline ( function ( ) self : login ( ) end ) then
2019-11-06 20:02:41 +00:00
return
2015-03-09 12:20:32 +00:00
end
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
2022-12-30 16:38:42 +00:00
local dialog
dialog = MultiInputDialog : new {
2015-03-10 07:49:33 +00:00
title = self.title ,
2022-12-30 16:38:42 +00:00
fields = {
{
text = self.kosync_username ,
hint = " username " ,
} ,
{
hint = " password " ,
text_type = " password " ,
} ,
} ,
2015-03-09 12:20:32 +00:00
buttons = {
{
{
text = _ ( " Cancel " ) ,
2022-12-30 16:38:42 +00:00
id = " close " ,
2015-03-09 12:20:32 +00:00
callback = function ( )
2022-12-30 16:38:42 +00:00
UIManager : close ( dialog )
2015-03-09 12:20:32 +00:00
end ,
} ,
{
text = _ ( " Login " ) ,
callback = function ( )
2022-12-30 16:38:42 +00:00
local username , password = unpack ( dialog : getFields ( ) )
2020-02-25 19:40:50 +00:00
local ok , err = validateUser ( username , password )
if not ok then
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Cannot login: %1 " ) , err ) ,
timeout = 2 ,
} )
else
2022-12-30 16:38:42 +00:00
UIManager : close ( dialog )
2020-02-25 19:40:50 +00:00
UIManager : scheduleIn ( 0.5 , function ( )
self : doLogin ( username , password )
end )
UIManager : show ( InfoMessage : new {
text = _ ( " Logging in. Please wait… " ) ,
timeout = 1 ,
} )
end
2015-03-09 12:20:32 +00:00
end ,
} ,
{
text = _ ( " Register " ) ,
callback = function ( )
2022-12-30 16:38:42 +00:00
local username , password = unpack ( dialog : getFields ( ) )
2020-02-25 19:40:50 +00:00
local ok , err = validateUser ( username , password )
if not ok then
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Cannot register: %1 " ) , err ) ,
timeout = 2 ,
} )
else
2022-12-30 16:38:42 +00:00
UIManager : close ( dialog )
2020-02-25 19:40:50 +00:00
UIManager : scheduleIn ( 0.5 , function ( )
self : doRegister ( username , password )
end )
UIManager : show ( InfoMessage : new {
text = _ ( " Registering. Please wait… " ) ,
timeout = 1 ,
} )
end
2015-03-09 12:20:32 +00:00
end ,
} ,
} ,
} ,
}
2022-12-30 16:38:42 +00:00
UIManager : show ( dialog )
dialog : onShowKeyboard ( )
2015-03-09 12:20:32 +00:00
end
function KOSync : doRegister ( username , password )
local KOSyncClient = require ( " KOSyncClient " )
local client = KOSyncClient : new {
2015-09-17 15:27:19 +00:00
custom_url = self.kosync_custom_server ,
2015-03-09 12:20:32 +00:00
service_spec = self.path .. " /api.json "
}
2020-06-13 11:47:07 +00:00
-- on Android to avoid ANR (no-op on other platforms)
2020-06-13 10:57:08 +00:00
Device : setIgnoreInput ( true )
2020-07-21 21:25:46 +00:00
local userkey = md5 ( password )
2015-03-09 12:20:32 +00:00
local ok , status , body = pcall ( client.register , client , username , userkey )
2016-09-24 08:20:28 +00:00
if not ok then
2015-03-09 12:20:32 +00:00
if status then
UIManager : show ( InfoMessage : new {
2016-09-24 08:20:28 +00:00
text = _ ( " An error occurred while registering: " ) ..
" \n " .. status ,
2015-03-09 12:20:32 +00:00
} )
else
UIManager : show ( InfoMessage : new {
2016-09-24 08:20:28 +00:00
text = _ ( " An unknown error occurred while registering. " ) ,
2015-03-09 12:20:32 +00:00
} )
end
2016-09-24 08:20:28 +00:00
elseif status then
self.kosync_username = username
self.kosync_userkey = userkey
2019-08-29 08:05:49 +00:00
self._menu_to_update : updateItems ( )
2016-09-24 08:20:28 +00:00
UIManager : show ( InfoMessage : new {
text = _ ( " Registered to KOReader server. " ) ,
} )
else
UIManager : show ( InfoMessage : new {
2019-08-07 10:37:07 +00:00
text = body and body.message or _ ( " Unknown server error " ) ,
2016-09-24 08:20:28 +00:00
} )
2015-03-09 12:20:32 +00:00
end
2020-06-13 10:57:08 +00:00
Device : setIgnoreInput ( false )
2017-05-16 09:11:11 +00:00
self : saveSettings ( )
2015-03-09 12:20:32 +00:00
end
function KOSync : doLogin ( username , password )
local KOSyncClient = require ( " KOSyncClient " )
local client = KOSyncClient : new {
2015-09-17 15:27:19 +00:00
custom_url = self.kosync_custom_server ,
2015-03-09 12:20:32 +00:00
service_spec = self.path .. " /api.json "
}
2020-06-13 10:57:08 +00:00
Device : setIgnoreInput ( true )
2020-07-21 21:25:46 +00:00
local userkey = md5 ( password )
2015-03-09 12:20:32 +00:00
local ok , status , body = pcall ( client.authorize , client , username , userkey )
2016-09-24 08:20:28 +00:00
if not ok then
2015-03-09 12:20:32 +00:00
if status then
UIManager : show ( InfoMessage : new {
2016-09-24 08:20:28 +00:00
text = _ ( " An error occurred while logging in: " ) ..
" \n " .. status ,
2015-03-09 12:20:32 +00:00
} )
else
UIManager : show ( InfoMessage : new {
2016-09-24 08:20:28 +00:00
text = _ ( " An unknown error occurred while logging in. " ) ,
2015-03-09 12:20:32 +00:00
} )
end
2020-06-13 10:57:08 +00:00
Device : setIgnoreInput ( false )
2016-09-24 08:20:28 +00:00
return
elseif status then
self.kosync_username = username
self.kosync_userkey = userkey
2019-08-29 08:05:49 +00:00
self._menu_to_update : updateItems ( )
2016-09-24 08:20:28 +00:00
UIManager : show ( InfoMessage : new {
text = _ ( " Logged in to KOReader server. " ) ,
} )
else
UIManager : show ( InfoMessage : new {
2019-08-07 10:37:07 +00:00
text = body and body.message or _ ( " Unknown server error " ) ,
2016-09-24 08:20:28 +00:00
} )
2015-03-09 12:20:32 +00:00
end
2020-06-13 10:57:08 +00:00
Device : setIgnoreInput ( false )
2017-05-16 09:11:11 +00:00
self : saveSettings ( )
2015-03-09 12:20:32 +00:00
end
function KOSync : logout ( )
self.kosync_userkey = nil
2015-03-10 07:09:42 +00:00
self.kosync_auto_sync = true
2019-08-29 08:05:49 +00:00
self._menu_to_update : updateItems ( )
2017-05-16 09:11:11 +00:00
self : saveSettings ( )
2015-03-09 12:20:32 +00:00
end
function KOSync : getLastPercent ( )
if self.ui . document.info . has_pages then
2020-04-15 10:50:13 +00:00
return Math.roundPercent ( self.ui . paging : getLastPercent ( ) )
2015-03-09 12:20:32 +00:00
else
2020-04-15 10:50:13 +00:00
return Math.roundPercent ( self.ui . rolling : getLastPercent ( ) )
2015-03-09 12:20:32 +00:00
end
end
function KOSync : getLastProgress ( )
if self.ui . document.info . has_pages then
return self.ui . paging : getLastProgress ( )
else
return self.ui . rolling : getLastProgress ( )
end
end
2021-06-21 16:53:06 +00:00
function KOSync : getDocumentDigest ( )
if self.kosync_checksum_method == CHECKSUM_METHOD.FILENAME then
return self : getFileNameDigest ( )
else
return self : getFileDigest ( )
end
end
function KOSync : getFileDigest ( )
return self.view . document : fastDigest ( )
end
function KOSync : getFileNameDigest ( )
local file = self.ui . document.file
if not file then return end
local file_path , file_name = util.splitFilePathName ( file ) -- luacheck: no unused
if not file_name then return end
return md5 ( file_name )
end
2015-03-09 12:20:32 +00:00
function KOSync : syncToProgress ( progress )
2020-06-13 11:47:07 +00:00
logger.dbg ( " sync to " , progress )
2015-03-09 12:20:32 +00:00
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
2016-07-30 00:38:02 +00:00
function KOSync : updateProgress ( manual )
2016-09-24 08:20:28 +00:00
if not self.kosync_username or not self.kosync_userkey then
if manual then
promptLogin ( )
2015-03-09 12:20:32 +00:00
end
2016-09-24 08:20:28 +00:00
return
end
2020-10-02 06:53:24 +00:00
if manual and NetworkMgr : willRerunWhenOnline ( function ( ) self : updateProgress ( manual ) end ) then
return
end
2016-09-24 08:20:28 +00:00
local KOSyncClient = require ( " KOSyncClient " )
local client = KOSyncClient : new {
custom_url = self.kosync_custom_server ,
service_spec = self.path .. " /api.json "
}
2021-06-21 16:53:06 +00:00
local doc_digest = self : getDocumentDigest ( )
2016-09-24 08:20:28 +00:00
local progress = self : getLastProgress ( )
local percentage = self : getLastPercent ( )
local ok , err = pcall ( client.update_progress ,
client ,
self.kosync_username ,
self.kosync_userkey ,
doc_digest ,
progress ,
percentage ,
2020-06-13 11:13:23 +00:00
Device.model ,
2016-09-24 08:20:28 +00:00
self.kosync_device_id ,
function ( ok , body )
2020-06-13 11:47:07 +00:00
logger.dbg ( " update progress for " , self.view . document.file , ok )
2016-09-24 08:20:28 +00:00
if manual then
if ok then
UIManager : show ( InfoMessage : new {
text = _ ( " Progress has been pushed. " ) ,
timeout = 3 ,
} )
else
showSyncError ( )
end
end
end )
if not ok then
if manual then showSyncError ( ) end
2020-06-13 11:47:07 +00:00
if err then logger.dbg ( " err: " , err ) end
2015-03-09 12:20:32 +00:00
end
end
2015-03-10 07:09:42 +00:00
function KOSync : getProgress ( manual )
2016-09-24 08:20:28 +00:00
if not self.kosync_username or not self.kosync_userkey then
if manual then
promptLogin ( )
end
return
end
2020-10-02 06:53:24 +00:00
if manual and NetworkMgr : willRerunWhenOnline ( function ( ) self : getProgress ( manual ) end ) then
return
end
2016-09-24 08:20:28 +00:00
local KOSyncClient = require ( " KOSyncClient " )
local client = KOSyncClient : new {
custom_url = self.kosync_custom_server ,
service_spec = self.path .. " /api.json "
}
2021-06-21 16:53:06 +00:00
local doc_digest = self : getDocumentDigest ( )
2016-09-24 08:20:28 +00:00
local ok , err = pcall ( client.get_progress ,
client ,
self.kosync_username ,
self.kosync_userkey ,
doc_digest ,
function ( ok , body )
2020-06-13 11:47:07 +00:00
logger.dbg ( " get progress for " , self.view . document.file , ok , body )
2016-09-24 08:20:28 +00:00
if not ok or not body then
if manual then
2016-07-30 00:38:02 +00:00
showSyncError ( )
2015-03-09 12:20:32 +00:00
end
2016-09-24 08:20:28 +00:00
return
end
if not body.percentage then
if manual then
UIManager : show ( InfoMessage : new {
text = _ ( " No progress found for this document. " ) ,
timeout = 3 ,
} )
end
return
end
2020-06-13 11:13:23 +00:00
if body.device == Device.model
2016-09-24 08:20:28 +00:00
and body.device_id == self.kosync_device_id then
if manual then
UIManager : show ( InfoMessage : new {
text = _ ( " Latest progress is coming from this device. " ) ,
timeout = 3 ,
} )
end
return
end
2020-04-15 13:25:29 +00:00
body.percentage = Math.roundPercent ( body.percentage )
2016-09-24 08:20:28 +00:00
local progress = self : getLastProgress ( )
local percentage = self : getLastPercent ( )
2020-06-13 11:47:07 +00:00
logger.dbg ( " current progress " , percentage )
2016-09-24 08:20:28 +00:00
if percentage == body.percentage
or body.progress == progress then
if manual then
UIManager : show ( InfoMessage : new {
text = _ ( " The progress has already been synchronized. " ) ,
timeout = 3 ,
} )
end
return
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.
self : syncToProgress ( body.progress )
showSyncedMessage ( )
return
end
local self_older
if body.timestamp ~= nil then
self_older = ( body.timestamp > self.last_page_turn_ticks )
else
-- If we are working with old sync server, we can only use
-- percentage field.
self_older = ( body.percentage > percentage )
end
if self_older then
if self.kosync_whisper_forward == SYNC_STRATEGY.WHISPER then
self : syncToProgress ( body.progress )
showSyncedMessage ( )
elseif self.kosync_whisper_forward == SYNC_STRATEGY.PROMPT then
UIManager : show ( ConfirmBox : new {
2016-10-17 09:54:20 +00:00
text = T ( _ ( " Sync to latest location %1% from device '%2'? " ) ,
2016-09-24 08:20:28 +00:00
Math.round ( body.percentage * 100 ) ,
body.device ) ,
ok_callback = function ( )
self : syncToProgress ( body.progress )
end ,
} )
end
else -- if not self_older then
if self.kosync_whisper_backward == SYNC_STRATEGY.WHISPER then
self : syncToProgress ( body.progress )
showSyncedMessage ( )
elseif self.kosync_whisper_backward == SYNC_STRATEGY.PROMPT then
UIManager : show ( ConfirmBox : new {
2016-10-17 09:54:20 +00:00
text = T ( _ ( " Sync to previous location %1% from device '%2'? " ) ,
2016-09-24 08:20:28 +00:00
Math.round ( body.percentage * 100 ) ,
body.device ) ,
ok_callback = function ( )
self : syncToProgress ( body.progress )
end ,
} )
end
end
end )
if not ok then
if manual then showSyncError ( ) end
2020-06-13 11:47:07 +00:00
if err then logger.dbg ( " err: " , err ) end
2015-03-09 12:20:32 +00:00
end
end
2017-05-16 09:11:11 +00:00
function KOSync : saveSettings ( )
2015-03-09 12:20:32 +00:00
local settings = {
2015-09-17 15:27:19 +00:00
custom_server = self.kosync_custom_server ,
2015-03-09 12:20:32 +00:00
username = self.kosync_username ,
userkey = self.kosync_userkey ,
2015-03-10 07:09:42 +00:00
auto_sync = self.kosync_auto_sync ,
2020-10-02 06:53:24 +00:00
pages_before_update = self.kosync_pages_before_update ,
2016-09-24 08:20:28 +00:00
whisper_forward =
2021-03-10 01:14:26 +00:00
( self.kosync_whisper_forward ~= SYNC_STRATEGY.DEFAULT_FORWARD
and self.kosync_whisper_forward
or nil ) ,
2016-09-24 08:20:28 +00:00
whisper_backward =
2021-03-10 01:14:26 +00:00
( self.kosync_whisper_backward ~= SYNC_STRATEGY.DEFAULT_BACKWARD
and self.kosync_whisper_backward
or nil ) ,
2021-06-21 16:53:06 +00:00
checksum_method = self.kosync_checksum_method ,
2015-03-09 12:20:32 +00:00
}
G_reader_settings : saveSetting ( " kosync " , settings )
end
function KOSync : onCloseDocument ( )
2020-06-13 11:47:07 +00:00
logger.dbg ( " on close document " )
2015-03-10 07:09:42 +00:00
if self.kosync_auto_sync then
self : updateProgress ( )
end
2015-03-09 12:20:32 +00:00
end
2016-09-24 08:20:28 +00:00
function KOSync : _onPageUpdate ( page )
if page == nil then
return
end
if self.last_page == - 1 then
self.last_page = page
elseif 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
2020-10-02 06:53:24 +00:00
if self.kosync_pages_before_update and self.page_update_times == self.kosync_pages_before_update then
2016-09-24 08:20:28 +00:00
self.page_update_times = 0
UIManager : scheduleIn ( 1 , function ( ) self : updateProgress ( ) end )
end
end
end
function KOSync : _onResume ( )
UIManager : scheduleIn ( 1 , function ( ) self : getProgress ( ) end )
end
2016-10-23 22:27:39 +00:00
function KOSync : _onFlushSettings ( )
2017-05-21 06:36:41 +00:00
if self.ui == nil or self.ui . document == nil then return end
2016-10-23 22:27:39 +00:00
self : updateProgress ( )
end
function KOSync : _onNetworkConnected ( )
self : _onResume ( )
end
2020-05-05 16:51:02 +00:00
function KOSync : onKOSyncPushProgress ( )
if not self.kosync_userkey then return end
self : updateProgress ( true )
end
function KOSync : onKOSyncPullProgress ( )
if not self.kosync_userkey then return end
self : getProgress ( true )
end
2016-09-24 08:20:28 +00:00
function KOSync : registerEvents ( )
if self.kosync_auto_sync then
self.onPageUpdate = self._onPageUpdate
self.onResume = self._onResume
2016-10-23 22:27:39 +00:00
self.onFlushSettings = self._onFlushSettings
self.onNetworkConnected = self._onNetworkConnected
2016-09-24 08:20:28 +00:00
else
self.onPageUpdate = nil
self.onResume = nil
2016-10-23 22:27:39 +00:00
self.onFlushSettings = nil
self.onNetworkConnected = nil
2016-09-24 08:20:28 +00:00
end
end
2015-03-09 12:20:32 +00:00
return KOSync