Loading pdfs from Google Drive

This post shows how to download pdf files from Google Drive to app data directory and then copy it to cache and load it in WebView.

1. Create a new project in Sketchware pro (package name in my project is com.my.newbooks).

2. Add all pdfs which you want in your app to google drive and make their access public.

3. In main.xml add a GridView gridview1, and a FloatingActionButton _fab.

4. Create a Custom View grid_item.xml with an ImageView imageview1 with fixed height 200dp and scale_type fit_center. Add a TextView text_filename and ImageView image_delete.


5. Select grid_item.xml as custom view of gridview1.

6. Add two images download icon and delete icon.

7. Create a new page download.xml, and in this add a SeekBar seekbar1 and a ListView listview1.

8. Create a Custom View downloadlist_item.xml with a TextView textview1 and an ImageView imageview1 with download icon.

9. Select downloadlist_item.xml as custom view of listview1.

10. In MainActivity.java, add an Intent component intent. In the event fab onClick, use Intent to move to DownloadActivity.

11. Switch On AppCompat and design.

12. In DownloadActivity, add a Map variable map, and a List Map variable online_books. In onCreate, make seekbar1 GONE, and add name and url of all pdf files to the list online_books. Then display online_books in listview1.

13. In DownloadActivity, add a Dialog component dialog, and a String variable filename.

14. Create a Java file PdfHelper.java and put following codes in it. Change package name to your own.

package com.my.newbooks;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.pdf.PdfRenderer;
import android.net.Uri;
import android.os.Handler;
import android.os.ParcelFileDescriptor;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;

public class PdfHelper {

    /* ────────────────────────────────────────────────────────────────
     *  INTERFACES
     * ──────────────────────────────────────────────────────────────── */
    public interface DownloadCallback {
        void onProgress(int percent);   // 0 → 100
        void onSuccess(File file);
        void onError(String message);
    }


