Example of Android webview to PDF
- 2021-08-12 03:37:39
- OfStack
1. I found a lot of things on the Internet that didn't show good results. Later, I saw that I called the mobile phone to print preview. After seeing the effect, I planned to use the system printing service to preview the download
2. 'webView. createPrintDocumentAdapter ()' Get the printed PrintDocumentAdapter. With this class, you can use the onWrite method to write to the specified file, but this method needs to pass in a callback. Tragically, this callback method is hiden, and we can't call it.
3. How to solve the word? There is a method
3.1 Replace the android. jar file in your own sdk with this open source library
https://github.com/anggrayudi/android-hidden-api
3.2 Using dexmaker to generate dynamic proxy proxy PrintDocumentAdapter. WriteResultCallback and PrintDocumentAdapter. LayoutResultCallback method dependent addresses
compile 'org.droidparts.dexmaker:dexmaker-mockito:1.5'
4. The complete code is as follows:
File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM + "/PDFTest.pdf");
File dexCacheFile;
// Object to be printed webview Adapter
PrintDocumentAdapter printAdapter;
PageRange[] ranges;
ParcelFileDescriptor descriptor;
/**
a* @param webView
*/
private void printPDFFile(WebView webView) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
/**
* android 5.0 After that, due to the security considerations of dynamic injection of bytecode, it is no longer allowed to specify the storage path of bytecode at will, and it needs to be placed in the folder of applying its own package name.
*/
// New creation DexMaker The way to cache the directory is directly through context Get the path
dexCacheFile = getDir("dex", 0);
if (!dexCacheFile.exists()) {
dexCacheFile.mkdir();
}
try {
// Object to be written to PDF Documents, pdfFilePath For self-specified PDF File path
if (file.exists()) {
file.delete();
}
file.createNewFile();
descriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
// Setting print parameters
PrintAttributes attributes = new PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.ISO_A4)
.setResolution(new PrintAttributes.Resolution("id", Context.PRINT_SERVICE, 300, 300))
.setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setMinMargins(PrintAttributes.Margins.NO_MARGINS)
.build();
// Print all interfaces
ranges = new PageRange[]{PageRange.ALL_PAGES};
printAdapter = webView.createPrintDocumentAdapter();
// Start printing
printAdapter.onStart();
printAdapter.onLayout(attributes, attributes, new CancellationSignal(), getLayoutResultCallback(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("onLayoutFinished")) {
// Listen to the internal call onLayoutFinished() Method, that is, the print succeeds
onLayoutSuccess();
} else {
// Listen to print failure or cancel printing
}
return null;
}
}, dexCacheFile.getAbsoluteFile()), new Bundle());
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @throws IOException
*/
private void onLayoutSuccess() throws IOException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
PrintDocumentAdapter.WriteResultCallback callback = getWriteResultCallback(new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
if (method.getName().equals("onWriteFinished")) {
Toast.makeText(MainActivity.this,"Success",Toast.LENGTH_SHORT).show();
// PDF The file was written locally and exported successfully
Log.e("onLayoutSuccess","onLayoutSuccess");
} else {
Toast.makeText(MainActivity.this," Export failed ",Toast.LENGTH_SHORT).show();
}
return null;
}
}, dexCacheFile.getAbsoluteFile());
// Write file to local
printAdapter.onWrite(ranges, descriptor, new CancellationSignal(), callback);
}else {
Toast.makeText(MainActivity.this," Not supported 4.4. The following ",Toast.LENGTH_SHORT).show();
}
}
@SuppressLint("NewApi")
public static PrintDocumentAdapter.LayoutResultCallback getLayoutResultCallback(InvocationHandler invocationHandler, File dexCacheDir) throws IOException {
return ProxyBuilder.forClass(PrintDocumentAdapter.LayoutResultCallback.class)
.dexCache(dexCacheDir)
.handler(invocationHandler)
.build();
}
@SuppressLint("NewApi")
public static PrintDocumentAdapter.WriteResultCallback getWriteResultCallback(InvocationHandler invocationHandler, File dexCacheDir) throws IOException {
return ProxyBuilder.forClass(PrintDocumentAdapter.WriteResultCallback.class)
.dexCache(dexCacheDir)
.handler(invocationHandler)
.build();
}