]> rtime.felk.cvut.cz Git - hornmich/skoda-qr-demo.git/commitdiff
Implement preview by the PdfView widget
authorMichal Horn <hornmich@fel.cvut.cz>
Tue, 17 Feb 2015 11:03:51 +0000 (12:03 +0100)
committerMichal Horn <hornmich@fel.cvut.cz>
Tue, 17 Feb 2015 11:03:51 +0000 (12:03 +0100)
QRScanner/mobile/src/main/java/cz/cvut/fel/dce/qrscanner/PreviewActivity.java
QRScanner/mobile/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfPageView.java [new file with mode: 0644]
QRScanner/mobile/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfViewerException.java [new file with mode: 0644]
QRScanner/mobile/src/main/res/layout-land/activity_preview.xml
QRScanner/mobile/src/main/res/layout/activity_preview.xml
QRScanner/mobile/src/main/res/values/strings.xml

index 2ea62c447757aaed8e82d9ea7153553a71296abb..767d4bc78a4512aae7b337d644c129c0e4820d62 100644 (file)
@@ -1,8 +1,11 @@
 package cz.cvut.fel.dce.qrscanner;
 
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.media.AudioManager;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.support.v7.app.ActionBarActivity;
 import android.os.Bundle;
 import android.util.Log;
@@ -12,10 +15,13 @@ import android.view.View;
 import android.view.ViewTreeObserver;
 import android.widget.Button;
 import android.widget.ImageView;
+import android.widget.RelativeLayout;
 import android.widget.Toast;
 
 import cz.cvut.fel.dce.qrscanner.mupdf.MuPDFActivity;
 import cz.cvut.fel.dce.qrscanner.mupdf.MuPDFCore;
+import cz.cvut.fel.dce.qrscanner.pdfviewer.PdfPageView;
+
 import java.io.File;
 
 public class PreviewActivity extends ActionBarActivity implements ViewTreeObserver.OnGlobalLayoutListener {
@@ -29,23 +35,32 @@ public class PreviewActivity extends ActionBarActivity implements ViewTreeObserv
        public static final String SKODA_COMP_MANUFACT_GUIDE = "Werkstatt_Einleitung.pdf";
 
        private ImageView mPreviewImg;
+       private RelativeLayout mProgressContainer;
        private ViewTreeObserver mPreviewImgObserver;
        private String mComponentId;
        private String mComponentRootPath;
+       private PdfPageView mPdfView;
+       private boolean mPdfLoaded;
+
 
        @Override
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_preview);
+               mPdfLoaded = false;
                mPreviewImg = (ImageView) findViewById(R.id.imgComponent);
-               mPreviewImg.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+               mProgressContainer = (RelativeLayout) findViewById(R.id.progress_container);
+               if (mProgressContainer == null) {
+                       Log.e(TAG, "Progress container was not found.");
+                       finish();
+               }
 
                if (mPreviewImg != null) {
+                       mPreviewImg.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
                        mPreviewImgObserver = mPreviewImg.getViewTreeObserver();
                        Log.i(TAG, "Registering mPreviewImgObserver OnGlobalLayoutListener.");
                        mPreviewImgObserver.addOnGlobalLayoutListener(this);
-               }
-               else {
+               } else {
                        Log.e(TAG, "ImageView for preview image could not be found in the resources.");
                        mPreviewImgObserver = null;
                }
@@ -62,8 +77,7 @@ public class PreviewActivity extends ActionBarActivity implements ViewTreeObserv
                                toast.show();
                                finish();
                        }
-               }
-               else {
+               } else {
                        Log.i(TAG, "No component id received");
                        finish();
                }
