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>