    /* ────────────────────────────────────────────────────────────────
     *  1. ASYNC PDF DOWNLOAD WITH PROGRESS LISTENER
     * ──────────────────────────────────────────────────────────────── */
    public static void downloadPdfToAppDataAsync(
            final Context context,
            final String url,
            final String outputName,
            final DownloadCallback callback
    ) {
        final Handler ui = new Handler(context.getMainLooper());

        new Thread(new Runnable() {
            @Override
            public void run() {
            InputStream input = null;
            FileOutputStream output = null;

            try {
                final File outFile = new File(context.getFilesDir(), outputName);

                URL downloadUrl = new URL(url);
                HttpURLConnection conn = (HttpURLConnection) downloadUrl.openConnection();
                conn.connect();

                if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                    throw new Exception("HTTP " + conn.getResponseCode());
                }

                int totalSize = conn.getContentLength();
                int downloaded = 0;

                input = conn.getInputStream();
                output = new FileOutputStream(outFile);

                byte[] buffer = new byte[4096];
                int read;

                while ((read = input.read(buffer)) != -1) {
                    output.write(buffer, 0, read);
                    downloaded += read;

                    int percent = (totalSize > 0)
                            ? (downloaded * 100 / totalSize)
                            : -1;

                    final int finalPercent = percent;
                    ui.post(new Runnable() {
                        @Override
                        public void run() {
                            callback.onProgress(finalPercent);
                        }
                    });
                }

                output.flush();
                ui.post(new Runnable() {
                @Override
                public void run() {
                    callback.onSuccess(outFile);
                }
            });
            } catch (final Exception e) {
                ui.post(new Runnable() {
                        @Override
                        public void run() {
                            callback.onError(e.getMessage());
                        }
                    });
            } finally {
                try { if (input != null) input.close(); } catch (Exception ignored) {}
                try { if (output != null) output.close(); } catch (Exception ignored) {}
            }
            
          }
        }).start();
    }


    /* ────────────────────────────────────────────────────────────────
     *  2. EXTRACT GOOGLE DRIVE FILE ID
     * ──────────────────────────────────────────────────────────────── */
    public static String extractFileId(String url) {
        try {
            if (url.contains("/d/")) {
                return url.split("/d/")[1].split("/")[0];
            }
            if (url.contains("id=")) {
                return url.split("id=")[1];
            }
        } catch (Exception ignored) {}
        return null;
    }


    /* ────────────────────────────────────────────────────────────────
     *  3. GET COVER PAGE BITMAP OF PDF
     * ──────────────────────────────────────────────────────────────── */
    public static Bitmap getBitmapFromCoverPageOfPdf(Context context, File pdfFile) {
        ParcelFileDescriptor descriptor = null;
        PdfRenderer renderer = null;

        try {
            descriptor = ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY);
            renderer = new PdfRenderer(descriptor);

            if (renderer.getPageCount() == 0) return null;

            PdfRenderer.Page page = renderer.openPage(0);

            Bitmap bitmap = Bitmap.createBitmap(
                    page.getWidth(),
                    page.getHeight(),
                    Bitmap.Config.ARGB_8888
            );

            page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);

            page.close();
            return bitmap;

        } catch (Exception e) {
            e.printStackTrace();
            return null;

        } finally {
            try { if (renderer != null) renderer.close(); } catch (Exception ignored) {}
            try { if (descriptor != null) descriptor.close(); } catch (Exception ignored) {}
        }
    }


    /* ────────────────────────────────────────────────────────────────
     *  4. Get all files in context.getFilesDir() to a Map List
     * ──────────────────────────────────────────────────────────────── */
    public static ArrayList<HashMap<String, Object>> getAllPdfFiles(Context context) {
    ArrayList<HashMap<String, Object>> list = new ArrayList<>();

    File dir = context.getFilesDir();
    File[] files = dir.listFiles();

    if (files == null) return list;

    for (File file : files) {
        if (file.isFile() && file.getName().toLowerCase().endsWith(".pdf")) {

            HashMap<String, Object> map = new HashMap<>();
            map.put("filename", file.getName());
            map.put("path", file.getAbsolutePath());

            // Get cover page
            Bitmap cover = getBitmapFromCoverPageOfPdf(context, file);

            if (cover != null) {
                map.put("cover_bitmap", cover);
            }

            list.add(map);
        }
    }

    return list;
}

    
    public static boolean isFileAlreadyDownloaded(Context context, String fileName) {
        File file = new File(context.getFilesDir(), fileName);
        return file.exists() && file.length() > 0;
    }

}

15. In DownloadActivity, add a More Block download (String: url) withName (String: filename). In this put codes to download pdf file from google drive url.

boolean exists = PdfHelper.isFileAlreadyDownloaded(
        this,
        _filename
);

if (exists) {
    Toast.makeText(this, "File already exists", Toast.LENGTH_SHORT).show();
} else {
String id = PdfHelper.extractFileId(_url);
String directUrl = "https://drive.google.com/uc?export=download&id=" + id;
binding.seekbar1.setVisibility(View.VISIBLE);

PdfHelper.downloadPdfToAppDataAsync(
        this,
        directUrl,
        _filename,
        new PdfHelper.DownloadCallback() {
            @Override
            public void onProgress(int percent) {
                binding.seekbar1.setProgress(percent);
            }

            @Override
            public void onSuccess(File file) {
                binding.seekbar1.setVisibility(View.GONE);
                Toast.makeText(DownloadActivity.this, "Downloaded: " + file.getPath(), Toast.LENGTH_LONG).show();
            }

            @Override
            public void onError(String message) {
                binding.seekbar1.setVisibility(View.GONE);
                Toast.makeText(DownloadActivity.this, "Error: " + message, Toast.LENGTH_LONG).show();
            }
        }
);
}

