A am working on an Android app which connects to a local flask server using WiFi. Then, the app displays images which are stored on the server (RPi3). A click on an image triggers a download request and I want the Android DownloadManager to enqueue the request and download the image. The WiFi network does not provide internet access.
So far, I have been able to test it on Android 6 and Android 8.1 devices and everything works fine. Testing on several Android 9 devices, the download does not start, but after disconnecting from the local network the failed attempts are shown.
Reading other threads about this, I have tried the following:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
And added in the AndroidManifest.xml:
android:networkSecurityConfig="@xml/network_security_config"
Binding the network as described in the first answer here: Using a WiFi without Internet Connection
Playing with the options the DownloadManager.Request class provides, but they do not seem related to this problem:
.setRequiresCharging(false)
.setAllowedOverMetered(false)
.setAllowedOverRoaming(false)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, (String) url);
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
This is the download request I create:
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url))
.setTitle("Some Title")
.setDescription("Downloading a file")
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setDestinationInExternalPublicDir("some_path", (String) url)
.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
DownloadManager downloadManager= (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
downloadID = downloadManager.enqueue(request);
The Flask server (Python3) uses this the return the image:
@app.route('/getFullImage/<image_file>')
def get_full_image(image_file):
log.debug("Client requests full image.")
folder = os.path.join(app.root_path, "media_images")
return flask.send_from_directory(directory=folder, filename=image_file, as_attachment=True)
As said, on Android 6 and 8.1, it worked fine but none of the Android 9 devices start the download. If there is any setting or method I do not know of, I would be happy to learn about it.
Thank you.
Please go to “Settings” -> “Mobile Data Usage”, and turn the options (Stream and Download) off. You can also choose to go to your phone's mobile data settings, and disable Castbox to use your mobile data. For Android users, if you want to save more data, you can turn off "Show Episode Covers" as well.
Before starting your application, Android studio will display following window to select an option where you want to run your Android application. Now just click on button, It will check internet connection as well as it will download image. Out would be as follows and it has fetch the logo from internet.
The key to this problem is to bind the process to the network. This allows your app to use wifi even if the wifi has no internet connectivity.
Here's my code, it's in kotlin but you can also convert it to Java
Just call this anywhere before trying to do a request. For me I placed this in the initialization of a singleton class that I use as network utility.
fun bindProcessToNetwork() {
// Unbind the process from the network
getConnectivityManager().bindProcessToNetwork(null)
// Bind the process to the network
getConnectivityManager().bindProcessToNetwork(getWifiNetwork())
}
These are the other methods I used in bindProcessToNetwork. You'll notice that I used a for loop to iterate through all network and try to determine which one is wifi instead of using connectionManager.activeNetworkInfo
because that will return the cellular network when wifi has no internet connectivity.
private fun getConnectivityManager() : ConnectivityManager {
val context = ApplicationContextProvider.getContext()
return context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
}
private fun getWifiNetwork(): Network? {
val networks = getConnectivityManager().allNetworks
for (network in networks) {
if (isNetworkWifi(network)) {
return network
}
}
return null
}
private fun isNetworkWifi(network: Network): Boolean {
return this.getConnectivityManager().getNetworkCapabilities(network)
.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
}
You can read the documentation for WifiManager and ConnectivityManager for more information. There's a lot that has been deprecated in Level 28 (Android 9). Make sure you only use this code in Android 9 or greater.
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P){
bindProcessToNetwork();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With