From: Michal Horn Date: Mon, 16 Feb 2015 12:11:24 +0000 (+0100) Subject: Implement first and naive version of pdf viewer activity X-Git-Url: https://rtime.felk.cvut.cz/gitweb/hornmich/skoda-qr-demo.git/commitdiff_plain/922ed1fc2868f859443deb3003c0b24ea3793846 Implement first and naive version of pdf viewer activity --- diff --git a/QRScanner/glass/src/main/AndroidManifest.xml b/QRScanner/glass/src/main/AndroidManifest.xml index e9ab2ee..7c6bcdf 100644 --- a/QRScanner/glass/src/main/AndroidManifest.xml +++ b/QRScanner/glass/src/main/AndroidManifest.xml @@ -4,22 +4,31 @@ android:versionCode="1" android:versionName="1.0" > - + + + + + @@ -31,6 +40,10 @@ android:name=".PreviewActivity" android:label="@string/title_activity_preview" > + + diff --git a/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/PreviewActivity.java b/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/PreviewActivity.java index dfd00c7..7ba8fcd 100644 --- a/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/PreviewActivity.java +++ b/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/PreviewActivity.java @@ -18,6 +18,7 @@ import android.widget.Toast; import java.io.File; import cz.cvut.fel.dce.qrscanner.mupdf.MuPDFCore; +import cz.cvut.fel.dce.qrscanner.pdfviewer.PdfViewActivity; public class PreviewActivity extends Activity implements ViewTreeObserver.OnGlobalLayoutListener{ @@ -41,11 +42,11 @@ public class PreviewActivity extends Activity implements ViewTreeObserver.OnGlob super.onCreate(savedInstanceState); setContentView(R.layout.activity_preview); mPreviewImg = (ImageView) findViewById(R.id.imgComponent); - mPreviewImg.setMaxWidth(640); - mPreviewImg.setMaxHeight(360); - mPreviewImg.setScaleType(ImageView.ScaleType.CENTER_INSIDE); if (mPreviewImg != null) { + mPreviewImg.setMaxWidth(640); + mPreviewImg.setMaxHeight(360); + mPreviewImg.setScaleType(ImageView.ScaleType.CENTER_INSIDE); mPreviewImgObserver = mPreviewImg.getViewTreeObserver(); Log.i(TAG, "Registering mPreviewImgObserver OnGlobalLayoutListener."); mPreviewImgObserver.addOnGlobalLayoutListener(this); @@ -104,7 +105,7 @@ public class PreviewActivity extends Activity implements ViewTreeObserver.OnGlob 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); + core.drawPage(previewBitmap, 0, pageW, pageH, 0, 0, pageW, pageH, cookie); mPreviewImg.setImageBitmap(previewBitmap); mPreviewImg.invalidate(); } catch (Exception e) { @@ -135,11 +136,9 @@ public class PreviewActivity extends Activity implements ViewTreeObserver.OnGlob } private void showPDF(String filePath) { - Uri uri = Uri.parse(filePath); - Intent pdfIntent = new Intent("com.artifex.mupdfdemo.VIEW"); - //pdfIntent.setAction(Intent.ACTION_VIEW); - pdfIntent.setData(uri); - startActivity(pdfIntent); + Intent preview = new Intent(this, PdfViewActivity.class); + preview.putExtra("FILE_PATH", filePath); + startActivity(preview); } @Override diff --git a/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfPageView.java b/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfPageView.java new file mode 100644 index 0000000..ffa8d1c --- /dev/null +++ b/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfPageView.java @@ -0,0 +1,35 @@ +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.view.View; + +/** + * Created by michal on 16.2.15. + */ +public class PdfPageView extends SceneView { + private Bitmap mPdfBitmap; + private Paint mBitmapPaint; + + public PdfPageView(Context context, Bitmap pdfBitmap, float defZoom, float minZoom, float maxZoom, float defXCenter, float minX, float maxX, float defYCenter, float minY, float maxY) throws Exception { + super(context, defZoom, minZoom, maxZoom, defXCenter, minX, maxX, defYCenter, minY, maxY); + mPdfBitmap = Bitmap.createBitmap(pdfBitmap); + mBitmapPaint = new Paint(); + } + + @Override + protected void computeSceneDimmensions() { + mSceneWidth = mPdfBitmap.getWidth(); + mSceneHeight = mPdfBitmap.getHeight(); + } + + @Override + public void draw(Canvas c) { + 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, mBitmapPaint); + } +} diff --git a/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfViewActivity.java b/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfViewActivity.java new file mode 100644 index 0000000..1ad54ae --- /dev/null +++ b/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/PdfViewActivity.java @@ -0,0 +1,201 @@ +package cz.cvut.fel.dce.qrscanner.pdfviewer; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.Toast; + +import java.io.File; + +import cz.cvut.fel.dce.qrscanner.R; +import cz.cvut.fel.dce.qrscanner.mupdf.MuPDFCore; + +public class PdfViewActivity extends Activity implements ViewTreeObserver.OnGlobalLayoutListener, SceneView.SceneChange, SensorEventListener { + public static final String TAG = "PdfViewActivity"; + + private FrameLayout mPdfImageContainer; + private ViewTreeObserver mPreviewImgObserver; + private String mFilePath; + private PdfPageView mPdfView; + private Boolean mPdfLoaded = false; + private SensorManager mSensorManager; + + /** + * Start position of the gesture on touchpad + */ + private float startX; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); + setContentView(R.layout.activity_pdf_view); + mPdfImageContainer = (FrameLayout) findViewById(R.id.pdf_view_container); + if (mPdfImageContainer != null) { + mPreviewImgObserver = mPdfImageContainer.getViewTreeObserver(); + Log.i(TAG, "Registering mPreviewImgObserver OnGlobalLayoutListener."); + mPreviewImgObserver.addOnGlobalLayoutListener(this); + } + else { + Log.e(TAG, "ImageView for preview image could not be found in the resources."); + mPreviewImgObserver = null; + } + + Intent intent = getIntent(); + mFilePath = intent.getStringExtra("FILE_PATH"); + + if (mFilePath != null) { + Log.i(TAG, "File path: " + mFilePath); + File rootPath = new File(mFilePath); + if (!rootPath.exists()) { + Toast toast = Toast.makeText(getApplicationContext(), "Document not found", Toast.LENGTH_LONG); + toast.show(); + finish(); + } + } + else { + Log.i(TAG, "No file path received"); + finish(); + } + } + + public void onResume() { + mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE), SensorManager.SENSOR_DELAY_NORMAL); + super.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mSensorManager.unregisterListener(this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mPreviewImgObserver != null && mPreviewImgObserver.isAlive()) { + mPreviewImgObserver.removeOnGlobalLayoutListener(this); + } + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_pdf_view, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + public void onGlobalLayout() { + if (mPdfLoaded) { + Log.d(TAG, "PDF file already loaded."); + return; + } + try { + String picturePath = mFilePath; + Log.i(TAG, "Path to component files: " + picturePath); + + 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 mPdfBitmapConf = Bitmap.Config.ARGB_8888; + Bitmap mPdfBitmap = Bitmap.createBitmap(pageW, pageH, mPdfBitmapConf); + core.drawPage(mPdfBitmap, 0, pageW, pageH,0 , 0, pageW, pageH, cookie); + mPdfView = new PdfPageView(getApplicationContext(), mPdfBitmap, 0, 0, 100, (float) mPdfBitmap.getWidth()/2, 0, (float) mPdfBitmap.getWidth(), (float) mPdfBitmap.getHeight()/2, 0, (float) mPdfBitmap.getHeight()); + mPdfView.setListener(this); + mPdfView.setDefaultValues(0, 0, 0); + if (mPdfImageContainer != null) { + mPdfImageContainer.addView(mPdfView); + mPdfImageContainer.invalidate(); + mPdfLoaded = true; + } + else { + Log.e(TAG, "Could not find container for PdfPageView."); + finish(); + } + } catch (Exception e) { + Toast toast = Toast.makeText(getApplicationContext(), "Component preview could not be loaded.", Toast.LENGTH_LONG); + toast.show(); + e.printStackTrace(); + } + } + + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + /* + Zoom ratio gained during one gesture on touchpad + */ + float dzoom; + if (event.getAction() == MotionEvent.ACTION_DOWN) { + startX = event.getRawX(); + return false; + } + if (event.getAction() == MotionEvent.ACTION_MOVE) { + dzoom = event.getRawX() - startX; + Log.d(TAG,"move_action, dzoom: " + dzoom); + mPdfView.zoom(dzoom /1000f); + startX = event.getRawX(); + return false; + } + return super.onGenericMotionEvent(event); + } + + @Override + public void onSceneChanged(SceneView source) { + if (mPdfView != null) { + mPdfView.invalidate(); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + + } + + @Override + public void onSensorChanged(SensorEvent event) { + if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) { + float dx = event.values[1]*12; + float dy = event.values[0]*12; + if (mPdfView != null) { + mPdfView.move(-dx, -dy); + } + } + } +} diff --git a/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/SceneView.java b/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/SceneView.java new file mode 100644 index 0000000..f974cad --- /dev/null +++ b/QRScanner/glass/src/main/java/cz/cvut/fel/dce/qrscanner/pdfviewer/SceneView.java @@ -0,0 +1,294 @@ +package cz.cvut.fel.dce.qrscanner.pdfviewer; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.view.View; + +/** + * Parent class for all views for drawing some graphic on canvas. + * The scenes can be zoomed and moved by an application. + * @author Michal Horn + * + */ +public abstract class SceneView extends View { + /** + * Value with some allowed minimum and maximum. + * @author Michal Horn + * + * @param some comparable numeric data type + */ + class IntervalValue> { + /** + * 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 . + * + * @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,
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 onSceneChanged(SceneView source); + } + + protected Paint mPaint; + protected IntervalValue mZoom; + protected IntervalValue mXPosition; + protected IntervalValue mYPosition; + protected SceneChange mListener; + protected float mSceneHeight; + protected float mSceneWidth; + protected float mDefaultZoomRatio; + protected float mDefaultXPos; + protected float mDefaultYPos; + + /** + * Class constructor. + * @param context the context of the application to provide the canvas + * @param defZoom initial zoom ratio + * @param minZoom maximal allowed zoom out + * @param maxZoom maximal allowed zoom in + * @param defXCenter initial position of the scene + * @param minX minimal scene position on X axis + * @param maxX maximal scene position on X axis + * @param defYCenter initial position of the scene + * @param minY minimal scene position on Y axis + * @param maxY maximal scene position on Y axis + * @throws Exception + */ + public SceneView(Context context, + float defZoom, float minZoom, float maxZoom, + float defXCenter, float minX, float maxX, + float defYCenter, float minY, float maxY) throws Exception { + super(context); + mDefaultZoomRatio = defZoom; + mDefaultXPos = defXCenter; + mDefaultYPos = defYCenter; + mZoom = new IntervalValue(defZoom, minZoom, maxZoom); + mXPosition = new IntervalValue(defXCenter, minX, maxX); + mYPosition = new IntervalValue(defYCenter, minY, maxY); + mListener = null; + mPaint = new Paint(); + } + + /** + * Default class constructor, all default values are set to zeros. + * @param context the context of the application to provide the canvas + * @throws Exception + */ + public SceneView(Context context) throws Exception { + this(context, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + } + + /** + * Set a listener of the events + * @param listener listener of the events + */ + public void setListener(SceneChange listener) { + mListener = listener; + } + + /** + * Set new default values. Default values are loaded when reset function is called. + * Values are stored only, not applied. + * @param zoom default zoom ratio + * @param x default x position + * @param y default y position + */ + public void setDefaultValues(float zoom, float x, float y) { + mDefaultXPos = x; + mDefaultYPos = y; + mDefaultZoomRatio = zoom; + } + + /** + * Zoom scene function. The scene can be zoomed in or out only in limits + * defined in constructor. + * @param deltaRatio when > 0 - zoom in,
when < 0 - zoom out + */ + public void zoom(float deltaRatio) { + float ratio = mZoom.getValue(); + ratio += deltaRatio; + if (mZoom.setValue(ratio) == true && mListener != null) { + mListener.onSceneChanged(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) { + 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.onSceneChanged(this); + } + } + + /** + * Reset the zoom ratio and scene position to default values. + */ + public void reset() { + boolean xWasSet = mXPosition.setValue(mDefaultXPos); + boolean yWasSet = mYPosition.setValue(mDefaultYPos); + boolean zoomWasSet = mZoom.setValue(mDefaultZoomRatio); + if ((xWasSet || yWasSet || zoomWasSet) && mListener != null) { + mListener.onSceneChanged(this); + } + } + + /** + * Abstract function for scene dimensions calculation to be implemented. + */ + protected abstract void computeSceneDimmensions(); + + /** + * Abstract function with the drawing process to be implemented + */ + public abstract void draw(Canvas c); + + /** + * @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) { + draw(c); + super.onDraw(c); + } +} diff --git a/QRScanner/glass/src/main/res/layout/activity_pdf_view.xml b/QRScanner/glass/src/main/res/layout/activity_pdf_view.xml new file mode 100644 index 0000000..cc0b6e7 --- /dev/null +++ b/QRScanner/glass/src/main/res/layout/activity_pdf_view.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + diff --git a/QRScanner/glass/src/main/res/menu/menu_pdf_view.xml b/QRScanner/glass/src/main/res/menu/menu_pdf_view.xml new file mode 100644 index 0000000..a4851c9 --- /dev/null +++ b/QRScanner/glass/src/main/res/menu/menu_pdf_view.xml @@ -0,0 +1,8 @@ + + + diff --git a/QRScanner/glass/src/main/res/values-w820dp/dimens.xml b/QRScanner/glass/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..63fc816 --- /dev/null +++ b/QRScanner/glass/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/QRScanner/glass/src/main/res/values/dimens.xml b/QRScanner/glass/src/main/res/values/dimens.xml new file mode 100644 index 0000000..47c8224 --- /dev/null +++ b/QRScanner/glass/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/QRScanner/glass/src/main/res/values/strings.xml b/QRScanner/glass/src/main/res/values/strings.xml index 0943504..bb8c7c2 100644 --- a/QRScanner/glass/src/main/res/values/strings.xml +++ b/QRScanner/glass/src/main/res/values/strings.xml @@ -29,4 +29,9 @@ limitations under the License. Pictured manuf Workshow guide Skoda Manufacturer Helper + PdfViewActivity + Settings + Page: + State: + scrolling