Julian Di
Julian Di

Reputation: 11

How to cancel PrintJob with Android PrintManager?

I am trying to print a document with a Sunmi Printer. The App crashes if I click on print, because he is not cancelling the already printed documents from the queue. There is no stacktrace or error message in the log console.

private fun createWebPrintJob(webView: WebView) {

    // Get a PrintManager instance
    (getSystemService(Context.PRINT_SERVICE) as? PrintManager)?.let { printManager ->

        val jobName = "${getString(R.string.app_name)} Document"

        // Get a print adapter instance
        val printAdapter = webView.createPrintDocumentAdapter(jobName)

        // Create a print job with name and adapter instance
        val builder = PrintAttributes.Builder()
        val custom = PrintAttributes.MediaSize("SUNMI", "80mm x auto", 80, 1016)
        val customMargin = PrintAttributes.Margins(0, 0, 0, 0)

        builder.setMediaSize(custom)
        builder.setMinMargins(customMargin)
        println("manager ${printManager.printJobs}")
        try {
            printManager.print(
                    jobName,
                    printAdapter,
                    builder.build()
            )
        } catch (e: Exception) {
            println(e)
        }

        printManager.printJobs.forEach { item ->
            printManager.printJobs.remove(item)
        }


        println("manager ${printManager.printJobs}")

    }
}

Upvotes: 1

Views: 1450

Answers (1)

Carlos González
Carlos González

Reputation: 11

I've faced a similar problem, I don't know if this will help you but here it goes. My app was crashing due to an already scheduled printJob: I could start a first printJob, but if I went back (during the preview phase, like cancelling it) and started a new one, it throwed me an "IllegalStateException: printing is already pending".

PrintDocumentAdapter can control the printer callbacks, but the one you're using is defined by the WebView, so you can't access them. What you can do (and what I did too) is create a wrapper class like this one (it's a Java class, no Kotlin):

package android.print;

import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.support.annotation.RequiresApi;

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class PrintDocumentAdapterWrapper extends PrintDocumentAdapter {
    private final String TAG = PrintDocumentAdapterWrapper.class.getSimpleName();

    public static final int STATE_ON_START    = 1;
    public static final int STATE_ON_LAYOUT   = 2;
    public static final int STATE_ON_WRITE    = 3;
    public static final int STATE_ON_FINISHED = 4;

    public static final int STATE_ON_LAYOUT_FINISHED  = 5;
    public static final int STATE_ON_LAYOUT_FAILED    = 6;
    public static final int STATE_ON_LAYOUT_CANCELLED = 7;
    public static final int ON_LAYOUT_CANCEL_LISTENER = 8;

    public static final int STATE_ON_WRITE_FINISHED  = 9;
    public static final int STATE_ON_WRITE_FAILED    = 10;
    public static final int STATE_ON_WRITE_CANCELLED = 11;
    public static final int ON_WRITE_CANCEL_LISTENER = 12;

    private PrintDocumentAdapter adapter;
    private PrintAdapterCallback listener;

    public PrintDocumentAdapterWrapper(PrintDocumentAdapter adapter, PrintAdapterCallback listener){
        this.adapter = adapter;
        this.listener = listener;
    }

    @Override
    public void onStart() {
        listener.callback(STATE_ON_START);

        adapter.onStart();
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, final LayoutResultCallback callback, Bundle extras) {
        listener.callback(STATE_ON_LAYOUT);

        adapter.onLayout(oldAttributes, newAttributes, cancellationSignal, callback, extras);
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, final WriteResultCallback callback) {
        WriteResultCallback myCallback = new WriteResultCallback(){
            @Override
            public void onWriteFinished(PageRange[] pages) {
                super.onWriteFinished(pages);

                callback.onWriteFinished(pages);
                listener.callback(STATE_ON_WRITE_FINISHED);
            }

            @Override
            public void onWriteFailed(CharSequence error) {
                super.onWriteFailed(error);
                callback.onWriteFailed(error);
            }

            @Override
            public void onWriteCancelled() {
                super.onWriteCancelled();
                callback.onWriteCancelled();
            }
        };

        adapter.onWrite(pages, destination, cancellationSignal, myCallback);
    }

    @Override
    public void onFinish() {
        adapter.onFinish();
    }

    /* PrintAdapterCallback interface *************************************************************/
    public interface PrintAdapterCallback {
        void callback(int tag);
    }
}

You only need to pass to its constructor the webview's adapter that you defined on your code. To avoid the error related to already scheduled printJobs, you should start the new ones after you receive the onWriteFinished() callback. Hope this will be helpful to someone.

PS: This class needs to be placed in android.print package to work

Upvotes: 1

Related Questions