@@ -86,23 +100,19 @@ public class PreviewActivity extends ActionBarActivity implements ViewTreeObserv
 
        @Override
        public void onGlobalLayout() {
+               if (mPdfLoaded) {
+                       Log.d(TAG, "PDF file already loaded.");
+                       return;
+               }
                try {
                        String picturePath = mComponentRootPath + SKODA_COMP_PICTURE_NAME;
                        Log.i(TAG, "Path to component files: " + picturePath);
+                       mPdfView = new PdfPageView(getApplicationContext(), picturePath);
+                       mPdfView.setPage(0);
+                       new LoadPageTask().execute();
+
+                       mPdfLoaded = true;
 
-                       MuPDFCore core = new MuPDFCore(getApplicationContext(), picturePath);
-                       Log.d(TAG, "numpages: "+ core.countPages());
-                       MuPDFCore.Cookie cookie = core.new Cookie();
-                       int pageW = (int)core.getPageSize(0).x;
-                       int pageH = (int)core.getPageSize(0).y;
-                       Log.d(TAG, "page size: " + pageW + ", " + pageH);
-                       Bitmap.Config conf = Bitmap.Config.ARGB_8888;
-                       Bitmap previewBitmap = Bitmap.createBitmap(pageW, pageH, conf);
-                       core.drawPage(previewBitmap, 0, pageW, pageH,0 , 0, pageW, pageH, cookie);
-                       mPreviewImg.setMaxWidth(mPreviewImg.getWidth());
-                       mPreviewImg.setMaxHeight(mPreviewImg.getHeight());
-                       mPreviewImg.setImageBitmap(previewBitmap);
-                       mPreviewImg.invalidate();
                } catch (Exception e) {
                        Toast toast = Toast.makeText(getApplicationContext(), "Component preview could not be loaded.", Toast.LENGTH_LONG);
                        toast.show();
@@ -110,33 +120,64 @@ public class PreviewActivity extends ActionBarActivity implements ViewTreeObserv
                }
        }
 
-       /** Called when the user touches the button */
+       /**
+        * Called when the user touches the button
+        */
        public void showContacts(View view) {
                showPDF(mComponentRootPath + SKODA_COMP_CONTACTS);
        }
 
-       /** Called when the user touches the button */
+       /**
+        * Called when the user touches the button
+        */
        public void showManufacturing(View view) {
                showPDF(mComponentRootPath + SKODA_COMP_MANUFACTURING);
        }
 
-       /** Called when the user touches the button */
+       /**
+        * Called when the user touches the button
+        */
        public void showManufactImages(View view) {
                showPDF(mComponentRootPath + SKODA_COMP_MANUFACT_IMAGES);
        }
 
-       /** Called when the user touches the button */
+       /**
+        * Called when the user touches the button
+        */
        public void showManufactGuide(View view) {
                showPDF(mComponentRootPath + SKODA_COMP_MANUFACT_GUIDE);
        }
 
        private void showPDF(String filePath) {
                Uri uri = Uri.parse(filePath);
-               Intent pdfIntent = new Intent(this,MuPDFActivity.class);
+               Intent pdfIntent = new Intent(this, MuPDFActivity.class);
                pdfIntent.setAction(Intent.ACTION_VIEW);
                pdfIntent.setData(uri);
                startActivity(pdfIntent);
        }
 
+       private class LoadPageTask extends AsyncTask<Void, Void, Void> {
+
+               @Override
+               protected Void doInBackground(Void[] objects) {
+                       mPdfView.loadPage();
+                       return null;
+               }
+
+               @Override
+               protected void onPreExecute() {
+                       super.onPreExecute();
+                       Log.d(TAG, "Starting loading of the PDF page asynchronously.");
+                       mProgressContainer.setVisibility(View.VISIBLE);
+               }
 
+               @Override
+               protected void onPostExecute(Void aVoid) {
+                       super.onPostExecute(aVoid);
+                       mProgressContainer.setVisibility(View.INVISIBLE);
+                       mPreviewImg.setImageBitmap(mPdfView.getPageBitmap());
+                       mPreviewImg.invalidate();
+                       Log.d(TAG, "PDF page loaded.");
+               }
+       }
 }
