skip to Main Content

Am new to Java concepts. I am trying to download contents generated from JavaScript of my html file in android assets but the application crashes the moment I click on the download button. I suppose that I have to create a JavaScript interface to communicate with Java however I don’t know how to do that and would like to get help on that. How can I download the file generated by JavaScript in webview?

package com.fun.fun;

import androidx.appcompat.app.AppCompatActivity;
import android.Manifest;
import android.app.DownloadManager;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.DownloadListener;
import android.webkit.URLUtil;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private WebView web;


// application crashes if I download a file generated from script side of the following url;
    String webUrl = "file:///android_asset/indexi.html";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        web = (WebView) findViewById(R.id.myweb);
        web.loadUrl(webUrl);
        
        WebSettings mywebsettings = web.getSettings();
        mywebsettings.setJavaScriptEnabled(true);
        
        web.setWebViewClient(new WebViewClient());
        
        //improve webview performance
        
        web.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
        web.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        web.getSettings().setAppCacheEnabled(true);
        web.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
        mywebsettings.setDomStorageEnabled(true);
        mywebsettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
        mywebsettings.setUseWideViewPort(true);
        mywebsettings.setSavePassword(true);
        mywebsettings.setSaveFormData(true);
        mywebsettings.setEnableSmoothTransition(true);
        
        
        
        //External storage permission for saving file
        
        if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.M){
            if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED){
                
                Log.d("permission","permission denied to WRITE_EXTERNAL_STORAGE - requesting it");
                String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
                requestPermissions(permissions,1);
            }
            
            
        }
        
        //handle downloading
        
        web.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.setMimeType(mimeType);
                String cookies = 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));
                DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
                dm.enqueue(request);
                Toast.makeText(getApplicationContext(),"Downloading File",Toast.LENGTH_SHORT).show();
                
                
            }
        });
        
    }
    
    
    @Override
    public void onBackPressed() {
        if(web.canGoBack()){
            
            web.goBack();
        }
        else {
            
            super.onBackPressed();
        }
    }
}

And my html file that I have loaded in webview is;


<html>
<div id="disp"></div>

<script>


function objectsToCSV(arr) { 
const array = [Object.keys(arr[0])].concat(arr) 
return array.map(row => { 
return Object.values(row).map(value => { 
return typeof value === 'string' ? JSON.stringify(value) : value }).toString() 
}).join('n') }

var items = [
              { "name": "Item 1", "color": "Green", "size": "X-Large" },
              { "name": "Item 2", "color": "Green", "size": "X-Large" },
              { "name": "Item 3", "color": "Green", "size": "X-Large" }];

var csv = objectsToCSV(items);
document.getElementById("disp").innerHTML = csv;

function downloadCSV(csv) {
  const blob = new Blob([csv], { type: 'text/csv' });
  const anchor = document.createElement('a');
  anchor.href = window.URL.createObjectURL(blob);
  anchor.download = 'my_data.csv';
  document.body.appendChild(anchor);
  anchor.click();
  document.body.removeChild(anchor);
}


const downloadButton = document.createElement('button');
downloadButton.textContent = 'Download CSV';
downloadButton.addEventListener('click', () => {
  downloadCSV(csv);
});
document.body.appendChild(downloadButton);
</script>
</html>

2

Answers


  1. Chosen as BEST ANSWER

    Hello to anyone facing the same problem of downloading their JavaScript generated file use JavaScript interface as shown below; Thanks to @blackapps suggestions.

    In your HTML code;

    
    
    <!DOCTYPE html>
    <html>
    <head>
      <title>send to js interface</title>
    </head>
    <body>
      
    <button onclick="send();">onclick</button>
    
      <script>
        
       
       var base64String = mybase64;
        
        function send(){
          // send my base64; b64 to javascript interface;   
          window.base64interface.processBase64(base64String); 
        }
    
      </script>
    </body>
    </html>
    

    In Mainactivity.java

    public class MainActivity extends AppCompatActivity {
        WebView webView;
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            webView = findViewById(R.id.interface_web);
             
            // opening the html file in webview
            webView.loadUrl("file:///android_asset/test.html");
     
            webView.getSettings().setJavaScriptEnabled(true);
            webView.getSettings().setSupportZoom(true);
            webView.addJavascriptInterface(this, "Dialog");
            // Add an instance of the Base64Interface class with the name "base64interface"
            webView.addJavascriptInterface(new Base64Interface(this), "base64interface");
        }
     
        // add javascript interface for our base64 decode and download in Downloads folder;
    
        // A new class that implements the JavascriptInterface annotation
        public class Base64Interface {
            // A reference to the context of the activity that uses the WebView
            private Context context;
    
            // A constructor that takes the context as an argument
            public Base64Interface(Context context) {
                this.context = context;
            }
    
            // A method that is exposed to the HTML code via the @JavascriptInterface annotation
            // This method takes the base64 object as a String argument
            @JavascriptInterface
            public void processBase64(String base64) {
                // Decode the base64 object to a byte array
                byte[] bytes = Base64.getDecoder().decode(base64);
    
                // Create a file name with the current timestamp and a random suffix
                String fileName = System.currentTimeMillis() + "_" + new Random().nextInt(1000) + ".jpg";
    
                // Get the downloads directory for saving the file
                File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
    
                // Create a file object with the directory and file name
                File file = new File(dir, fileName);
    
                // Write the byte array to the file using a FileOutputStream
                try (FileOutputStream fos = new FileOutputStream(file)) {
                    fos.write(bytes);
                    fos.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
                // Scan the file to make it visible in the downloads app
                MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null);
    
                // Show a toast message to inform the user that the file is saved
                Toast.makeText(context, "File saved: " + file.getAbsolutePath(), Toast.LENGTH_SHORT).show();
            }
        }
       
    }
    

    Someone to improve on detecting the file type after decoding.

    Have a great day!


  2. You cannot use DownloadManager for what you want. You wil get something like:

    java.lang.IllegalArgumentException: Can only download HTTP/HTTPS URIs: blob:null/11d27a3a-c76e-4b6f-9030-f3c0dfe1dd61
    

    Now with a little googling we found that all has been reported before and solved.

    I just tried the suggestions and it worked for me using your indexi.html file:

    Unable to download Blob file type in WebView on Android

    Download Blob file from Website inside Android WebViewClient

    Only had to change a few things to make it work for a html file loaded from assets.

    Please edit the subject of your post to include loading html from assets and changing pdf to csv or txt.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search