reader/android: use android.graphics.pdf.PdfRenderer
Now the android.graphics.pdf.PdfRenderer is used instead of the pdf.js
wrapped in a WebView.
This version has a text rendering issue on Nexus 5X, but it is working
fine on other devices. Needs more investigation on this issue.
Change-Id: Iebdfec94b4e003373e17b8dd85743fe940ffd809
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 00a88cc..5e476c3 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -104,23 +104,6 @@
packageTranslations += 'vdl->io/v/android/apps/reader/vdl'
}
-// The two tasks below are used for copying the pdf.js files into assets/pdfjs directory.
-task generatePdfJsFiles(type: Exec) {
- workingDir '../../web'
- commandLine 'make', 'public/pdf-web-view.js'
-}
-
-task copyPdfJsFiles(type: Copy, dependsOn: 'generatePdfJsFiles') {
- from('../../web/public') {
- include 'pdf*'
- }
-
- into 'src/main/assets/pdfjs'
-}
-
-// These two tasks should run before 'preBuild' task
-tasks['preBuild'].dependsOn('copyPdfJsFiles')
-
// To use the local version of vanadium-android library,
// use "-DuseLocalVanadiumLib" parameter in the gradle command.
diff --git a/android/app/src/main/java/io/v/android/apps/reader/PdfViewWrapper.java b/android/app/src/main/java/io/v/android/apps/reader/PdfViewWrapper.java
index 3a6ee81..e02c5a6 100644
--- a/android/app/src/main/java/io/v/android/apps/reader/PdfViewWrapper.java
+++ b/android/app/src/main/java/io/v/android/apps/reader/PdfViewWrapper.java
@@ -5,20 +5,18 @@
package io.v.android.apps.reader;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.pdf.PdfRenderer;
+import android.graphics.pdf.PdfRenderer.Page;
+import android.os.ParcelFileDescriptor;
import android.util.AttributeSet;
-import android.util.Log;
-import android.webkit.JavascriptInterface;
-import android.webkit.WebChromeClient;
-import android.webkit.WebResourceRequest;
-import android.webkit.WebResourceResponse;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
+import android.widget.ImageView;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.SettableFuture;
+import com.google.common.io.ByteStreams;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import io.v.android.apps.reader.db.DB;
@@ -28,110 +26,72 @@
*
* May be replaced with another library if needed.
*/
-public class PdfViewWrapper extends WebView {
+public class PdfViewWrapper extends ImageView {
- private static final String TAG = PdfViewWrapper.class.getSimpleName();
-
- private SettableFuture<Boolean> mPageLoaded;
- private int mPageCount;
+ private PdfRenderer mRenderer;
public PdfViewWrapper(Context context, AttributeSet attrs) {
super(context, attrs);
-
- mPageLoaded = SettableFuture.create();
- }
-
- public void init() {
- WebSettings settings = getSettings();
- settings.setJavaScriptEnabled(true);
- settings.setAllowUniversalAccessFromFileURLs(true);
- setWebChromeClient(new WebChromeClient());
- setWebViewClient(new PdfViewClient());
- addJavascriptInterface(new JSInterface(), "android");
-
- loadUrl("file:///android_asset/pdfjs/pdf-web-view.html");
}
/**
* Loads the PDF file at the given path into the pdf.js component within WebView.
*/
- public void loadPdfFile(final String filePath) {
- Futures.addCallback(mPageLoaded, new FutureCallback<Boolean>() {
- @Override
- public void onSuccess(Boolean result) {
- Log.i(TAG, "loadPdfFile called: " + filePath);
- evaluateJavascript("window.client.open(\"" + filePath + "\");", null);
+ public void loadPdfFile(final String fileId) throws IOException {
+ File pdfFile = new File(getContext().getCacheDir(), fileId);
- // leave the page count as 0 until the page count value is properly set from JS side.
- mPageCount = 0;
- }
+ try (InputStream in = DB.Singleton.get(getContext()).getInputStreamForFile(fileId);
+ FileOutputStream out = new FileOutputStream(pdfFile)) {
+ ByteStreams.copy(in, out);
+ }
- @Override
- public void onFailure(Throwable t) {
- // Nothing to do.
- }
- });
+ mRenderer = new PdfRenderer(
+ ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY));
+
+ setPage(1);
}
/**
* Jumps to the given page number. Page number is one-based.
*
- * @param page the page number to jump to. Page number is one-based.
+ * @param pageNumber the page number to jump to. Page number is one-based.
*/
- public void setPage(int page) {
- evaluateJavascript("window.client.page(" + page + ");", null);
+ public void setPage(int pageNumber) {
+ if (pageNumber < 1 || pageNumber > mRenderer.getPageCount()) {
+ // TODO(youngseokyoon): display not available page.
+ return;
+ }
+
+ try (Page page = mRenderer.openPage(pageNumber - 1)) {
+ // Create a bitmap that fits the entire view while keeping the aspect ratio of the source.
+ float pageRatio = (float) page.getWidth() / (float) page.getHeight();
+ float viewRatio = (float) getWidth() / (float) getHeight();
+
+ Bitmap bitmap;
+ if (pageRatio >= viewRatio) {
+ bitmap = Bitmap.createBitmap(
+ getWidth(),
+ (int) (getWidth() / pageRatio),
+ Bitmap.Config.ARGB_8888);
+ } else {
+ bitmap = Bitmap.createBitmap(
+ (int) (getHeight() * pageRatio),
+ getHeight(),
+ Bitmap.Config.ARGB_8888);
+ }
+
+ // Render the page on the bitmap and display it on the ImageView.
+ page.render(bitmap, null, null, Page.RENDER_MODE_FOR_DISPLAY);
+ setImageBitmap(bitmap);
+ }
}
public int getPageCount() {
- return mPageCount;
- }
-
- /**
- * This class provides public methods that can be called from the JavaScript side.
- */
- private class JSInterface {
-
- private final String TAG = JSInterface.class.getSimpleName();
-
- @JavascriptInterface
- public void setPageCount(int pageCount) {
- Log.d(TAG, "setPageCount(" + pageCount + ") called.");
- mPageCount = pageCount;
- }
- }
-
- private class PdfViewClient extends WebViewClient {
- @Override
- public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
- Log.i(TAG, "shouldInterceptRequest called");
-
- String path = request.getUrl().getPath();
- if (!path.startsWith("/file_id/")) {
- Log.i(TAG, "Not a file id path. Falling back to super.shouldInterceptRequest");
- return super.shouldInterceptRequest(view, request);
- }
-
- String fileId = request.getUrl().getLastPathSegment();
- Log.i(TAG, "File ID: " + fileId);
-
- // Should NOT close the stream here, so that the stream can be read by WebView.
- InputStream in = DB.Singleton.get(getContext()).getInputStreamForFile(fileId);
-
- if (in != null) {
- Log.i(TAG, "returning a custom WebResourceResponse");
- return new WebResourceResponse("application/pdf", "binary", in);
- } else {
- Log.i(TAG, "Could not open an input stream. " +
- "Falling back to super.shouldInterceptRequest");
- return super.shouldInterceptRequest(view, request);
- }
+ if (mRenderer == null) {
+ return 0;
}
- @Override
- public void onPageFinished(WebView view, String url) {
- super.onPageFinished(view, url);
- mPageLoaded.set(true);
- }
+ return mRenderer.getPageCount();
}
}
diff --git a/android/app/src/main/java/io/v/android/apps/reader/PdfViewerActivity.java b/android/app/src/main/java/io/v/android/apps/reader/PdfViewerActivity.java
index c4331e7..df59143 100644
--- a/android/app/src/main/java/io/v/android/apps/reader/PdfViewerActivity.java
+++ b/android/app/src/main/java/io/v/android/apps/reader/PdfViewerActivity.java
@@ -15,7 +15,6 @@
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MotionEvent;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -80,8 +79,6 @@
setContentView(R.layout.activity_pdf_viewer);
mPdfView = (PdfViewWrapper) findViewById(R.id.pdfview);
- mPdfView.init();
-
mProgressBar = (ProgressBar) findViewById(R.id.pdf_progress_bar);
mProgressText = (TextView) findViewById(R.id.pdf_progress_text);
@@ -102,12 +99,7 @@
}
});
- mPdfView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return swipeDetector.onTouchEvent(event);
- }
- });
+ mPdfView.setOnTouchListener((v, e) -> swipeDetector.onTouchEvent(e));
}
@Override
@@ -304,7 +296,13 @@
private void joinDeviceSet(DeviceSet ds) {
showProgressWidgets(false);
- mPdfView.loadPdfFile("/file_id/" + ds.getFileId());
+ try {
+ mPdfView.loadPdfFile(ds.getFileId());
+ } catch(IOException e) {
+ handleException(e);
+ finish();
+ return;
+ }
// Create a new device meta, and update the device set with it.
Log.i(TAG, "Joining device set: " + ds.getId());
diff --git a/android/app/src/main/java/io/v/android/apps/reader/db/FakeDB.java b/android/app/src/main/java/io/v/android/apps/reader/db/FakeDB.java
index 8cea9a2..faeca92 100644
--- a/android/app/src/main/java/io/v/android/apps/reader/db/FakeDB.java
+++ b/android/app/src/main/java/io/v/android/apps/reader/db/FakeDB.java
@@ -181,7 +181,7 @@
String id = VomUtil.bytesToHexString(mDigest.digest());
- java.io.File jFile = new java.io.File(mContext.getCacheDir(), id);
+ java.io.File jFile = new java.io.File(mContext.getCacheDir(), id + ".db");
try (FileOutputStream out = new FileOutputStream(jFile)) {
out.write(mOutputStream.toByteArray());
} catch (IOException e) {
@@ -270,7 +270,7 @@
@Override
public InputStream getInputStreamForFile(String fileId) {
- java.io.File jFile = new java.io.File(mContext.getCacheDir(), fileId);
+ java.io.File jFile = new java.io.File(mContext.getCacheDir(), fileId + ".db");
try {
return new FileInputStream(jFile);
} catch (IOException e) {
diff --git a/android/app/src/main/res/layout/activity_pdf_viewer.xml b/android/app/src/main/res/layout/activity_pdf_viewer.xml
index fe01f63..da7702c 100644
--- a/android/app/src/main/res/layout/activity_pdf_viewer.xml
+++ b/android/app/src/main/res/layout/activity_pdf_viewer.xml
@@ -16,21 +16,24 @@
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_alignParentTop="true"
+ android:adjustViewBounds="true"
+ android:background="@android:color/white"
+ android:clickable="true"
android:visibility="invisible" />
<ProgressBar
android:id="@+id/pdf_progress_bar"
+ style="@android:style/Widget.Material.Light.ProgressBar.Horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- style="@android:style/Widget.Material.Light.ProgressBar.Horizontal" />
+ android:layout_centerVertical="true" />
<TextView
android:id="@+id/pdf_progress_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="@id/pdf_progress_bar"
- android:textColor="@color/textPrimary"
- android:gravity="center" />
+ android:gravity="center"
+ android:textColor="@color/textPrimary" />
</RelativeLayout>