diff --git a/base b/base index 171222f22..a9e32ffc7 160000 --- a/base +++ b/base @@ -1 +1 @@ -Subproject commit 171222f22686fb11eb611fa25ed91cb132a79b76 +Subproject commit a9e32ffc7c6ba1644a788f59fc228ba4d056571f diff --git a/frontend/device/android/device.lua b/frontend/device/android/device.lua index 4b4ed49ee..a0c403172 100644 --- a/frontend/device/android/device.lua +++ b/frontend/device/android/device.lua @@ -288,9 +288,15 @@ end function Device:initNetworkManager(NetworkMgr) function NetworkMgr:turnOnWifi(complete_callback) android.openWifiSettings() + if complete_callback then + complete_callback() + end end function NetworkMgr:turnOffWifi(complete_callback) android.openWifiSettings() + if complete_callback then + complete_callback() + end end function NetworkMgr:openSettings() diff --git a/frontend/device/kindle/device.lua b/frontend/device/kindle/device.lua index 78509a5ee..76fd37363 100644 --- a/frontend/device/kindle/device.lua +++ b/frontend/device/kindle/device.lua @@ -15,10 +15,181 @@ require("ffi/fbink_input_h") local function yes() return true end local function no() return false end -- luacheck: ignore +local function kindleGetSavedNetworks() + local haslipc, lipc = pcall(require, "libopenlipclua") -- use our lua lipc library with access to hasharray properties + local lipc_handle + if haslipc then + lipc_handle = lipc.open_no_name() + end + if lipc_handle then + local ha_input = lipc_handle:new_hasharray() -- an empty hash array since we only want to read + local ha_result = lipc_handle:access_hash_property("com.lab126.wifid", "profileData", ha_input) + local profiles = ha_result:to_table() + ha_result:destroy() + ha_input:destroy() + lipc_handle:close() + return profiles + end +end + +local function kindleGetCurrentProfile() + local haslipc, lipc = pcall(require, "libopenlipclua") -- use our lua lipc library with access to hasharray properties + local lipc_handle + if haslipc then + lipc_handle = lipc.open_no_name() + end + if lipc_handle then + local ha_input = lipc_handle:new_hasharray() -- an empty hash array since we only want to read + local ha_result = lipc_handle:access_hash_property("com.lab126.wifid", "currentEssid", ha_input) + local profile = ha_result:to_table()[1] -- theres only a single element + ha_input:destroy() + ha_result:destroy() + lipc_handle:close() + return profile + else + return nil + end +end + +local function kindleAuthenticateNetwork(essid) + local haslipc, lipc = pcall(require, "liblipclua") + local lipc_handle + if haslipc then + lipc_handle = lipc.init("com.github.koreader.networkmgr") + end + if lipc_handle then + lipc_handle:set_string_property("com.lab126.cmd", "ensureConnection", "wifi:" .. essid) + lipc_handle:close() + end +end + +local function kindleSaveNetwork(data) + local haslipc, lipc = pcall(require, "libopenlipclua") -- use our lua lipc library with access to hasharray properties + local lipc_handle + if haslipc then + lipc_handle = lipc.open_no_name() + end + if lipc_handle then + local profile = lipc_handle:new_hasharray() + profile:add_hash() + profile:put_string(0, "essid", data.ssid) + if string.find(data.flags, "WPA") then + profile:put_string(0, "secured", "yes") + profile:put_string(0, "psk", data.password) + profile:put_int(0, "store_nw_user_pref", 0) -- tells amazon we don't want them to have our password + else + profile:put_string(0, "secured", "no") + end + lipc_handle:access_hash_property("com.lab126.wifid", "createProfile", profile):destroy() -- destroy the returned empty ha + profile:destroy() + lipc_handle:close() + end +end + +local function kindleGetScanList() + local _ = require("gettext") + local haslipc, lipc = pcall(require, "libopenlipclua") -- use our lua lipc library with access to hasharray properties + local lipc_handle + if haslipc then + lipc_handle = lipc.open_no_name() + end + if lipc_handle then + if lipc_handle:get_string_property("com.lab126.wifid", "cmState") ~= "CONNECTED" then + local ha_input = lipc_handle:new_hasharray() + local ha_results = lipc_handle:access_hash_property("com.lab126.wifid", "scanList", ha_input) + if ha_results == nil then + -- Shouldn't really happen, access_hash_property will throw if LipcAccessHasharrayProperty failed + ha_input:destroy() + lipc_handle:close() + -- NetworkMgr will ask for a re-scan on seeing an empty table, the second attempt *should* work ;). + return {}, nil + end + local scan_result = ha_results:to_table() + ha_results:destroy() + ha_input:destroy() + lipc_handle:close() + if not scan_result then + -- e.g., to_table hit lha->ha == NULL + return {}, nil + else + return scan_result, nil + end + end + lipc_handle:close() + -- return a fake scan list containing only the currently connected profile :) + local profile = kindleGetCurrentProfile() + return { profile }, nil + else + logger.dbg("kindleGetScanList: Failed to acquire an anonymous lipc handle") + return nil, _("Unable to communicate with the Wi-Fi backend") + end +end + +local function kindleScanThenGetResults() + local _ = require("gettext") + local haslipc, lipc = pcall(require, "liblipclua") + local lipc_handle + if haslipc then + lipc_handle = lipc.init("com.github.koreader.networkmgr") + end + if not lipc_handle then + logger.dbg("kindleScanThenGetResults: Failed to acquire a lipc handle for NetworkMgr") + return nil, _("Unable to communicate with the Wi-Fi backend") + end + + lipc_handle:set_string_property("com.lab126.wifid", "scan", "") -- trigger a scan + + -- Mimic WpaClient:scanThenGetResults: block while waiting for the scan to finish. + -- Ideally, we'd do this via a poll/event workflow, but, eh', this is going to be good enough for now ;p. + -- For future reference, see `lipc-wait-event -m -s 0 -t com.lab126.wifid '*'` + --[[ + -- For a connection: + [00:00:04.675699] cmStateChange "PENDING" + [00:00:04.677402] scanning + [00:00:05.488043] scanComplete + [00:00:05.973188] cmConnected + [00:00:05.977862] cmStateChange "CONNECTED" + [00:00:05.980698] signalStrength "1/5" + [00:00:06.417549] cmConnected + + -- And a disconnection: + [00:01:34.094652] cmDisconnected + [00:01:34.096088] cmStateChange "NA" + [00:01:34.219802] signalStrength "0/5" + [00:01:34.221802] cmStateChange "READY" + [00:01:35.656375] cmIntfNotAvailable + [00:01:35.658710] cmStateChange "NA" + --]] + local done_scanning = false + local wait_cnt = 80 -- 20s in chunks on 250ms + while wait_cnt > 0 do + local scan_state = lipc_handle:get_string_property("com.lab126.wifid", "scanState") + + if scan_state == "idle" then + done_scanning = true + logger.dbg("kindleScanThenGetResults: Wi-Fi scan took", (80 - wait_cnt) * 0.25, "seconds") + break + end + + -- Whether it's still "scanning" or in whatever other state we don't know about, + -- try again until it says it's done. + wait_cnt = wait_cnt - 1 + C.usleep(250 * 1000) + end + lipc_handle:close() + + if done_scanning then + return kindleGetScanList() + else + logger.warn("kindleScanThenGetResults: Timed-out scanning for Wi-Fi networks") + return nil, _("Scanning for Wi-Fi networks timed out") + end +end + local function kindleEnableWifi(toggle) local haslipc, lipc = pcall(require, "liblipclua") - local lipc_handle = nil - if haslipc and lipc then + local lipc_handle + if haslipc then lipc_handle = lipc.init("com.github.koreader.networkmgr") end if lipc_handle then @@ -44,8 +215,8 @@ end --[[ local function isWifiUp() local haslipc, lipc = pcall(require, "liblipclua") - local lipc_handle = nil - if haslipc and lipc then + local lipc_handle + if haslipc then lipc_handle = lipc.init("com.github.koreader.networkmgr") end if lipc_handle then @@ -77,7 +248,7 @@ Test if a kindle device is flagged as a Special Offers device (i.e., ad supporte local function isSpecialOffers() -- Look at the current blanket modules to see if the SO screensavers are enabled... local haslipc, lipc = pcall(require, "liblipclua") - if not (haslipc and lipc) then + if not haslipc then logger.warn("could not load liblipclua:", lipc) return true end @@ -115,7 +286,7 @@ end local function frameworkStopped() if os.getenv("STOP_FRAMEWORK") == "yes" then local haslipc, lipc = pcall(require, "liblipclua") - if not (haslipc and lipc) then + if not haslipc then logger.warn("could not load liblibclua") return end @@ -168,13 +339,19 @@ local Kindle = Generic:extend{ } function Kindle:initNetworkManager(NetworkMgr) - function NetworkMgr:turnOnWifi(complete_callback) - kindleEnableWifi(1) - -- NOTE: As we defer the actual work to lipc, - -- we have no guarantee the Wi-Fi state will have changed by the time kindleEnableWifi returns, - -- so, delay the callback until we at least can ensure isConnect is true. - if complete_callback then - NetworkMgr:scheduleConnectivityCheck(complete_callback) + local haslipc, _ = pcall(require, "liblipclua") + if haslipc then + function NetworkMgr:turnOnWifi(complete_callback, interactive) + kindleEnableWifi(1) + return self:reconnectOrShowNetworkMenu(complete_callback, interactive) + end + else + -- If we can't use the lipc Lua bindings, we can't support any kind of interactive Wi-Fi UI... + function NetworkMgr:turnOnWifi(complete_callback, interactive) + kindleEnableWifi(1) + if complete_callback then + complete_callback() + end end end @@ -194,6 +371,66 @@ function Kindle:initNetworkManager(NetworkMgr) kindleEnableWifi(1) end + function NetworkMgr:authenticateNetwork(network) + kindleAuthenticateNetwork(network.ssid) + return true, nil + end + + -- NOTE: We don't have a disconnectNetwork & releaseIP implementation, + -- which means the "disconnect" button in NetworkSetting kind of does nothing ;p. + + function NetworkMgr:saveNetwork(setting) + kindleSaveNetwork(setting) + end + + function NetworkMgr:getNetworkList() + local scan_list, err = kindleScanThenGetResults() + if not scan_list then + return nil, err + end + + -- trick ui/widget/networksetting into displaying the correct signal strength icon + local qualities = { + [1] = 0, + [2] = 6, + [3] = 31, + [4] = 56, + [5] = 81 + } + + local network_list = {} + local saved_profiles = kindleGetSavedNetworks() + local current_profile = kindleGetCurrentProfile() + for _, network in ipairs(scan_list) do + local password = nil + if network.known == "yes" then + for _, p in ipairs(saved_profiles) do + -- Earlier FW do not have a netid field at all, fall back to essid as that's the best we'll get (we don't get bssid either)... + if (p.netid and p.netid == network.netid) or (p.netid == nil and p.essid == network.essid) then + password = p.psk + break + end + end + end + table.insert(network_list, { + -- signal_level is purely for fun, the widget doesn't do anything with it. The WpaClient backend stores the raw dBa attenuation in it. + signal_level = string.format("%d/%d", network.signal, network.signal_max), + signal_quality = qualities[network.signal], + -- See comment above about netid being unfortunately optional... + connected = (current_profile.netid and current_profile.netid ~= -1 and current_profile.netid == network.netid) + or (current_profile.netid == nil and current_profile.essid ~= "" and current_profile.essid == network.essid), + flags = network.key_mgmt, + ssid = network.essid ~= "" and network.essid, + password = password, + }) + end + return network_list, nil + end + + function NetworkMgr:getCurrentNetwork() + return { ssid = kindleGetCurrentProfile().essid } + end + NetworkMgr.isWifiOn = NetworkMgr.sysfsWifiOn NetworkMgr.isConnected = NetworkMgr.ifHasAnAddress end @@ -835,6 +1072,7 @@ function KindlePaperWhite2:init() fl_intensity_file = "/sys/class/backlight/max77696-bl/brightness", batt_capacity_file = "/sys/devices/system/wario_battery/wario_battery0/battery_capacity", is_charging_file = "/sys/devices/system/wario_charger/wario_charger0/charging", + batt_status_file = "/sys/class/power_supply/max77696-battery/status", hall_file = "/sys/devices/system/wario_hall/wario_hall0/hall_enable", } @@ -991,7 +1229,7 @@ function KindleOasis:init() --- @note See comments in KindleOasis2:init() for details. local haslipc, lipc = pcall(require, "liblipclua") - if haslipc and lipc then + if haslipc then local lipc_handle = lipc.init("com.github.koreader.screen") if lipc_handle then local orientation_code = lipc_handle:get_string_property( @@ -1102,7 +1340,7 @@ function KindleOasis2:init() -- NOTE: It'd take some effort to actually start KOReader while in a LANDSCAPE orientation, -- since they're only exposed inside the stock reader, and not the Home/KUAL Booklets. local haslipc, lipc = pcall(require, "liblipclua") - if haslipc and lipc then + if haslipc then local lipc_handle = lipc.init("com.github.koreader.screen") if lipc_handle then local orientation_code = lipc_handle:get_string_property( @@ -1170,7 +1408,7 @@ function KindleOasis3:init() --- @note The same quirks as on the Oasis 2 apply ;). local haslipc, lipc = pcall(require, "liblipclua") - if haslipc and lipc then + if haslipc then local lipc_handle = lipc.init("com.github.koreader.screen") if lipc_handle then local orientation_code = lipc_handle:get_string_property( @@ -1313,7 +1551,7 @@ function KindleScribe:init() --- @note The same quirks as on the Oasis 2 and 3 apply ;). local haslipc, lipc = pcall(require, "liblipclua") - if haslipc and lipc then + if haslipc then local lipc_handle = lipc.init("com.github.koreader.screen") if lipc_handle then local orientation_code = lipc_handle:get_string_property( diff --git a/frontend/device/kindle/powerd.lua b/frontend/device/kindle/powerd.lua index 24fd0e30c..8f2f5886c 100644 --- a/frontend/device/kindle/powerd.lua +++ b/frontend/device/kindle/powerd.lua @@ -14,7 +14,7 @@ local KindlePowerD = BasePowerD:new{ function KindlePowerD:init() local haslipc, lipc = pcall(require, "liblipclua") - if haslipc and lipc then + if haslipc then self.lipc_handle = lipc.init("com.github.koreader.kindlepowerd") end diff --git a/frontend/device/sdl/device.lua b/frontend/device/sdl/device.lua index 12d8f2d36..6930de770 100644 --- a/frontend/device/sdl/device.lua +++ b/frontend/device/sdl/device.lua @@ -417,20 +417,23 @@ end -- fake network manager for the emulator function Emulator:initNetworkManager(NetworkMgr) - local connectionChangedEvent = function() + local connectionChangedEvent = function(complete_callback) if G_reader_settings:nilOrTrue("emulator_fake_wifi_connected") then UIManager:broadcastEvent(Event:new("NetworkConnected")) else UIManager:broadcastEvent(Event:new("NetworkDisconnected")) end + if complete_callback then + complete_callback() + end end function NetworkMgr:turnOffWifi(complete_callback) G_reader_settings:flipNilOrTrue("emulator_fake_wifi_connected") - UIManager:scheduleIn(2, connectionChangedEvent) + UIManager:scheduleIn(2, connectionChangedEvent, complete_callback) end function NetworkMgr:turnOnWifi(complete_callback) G_reader_settings:flipNilOrTrue("emulator_fake_wifi_connected") - UIManager:scheduleIn(2, connectionChangedEvent) + UIManager:scheduleIn(2, connectionChangedEvent, complete_callback) end function NetworkMgr:isWifiOn() return G_reader_settings:nilOrTrue("emulator_fake_wifi_connected") diff --git a/frontend/ui/network/manager.lua b/frontend/ui/network/manager.lua index 826f25851..8559215fb 100644 --- a/frontend/ui/network/manager.lua +++ b/frontend/ui/network/manager.lua @@ -156,6 +156,9 @@ end -- as opposed to an indirect one (like the beforeWifiAction framework). -- It allows the backend to skip UI prompts for non-interactive use-cases. -- NOTE: May optionally return a boolean, e.g., return false if the backend can guarantee the connection failed. +-- NOTE: These *must* run or appropriately forward complete_callback (e.g., to reconnectOrShowNetworkMenu), +-- as said callback is responsible for schedulig the connectivity check, +-- which, in turn, is responsible for the Event signaling! function NetworkMgr:turnOnWifi(complete_callback, interactive) end function NetworkMgr:turnOffWifi(complete_callback) end -- This function returns the current status of the WiFi radio @@ -175,8 +178,8 @@ end function NetworkMgr:getNetworkInterfaceName() end function NetworkMgr:getNetworkList() end function NetworkMgr:getCurrentNetwork() end -function NetworkMgr:authenticateNetwork() end -function NetworkMgr:disconnectNetwork() end +function NetworkMgr:authenticateNetwork(network) end +function NetworkMgr:disconnectNetwork(network) end -- NOTE: This is currently only called on hasWifiManager platforms! function NetworkMgr:obtainIP() end function NetworkMgr:releaseIP() end @@ -317,55 +320,47 @@ function NetworkMgr:canResolveHostnames() end -- Wrappers around turnOnWifi & turnOffWifi with proper Event signaling -function NetworkMgr:enableWifi(wifi_cb, connectivity_cb, connectivity_widget, interactive) - local status = self:requestToTurnOnWifi(wifi_cb, interactive) +function NetworkMgr:enableWifi(wifi_cb, interactive) + -- NOTE: Let the backend run the wifi_cb via a connectivity check once it's *actually* attempted a connection, + -- as it knows best when that actually happens (especially reconnectOrShowNetworkMenu), unlike us. + local connectivity_cb = function() + -- NOTE: We *could* arguably have multiple connectivity checks running concurrently, + -- but only having a single one running makes things somewhat easier to follow... + if self.pending_connectivity_check then + self:unscheduleConnectivityCheck() + end + + -- This will handle sending the proper Event, manage wifi_was_on, as well as tearing down Wi-Fi in case of failures. + self:scheduleConnectivityCheck(wifi_cb) + end + + -- Some implementations (usually, hasWifiManager) can report whether they were successful + local status = self:requestToTurnOnWifi(connectivity_cb, interactive) -- If turnOnWifi failed, abort early if status == false then logger.warn("NetworkMgr:enableWifi: Connection failed!") self:_abortWifiConnection() return false elseif status == EBUSY then + -- NOTE: This means turnOnWifi was *not* called (this time). logger.warn("NetworkMgr:enableWifi: A previous connection attempt is still ongoing!") - -- We warn, but do keep going on with scheduling the callback iff it was interactive. - -- If it wasn't, it might have been from a beforeWifiAction, and, much like in turnOnWifiAndWaitForConnection, - -- we don't want to risk rescheduling the same thing over and over again. + -- We don't really have a great way of dealing with the wifi_cb in this case, + -- so, much like in turnOnWifiAndWaitForConnection, we'll just drop it... + -- We don't want to run multiple concurrent connectivity checks, + -- which means we'd need to unschedule the pending one, which would effectively rewind the timer, + -- which we don't want, especially if we're non-interactive, + -- as that would risk rescheduling the same thing over and over again... + if wifi_cb then + logger.warn("NetworkMgr:enableWifi: We've had to drop wifi_cb:", wifi_cb) + end + -- Make it more obvious to the user when interactive... if interactive then - -- Unlike the next branch, turnOnWifi was *not* called, so we don't need the extra checks. - self:scheduleConnectivityCheck(connectivity_cb, connectivity_widget) - else - -- No connectivity check to handle that for us, so close the widget *now* - if connectivity_widget then - UIManager:close(connectivity_widget) - end + UIManager:show(InfoMessage:new{ + text = _("A previous connection attempt is still ongoing, this one will be ignored!"), + timeout = 3, + }) end return - else - -- Some turnOnWifi implementations may fire a connectivity check, - -- but we *need* our own, because of the callback & widget passing, - -- as we might have been called by the "prompt" beforeWifiAction... - -- NOTE: We *could* arguably have multiple connectivity checks running concurrently, - -- but only having a single one running makes things somewhat easier to follow... - -- NOTE: Also, most of the platforms that use a connectivity check in turnOnWifi do it to handle wifi_cb, - -- so we'll want to preserve it by wrapping both possible callbacks in a single function... - local wrapped_cb - if self.pending_connectivity_check then - self:unscheduleConnectivityCheck() - - wrapped_cb = function() - if wifi_cb then - wifi_cb() - end - if connectivity_cb then - connectivity_cb() - end - end - else - -- If the turnOnWifi implementation didn't rely on a connectivity check, assume wifi_cb was already run. - wrapped_cb = connectivity_cb - end - - -- This will handle sending the proper Event, manage wifi_was_on, as well as tearing down Wi-Fi in case of failures. - self:scheduleConnectivityCheck(wrapped_cb, connectivity_widget) end return true @@ -379,6 +374,17 @@ function NetworkMgr:disableWifi(cb, interactive) end end UIManager:broadcastEvent(Event:new("NetworkDisconnecting")) + + -- NOTE: This is a subset of _abortWifiConnection, in case we disable wifi during a connection attempt. + -- Cancel any pending connectivity check, because it wouldn't achieve anything + self:unscheduleConnectivityCheck() + -- Make sure we don't have an async script running... + if Device:hasWifiRestore() and not Device:isKindle() then + os.execute("pkill -TERM restore-wifi-async.sh 2>/dev/null") + end + -- Can't be connecting since we're killing Wi-Fi ;) + self.pending_connection = false + self:turnOffWifi(complete_callback) if interactive then @@ -396,7 +402,7 @@ function NetworkMgr:toggleWifiOn(complete_callback, long_press, interactive) self.wifi_toggle_long_press = long_press - self:enableWifi(complete_callback, nil, nil, interactive) + self:enableWifi(complete_callback, interactive) UIManager:close(toggle_im) end @@ -445,13 +451,26 @@ end -- NOTE: Currently only has a single caller, the Menu entry, so it's always flagged as interactive function NetworkMgr:promptWifi(complete_callback, long_press, interactive) + local text = _("Wi-Fi is enabled, but you're currently not connected to a network.") + -- Detail whether there's an attempt and/or a connectivity check in progress. + if self.pending_connection then + -- NOTE: Incidentally, this means that tapping Connect would yield EBUSY, so we gray it out... + text = text .. "\n" .. _("Please note that a connection attempt is currently in progress!") + end + if self.pending_connectivity_check then + text = text .. "\n" .. _("KOReader is currently waiting for connectivity. This may take up to 45s, so you may just want to try again later.") + end + text = text .. "\n" .. _("How would you like to proceed?") UIManager:show(MultiConfirmBox:new{ - text = _("Wi-Fi is enabled, but you're currently not connected to a network.\nHow would you like to proceed?"), + text = text, + -- "Cancel" could be construed as "cancel the current attempt", which is not what this does ;p. + cancel_text = _("Dismiss"), choice1_text = _("Turn Wi-Fi off"), choice1_callback = function() self:toggleWifiOff(complete_callback, interactive) end, choice2_text = _("Connect"), + choice2_enabled = not self.pending_connection, choice2_callback = function() self:toggleWifiOn(complete_callback, long_press, interactive) end, @@ -474,10 +493,15 @@ function NetworkMgr:turnOnWifiAndWaitForConnection(callback) UIManager:show(info) UIManager:forceRePaint() - -- NOTE: This is a slightly tweaked variant of enableWifi, because of our peculiar connectivityCheck usage... - -- Some implementations (usually, hasWifiManager) can report whether they were successfull - local status = self:requestToTurnOnWifi() - -- If turnOnWifi failed, abort early + -- NOTE: This is a slightly tweaked variant of enableWifi, in order to handle our info widget... + local connectivity_cb = function() + if self.pending_connectivity_check then + self:unscheduleConnectivityCheck() + end + + self:scheduleConnectivityCheck(callback, info) + end + local status = self:requestToTurnOnWifi(connectivity_cb) if status == false then logger.warn("NetworkMgr:turnOnWifiAndWaitForConnection: Connection failed!") self:_abortWifiConnection() @@ -489,16 +513,8 @@ function NetworkMgr:turnOnWifiAndWaitForConnection(callback) -- but it's just plain saner to just abort here, as we'd risk calling the same thing over and over... UIManager:close(info) return - else - -- Some turnOnWifi implementations may fire a connectivity check, - -- but we *need* our own, because of the callback & widget passing. - if self.pending_connectivity_check then - self:unscheduleConnectivityCheck() - end end - self:scheduleConnectivityCheck(callback, info) - return info end @@ -1002,7 +1018,7 @@ 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:hasWifiManager() and not Device:isEmulator() end, + enabled_func = function() return Device:hasWifiManager() or Device:isEmulator() end, callback = function() G_reader_settings:flipNilOrTrue("auto_dismiss_wifi_scan") end, } end @@ -1041,7 +1057,7 @@ function NetworkMgr:reconnectOrShowNetworkMenu(complete_callback, interactive) UIManager:close(info) if network_list == nil then UIManager:show(InfoMessage:new{text = err}) - return + return false end -- NOTE: Fairly hackish workaround for #4387, -- rescan if the first scan appeared to yield an empty list. @@ -1051,66 +1067,75 @@ function NetworkMgr:reconnectOrShowNetworkMenu(complete_callback, interactive) network_list, err = self:getNetworkList() if network_list == nil then UIManager:show(InfoMessage:new{text = err}) - return + return false end 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 + -- true: we're connected; false: things went kablooey; nil: we don't know yet (e.g., interactive) + -- NOTE: false *will* lead enableWifi to kill Wi-Fi via _abortWifiConnection! + local success + 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 preferred 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... + -- NOTE: We can't really skip this, even when we force showing the scan list, + -- as the backend *will* connect in the background regardless of what we do, + -- and we *need* our complete_callback to run, + -- which would not be the case if we were to just dismiss the scan list, + -- especially since it wouldn't show as "connected" in this case... + 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 probably 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 preferred 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 + network.connected = true + break end end end + end - if success then - self:obtainIP() - if complete_callback then - complete_callback() - end - UIManager:show(InfoMessage:new{ - tag = "NetworkMgr", -- for crazy KOSync purposes - text = T(_("Connected to network %1"), BD.wrap(util.fixUtf8(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 + -- NOTE: On Kindle, we don't have an explicit obtainIP implementation, + -- and authenticateNetwork is async, + -- so we don't *actually* have a full connection yet, + -- we've just *started* connecting to the requested network... + UIManager:show(InfoMessage:new{ + tag = "NetworkMgr", -- for crazy KOSync purposes + text = T(_(Device:isKindle() and "Connecting to network %1…" or "Connected to network %1"), BD.wrap(util.fixUtf8(ssid, "�"))), + timeout = 3, + }) + else + UIManager:show(InfoMessage:new{ + text = err_msg, + timeout = 3, + }) 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... @@ -1120,9 +1145,19 @@ function NetworkMgr:reconnectOrShowNetworkMenu(complete_callback, interactive) network_list = network_list, connect_callback = complete_callback, }) + else + -- Let enableWifi tear it all down when we're non-interactive + success = false end + elseif self.wifi_toggle_long_press then + -- Success, but we asked for the list, show it w/o any callbacks. + -- (We *could* potentially setup a pair of callbacks that just send Network* events, but it's probably not worth it). + UIManager:show(require("ui/widget/networksetting"):new{ + network_list = network_list, + }) end + self.wifi_toggle_long_press = nil return success end diff --git a/frontend/ui/network/networklistener.lua b/frontend/ui/network/networklistener.lua index 2e783eaef..3a0d0cf1f 100644 --- a/frontend/ui/network/networklistener.lua +++ b/frontend/ui/network/networklistener.lua @@ -30,7 +30,7 @@ local function enableWifi() -- NB Normal widgets should use NetworkMgr:promptWifiOn() -- (or, better yet, the NetworkMgr:beforeWifiAction wrappers: NetworkMgr:runWhenOnline() & co.) -- This is specifically the toggle Wi-Fi action, so consent is implied. - NetworkMgr:enableWifi(nil, nil, nil, true) -- flag it as interactive + NetworkMgr:enableWifi(nil, true) -- flag it as interactive UIManager:close(toggle_im) end diff --git a/frontend/ui/widget/networksetting.lua b/frontend/ui/widget/networksetting.lua index eb16cccd7..2ed25ddb9 100644 --- a/frontend/ui/widget/networksetting.lua +++ b/frontend/ui/widget/networksetting.lua @@ -483,6 +483,14 @@ function NetworkSetting:init() } end + -- If the backend is already authenticated, + -- and NetworkMgr:reconnectOrShowNetworkMenu somehow missed it, + -- expedite the process. + -- Yes, this is a very old codepath that's hardly ever exercised anymore... + if not self.connect_callback then + return + end + UIManager:nextTick(function() local connected_item = self:getConnectedItem() if connected_item ~= nil then @@ -494,9 +502,7 @@ function NetworkSetting:init() text = T(_("Connected to network %1"), BD.wrap(connected_item.display_ssid)), timeout = 3, }) - if self.connect_callback then - self.connect_callback() - end + self.connect_callback() end end) end @@ -516,4 +522,11 @@ function NetworkSetting:onTapClose(arg, ges_ev) end end +function NetworkSetting:onCloseWidget() + -- If we don't have a connectivity check ticking, assume we're done with this connection attempt *now* + if not NetworkMgr.pending_connectivity_check then + NetworkMgr.pending_connection = false + end +end + return NetworkSetting