Android WebView — Downloading Images
This blog post details how to download an image from a
WebView which is triggered by the user long pressing on the image.
There are a few parts of the problem needing solved
- Capturing the long press event
- Identifying if the user has long pressed on an image
- Naming the image
- Downloading the image
Capturing the Long Press Event in a WebView
To capture a long press event on a
WebView, you need to handle the overridable method in your
Activity subclass called
This method will be invoked upon the user long pressing on something within the
Identifying if the User has Selected an Image
Now that we have a method which is invoked on a long press, we need to determine what the user has actually long pressed on.
We can obtain the
HitTestResult from the
WebView noting that it can be null. We interrogate the type of result to ensure the user has long pressed on an image by limiting hit types to only:
If it is either of these types, we add a menu item to the context menu to give the user the option of downloading the image or not.
Network Requests Only
You might want to add one additional check here to ensure you are dealing with an http or https request.
We can use our good friend
URLUtil to help with that. The method
URLUtil.isNetworkUrl(string) will return true if the URL begins
If it isn’t a network URL, we can refuse to add the download image option to the context menu.
We’ve now added a context menu which lets the user choose to download the image, and so we have to handle the user selecting that context menu item.
By overriding the
onContextItemSelected menu, we can respond to the user selecting our
Download Image option from the context menu, using
CONTEXT_MENU_ID_DOWNLOAD_IMAGE that we defined above.
Can’t forget about those pesky runtime permissions. If you’re planning to download the image to somewhere like
Environment.DIRECTORY_PICTURES in the external storage, you’ll need the user’s permission.
For my app, there’s a chance I haven’t asked for this permission before and so I have to keep a variable which encapsulates what the user is about to download, before possibly segueing down the permission request/response flow. I encapsulate this intention in the
pendingFileDownload variable above.
If I already have the required permissions, I go ahead and instigate the download. Otherwise, I have to ask for the permission first and only upon being granted the permission can I then retrieve the
pendingFileDownload and attempt the download.
Naming the File
In testing, I found that some images on the web downloaded with a sensible file name and others did not. As such, I would recommend manually applying a filename to your download if you cannot fully control which images will be downloaded.
It would be great if there was a utility method which took a String of a URL and tried to guess a suitable filename for it. Oh hello
URLUtil.guessFileName 👋. Glad you could join us. This provides a good guess for the name of the image we are downloading.
Downloading the File
Now we know that the user has long pressed on an image, we know the location of that image, and we have a sensible filename to use when downloading the image, the only thing left to do is … actually download the image.
One way of doing that is to use the built-in Android
After guessing a filename, we can build a
DownloadManager.Request object which encapsulates what we want to download, and how it should be downloaded.
We pass the request onto the
DownloadManager using its
enqueue() method, and then finally set the
pendingFileDownload to null to indicate we have handled it.