0

I need to download file when user clicks on link in WebView. The action works properly when I do the same action in chrome app, but it doesn't work in webview. I have referred multiple questions and tried implementing solutions provided but it doesn't work.

Stackoverflow questions referred and tried solutions mentioned.

Download file inside WebView

Android Webview not triggering a href download file

How to download files from webview Android?

WebView code that I used,

    wv_main.getSettings().setJavaScriptEnabled(true);
    wv_main.getSettings().setDomStorageEnabled(true);
    wv_main.getSettings().setAllowFileAccess(true);
    wv_main.getSettings().setAllowFileAccessFromFileURLs(true);
    wv_main.getSettings().setAllowUniversalAccessFromFileURLs(true);
    wv_main.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
    wv_main.getSettings().setBlockNetworkLoads(false);
    wv_main.getSettings().setAllowContentAccess(true);
    wv_main.getSettings().setPluginState(WebSettings.PluginState.ON);
    wv_main.setDownloadListener(new DownloadListener() {
        @Override
        public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));

            request.setDescription("Download file...");
            request.setTitle(URLUtil.guessFileName(url, contentDisposition, mimetype));
            request.allowScanningByMediaScanner();
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //Notify client once download is completed!
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, URLUtil.guessFileName(url, contentDisposition, mimetype));
            DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
            dm.enqueue(request);
            Toast.makeText(getApplicationContext(), "Downloading File", Toast.LENGTH_LONG).show();
        }
    });

    wv_main.setWebViewClient(new WebViewClient() {

        private int webViewPreviousState;
        private final int PAGE_STARTED = 0x1;
        private final int PAGE_REDIRECTED = 0x2;

        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            // Get cert from SslError
            SslCertificate sslCertificate = error.getCertificate();
            Certificate cert = getX509Certificate(sslCertificate);
            if (cert != null && certificate != null) {
                try {
                    // Reference: https://developer.android.com/reference/java/security/cert/Certificate.html#verify(java.security.PublicKey)
                    cert.verify(certificate.getPublicKey()); // Verify here...
                    handler.proceed();
                } catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException | NoSuchProviderException | SignatureException e) {
                    super.onReceivedSslError(view, handler, error);
                    e.printStackTrace();
                }
            } else {
                super.onReceivedSslError(view, handler, error);
            }
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String urlNewString) {
            webViewPreviousState = PAGE_REDIRECTED;
            wv_main.loadUrl(urlNewString);
            return true;
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            webViewPreviousState = PAGE_STARTED;
            Log.e("Start Loading", "Start Loading.........");
            /*if (dialog == null || !dialog.isShowing())
            dialog = ProgressDialog.show(MainActivity.this, "", "Loading", true, true);*/
        }

        @Override
        public void onPageFinished(WebView view, String url) {

            if (webViewPreviousState == PAGE_STARTED) {
                //dialog.dismiss();
                Log.e("Loading Done", "Loading Done.........");
            }
        }

        @Override
        public void onReceivedError(WebView view, WebResourceRequest request,
                                    WebResourceError error) {
            super.onReceivedError(view, request, error);
            Toast.makeText(SecretLinkBrowserActivity.this, "Error:" + error.toString(), Toast.LENGTH_SHORT).show();
        }
    });
    wv_main.setWebChromeClient(new WebChromeClient() {
        @Override
        public void onPermissionRequest(final PermissionRequest request) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                request.grant(request.getResources());
            }
        }

        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {

            Log.d("LogTag", message);
            result.confirm();
            return true;
        }
    });

While debugging I noticed that when I click on link the shouldOverrideUrlLoading() is not being called. The way website is doing the download is they have used doPostBack() that generated the url dynamically and downloads the file.

karan
  • 8,637
  • 3
  • 41
  • 78

2 Answers2

0
If you do this steps in Kotlin you can Download files from any WebView 

1- Add these to your Manifest 

<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" /> 

2- Add these settings to your webview 

val webViewSettings = binding.your@id@webview(InsideXML).settings 

        webViewSettings.javaScriptEnabled = true //Needed 

        webViewSettings.domStorageEnabled = true //Needed 

        webViewSettings.allowFileAccess = true //Needed 

        webViewSettings.loadsImagesAutomatically = true //Needed 

----Yo can have more here but this are needed ----- 

3-Handle the downloads 

   //handle downloading 

        binding.wvBase.setDownloadListener { url, userAgent, contentDisposition, mimeType, _ -> 

            val request = DownloadManager.Request( 

                Uri.parse(url) 

            ) 

            request.setMimeType(mimeType) 

            val cookies: String = CookieManager.getInstance().getCookie(url) 

            request.addRequestHeader("cookie", cookies) 

            request.addRequestHeader("User-Agent", userAgent) 

           // request.setDescription("Downloading File...") 

            request.setTitle(URLUtil.guessFileName(url, contentDisposition, mimeType)) 

            // request.allowScanningByMediaScanner() 

        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) 

            request.setDestinationInExternalPublicDir( 

                Environment.DIRECTORY_DOWNLOADS, URLUtil.guessFileName( 

                    url, contentDisposition, mimeType 

                ) 

            ) 

            val dm = getSystemService(DOWNLOAD_SERVICE) as DownloadManager 

            dm.enqueue(request) 

            alertdialog() 

        } 

In my case I have a custom alert Dialog to control if user says no, yes  
Deiivid
  • 1
  • 2
-1

You could launch a new intent and allow the user to choose another app for downloading like this:

wv_main.setDownloadListener(new DownloadListener() {
public void onDownloadStart(String url, String userAgent,
            String contentDisposition, String mimetype,
            long contentLength) {
    Intent i = new Intent(Intent.ACTION_VIEW);
    i.setData(Uri.parse(url));
    startActivity(i);
}
});

But I suppose you want to use download manager, you are missing the setDestinationUri method call, should be something like this

DownloadManager.Request request=new DownloadManager.Request(Uri.parse(url))
           .setTitle("Dummy File")// Title of the Download Notification
           .setDescription("Downloading")// Description of the Download Notification
           .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)// Visibility of the download Notification
           .setDestinationUri(Uri.fromFile(file))// Uri of the destination file
           .setRequiresCharging(false)// Set if charging is required to begin the download
           .setAllowedOverMetered(true)// Set if download is allowed on Mobile network
           .setAllowedOverRoaming(true);// Set if download is allowed on roaming network
Dr BigKitkat
  • 283
  • 1
  • 5
  • 13