16. In listview1 onBindCustomView, display file name in textview1 and when imageview1 is clicked, use more block to download the file to app data directory (getFilesDir()).

17. In MainActivity, add a String variable filename, and a List Map all_files. In onCreate event set gridview1 number columns to 2. Also add an Intent intent and a Dialog component dialog.

18. In MainActivity, create a more block DataDirectoryListAllFiles. Use codes to get list of all files to Map List all_files.
all_files = PdfHelper.getAllPdfFiles(this);

Then display all_files in gridview1.

19. In MainActivity onStart event, use the more block DataDirectoryListAllFiles.


20. In MainActivity, create a more block refresh, and put following codes in it.

((BaseAdapter)binding.gridview1.getAdapter()).notifyDataSetChanged();

21. In gridview1 onBindCustomView, display the file name and cover page. And when delete button is clicked, delete the file and refresh the GridView.


22. Create a new page pdfreader.xml and add a WebView pdfWebView in it.

23. In MainActivity, gridview1 onItemClick event, use intent to move to PdfreaderActivity.

24. Download pdf.js from following link:
or

25. Extract the contents of the downloaded zip file.

26. In Sketchware pro project, in Asset Manager, create a new folder pdfjs.

27. In pdfjs folder import all the contents extracted from the downloaded zip file.

28. In assets folder, for showing custom error page, add error.html. Below is a sample error.html page.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Error Loading PDF</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            padding: 20px;
            color: #333;
        }
        
        .error-container {
            max-width: 600px;
            width: 100%;
            background-color: white;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            padding: 40px;
            text-align: center;
            position: relative;
            overflow: hidden;
        }
        
        .error-container::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 6px;
            background: linear-gradient(90deg, #ff6b6b, #4ecdc4, #45b7d1);
        }
        
        .error-icon {
            font-size: 80px;
            margin-bottom: 20px;
            color: #ff6b6b;
        }
        
        h1 {
            font-size: 28px;
            margin-bottom: 15px;
            color: #2c3e50;
        }
        
        .error-message {
            font-size: 18px;
            line-height: 1.6;
            margin-bottom: 25px;
            color: #555;
        }
        
        .error-details {
            background-color: #f8f9fa;
            border-left: 4px solid #4ecdc4;
            padding: 15px;
            margin: 25px 0;
            text-align: left;
            border-radius: 0 8px 8px 0;
        }
        
        .error-details h3 {
            margin-bottom: 10px;
            color: #2c3e50;
        }
        
        .error-details ul {
            padding-left: 20px;
        }
        
        .error-details li {
            margin-bottom: 8px;
        }
        
        .action-buttons {
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
            justify-content: center;
            margin-top: 30px;
        }
        
        .btn {
            padding: 12px 24px;
            border: none;
            border-radius: 6px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
        }
        
        .btn-primary {
            background-color: #4ecdc4;
            color: white;
        }
        
        .btn-primary:hover {
            background-color: #3db8af;
            transform: translateY(-2px);
        }
        
        .btn-secondary {
            background-color: #f8f9fa;
            color: #495057;
            border: 1px solid #dee2e6;
        }
        
        .btn-secondary:hover {
            background-color: #e9ecef;
            transform: translateY(-2px);
        }
        
        .btn-icon {
            font-size: 18px;
        }
        
        .contact-support {
            margin-top: 30px;
            font-size: 14px;
            color: #6c757d;
        }
        
        .contact-support a {
            color: #4ecdc4;
            text-decoration: none;
        }
        
        .contact-support a:hover {
            text-decoration: underline;
        }
        
        @media (max-width: 600px) {
            .error-container {
                padding: 30px 20px;
            }
            
            .action-buttons {
                flex-direction: column;
            }
            
            .btn {
                width: 100%;
            }
        }
    </style>
