Enable download from WebView in Sketchware pro

 

Enable download from WebView in Sketchware pro

To enable download from WebView, following steps are required.

1. Add a WebView webview1 in main.xml.

2. Switch ON AppCompat and design.

3. In permission manager add INTERNET permission.

4. In Java/Kotlin manager, add a new Java class file DownloadHelper.java. Put following codes in this file (change package name in first line to your own package name).

👇👇👇 Code 👇👇👇


package com.my.newproject28;


import android.app.DownloadManager;

import android.content.Context;

import android.database.Cursor;

import android.media.MediaScannerConnection;

import android.net.Uri;

import android.os.Environment;

import android.os.Handler;

import android.util.Base64;

import android.util.Log;

import android.webkit.CookieManager;

import android.webkit.JavascriptInterface;

import android.webkit.WebView;

import android.widget.Toast;


import androidx.core.content.FileProvider;


import android.print.PrintManager;

import android.print.PrintDocumentAdapter;

import android.print.PrintAttributes;


import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.URLDecoder;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.Locale;


public class DownloadHelper {


    private Context context;

    private WebView webView;

    private static final String JS_INTERFACE_NAME = "Android";

    private static final String TAG = "DownloadHelper";


    public DownloadHelper(Context context, WebView webView) {

        this.context = context;

        this.webView = webView;

        setupJavaScriptInterface();

    }


    // Set up JavaScript interface for WebView communication

    private void setupJavaScriptInterface() {

        webView.addJavascriptInterface(this, JS_INTERFACE_NAME);

    }


    // Extract filename from URL or Content-Disposition

    private String extractFilenameFromUrl(String url, String contentDisposition, String mimeType) {

        String filename = null;

        

        // Try to get filename from Content-Disposition header

        if (contentDisposition != null && !contentDisposition.isEmpty()) {

            filename = parseContentDisposition(contentDisposition);

        }

        

        // If not found in header, extract from URL

        if (filename == null || filename.isEmpty()) {

            filename = extractFilenameFromUrlPath(url);

        }

        

        // If still no filename, generate one based on mime type and timestamp

        if (filename == null || filename.isEmpty()) {

            filename = generateDefaultFilename(mimeType);

        }

        

        // Sanitize filename (remove invalid characters)

        filename = sanitizeFilename(filename);

        

        return filename;

    }


    // Parse Content-Disposition header to get filename

    private String parseContentDisposition(String contentDisposition) {

        try {

            // Look for filename* (RFC 5987) or filename parameter

            String[] parts = contentDisposition.split(";");

            for (String part : parts) {

                part = part.trim();

                if (part.toLowerCase().startsWith("filename*=")) {

                    // RFC 5987 format: filename*=UTF-8''filename.ext

                    String value = part.substring(part.indexOf("=") + 1);

                    if (value.startsWith("UTF-8''")) {

                        value = value.substring(7);

                        return URLDecoder.decode(value, "UTF-8");

                    }

                } else if (part.toLowerCase().startsWith("filename=")) {

                    // Simple filename format

                    String value = part.substring(part.indexOf("=") + 1);

                    // Remove quotes if present

                    if (value.startsWith("\"") && value.endsWith("\"")) {

                        value = value.substring(1, value.length() - 1);

                    }

                    return value;

                }

            }

        } catch (Exception e) {

            Log.e(TAG, "Error parsing Content-Disposition: " + e.getMessage());

        }

        return null;

    }


    // Extract filename from URL path

    private String extractFilenameFromUrlPath(String url) {

        try {

            // Remove query parameters

            String path = url;

            int queryIndex = url.indexOf('?');

            if (queryIndex > 0) {

                path = url.substring(0, queryIndex);

            }

            

            // Get last segment after '/'

            int lastSlash = path.lastIndexOf('/');

            if (lastSlash >= 0 && lastSlash < path.length() - 1) {

                String filename = path.substring(lastSlash + 1);

                if (!filename.isEmpty() && !filename.contains("/")) {

                    // URL decode the filename

                    return URLDecoder.decode(filename, "UTF-8");

                }

            }

        } catch (Exception e) {

            Log.e(TAG, "Error extracting filename from URL: " + e.getMessage());

        }

        return null;

    }


    // Generate default filename based on mime type and timestamp

    private String generateDefaultFilename(String mimeType) {

        String extension = getFileExtensionFromMimeType(mimeType);

        String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());

