diff --git a/app/src/main/java/org/grapheneos/pdfviewer/PdfDocumentAdapter.java b/app/src/main/java/org/grapheneos/pdfviewer/PdfDocumentAdapter.java new file mode 100644 index 000000000..05c8c7745 --- /dev/null +++ b/app/src/main/java/org/grapheneos/pdfviewer/PdfDocumentAdapter.java @@ -0,0 +1,77 @@ +package org.grapheneos.pdfviewer; + +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.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class PdfDocumentAdapter extends PrintDocumentAdapter { + + private Context mContext; + private String mPrintJobName; + private Uri mUri; + + public PdfDocumentAdapter(Context ctx, String jobName, Uri uri) { + this.mContext = ctx; + this.mPrintJobName = jobName; + this.mUri = uri; + } + + @Override + public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback layoutResultCallback, Bundle bundle) { + if (cancellationSignal.isCanceled()) { + layoutResultCallback.onLayoutCancelled(); + } else { + PrintDocumentInfo.Builder pdinfoBuilder = new PrintDocumentInfo.Builder(mPrintJobName); + pdinfoBuilder.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT); + pdinfoBuilder.setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN); + PrintDocumentInfo pdInfo = pdinfoBuilder.build(); + layoutResultCallback.onLayoutFinished(pdInfo, !newAttributes.equals(oldAttributes)); + } + } + + @Override + public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor parcelFileDescriptor, CancellationSignal cancellationSignal, WriteResultCallback writeResultCallback) { + InputStream inputStream = null; + OutputStream outputStream = null; + + try { + inputStream = mContext.getContentResolver().openInputStream(mUri);; + outputStream = new FileOutputStream(parcelFileDescriptor.getFileDescriptor()); + byte[] buffer = new byte[mContext.getResources().getInteger(R.integer.print_adapter_buffer_size)]; + + int readSize = inputStream.read(buffer); + while (readSize >= 0 && !cancellationSignal.isCanceled()) { + outputStream.write(buffer, 0, readSize); + readSize = inputStream.read(buffer); + } + + if (cancellationSignal.isCanceled()) { + writeResultCallback.onWriteCancelled(); + } else { + writeResultCallback.onWriteFinished(new PageRange[] { PageRange.ALL_PAGES }); + } + } catch (IOException ex) { + writeResultCallback.onWriteFailed(ex.getMessage()); + } finally { + try { + if (outputStream != null) + outputStream.close(); + if (inputStream != null) + inputStream.close(); + } catch (IOException ignored) { + + } + } + } +} diff --git a/app/src/main/java/org/grapheneos/pdfviewer/PdfViewer.java b/app/src/main/java/org/grapheneos/pdfviewer/PdfViewer.java index 606a1da79..78bad5cf4 100644 --- a/app/src/main/java/org/grapheneos/pdfviewer/PdfViewer.java +++ b/app/src/main/java/org/grapheneos/pdfviewer/PdfViewer.java @@ -7,6 +7,9 @@ import android.graphics.Color; import android.net.Uri; import android.os.Bundle; +import android.print.PrintAttributes; +import android.print.PrintDocumentAdapter; +import android.print.PrintManager; import android.util.Log; import android.view.Gravity; import android.view.Menu; @@ -22,6 +25,7 @@ import android.webkit.WebViewClient; import android.widget.TextView; import android.widget.Toast; +import android.content.Context; import androidx.appcompat.app.AppCompatActivity; import androidx.loader.app.LoaderManager; @@ -345,6 +349,14 @@ private void renderPage(final int zoom) { mWebView.evaluateJavascript("onRenderPage(" + zoom + ")", null); } + private void printDocument() { + PrintManager printManager = (PrintManager)getSystemService(Context.PRINT_SERVICE); + String jobName = getString(R.string.print_job_base_name); + PrintDocumentAdapter printDocumentAdapter = new PdfDocumentAdapter(this, jobName, mUri); + PrintAttributes printAttributes = new PrintAttributes.Builder().build(); + printManager.print(jobName, printDocumentAdapter, printAttributes); + } + private void documentOrientationChanged(final int orientationDegreesOffset) { mDocumentOrientationDegrees = (mDocumentOrientationDegrees + orientationDegreesOffset) % 360; if (mDocumentOrientationDegrees < 0) { @@ -465,6 +477,7 @@ public boolean onPrepareOptionsMenu(Menu menu) { final int ids[] = { R.id.action_zoom_in, R.id.action_zoom_out, R.id.action_jump_to_page, R.id.action_next, R.id.action_previous, R.id.action_first, R.id.action_last, R.id.action_rotate_clockwise, R.id.action_rotate_counterclockwise, + R.id.action_print, R.id.action_view_document_properties }; if (mDocumentState < STATE_LOADED) { for (final int id : ids) { @@ -541,6 +554,10 @@ public boolean onOptionsItemSelected(MenuItem item) { .show(getSupportFragmentManager(), JumpToPageFragment.TAG); return true; + case R.id.action_print: + printDocument(); + return true; + default: return super.onOptionsItemSelected(item); } diff --git a/app/src/main/res/menu/pdf_viewer.xml b/app/src/main/res/menu/pdf_viewer.xml index e1cc94d2f..5faa41cd5 100644 --- a/app/src/main/res/menu/pdf_viewer.xml +++ b/app/src/main/res/menu/pdf_viewer.xml @@ -67,6 +67,11 @@ android:title="@string/action_rotate_counterclockwise" app:showAsAction="ifRoom" /> + + + + 16384 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 345df9b00..047fe2ab1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,11 @@ Rotate counterclockwise Jump to page Properties + Print + + PDF Document + Print job completed successfully + Print job failed Invalid date Failed to obtain document metadata