</head>
<body>
    <div class="error-container">
        <div class="error-icon">📄❌</div>
        <h1>We're having trouble loading this PDF</h1>
        
        <div class="error-message">
            The PDF file you're trying to access cannot be loaded. This might be due to one of the following reasons:
        </div>
        
        <div class="error-details">
            <h3>Possible causes:</h3>
            <ul>
                <li>The file has been moved or deleted</li>
                <li>There's a network connectivity issue</li>
                <li>The file is corrupted or in an unsupported format</li>
                <li>Your browser doesn't support PDF viewing</li>
            </ul>
        </div>
        
        <div class="action-buttons">
            <button class="btn btn-primary" onclick="window.location.reload()">
                <span class="btn-icon">🔄</span> Try Again
            </button>
            <button class="btn btn-secondary" onclick="history.back()">
                <span class="btn-icon">⬅️</span> Go Back
            </button>
            <button class="btn btn-secondary" onclick="goToHome()">
                <span class="btn-icon">🏠</span> Home
            </button>
        </div>
        
        <div class="contact-support">
            If the problem persists, please <a href="mailto:sanjeevk4571@gmail.com">contact our support team</a>.
        </div>
    </div>

    <script>
        function goToHome() {
            // Load the PDF viewer with sample.pdf
            window.location.href = "file:///android_asset/pdfjs/web/viewer.html?file=" + "file:///android_asset/sample.pdf";
        }
        
        // You can customize the error message based on the specific error
        function setErrorMessage(errorType) {
            const titleElement = document.querySelector('h1');
            const messageElement = document.querySelector('.error-message');
            
            if (errorType === 'not-found') {
                titleElement.textContent = "PDF File Not Found";
                messageElement.textContent = "The PDF file you're looking for doesn't exist or has been moved. Please check the URL or contact the document owner.";
            } else if (errorType === 'loading-error') {
                titleElement.textContent = "Error Loading PDF";
                messageElement.textContent = "We encountered an error while trying to load the PDF file. This might be due to a network issue or file corruption.";
            }
            // Default message is already set in HTML
        }
    </script>
</body>
</html>

29. In AndroidManifest Manager:
a. Click on Permissions and add following permission
android.permission.PRINT
b. Click on App Components and put following codes.

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
30. In Resource manager, add a new folder xml. Inside xml folder, create a file named file_paths.xml. In this file put following codes:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <cache-path name="cache" path="." />
    <files-path name="files" path="." />
    <external-cache-path name="external_cache" path="." />
    <external-files-path name="external_files" path="." />
</paths>
31. In permission manager, add following permissions.
  • android.permission.INTERNET
  • android.permission.READ_EXTERNAL_STORAGE
  • android.permission.WRITE_EXTERNAL_STORAGE
  • android.permission.ACCESS_NETWORK_STATE
  • android.permission.PRINT

32. Create a new Java file PrintDocumentAdapterFactory.java and put following codes in it.

package com.my.newbooks;

import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintDocumentInfo;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class PrintDocumentAdapterFactory {

    public static PrintDocumentAdapter createPrintDocumentAdapter(Context context, Uri pdfUri) {

        final Context ctx = context;
        final Uri fileUri = pdfUri;

        return new PrintDocumentAdapter() {

            @Override
            public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
                                 CancellationSignal cancellationSignal,
                                 LayoutResultCallback callback, Bundle extras) {

                PrintDocumentInfo info = new PrintDocumentInfo
                        .Builder("document.pdf")
                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
                        .build();

                callback.onLayoutFinished(info, true);
            }

            @Override
            public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
                                CancellationSignal cancellationSignal,
                                WriteResultCallback callback) {

                try {

                    ParcelFileDescriptor fd =
                            ctx.getContentResolver().openFileDescriptor(fileUri, "r");

                    FileInputStream input = new FileInputStream(fd.getFileDescriptor());
                    FileOutputStream output = new FileOutputStream(destination.getFileDescriptor());

                    byte[] buf = new byte[8192];
                    int size;

                    while ((size = input.read(buf)) > 0 && !cancellationSignal.isCanceled()) {
                        output.write(buf, 0, size);
                    }

                    callback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES});

                    input.close();
                    output.close();

                } catch (IOException e) {
                    callback.onWriteFailed(e.getMessage());
                }
            }
        };
    }
}