        return "download_" + timestamp + extension;

    }


    // Get file extension from mime type

    private String getFileExtensionFromMimeType(String mimeType) {

        if (mimeType == null) return ".bin";

        

        switch (mimeType.toLowerCase()) {

            case "application/pdf":

                return ".pdf";

            case "image/jpeg":

                return ".jpg";

            case "image/png":

                return ".png";

            case "image/gif":

                return ".gif";

            case "text/plain":

                return ".txt";

            case "text/html":

                return ".html";

            case "application/zip":

                return ".zip";

            case "application/x-zip-compressed":

                return ".zip";

            case "application/msword":

                return ".doc";

            case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":

                return ".docx";

            case "application/vnd.ms-excel":

                return ".xls";

            case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":

                return ".xlsx";

            case "application/vnd.ms-powerpoint":

                return ".ppt";

            case "application/vnd.openxmlformats-officedocument.presentationml.presentation":

                return ".pptx";

            case "audio/mpeg":

                return ".mp3";

            case "audio/mp4":

                return ".m4a";

            case "video/mp4":

                return ".mp4";

            case "video/x-matroska":

                return ".mkv";

            default:

                return ".bin";

        }

    }


    // Remove invalid characters from filename

    private String sanitizeFilename(String filename) {

        if (filename == null) return "download.bin";

        

        // Remove path separators and other invalid characters

        filename = filename.replaceAll("[\\\\/:*?\"<>|]", "_");

        

        // Ensure filename is not empty after sanitization

        if (filename.isEmpty() || filename.equals(".") || filename.equals("..")) {

            return "download.bin";

        }

        

        // Truncate if too long

        if (filename.length() > 200) {

            String ext = "";

            int dotIndex = filename.lastIndexOf('.');

            if (dotIndex > 0 && dotIndex < filename.length() - 1) {

                ext = filename.substring(dotIndex);

                filename = filename.substring(0, Math.min(dotIndex, 190));

            }

            filename = filename + ext;

        }

        

        return filename;

    }


    // Get unique filename if file already exists

    private File getUniqueFile(File directory, String filename) {

        File file = new File(directory, filename);

        

        if (!file.exists()) {

            return file;

        }

        

        String baseName = filename;

        String extension = "";

        int dotIndex = filename.lastIndexOf('.');

        if (dotIndex > 0) {

            baseName = filename.substring(0, dotIndex);

            extension = filename.substring(dotIndex);

        }

        

        int counter = 1;

        while (file.exists()) {

            String newFilename = baseName + " (" + counter + ")" + extension;

            file = new File(directory, newFilename);

            counter++;

        }

        

        return file;

    }


    // Handle regular HTTP/HTTPS file downloads

    public void handleRegularDownload(String url, String userAgent, 

                                    String contentDisposition, String mimeType, 

                                    long contentLength) {

        try {

            // Extract correct filename

            String filename = extractFilenameFromUrl(url, contentDisposition, mimeType);

            

            Uri uri = Uri.parse(url);

            DownloadManager.Request request = new DownloadManager.Request(uri);

            

            request.setMimeType(mimeType);

            String cookies = CookieManager.getInstance().getCookie(url);

            if (cookies != null) {

                request.addRequestHeader("cookie", cookies);

            }

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

            

            request.setTitle(filename);

            request.setDescription("Downloading file");

            request.allowScanningByMediaScanner();

            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

            

            // Set destination in Downloads folder

            File downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

            File destinationFile = getUniqueFile(downloadsDir, filename);

            

            // Use Uri.fromFile for the destination

            request.setDestinationUri(Uri.fromFile(destinationFile));

            

            DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);

            if (dm != null) {

                long downloadId = dm.enqueue(request);

                Log.d(TAG, "Download started with ID: " + downloadId + ", filename: " + destinationFile.getName());

                

                // Monitor download completion to scan the file

                monitorDownloadCompletion(dm, downloadId, destinationFile);

                

                Toast.makeText(context, "Download started: " + destinationFile.getName(), Toast.LENGTH_LONG).show();

            }

        } catch (Exception e) {

            Log.e(TAG, "Regular download failed: " + e.getMessage());

            Toast.makeText(context, "Download failed: " + e.getMessage(), Toast.LENGTH_SHORT).show();

        }

    }


    // Monitor download completion to scan the file

    private void monitorDownloadCompletion(final DownloadManager dm, final long downloadId, final File file) {

        new Thread(new Runnable() {

            @Override

            public void run() {

                boolean downloading = true;

                while (downloading) {

                    DownloadManager.Query q = new DownloadManager.Query();

                    q.setFilterById(downloadId);

                    Cursor cursor = dm.query(q);

                    if (cursor != null && cursor.moveToFirst()) {

                        int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));

                        if (status == DownloadManager.STATUS_SUCCESSFUL) {

                            downloading = false;

                            // Scan the file so it appears in gallery/file managers

                            MediaScannerConnection.scanFile(context, 

                                new String[]{file.getAbsolutePath()}, null, null);

                            Log.d(TAG, "Download completed and scanned: " + file.getName());

                        } else if (status == DownloadManager.STATUS_FAILED) {

                            downloading = false;

                            Log.e(TAG, "Download failed");

                        }

                        cursor.close();

                    }

                    try {

                        Thread.sleep(1000); // Check every second

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

            }

        }).start();

    }


    // Handle blob URL downloads using JavaScript fetch API

    public void handleBlobUrlDownload(String blobUrl, String fileName, String mimeType) {

        try {

            // If filename is not provided or is empty, generate one

            if (fileName == null || fileName.isEmpty()) {

                fileName = generateDefaultFilename(mimeType);

            } else {

                fileName = sanitizeFilename(fileName);

            }

            

            final String javascript = "javascript: (function() {" +

                "var blobUrl = '" + blobUrl + "';" +

                "fetch(blobUrl)" +

                ".then(response => response.blob())" +

                ".then(blob => {" +

                "   var reader = new FileReader();" +

                "   reader.onloadend = function() {" +

                "       var base64data = reader.result;" +

                "       " + JS_INTERFACE_NAME + ".handleBase64Data(base64data, '" + mimeType + "', '" + fileName + "');" +

                "   };" +

                "   reader.readAsDataURL(blob);" +

                "})" +

                ".catch(error => {" +

                "   console.error('Blob download error:', error);" +

                "   " + JS_INTERFACE_NAME + ".handleBase64Data('', '" + mimeType + "', '" + fileName + "');" +

                "});" +

                "})()";

            

            webView.post(new Runnable() {

                @Override

                public void run() {

                    webView.loadUrl(javascript);

                    Log.d(TAG, "Blob download initiated");

                }

            });

        } catch (Exception e) {

            Log.e(TAG, "Blob URL download setup failed: " + e.getMessage());

            Toast.makeText(context, "Download setup failed: " + e.getMessage(), Toast.LENGTH_SHORT).show();

        }

    }


    // JavaScript interface method to handle base64 data from blob URLs

    @JavascriptInterface

    public void handleBase64Data(String base64Data, String mimeType, String fileName) {

        try {

            if (base64Data == null || base64Data.isEmpty()) {

                throw new Exception("Empty base64 data received");

            }

            

            String pureBase64 = base64Data.substring(base64Data.indexOf(",") + 1);

            byte[] fileData = Base64.decode(pureBase64, Base64.DEFAULT);

            

            saveBase64Data(fileData, fileName, mimeType);

            

        } catch (final Exception e) {

            Log.e(TAG, "Blob download failed: " + e.getMessage());

            new Handler(context.getMainLooper()).post(new Runnable() {

                @Override

                public void run() {

                    Toast.makeText(context, "Download failed: " + e.getMessage(), Toast.LENGTH_LONG).show();

                }

            });

        }

    }


    // Save base64 data to Downloads directory with duplicate handling

    public void saveBase64Data(byte[] data, final String fileName, String mimeType) {

        try {

            File downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

            if (!downloadsDir.exists()) {

                downloadsDir.mkdirs();

            }

            

            // Sanitize filename and handle duplicates

            String safeFileName = sanitizeFilename(fileName);

            File file = getUniqueFile(downloadsDir, safeFileName);

            

            final String savedFileName = file.getName();

            

            // Write file

            FileOutputStream fos = new FileOutputStream(file);

            fos.write(data);

            fos.close();

            

            // Scan file so it appears in gallery/file managers

            MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, 

                new String[]{mimeType}, new MediaScannerConnection.OnScanCompletedListener() {

                    @Override

                    public void onScanCompleted(String path, Uri uri) {

                        Log.d(TAG, "Media scan completed for: " + path);

                    }

                });

            

            // Show success message

            new Handler(context.getMainLooper()).post(new Runnable() {

                @Override

                public void run() {

                    Toast.makeText(context, "File saved: " + savedFileName, Toast.LENGTH_LONG).show();

                }

            });

            

            Log.d(TAG, "File saved successfully: " + file.getAbsolutePath());

            

        } catch (final Exception e) {

            Log.e(TAG, "Save base64 data failed: " + e.getMessage());

            new Handler(context.getMainLooper()).post(new Runnable() {

                @Override

                public void run() {

                    Toast.makeText(context, "Save failed: " + e.getMessage(), Toast.LENGTH_LONG).show();

                }

            });

        }

    }


    // Clean up JavaScript interface when no longer needed

    public void cleanup() {

        try {

            webView.removeJavascriptInterface(JS_INTERFACE_NAME);

            Log.d(TAG, "Cleanup completed");

        } catch (Exception e) {

            Log.e(TAG, "Cleanup failed: " + e.getMessage());

        }

    }

}




5. In onCreate, put following codes (change the url to the url you want to load).

👇👇👇 Code 👇👇👇



WebSettings settings = binding.webview1.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true);

// Initialize download helper
final DownloadHelper downloadHelper = new DownloadHelper(this, binding.webview1);

binding.webview1.loadUrl("https://www.apkmirror.com/");

// Handle download requests
binding.webview1.setDownloadListener(new DownloadListener() {
    @Override
    public void onDownloadStart(String url, String userAgent, 
    String contentDisposition, String mimeType, 
    long contentLength) {
        
        if (url.startsWith("blob:") || url.startsWith("data:")) {
            // For blob URLs, let the DownloadHelper generate a filename
            downloadHelper.handleBlobUrlDownload(
                url,           // Blob URL
                null,          // Let DownloadHelper generate filename
                mimeType
            );
        } else {
            // For regular HTTP downloads
            downloadHelper.handleRegularDownload(
                url,
                userAgent,
                contentDisposition,
                mimeType,
                contentLength
            );
        }
    }
});



6. Run the project.

Post a Comment

0 Comments