The connection¶
Whether the phone is online, over what, and being told when that changes: the "No connection" banner, sending what waited once the connection is back, keeping a big download for Wi-Fi.
from apkpy_lib import *
home = Screen(id="home")
online = state(True)
banner = label("No connection", id="banner", screen=home)
online.bind_visibility(banner, when=False)
def changed():
online.set(network.online())
def opened():
changed()
network.on_change(changed)
lifecycle(home, on_mount=opened)
run(start_screen=home)
| Call | |
|---|---|
network.online() |
Whether the phone reaches the internet right now — for an if. |
network.kind() |
What the connection is: "wifi", "mobile", "ethernet", "other" or "none". |
network.metered() |
Whether the person pays for what goes over it — for an if. |
network.on_change(function) |
Call the function whenever one of those three answers changes. |
Nobody is asked for anything: the permission this needs is one Android gives every app, and ApkPy adds it to the manifest when you use one of these calls.
Online¶
network.online() is yes when the phone has a connection and has seen it
reach the internet. That second half matters:
| The phone is on… | kind() |
online() |
|---|---|---|
| Wi-Fi at home | wifi |
yes |
| Mobile data | mobile |
yes |
| A hotel Wi-Fi that wants a login page first | wifi |
no |
| A router whose line is down | wifi |
no |
| Aeroplane mode, or no signal | none |
no |
| Wi-Fi, through a VPN | wifi |
yes |
| Aeroplane mode, with a VPN app still running | none |
no |
The last row is one Android gets wrong on its own: it hands every app the VPN's tunnel, and the tunnel stays up, still marked as reaching the internet, with nothing beneath it. ApkPy looks beneath.
It is the answer to "is it worth trying?". It is not a promise that the next
request works — your own server can be down with the phone perfectly online —
so https still tells you how each call went.
def send():
if network.online():
https.post(URL, data={"text": text.get_value()}, on_response=sent)
else:
note.set_value("Waiting for a connection")
Metered¶
Mobile data, and a Wi-Fi that is somebody's phone shared as a hotspot, are
connections the person pays for by what goes over them. network.metered()
says so, and is the question to ask before a large download — rather than
kind() == "wifi", which is wrong for a hotspot and for a mobile plan with
no limit.
def download():
if network.online() and not network.metered():
downloads.start(MAP_URL, on_result=landed)
else:
note.set_value("Waiting for Wi-Fi")
Being told¶
network.on_change(changed) calls changed() whenever what online(),
kind() or metered() answer is no longer what it was.
The function is one you wrote with def at the top level of the file,
that takes nothing — it is told that something changed, and asks what:
Write its name without brackets. A lambda, changed() with its brackets, or
a function that takes arguments stops the build and says which line to write
instead (U2048); the Previewer refuses them in the same words.
It is told of changes, not of how things are. Asking does not call the
function, so call it once yourself as the screen opens, as opened() does at
the top of this page.
It belongs to the screen that asked, and is told only while that screen is on show:
- a change that comes and goes while the screen is away — the app in the background, another screen on top — is not told;
- one that is still there when the screen comes back is told once, then;
- closing the screen ends it. Turning the phone or changing the theme does not: Android builds the screen again and it keeps what it asked for.
Asking twice with the same function does nothing more; a screen can ask with several functions, and each is called once for a change.
Wi-Fi to mobile data is one change. Walking out of the house, the phone
has mobile data before it lets go of the Wi-Fi: the function is called once,
with kind() now "mobile" — not for a loss and then a return.
Ask from a function — the screen's on_mount, or a button. A
network.on_change(...) at the top of the file would run before there is a
screen for it to belong to, and stops the build (U2048).
Showing something while offline¶
Bind it to a state, as at the top of this page:
and set the state where the connection is read: online.set(network.online()).
The banner then starts hidden, on the phone and in the Previewer, with nothing
to flash as the app opens.
What a change is not for¶
Work that has to wait for a connection with the app closed: that is
background_job(..., requires_network=True), which
Android runs when the connection is there. Inside a background job these
calls stop the build (J7004): the job is already told to wait for the
network.
In the Previewer¶
A computer is never in a lift, so nothing an app does without a connection would ever be seen at the desk. The connection is the Previewer's to take away: click the connection in the status bar, top right, and it goes from Wi-Fi to mobile data to none, and round again.
| The status bar shows | kind() |
online() |
metered() |
|---|---|---|---|
| Bars and the Wi-Fi fan | wifi |
yes | no |
| Bars alone | mobile |
yes | yes |
| An aeroplane | none |
no | no |
With none, an https call fails with the words Android gives on a phone that
is offline (Unable to resolve host …), and a background_job that needs the
network waits. Pictures, uploads and a websocket still use the computer's own
connection.
"ethernet", "other" and a Wi-Fi that is not online are answers a phone
gives and the Previewer does not stand in for.