33. In PdfreaderActivity, create two String variables pdfName, and pdfPath.

34. Create a Custom Variable downloadHelper.
Modifier: private
Type: DownloadHelper
Name: downloadHelper

35. Create a new Java file DownloadHelper.java and put following codes in it.

package com.my.newbooks;

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.FileInputStream;
import java.io.OutputStream;

public class DownloadHelper {

    private Context context;
    private WebView webView;
    private static final String JS_INTERFACE_NAME = "Android";

    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);
    }

    // Handle regular HTTP/HTTPS file downloads
    public void handleRegularDownload(String url, String userAgent, 
                                    String fileName, String mimeType, 
                                    long contentLength) {
        try {
            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);
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
            
            DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
            if (dm != null) {
                dm.enqueue(request);
                Toast.makeText(context, "Download started: " + fileName, Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            Log.e("Download", "Regular download failed: " + e.getMessage());
            Toast.makeText(context, "Download failed", Toast.LENGTH_SHORT).show();
        }
    }

    // Handle blob URL downloads using JavaScript fetch API
    public void handleBlobUrlDownload(String blobUrl, String fileName, String mimeType) {
        try {
            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 => {" +
                "   " + JS_INTERFACE_NAME + ".handleBase64Data('', '" + mimeType + "', '" + fileName + "');" +
                "});" +
                "})()";
            
            webView.post(new Runnable() {
                @Override
                public void run() {
                    webView.loadUrl(javascript);
                }
            });
        } catch (Exception e) {
            Log.e("Download", "Blob URL download setup failed: " + e.getMessage());
            Toast.makeText(context, "Download setup failed", 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("Download", "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();
            }
            
            File file = new File(downloadsDir, fileName);
            
            int counter = 1;
            String baseName = fileName;
            String extension = "";
            int dotIndex = fileName.lastIndexOf('.');
            if (dotIndex > 0) {
                baseName = fileName.substring(0, dotIndex);
                extension = fileName.substring(dotIndex);
            }
            
            String finalFileName = fileName;
            while (file.exists()) {
                finalFileName = baseName + " (" + counter + ")" + extension;
                file = new File(downloadsDir, finalFileName);
                counter++;
            }
            
            final String savedFileName = finalFileName;
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(data);
            fos.close();
            
            MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, 
                new String[]{mimeType}, null);
            
            new Handler(context.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(context, "File saved: " + savedFileName, Toast.LENGTH_LONG).show();
                }
            });
            
        } catch (final Exception e) {
            Log.e("Download", "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();
                }
            });
        }
    }
    
    //Copy pdf file from getFilesDir() to getCacheDir() with name temp.pdf
    public File copyPdfToCache(Context context, String fileName) {
    File source = new File(context.getFilesDir(), fileName);
    File dest = new File(context.getCacheDir(), "temp.pdf");

    try (InputStream in = new FileInputStream(source);
         OutputStream out = new FileOutputStream(dest)) {

        byte[] buffer = new byte[4096];
        int len;

        while ((len = in.read(buffer)) > 0) {
            out.write(buffer, 0, len);
        }
        return dest;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

    
    // Print PDF file using Android Print Manager
    public void printPdf(Context context, String pdfName) {
        File pdfFile = new File(context.getCacheDir(), pdfName);
    
        Uri fileUri = FileProvider.getUriForFile(
                context,
                context.getPackageName() + ".fileprovider",
                pdfFile
        );

        PrintManager printManager = (PrintManager) context.getSystemService(Context.PRINT_SERVICE);

        PrintDocumentAdapter printAdapter =
                PrintDocumentAdapterFactory.createPrintDocumentAdapter(context, fileUri);

        printManager.print("Printing PDF", printAdapter, new PrintAttributes.Builder().build());
    }

    // Clean up JavaScript interface when no longer needed
    public void cleanup() {
        try {
            webView.removeJavascriptInterface(JS_INTERFACE_NAME);
        } catch (Exception e) {
            Log.e("Download", "Cleanup failed: " + e.getMessage());
        }
    }
}

36. In onCreate event, put following codes.

pdfPath = getIntent().getStringExtra("filepath");
pdfName = getIntent().getStringExtra("filename");

// Configure WebView settings
WebSettings settings = binding.pdfWebView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setAllowFileAccess(true);
settings.setAllowUniversalAccessFromFileURLs(true);
settings.setAllowFileAccessFromFileURLs(true);
settings.setBuiltInZoomControls(true);
settings.setDisplayZoomControls(false);
settings.setSupportZoom(true);

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

// Copy PDF to cache and verify
File cacheFile = downloadHelper.copyPdfToCache(this, pdfName);
if (cacheFile == null || !cacheFile.exists()) {
    binding.pdfWebView.loadUrl("file:///android_asset/error.html");
    return;
}

// Construct the viewer URL properly
String fileUrl = "file://" + cacheFile.getAbsolutePath();
String encodedFileUrl = Uri.encode(fileUrl);
String viewerUrl = "file:///android_asset/pdfjs/web/viewer.html?file=" + encodedFileUrl;

Log.d("PDFViewer", "Loading PDF from: " + viewerUrl);
binding.pdfWebView.loadUrl(viewerUrl);

binding.pdfWebView.setWebViewClient(new WebViewClient() {
    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        super.onPageStarted(view, url, favicon);
    }
    
    @Override
    public void onPageFinished(WebView view, String url) {
        // Override window.print() to use Android's print system
        view.evaluateJavascript(
            "window.print = function() { AndroidPrint.onPrintRequested(); };",
            null
        );
    }
    
    @Override
    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
    super.onReceivedError(view, request, error);
    String errorDetails = "Error: " + error.getDescription();
    Toast.makeText(getApplicationContext(), errorDetails+", "+request.getUrl(), Toast.LENGTH_LONG).show();
    view.loadUrl("file:///android_asset/error.html");
    }
    
});