diff --git a/QRScanner/mobile/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfPageView.java b/QRScanner/mobile/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfPageView.java
new file mode 100644 (file)
index 0000000..7e18f17
--- /dev/null
@@ -0,0 +1,310 @@
+package cz.cvut.fel.dce.qrscanner.pdfviewer;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.Log;
+import android.view.View;
+
+import java.io.File;
+
+import cz.cvut.fel.dce.qrscanner.mupdf.MuPDFCore;
+
+/**
+ * Created by Michal Horn on 16.2.15.
+ */
+public class PdfPageView  extends View {
+       /**
+        * Value with some allowed minimum and maximum.
+        * @author Michal Horn
+        *
+        * @param <T> some comparable numeric data type
+        */
+       class IntervalValue<T extends Number & Comparable<? super T>> {
+               /**
+                * Stored value
+                */
+               private T mValue;
+               /**
+                * The lowest allowed value. All values stored in the object are
+                * higher or equal this minimum.
+                */
+               private T mMinimum;
+               /**
+                * The highest allowed value. All values stored in the object are
+                * lower or equal this maximum.
+                */
+               private T mMaximum;
+
+               /**
+                * Class constructor.
+                * Creates object that represents a value in range <minimum; maximum>.
+                *
+                * @param defValue      Value to be stored in the object. Must be in the allowed range specified by other two parameters.
+                * @param minimum       The lowes allowed value to be stored in the object.
+                * @param maximum       The highest allowed value to be stored in the object.
+                * @throws Exception Throwed when minimal, maximal or inserted value were rejected.
+                */
+               public IntervalValue(T defValue, T minimum, T maximum) throws Exception {
+                       setMinimum(minimum);
+                       setMaximum(maximum);
+                       if (!setValue(defValue)) {
+                               throw new Exception("Provided value is not in the range.");
+                       }
+               }
+
+               /**
+                * Store value in the object. The value must lie between the minimum and maximum
+                * defined for the object.
+                * @param value value to be stored in the object
+                * @return true - vale was stored successfully,<br>false - value could not be inserted because it was out of range.
+                */
+               public boolean setValue(T value) {
+                       if (value.compareTo(mMinimum) >= 0 && value.compareTo(mMaximum) <= 0) {
+                               mValue = value;
+                               return true;
+                       }
+                       else {
+                               return false;
+                       }
+               }
+
+               /**
+                * Set minimum for values in the object.
+                * Last inserted value is not changed even if it is lower then the new minimum.
+                * @param min new allowed minimum for the values stored to the object.
+                * @throws Exception throwed when new minimum is higher than old maximum.
+                */
+               public void setMinimum(T min) throws Exception {
+                       if (mMaximum != null && mMinimum != null &&  mMinimum.compareTo(mMaximum) > 0) {
+                               throw new Exception("Minimum is higher then maximum.");
+                       }
+                       mMinimum = min;
+               }
+
+               /**
+                * Set maximum for values in the object.
+                * Last inserted value is not changed even if it is higher then the new maximum.
+                * @param max new allowed maximum for the values stored to the object.
+                * @throws Exception throwed when new maximum is lower than old minimum.
+                */
+               public void setMaximum(T max) throws Exception {
+                       if (mMinimum != null && mMaximum != null &&  mMaximum.compareTo(mMinimum) < 0) {
+                               throw new Exception("Maximum is lower then minimum.");
+                       }
+                       mMaximum = max;
+               }
+
+               /**
+                * Get value stored in the object
+                * @return value stored in the object.
+                */
+               public T getValue() {
+                       return mValue;
+               }
+
+               /**
+                * Get the lowest allowed value that can be stored in the object.
+                * @return minimal value
+                */
+               public T getMinimum() {
+                       return mMinimum;
+               }
+
+               /**
+                * Get the highest allowed value that can be stored in the object.
+                * @return maximal value
+                */
+               public T getMaximum() {
+                       return mMaximum;
+               }
+       }
+
+       /**
+        * Interface for events generated by the view
+        * @author Michal Horn
+        *
+        */
+       public interface SceneChange {
+               /**
+                * Invoked every time when the scene should be redrawed.
+                * @param source
+                */
+               public void onViewChanged(PdfPageView source);
+
+               /**
+                * Invoked once the PDF page is loaded and prepared as a bitmap to be shown
+                * @param source
+                */
+               public void onPageLoaded(PdfPageView source);
+       }
+
+       public static final String TAG = "PdfPageView";
+       protected Paint mPaint;
+       protected IntervalValue<Float> mZoom;
+       protected IntervalValue<Float> mXPosition;
+       protected IntervalValue<Float> mYPosition;
+       protected SceneChange mListener;
+       protected float mSceneHeight;
+       protected float mSceneWidth;
+       protected Bitmap mPdfBitmap;
+       protected File mPdfFile;
+       protected Boolean mPageLoaded;
+       private MuPDFCore mPdfCore;
+       private IntervalValue<Integer> mPage;
+       private MuPDFCore.Cookie mPdfCookie;
+
+       public PdfPageView(Context context, String filePath) throws Exception {
+               super(context);
+               mPageLoaded = false;
+               mPdfFile = new File(filePath);
+               if (!mPdfFile.exists()) {
+                       throw new PdfViewerException("File " + filePath + " does not exist.");
+               }
+               mPaint = new Paint();
+               mPdfCore = new MuPDFCore(context, mPdfFile.getAbsolutePath());
+               mPdfCookie = mPdfCore.new Cookie();
+               if (mPdfCore == null) {
+                       throw new PdfViewerException("The PDF file could not be loaded.");
+               }
+               mPage = new IntervalValue<>(0, 0, mPdfCore.countPages()-1);
+               Log.i(TAG, "Pages: " + mPage.toString());
+       }
+
+       public int getActualPage() {
+               return mPage.getValue();
+       }
+
+       public int getLastPage() {
+               return mPage.getMaximum();
+       }
+
+       public boolean setPage(int page) {
+               return mPage.setValue(page);
+       }
+
+       public void loadPage() {
+               long startTime = System.currentTimeMillis();
+               try {
+                       Log.i(TAG, "Loading PDF page " + getActualPage());
+                       mSceneWidth = mPdfCore.getPageSize(getActualPage()).x;
+                       mSceneHeight = mPdfCore.getPageSize(getActualPage()).y;
+                       Log.i(TAG, "Page size: " + mSceneWidth + "x" + mSceneHeight);
+                       Bitmap.Config mPdfBitmapConf = Bitmap.Config.ARGB_8888;
+                       mPdfBitmap = Bitmap.createBitmap((int)mSceneWidth, (int)mSceneHeight, mPdfBitmapConf);
+                       mPdfCore.drawPage(mPdfBitmap, 0, (int) mSceneWidth, (int) mSceneHeight, 0, 0, (int) mSceneWidth, (int) mSceneHeight, mPdfCookie);
+
+                       mXPosition = new IntervalValue<>(640.0f/2, -mSceneWidth+640.0f/2, mSceneWidth+640.0f/2);
+                       mYPosition = new IntervalValue<>(360.0f/2, -mSceneHeight+360.0f/2, mSceneHeight+360.0f/2);
+                       mZoom = new IntervalValue<>(1.0f, 0.2f, 2.0f);
+               } catch (Exception e) {
+                       Log.e(TAG, "Error in setting page dimensions.");
+                       e.printStackTrace();
+               }
+               long endTime = System.currentTimeMillis();
+               mPageLoaded = true;
+               Log.i(TAG, "PDF page loaded in " + Long.toString(endTime - startTime) + "ms.");
+               if (mListener != null) {
+                       mListener.onPageLoaded(this);
+               }
+       }
+
+       public Bitmap getPageBitmap() {
+               return mPdfBitmap;
+       }
+
+
+
+       /**
+        * Set a listener of the events
+        * @param listener listener of the events
+        */
+       public void setListener(SceneChange listener) {
+               mListener = listener;
+       }
+
+       public void clearListener() {
+               mListener = null;
+       }
+
+       /**
+        * Zoom scene function. The scene can be zoomed in or out only in limits
+        * defined in constructor.
+        * @param deltaRatio when > 0 - zoom in,<br> when < 0 - zoom out
+        */
+       public  void zoom(float deltaRatio) {
+               if (!mPageLoaded) return;
+               float ratio = mZoom.getValue();
+               ratio += deltaRatio;
+               if (mZoom.setValue(ratio) == true && mListener != null) {
+                       mListener.onViewChanged(this);
+               }
+       }
+
+       /**
+        * Move scene function. The scene can be moved only in the rectangle specified
+        * by limits defined in constructor.
+        * @param x change of the x coordinate relatively to the actual position
+        * @param y change of the y coordinate relatively to the actual position
+        */
+       public void move(float x, float y) {
+               if (!mPageLoaded) return;
+               boolean xWasSet = mXPosition.setValue(mXPosition.getValue() + x/mZoom.getValue()*2.2f);
+               boolean yWasSet = mYPosition.setValue(mYPosition.getValue() + y/mZoom.getValue()*2.2f);
+
+               if ((xWasSet || yWasSet) && mListener != null) {
+                       mListener.onViewChanged(this);
+               }
+       }
+       /**
+        * @return actual scene zoom ratio
+        */
+       public float getZoomRatio() {
+               return mZoom.getValue();
+       }
+
+       /**
+        * @return actual scene position on X axis
+        */
+       public float getXPosition() {
+               return mXPosition.getValue();
+       }
+
+       /**
+        * @return actual scene positoin on Y axis
+        */
+       public float getYPosition() {
+               return mYPosition.getValue();
+       }
+
+       /**
+        * @return how tall the scene is
+        */
+       public float getSceneHeight() {
+               return mSceneHeight;
+       }
+
+       /**
+        * @return how wide the scene is
+        */
+       public float getSceneWidth() {
+               return mSceneWidth;
+       }
+
+       @Override
+       protected void onDraw(Canvas c) {
+               if (!mPageLoaded) return;
+               draw(c);
+               super.onDraw(c);
+       }
+
+       @Override
+       public void draw(Canvas c) {
+               if (!mPageLoaded) return;
+               c.translate(c.getWidth() / 2, c.getHeight() / 2);
+               c.scale(mZoom.getValue(), mZoom.getValue());
+               c.translate(-mXPosition.getValue(), -mYPosition.getValue());
+               c.drawBitmap(mPdfBitmap, 0, 0, mPaint);
+       }
+}
diff --git a/QRScanner/mobile/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfViewerException.java b/QRScanner/mobile/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfViewerException.java
new file mode 100644 (file)
index 0000000..36cf105
--- /dev/null
@@ -0,0 +1,10 @@
+package cz.cvut.fel.dce.qrscanner.pdfviewer;
+
+/**
+ * Created by michal on 16.2.15.
+ */
+public class PdfViewerException extends Exception {
+       PdfViewerException(String s){
+               super(s);
+       }
+}
index b3f52fea4ba3b84c0e90f0ae4539e0683b45658c..818eb65be30b4a0bd4b5c478a047d63c428f1494 100644 (file)
 
     </LinearLayout>
 
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:id="@+id/imgComponent"
-        android:layout_weight="0.95"
-        android:layout_gravity="center_horizontal"
-        android:scaleType="center"/>
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                    android:orientation="horizontal"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent">
+
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/imgComponent"
+            android:layout_gravity="center_horizontal"
+            android:scaleType="center"/>
+
+        <RelativeLayout
+            android:layout_width="73dp"
+            android:layout_height="match_parent"
+            android:id="@+id/progress_container"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentEnd="true"
+            android:layout_alignParentStart="true"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentRight="true">
+
+            <ProgressBar
+                style="?android:attr/progressBarStyleLarge"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/progressBar"
+                android:layout_centerVertical="true"
+                android:layout_centerHorizontal="true"/>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:text="@string/loading_page"
+                android:id="@+id/textView7"
+                android:layout_below="@+id/progressBar"
+                android:layout_alignStart="@+id/progressBar"
+                android:layout_alignLeft="@+id/progressBar"/>
+        </RelativeLayout>
+    </RelativeLayout>
 
 </LinearLayout>
