mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
* On Kobo, kill WiFi on startup if we detect an inconsistent state... Untested, not terribly pretty. The other solution is to slow down the Wi-Fi meny by doing the same check for the "Wi-Fi connection" checkbox as in the later wifi_status one... * Don't enable auto_restore_wifi by default It's liable to silently murder batteries for no good reason, given that we prompt to enable WiFi by default when needed, and we otherwise have no actual need to keep WiFi on in the background. re #2215 (in particular, this directly contradicts @houqp in https://github.com/koreader/koreader/pull/2215#discussion_r74696133 ;)).
376 lines
13 KiB
Lua
376 lines
13 KiB
Lua
local ConfirmBox = require("ui/widget/confirmbox")
|
|
local DataStorage = require("datastorage")
|
|
local Device = require("device")
|
|
local InfoMessage = require("ui/widget/infomessage")
|
|
local LuaSettings = require("luasettings")
|
|
local UIManager = require("ui/uimanager")
|
|
local ffiutil = require("ffi/util")
|
|
local logger = require("logger")
|
|
local _ = require("gettext")
|
|
local T = ffiutil.template
|
|
|
|
local NetworkMgr = {}
|
|
|
|
function NetworkMgr:readNWSettings()
|
|
self.nw_settings = LuaSettings:open(DataStorage:getSettingsDir().."/network.lua")
|
|
end
|
|
|
|
function NetworkMgr:init()
|
|
-- On Kobo, kill WiFi if NetworkMgr:isWifiOn() and NOT NetworkMgr:isConnected()
|
|
-- (i.e., if the launcher left the WiFi in an inconsistent state: modules loaded, but no route to gateway).
|
|
if Device:isKobo() and self:isWifiOn() and not self:isConnected() then
|
|
logger.info("Kobo WiFi: Left in an inconsistent state by launcher!")
|
|
self:turnOffWifi()
|
|
end
|
|
|
|
self.wifi_was_on = G_reader_settings:isTrue("wifi_was_on")
|
|
if self.wifi_was_on and G_reader_settings:isTrue("auto_restore_wifi") then
|
|
self:restoreWifiAsync()
|
|
end
|
|
end
|
|
|
|
-- Following methods are Device specific which need to be initialized in
|
|
-- Device:initNetworkManager. Some of them can be set by calling
|
|
-- NetworkMgr:setWirelessBackend
|
|
function NetworkMgr:turnOnWifi() end
|
|
function NetworkMgr:turnOffWifi() end
|
|
function NetworkMgr:isWifiOn() end
|
|
function NetworkMgr:getNetworkList() end
|
|
function NetworkMgr:getCurrentNetwork() end
|
|
function NetworkMgr:authenticateNetwork() end
|
|
function NetworkMgr:disconnectNetwork() end
|
|
function NetworkMgr:obtainIP() end
|
|
function NetworkMgr:releaseIP() end
|
|
-- This function should unblockly call both turnOnWifi() and obtainIP().
|
|
function NetworkMgr:restoreWifiAsync() end
|
|
-- End of device specific methods
|
|
|
|
function NetworkMgr:promptWifiOn(complete_callback)
|
|
UIManager:show(ConfirmBox:new{
|
|
text = _("Do you want to turn on Wi-Fi?"),
|
|
ok_text = _("Turn on"),
|
|
ok_callback = function()
|
|
self.wifi_was_on = true
|
|
G_reader_settings:saveSetting("wifi_was_on", true)
|
|
self:turnOnWifi(complete_callback)
|
|
end,
|
|
})
|
|
end
|
|
|
|
function NetworkMgr:promptWifiOff(complete_callback)
|
|
UIManager:show(ConfirmBox:new{
|
|
text = _("Do you want to turn off Wi-Fi?"),
|
|
ok_text = _("Turn off"),
|
|
ok_callback = function()
|
|
self.wifi_was_on = false
|
|
G_reader_settings:saveSetting("wifi_was_on", false)
|
|
self:turnOffWifi(complete_callback)
|
|
end,
|
|
})
|
|
end
|
|
|
|
function NetworkMgr:turnOnWifiAndWaitForConnection(callback)
|
|
NetworkMgr:turnOnWifi()
|
|
local timeout = 30
|
|
local retry_count = 0
|
|
local info = InfoMessage:new{ text = T(_("Enabling Wi-Fi. Waiting for Internet connection…\nTimeout %1 seconds."), timeout)}
|
|
UIManager:show(info)
|
|
UIManager:forceRePaint()
|
|
while not NetworkMgr:isOnline() and retry_count < timeout do
|
|
ffiutil.sleep(1)
|
|
retry_count = retry_count + 1
|
|
end
|
|
UIManager:close(info)
|
|
if retry_count == timeout then
|
|
UIManager:show(InfoMessage:new{ text = _("Error connecting to the network") })
|
|
return
|
|
end
|
|
if callback then callback() end
|
|
end
|
|
|
|
function NetworkMgr:beforeWifiAction(callback)
|
|
local wifi_enable_action = G_reader_settings:readSetting("wifi_enable_action")
|
|
if wifi_enable_action == "turn_on" then
|
|
NetworkMgr:turnOnWifiAndWaitForConnection(callback)
|
|
else
|
|
NetworkMgr:promptWifiOn(callback)
|
|
end
|
|
end
|
|
|
|
function NetworkMgr:isConnected()
|
|
if Device:isAndroid() or Device:isCervantes() then
|
|
return self:isWifiOn()
|
|
else
|
|
-- `-c1` try only once; `-w2` wait 2 seconds
|
|
return 0 == os.execute([[ping -c1 -w2 $(/sbin/route -n | awk '$4 == "UG" {print $2}')]])
|
|
end
|
|
end
|
|
|
|
function NetworkMgr:isOnline()
|
|
local socket = require("socket")
|
|
-- Microsoft uses `dns.msftncsi.com` for Windows, see
|
|
-- <https://technet.microsoft.com/en-us/library/ee126135#BKMK_How> for
|
|
-- more information. They also check whether <http://www.msftncsi.com/ncsi.txt>
|
|
-- returns `Microsoft NCSI`.
|
|
return socket.dns.toip("dns.msftncsi.com") ~= nil
|
|
end
|
|
|
|
function NetworkMgr:setHTTPProxy(proxy)
|
|
local http = require("socket.http")
|
|
http.PROXY = proxy
|
|
if proxy then
|
|
G_reader_settings:saveSetting("http_proxy", proxy)
|
|
G_reader_settings:saveSetting("http_proxy_enabled", true)
|
|
else
|
|
G_reader_settings:saveSetting("http_proxy_enabled", false)
|
|
end
|
|
end
|
|
|
|
function NetworkMgr:getWifiMenuTable()
|
|
return {
|
|
text = _("Wi-Fi connection"),
|
|
enabled_func = function() return Device:isAndroid() or Device:isCervantes() or Device:isKindle() or Device:isKobo() or Device:isSonyPRSTUX() end,
|
|
checked_func = function() return NetworkMgr:isWifiOn() end,
|
|
callback = function(touchmenu_instance)
|
|
local wifi_status = NetworkMgr:isWifiOn() and NetworkMgr:isConnected()
|
|
local complete_callback = function()
|
|
-- notify touch menu to update item check state
|
|
touchmenu_instance:updateItems()
|
|
local Event = require("ui/event")
|
|
-- if wifi was on, this callback will only be executed when the network has been
|
|
-- disconnected.
|
|
if wifi_status then
|
|
UIManager:broadcastEvent(Event:new("NetworkDisconnected"))
|
|
else
|
|
UIManager:broadcastEvent(Event:new("NetworkConnected"))
|
|
end
|
|
end
|
|
if wifi_status then
|
|
NetworkMgr:promptWifiOff(complete_callback)
|
|
else
|
|
NetworkMgr:promptWifiOn(complete_callback)
|
|
end
|
|
end
|
|
}
|
|
end
|
|
|
|
function NetworkMgr:getProxyMenuTable()
|
|
local proxy_enabled = function()
|
|
return G_reader_settings:readSetting("http_proxy_enabled")
|
|
end
|
|
local proxy = function()
|
|
return G_reader_settings:readSetting("http_proxy")
|
|
end
|
|
return {
|
|
text_func = function()
|
|
return T(_("HTTP proxy %1"), (proxy_enabled() and proxy() or ""))
|
|
end,
|
|
checked_func = function() return proxy_enabled() end,
|
|
callback = function()
|
|
if not proxy_enabled() and proxy() then
|
|
NetworkMgr:setHTTPProxy(proxy())
|
|
elseif proxy_enabled() then
|
|
NetworkMgr:setHTTPProxy(nil)
|
|
end
|
|
if not proxy() then
|
|
UIManager:show(InfoMessage:new{
|
|
text = _("Tip:\nLong press on this menu entry to configure HTTP proxy."),
|
|
})
|
|
end
|
|
end,
|
|
hold_input = {
|
|
title = _("Enter proxy address"),
|
|
type = "text",
|
|
hint = proxy() or "",
|
|
callback = function(input)
|
|
if input ~= "" then
|
|
NetworkMgr:setHTTPProxy(input)
|
|
end
|
|
end,
|
|
}
|
|
}
|
|
end
|
|
|
|
function NetworkMgr:getRestoreMenuTable()
|
|
return {
|
|
text = _("Automatically restore Wi-Fi connection after resume"),
|
|
checked_func = function() return G_reader_settings:isTrue("auto_restore_wifi") end,
|
|
enabled_func = function() return Device:isKobo() or Device:isCervantes() end,
|
|
callback = function() G_reader_settings:flipNilOrFalse("auto_restore_wifi") end,
|
|
}
|
|
end
|
|
|
|
function NetworkMgr:getInfoMenuTable()
|
|
return {
|
|
text = _("Network info"),
|
|
keep_menu_open = true,
|
|
-- TODO: also show network info when device is authenticated to router but offline
|
|
enabled_func = function() return self:isWifiOn() end,
|
|
callback = function()
|
|
if Device.retrieveNetworkInfo then
|
|
UIManager:show(InfoMessage:new{
|
|
text = Device:retrieveNetworkInfo(),
|
|
})
|
|
else
|
|
UIManager:show(InfoMessage:new{
|
|
text = _("Could not retrieve network info."),
|
|
timeout = 3,
|
|
})
|
|
end
|
|
end
|
|
}
|
|
end
|
|
|
|
|
|
function NetworkMgr:getBeforeWifiActionMenuTable()
|
|
local wifi_enable_action_setting = G_reader_settings:readSetting("wifi_enable_action") or "prompt"
|
|
local wifi_enable_actions = {
|
|
turn_on = {_("turn on"), _("Turn on (experimental)")},
|
|
prompt = {_("prompt"), _("Prompt")},
|
|
}
|
|
local action_table = function(wifi_enable_action)
|
|
return {
|
|
text = wifi_enable_actions[wifi_enable_action][2],
|
|
checked_func = function()
|
|
return wifi_enable_action_setting == wifi_enable_action
|
|
end,
|
|
callback = function()
|
|
wifi_enable_action_setting = wifi_enable_action
|
|
G_reader_settings:saveSetting("wifi_enable_action", wifi_enable_action)
|
|
end,
|
|
}
|
|
end
|
|
return {
|
|
text_func = function()
|
|
return T(_("Action when Wi-Fi is off: %1"),
|
|
wifi_enable_actions[wifi_enable_action_setting][1]
|
|
)
|
|
end,
|
|
sub_item_table = {
|
|
action_table("turn_on"),
|
|
action_table("prompt"),
|
|
}
|
|
}
|
|
end
|
|
|
|
function NetworkMgr:getDismissScanMenuTable()
|
|
return {
|
|
text = _("Dismiss Wi-Fi scan popup after connection"),
|
|
checked_func = function() return G_reader_settings:nilOrTrue("auto_dismiss_wifi_scan") end,
|
|
--enabled_func = function() return Device:isKobo() end,
|
|
callback = function() G_reader_settings:flipNilOrTrue("auto_dismiss_wifi_scan") end,
|
|
}
|
|
end
|
|
|
|
function NetworkMgr:getMenuTable(common_settings)
|
|
common_settings.network_wifi = self:getWifiMenuTable()
|
|
common_settings.network_proxy = self:getProxyMenuTable()
|
|
common_settings.network_restore = self:getRestoreMenuTable()
|
|
common_settings.network_info = self:getInfoMenuTable()
|
|
common_settings.network_before_wifi_action = self:getBeforeWifiActionMenuTable()
|
|
common_settings.network_dismiss_scan = self:getDismissScanMenuTable()
|
|
end
|
|
|
|
function NetworkMgr:showNetworkMenu(complete_callback)
|
|
local info = InfoMessage:new{text = _("Scanning…")}
|
|
UIManager:show(info)
|
|
UIManager:nextTick(function()
|
|
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 (table.getn(network_list) == 0) then
|
|
network_list, err = self:getNetworkList()
|
|
if network_list == nil then
|
|
UIManager:show(InfoMessage:new{text = err})
|
|
return
|
|
end
|
|
end
|
|
UIManager:show(require("ui/widget/networksetting"):new{
|
|
network_list = network_list,
|
|
connect_callback = complete_callback,
|
|
})
|
|
end)
|
|
end
|
|
|
|
function NetworkMgr:reconnectOrShowNetworkMenu(complete_callback)
|
|
local info = InfoMessage:new{text = _("Scanning…")}
|
|
UIManager:show(info)
|
|
UIManager:nextTick(function()
|
|
local network_list, err = self:getNetworkList()
|
|
UIManager:close(info)
|
|
if network_list == nil then
|
|
UIManager:show(InfoMessage:new{text = err})
|
|
return
|
|
end
|
|
table.sort(network_list,
|
|
function(l, r) return l.signal_quality > r.signal_quality end)
|
|
local success = false
|
|
table.foreach(network_list,
|
|
function(idx, network)
|
|
if network.password then
|
|
success = NetworkMgr:authenticateNetwork(network)
|
|
if success then
|
|
NetworkMgr:obtainIP()
|
|
if complete_callback then
|
|
complete_callback()
|
|
end
|
|
UIManager:show(InfoMessage:new{
|
|
text = T(_("Connected to network %1"), network.ssid),
|
|
timeout = 3,
|
|
})
|
|
return
|
|
end
|
|
end
|
|
end)
|
|
if not success then
|
|
UIManager:show(require("ui/widget/networksetting"):new{
|
|
network_list = network_list,
|
|
connect_callback = complete_callback,
|
|
})
|
|
end
|
|
end)
|
|
end
|
|
|
|
function NetworkMgr:saveNetwork(setting)
|
|
if not self.nw_settings then self:readNWSettings() end
|
|
|
|
self.nw_settings:saveSetting(setting.ssid, {
|
|
ssid = setting.ssid,
|
|
password = setting.password,
|
|
psk = setting.psk,
|
|
flags = setting.flags,
|
|
})
|
|
self.nw_settings:flush()
|
|
end
|
|
|
|
function NetworkMgr:deleteNetwork(setting)
|
|
if not self.nw_settings then self:readNWSettings() end
|
|
self.nw_settings:delSetting(setting.ssid)
|
|
self.nw_settings:flush()
|
|
end
|
|
|
|
function NetworkMgr:getAllSavedNetworks()
|
|
if not self.nw_settings then self:readNWSettings() end
|
|
return self.nw_settings
|
|
end
|
|
|
|
function NetworkMgr:setWirelessBackend(name, options)
|
|
require("ui/network/"..name).init(self, options)
|
|
end
|
|
|
|
-- set network proxy if global variable NETWORK_PROXY is defined
|
|
if NETWORK_PROXY then
|
|
NetworkMgr:setHTTPProxy(NETWORK_PROXY)
|
|
end
|
|
|
|
Device:initNetworkManager(NetworkMgr)
|
|
NetworkMgr:init()
|
|
|
|
return NetworkMgr
|