// Handle download requests from PDF.js
binding.pdfWebView.setDownloadListener(new DownloadListener() {
	@Override
	public void onDownloadStart(String url, String userAgent, 
	String contentDisposition, String mimeType, 
	long contentLength) {
		
		if (url.startsWith("blob:") || url.startsWith("data:")) {
			// Handle blob/data URLs (PDF.js generated downloads)
			downloadHelper.handleBlobUrlDownload(url, pdfName, mimeType);
			return;
		}
		
		// Handle regular HTTP downloads
		downloadHelper.handleRegularDownload(url, userAgent, pdfName, mimeType, contentLength);
	}
});

// JavaScript interface for handling print requests from PDF.js
binding.pdfWebView.addJavascriptInterface(new Object() {
    @JavascriptInterface
    public void onPrintRequested() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                downloadHelper.printPdf(PdfreaderActivity.this, "temp.pdf");
            }
        });
    }
}, "AndroidPrint");

37. In import, add following.

import android.print.PrintManager;
import android.print.PrintJob;
import android.print.PrintDocumentAdapter;
import android.print.PrintAttributes;
import androidx.core.content.FileProvider;
38. Add onDestroy event and put following codes in it.

if (downloadHelper != null) {
    downloadHelper.cleanup();
}

39. Save and run the project.

Comments

Popular posts from this blog

Simple car racing android game in Sketchware

How to enable upload from webview in Sketchware?

Simple Audio recorder app in Sketchware

Retrieve contact list in Sketchware

Creating a Drawing View in Sketchware