\ No newline at end of file
index 7573b142389c8233cfbd16ddf48939f78084a216..f2afe0295ec9bab9a570dcb5f9f4a1b0ef14ce2f 100644 (file)
         android:onClick="showContacts"
         />
 
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:id="@+id/imgComponent"
-        android:layout_weight="0.95"
-        android:layout_gravity="center_vertical"
-        android:scaleType="center"/>
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                    android:orientation="horizontal"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent">
+
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/imgComponent"
+            android:layout_gravity="center_vertical"
+            android:scaleType="center"/>
+
+        <RelativeLayout
+            android:layout_width="73dp"
+            android:layout_height="match_parent"
+            android:id="@+id/progress_container"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentEnd="true"
+            android:layout_alignParentStart="true"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentRight="true">
+
+            <ProgressBar
+                style="?android:attr/progressBarStyleLarge"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/progressBar2"
+                android:layout_centerVertical="true"
+                android:layout_centerHorizontal="true"/>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:text="@string/loading_page"
+                android:id="@+id/textView6"
+                android:layout_below="@+id/progressBar2"
+                android:layout_alignStart="@+id/progressBar2"
+                android:layout_alignLeft="@+id/progressBar2"/>
+
+        </RelativeLayout>
+    </RelativeLayout>
 
 </LinearLayout>
index 99c1eb1102970e72a3c35a029ff42f530dc6d0c1..1d7a6c0bdfbee01b06c5a1c16c8d42ffccf36cf6 100644 (file)
@@ -65,6 +65,7 @@
     <string name="butPicturedmanufacturing">Pictured manufacturing process</string>
     <string name="butManufacturingGuide">Workshop manual</string>
     <string name="previewHeading">Select document to view</string>
+    <string name="loading_page">Loading...</string>
 
 
 </resources>