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.media.AudioManager;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
+import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
import android.widget.Toast;
+import com.google.android.glass.media.Sounds;
+
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 {
+/**
+ * PDF Viewer for Google Glass.
+ *
+ * This activity is designed to view PDF in Google Glass. The document can be scrolled by the
+ * moving of the user's head and zoomed in and out by sliding forward and backward on the touchpad.
+ * The activity has two states, which can be toggled by taping on the touchpad. When in the Scrolling
+ * state, the PDF document can be scrolled and zoomed. When in the viewing state, those features are
+ * disabled to let the user read the document comfortably.
+ *
+ * The activity is prepared to switch between pages, which should be implemented in the future by sliding
+ * forward and backward in the viewing state.
+ */
+public class PdfViewActivity extends Activity implements ViewTreeObserver.OnGlobalLayoutListener, PdfPageView.SceneChange, SensorEventListener {
+ /**
+ * The Key of the file path value, passed as a data to the activity launch intent.
+ */
+ public static final String FILE_PATH_INTENT_KEY = "FILE_PATH";
+
+ /**
+ * The states of the PDF viewer.
+ */
+ enum ViewStates {
+ /**
+ * Scrolling state - user can scroll in the page by moving of the head and zoom by sliding the touchpad.
+ */
+ SCROLLING,
+ /**
+ * Viewing state - user can read the document comfortably and in the future will be able to change the pages.
+ */
+ VIEWING
+ }
+
+ /**
+ * An activity tag for debug, error and info messages.
+ */
public static final String TAG = "PdfViewActivity";
+ /**
+ * The container for progress bar widget and text view.
+ * <p>Those two are in the container to make them easily disappear and appear when needed.</p>
+ */
private FrameLayout mPdfImageContainer;
+ /**
+ * The text view widget showing the current page numeber.
+ */
+ private TextView mCurrentPageNumber;
+ /**
+ * The text view widget showing total number of the pages.
+ */
+ private TextView mLastPageNumber;
+ /**
+ * The text view widget showing current state of the viewer.
+ */
+ private TextView mViewerStateText;
+ /**
+ * The text view widget showing the zoom in percent.
+ */
+ private TextView mZoomValue;
+ /**
+ * The observer of the view tree for detection of the end of the View tree loading.
+ */
private ViewTreeObserver mPreviewImgObserver;
+ /**
+ * The path to the PDF file to be loaded.
+ */
private String mFilePath;
+ /**
+ * Widget containing loaded page from a PDF file.
+ */
private PdfPageView mPdfView;
- private Boolean mPdfLoaded = false;
+ /**
+ * Flag signalling whether the PDF page has been loaded.
+ */
+ private Boolean mPdfLoaded;
+ /**
+ * Sensor manager for receiving data from gyroscope to scroll the PDF page.
+ */
private SensorManager mSensorManager;
-
/**
- * Start position of the gesture on touchpad
+ * Stores the current state of the PDF viewer.
+ */
+ private ViewStates mViewStates;
+ /**
+ * The container for progress bar widget and text view.
+ * <p>Those two are in the container to make them easily disappear and appear when needed.</p>
+ */
+ private RelativeLayout mProgressContainer;
+ /**
+ * Start position of the gesture on touchpad.
*/
private float startX;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mPdfLoaded = false;
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);
+ mCurrentPageNumber = (TextView) findViewById(R.id.page_number);
+ mLastPageNumber = (TextView) findViewById(R.id.total_pages_num);
+ mViewerStateText = (TextView) findViewById(R.id.pdf_viewer_state);
+ mZoomValue = (TextView) findViewById(R.id.zoom_value);
+ mProgressContainer = (RelativeLayout) findViewById(R.id.progress_container);
+ if (mProgressContainer == null) {
+ Log.e(TAG, "Progress container not found in the activity layout.");
+ finish();
}
- else {
- Log.e(TAG, "ImageView for preview image could not be found in the resources.");
- mPreviewImgObserver = null;
+ if (mCurrentPageNumber == null) {
+ Log.e(TAG, "Current page number view could not be found in the activity layout.");
+ finish();
+ }
+ if (mLastPageNumber == null) {
+ Log.e(TAG, "Last page number view could not be found in the activity layout.");
+ finish();
+ }
+ if (mViewerStateText == null) {
+ Log.e(TAG, "Viewer state view could not be found in the activity layout.");
+ finish();
+ }
+ if (mZoomValue == null) {
+ Log.e(TAG, "Zoom number view could not be found in the activity layout.");
+ finish();
}
+ if (mPdfImageContainer == null) {
+ Log.e(TAG, "Current page number view could not be found in the activity layout.");
+ finish();
+ }
+ mViewStates = ViewStates.SCROLLING;
+
+
+ mPreviewImgObserver = mPdfImageContainer.getViewTreeObserver();
+ Log.d(TAG, "Registering mPreviewImgObserver OnGlobalLayoutListener.");
+ mPreviewImgObserver.addOnGlobalLayoutListener(this);
Intent intent = getIntent();
- mFilePath = intent.getStringExtra("FILE_PATH");
+ mFilePath = intent.getStringExtra(FILE_PATH_INTENT_KEY);
if (mFilePath != null) {
Log.i(TAG, "File path: " + mFilePath);
File rootPath = new File(mFilePath);
if (!rootPath.exists()) {
+ Log.e(TAG, "File " + rootPath.getAbsolutePath() + "not found.");
Toast toast = Toast.makeText(getApplicationContext(), "Document not found", Toast.LENGTH_LONG);
toast.show();
finish();
}
}
else {
- Log.i(TAG, "No file path received");
+ Log.e(TAG, "No file path received");
finish();
}
}
public void onResume() {
+ Log.d(TAG, "Registering sensor listener");
mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE), SensorManager.SENSOR_DELAY_NORMAL);
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
+ Log.d(TAG, "Unregister sensor listener.");
mSensorManager.unregisterListener(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) {
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 = new PdfPageView(getApplicationContext(), picturePath);
+ mPdfView.setPage(0);
+ mCurrentPageNumber.setText(Integer.toString(mPdfView.getActualPage()+1));
+ mLastPageNumber.setText(Integer.toString(mPdfView.getLastPage() + 1));
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();
- }
+ new LoadPageTask().execute();
+
+ mPdfLoaded = true;
} catch (Exception e) {
- Toast toast = Toast.makeText(getApplicationContext(), "Component preview could not be loaded.", Toast.LENGTH_LONG);
+ Log.e(TAG, "PDF file could not be loaded: " + e.getMessage());
+ Toast toast = Toast.makeText(getApplicationContext(), "PDF file 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;
+ if (mViewStates == ViewStates.SCROLLING) {
+ /*
+ 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;
+ zoomPage(dzoom / 1000f);
+ startX = event.getRawX();
+ return false;
+ }
}
return super.onGenericMotionEvent(event);
}
@Override
- public void onSceneChanged(SceneView source) {
- if (mPdfView != null) {
- mPdfView.invalidate();
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ AudioManager audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ audio.playSoundEffect(Sounds.TAP);
+ switchState();
}
+ return super.onKeyDown(keyCode, event);
}
@Override
float dx = event.values[1]*12;
float dy = event.values[0]*12;
if (mPdfView != null) {
- mPdfView.move(-dx, -dy);
+ if (mViewStates == ViewStates.SCROLLING) {
+ mPdfView.move(-dx, -dy);
+ }
}
}
}
+
+ @Override
+ public void onViewChanged(PdfPageView source) {
+ if (mPdfView != null) {
+ mPdfView.invalidate();
+ }
+ }
+
+ @Override
+ public void onPageLoaded(PdfPageView source) {
+
+ }
+
+ /**
+ * Switch the viewer state, update the state text view in the layout.
+ */
+ private void switchState() {
+ if (mViewStates == ViewStates.SCROLLING) {
+ mViewStates = ViewStates.VIEWING;
+ mViewerStateText.setText(getText(R.string.state_viewing));
+ }
+ else {
+ mViewStates = ViewStates.SCROLLING;
+ mViewerStateText.setText(getText(R.string.state_scrolling));
+
+ }
+ }
+
+ /**
+ * Zoom the PDF page, update the zoom value in the layout.
+ * @param deltaZoom The value how much to zoom the page.
+ */
+ private void zoomPage(float deltaZoom) {
+ mPdfView.zoom(deltaZoom);
+ mZoomValue.setText(Integer.toString(Math.round(mPdfView.getZoomRatio() * 100)));
+ }
+
+ /**
+ * Class for asynchronous loading of the PDF page to a bitmap.
+ */
+ 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);
+ switchState();
+ mPdfImageContainer.addView(mPdfView);
+ zoomPage(0.0f); // just for update the zoom value indicator
+ Log.d(TAG, "PDF page loaded.");
+ }
+ }
}