summaryrefslogtreecommitdiff
path: root/android/src/com/artifex/mupdfdemo/MuPDFActivity.java
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2013-01-21 12:54:06 +0000
committerRobin Watts <robin.watts@artifex.com>2013-01-21 16:43:19 +0000
commit5822ff1619fda7f0cc0b10f77d351eb459e3c67c (patch)
treebde594a8cd36b72bc7c698ab3eec3861a1802b9f /android/src/com/artifex/mupdfdemo/MuPDFActivity.java
parent18d50b1c3ddfef1b08059a3fc898dad0fb98d6a7 (diff)
downloadmupdf-5822ff1619fda7f0cc0b10f77d351eb459e3c67c.tar.xz
Rename app.
Due to a clash on Google Play, we need to rename the apps main class from com.artifex.mupdf to something else. We choose com.artifex.mupdfdemo. Any user of the code in their own app should rename it similarly. To simplify this process we add some macros in the C. Various renames and lots of tedious package name editing is still required in the Java though.
Diffstat (limited to 'android/src/com/artifex/mupdfdemo/MuPDFActivity.java')
-rw-r--r--android/src/com/artifex/mupdfdemo/MuPDFActivity.java1031
1 files changed, 1031 insertions, 0 deletions
diff --git a/android/src/com/artifex/mupdfdemo/MuPDFActivity.java b/android/src/com/artifex/mupdfdemo/MuPDFActivity.java
new file mode 100644
index 00000000..25cb68e9
--- /dev/null
+++ b/android/src/com/artifex/mupdfdemo/MuPDFActivity.java
@@ -0,0 +1,1031 @@
+package com.artifex.mupdfdemo;
+
+import java.util.concurrent.Executor;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.graphics.Color;
+import android.graphics.RectF;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.text.method.PasswordTransformationMethod;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.ViewAnimator;
+
+class ThreadPerTaskExecutor implements Executor {
+ public void execute(Runnable r) {
+ new Thread(r).start();
+ }
+}
+
+class SearchTaskResult {
+ public final String txt;
+ public final int pageNumber;
+ public final RectF searchBoxes[];
+ static private SearchTaskResult singleton;
+
+ SearchTaskResult(String _txt, int _pageNumber, RectF _searchBoxes[]) {
+ txt = _txt;
+ pageNumber = _pageNumber;
+ searchBoxes = _searchBoxes;
+ }
+
+ static public SearchTaskResult get() {
+ return singleton;
+ }
+
+ static public void set(SearchTaskResult r) {
+ singleton = r;
+ }
+}
+
+class ProgressDialogX extends ProgressDialog {
+ public ProgressDialogX(Context context) {
+ super(context);
+ }
+
+ private boolean mCancelled = false;
+
+ public boolean isCancelled() {
+ return mCancelled;
+ }
+
+ @Override
+ public void cancel() {
+ mCancelled = true;
+ super.cancel();
+ }
+}
+
+public class MuPDFActivity extends Activity
+{
+ /* The core rendering instance */
+ private final int TAP_PAGE_MARGIN = 5;
+ private static final int SEARCH_PROGRESS_DELAY = 200;
+ private MuPDFCore core;
+ private String mFileName;
+ private ReaderView mDocView;
+ private View mButtonsView;
+ private boolean mButtonsVisible;
+ private EditText mPasswordView;
+ private TextView mFilenameView;
+ private SeekBar mPageSlider;
+ private int mPageSliderRes;
+ private TextView mPageNumberView;
+ private TextView mInfoView;
+ private ImageButton mSearchButton;
+ private ImageButton mSelectButton;
+ private ImageButton mCancelSelectButton;
+ private ImageButton mCopySelectButton;
+ private ImageButton mCancelButton;
+ private ImageButton mOutlineButton;
+ private ViewAnimator mTopBarSwitcher;
+ private ImageButton mLinkButton;
+ private boolean mTopBarIsSearch;
+ private ImageButton mSearchBack;
+ private ImageButton mSearchFwd;
+ private EditText mSearchText;
+ private AsyncTask<Void,Integer,SearchTaskResult> mSearchTask;
+ //private SearchTaskResult mSearchTaskResult;
+ private AlertDialog.Builder mAlertBuilder;
+ private boolean mLinkHighlight = false;
+ private boolean mSelecting = false;
+ private final Handler mHandler = new Handler();
+ private boolean mAlertsActive= false;
+ private AsyncTask<Void,Void,MuPDFAlert> mAlertTask;
+ private AlertDialog mAlertDialog;
+
+ public void createAlertWaiter() {
+ mAlertsActive = true;
+ // All mupdf library calls are performed on asynchronous tasks to avoid stalling
+ // the UI. Some calls can lead to javascript-invoked requests to display an
+ // alert dialog and collect a reply from the user. The task has to be blocked
+ // until the user's reply is received. This method creates an asynchronous task,
+ // the purpose of which is to wait of these requests and produce the dialog
+ // in response, while leaving the core blocked. When the dialog receives the
+ // user's response, it is sent to the core via replyToAlert, unblocking it.
+ // Another alert-waiting task is then created to pick up the next alert.
+ if (mAlertTask != null) {
+ mAlertTask.cancel(true);
+ mAlertTask = null;
+ }
+ if (mAlertDialog != null) {
+ mAlertDialog.cancel();
+ mAlertDialog = null;
+ }
+ mAlertTask = new AsyncTask<Void,Void,MuPDFAlert>() {
+
+ @Override
+ protected MuPDFAlert doInBackground(Void... arg0) {
+ if (!mAlertsActive)
+ return null;
+
+ return core.waitForAlert();
+ }
+
+ @Override
+ protected void onPostExecute(final MuPDFAlert result) {
+ // core.waitForAlert may return null when shutting down
+ if (result == null)
+ return;
+ final MuPDFAlert.ButtonPressed pressed[] = new MuPDFAlert.ButtonPressed[3];
+ for(int i = 0; i < 3; i++)
+ pressed[i] = MuPDFAlert.ButtonPressed.None;
+ DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mAlertDialog = null;
+ if (mAlertsActive) {
+ int index = 0;
+ switch (which) {
+ case AlertDialog.BUTTON1: index=0; break;
+ case AlertDialog.BUTTON2: index=1; break;
+ case AlertDialog.BUTTON3: index=2; break;
+ }
+ result.buttonPressed = pressed[index];
+ // Send the user's response to the core, so that it can
+ // continue processing.
+ core.replyToAlert(result);
+ // Create another alert-waiter to pick up the next alert.
+ createAlertWaiter();
+ }
+ }
+ };
+ mAlertDialog = mAlertBuilder.create();
+ mAlertDialog.setTitle(result.title);
+ mAlertDialog.setMessage(result.message);
+ switch (result.iconType)
+ {
+ case Error:
+ break;
+ case Warning:
+ break;
+ case Question:
+ break;
+ case Status:
+ break;
+ }
+ switch (result.buttonGroupType)
+ {
+ case OkCancel:
+ mAlertDialog.setButton(AlertDialog.BUTTON2, "Cancel", listener);
+ pressed[1] = MuPDFAlert.ButtonPressed.Cancel;
+ case Ok:
+ mAlertDialog.setButton(AlertDialog.BUTTON1, "Ok", listener);
+ pressed[0] = MuPDFAlert.ButtonPressed.Ok;
+ break;
+ case YesNoCancel:
+ mAlertDialog.setButton(AlertDialog.BUTTON3, "Cancel", listener);
+ pressed[2] = MuPDFAlert.ButtonPressed.Cancel;
+ case YesNo:
+ mAlertDialog.setButton(AlertDialog.BUTTON1, "Yes", listener);
+ pressed[0] = MuPDFAlert.ButtonPressed.Yes;
+ mAlertDialog.setButton(AlertDialog.BUTTON2, "No", listener);
+ pressed[1] = MuPDFAlert.ButtonPressed.No;
+ break;
+ }
+ mAlertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ mAlertDialog = null;
+ if (mAlertsActive) {
+ result.buttonPressed = MuPDFAlert.ButtonPressed.None;
+ core.replyToAlert(result);
+ createAlertWaiter();
+ }
+ }
+ });
+
+ mAlertDialog.show();
+ }
+ };
+
+ mAlertTask.executeOnExecutor(new ThreadPerTaskExecutor());
+ }
+
+ public void destroyAlertWaiter() {
+ mAlertsActive = false;
+ if (mAlertDialog != null) {
+ mAlertDialog.cancel();
+ mAlertDialog = null;
+ }
+ if (mAlertTask != null) {
+ mAlertTask.cancel(true);
+ mAlertTask = null;
+ }
+ }
+
+ private MuPDFCore openFile(String path)
+ {
+ int lastSlashPos = path.lastIndexOf('/');
+ mFileName = new String(lastSlashPos == -1
+ ? path
+ : path.substring(lastSlashPos+1));
+ System.out.println("Trying to open "+path);
+ try
+ {
+ core = new MuPDFCore(path);
+ // New file: drop the old outline data
+ OutlineActivityData.set(null);
+ }
+ catch (Exception e)
+ {
+ System.out.println(e);
+ return null;
+ }
+ return core;
+ }
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ mAlertBuilder = new AlertDialog.Builder(this);
+
+ if (core == null) {
+ core = (MuPDFCore)getLastNonConfigurationInstance();
+
+ if (savedInstanceState != null && savedInstanceState.containsKey("FileName")) {
+ mFileName = savedInstanceState.getString("FileName");
+ }
+ }
+ if (core == null) {
+ Intent intent = getIntent();
+ if (Intent.ACTION_VIEW.equals(intent.getAction())) {
+ Uri uri = intent.getData();
+ if (uri.toString().startsWith("content://")) {
+ // Handle view requests from the Transformer Prime's file manager
+ // Hopefully other file managers will use this same scheme, if not
+ // using explicit paths.
+ Cursor cursor = getContentResolver().query(uri, new String[]{"_data"}, null, null, null);
+ if (cursor.moveToFirst()) {
+ uri = Uri.parse(cursor.getString(0));
+ }
+ }
+ core = openFile(Uri.decode(uri.getEncodedPath()));
+ SearchTaskResult.set(null);
+ }
+ if (core != null && core.needsPassword()) {
+ requestPassword(savedInstanceState);
+ return;
+ }
+ }
+ if (core == null)
+ {
+ AlertDialog alert = mAlertBuilder.create();
+ alert.setTitle(R.string.open_failed);
+ alert.setButton(AlertDialog.BUTTON_POSITIVE, "Dismiss",
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ });
+ alert.show();
+ return;
+ }
+
+ createUI(savedInstanceState);
+ }
+
+ public void requestPassword(final Bundle savedInstanceState) {
+ mPasswordView = new EditText(this);
+ mPasswordView.setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
+ mPasswordView.setTransformationMethod(new PasswordTransformationMethod());
+
+ AlertDialog alert = mAlertBuilder.create();
+ alert.setTitle(R.string.enter_password);
+ alert.setView(mPasswordView);
+ alert.setButton(AlertDialog.BUTTON_POSITIVE, "Ok",
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (core.authenticatePassword(mPasswordView.getText().toString())) {
+ createUI(savedInstanceState);
+ } else {
+ requestPassword(savedInstanceState);
+ }
+ }
+ });
+ alert.setButton(AlertDialog.BUTTON_NEGATIVE, "Cancel",
+ new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ });
+ alert.show();
+ }
+
+ public void createUI(Bundle savedInstanceState) {
+ if (core == null)
+ return;
+
+ // Now create the UI.
+ // First create the document view making use of the ReaderView's internal
+ // gesture recognition
+ mDocView = new ReaderView(this) {
+ private boolean showButtonsDisabled;
+
+ public boolean onSingleTapUp(MotionEvent e) {
+ LinkInfo link = null;
+
+ if (!mSelecting && !showButtonsDisabled) {
+ MuPDFPageView pageView = (MuPDFPageView) mDocView.getDisplayedView();
+ if (MuPDFCore.javascriptSupported() && pageView.passClickEvent(e.getX(), e.getY())) {
+ // If the page consumes the event do nothing else
+ } else if (mLinkHighlight && pageView != null && (link = pageView.hitLink(e.getX(), e.getY())) != null) {
+ link.acceptVisitor(new LinkInfoVisitor() {
+ @Override
+ public void visitInternal(LinkInfoInternal li) {
+ // Clicked on an internal (GoTo) link
+ mDocView.setDisplayedViewIndex(li.pageNumber);
+ }
+
+ @Override
+ public void visitExternal(LinkInfoExternal li) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(li.url));
+ startActivity(intent);
+ }
+
+ @Override
+ public void visitRemote(LinkInfoRemote li) {
+ // Clicked on a remote (GoToR) link
+ }
+ });
+ } else if (e.getX() < super.getWidth()/TAP_PAGE_MARGIN) {
+ super.moveToPrevious();
+ } else if (e.getX() > super.getWidth()*(TAP_PAGE_MARGIN-1)/TAP_PAGE_MARGIN) {
+ super.moveToNext();
+ } else if (!mButtonsVisible) {
+ showButtons();
+ } else {
+ hideButtons();
+ }
+ }
+ return super.onSingleTapUp(e);
+ }
+
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ if (!mSelecting) {
+ if (!showButtonsDisabled)
+ hideButtons();
+
+ return super.onScroll(e1, e2, distanceX, distanceY);
+ } else {
+ MuPDFPageView pageView = (MuPDFPageView) mDocView.getDisplayedView();
+ if (pageView != null)
+ pageView.selectText(e1.getX(), e1.getY(), e2.getX(), e2.getY());
+ return true;
+ }
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2,
+ float velocityX, float velocityY) {
+ if (!mSelecting)
+ return super.onFling(e1, e2, velocityX, velocityY);
+ else
+ return true;
+ }
+
+ public boolean onScaleBegin(ScaleGestureDetector d) {
+ // Disabled showing the buttons until next touch.
+ // Not sure why this is needed, but without it
+ // pinch zoom can make the buttons appear
+ showButtonsDisabled = true;
+ return super.onScaleBegin(d);
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN)
+ showButtonsDisabled = false;
+
+ return super.onTouchEvent(event);
+ }
+
+ protected void onChildSetup(int i, View v) {
+ if (SearchTaskResult.get() != null && SearchTaskResult.get().pageNumber == i)
+ ((PageView)v).setSearchBoxes(SearchTaskResult.get().searchBoxes);
+ else
+ ((PageView)v).setSearchBoxes(null);
+
+ ((PageView)v).setLinkHighlighting(mLinkHighlight);
+
+ ((MuPDFPageView)v).setChangeReporter(new Runnable() {
+ public void run() {
+ mDocView.applyToChildren(new ReaderView.ViewMapper() {
+ @Override
+ void applyToView(View view) {
+ ((MuPDFPageView)view).update();
+ }
+ });
+ }
+ });
+ }
+
+ protected void onMoveToChild(int i) {
+ if (core == null)
+ return;
+ mPageNumberView.setText(String.format("%d / %d", i+1, core.countPages()));
+ mPageSlider.setMax((core.countPages()-1) * mPageSliderRes);
+ mPageSlider.setProgress(i * mPageSliderRes);
+ if (SearchTaskResult.get() != null && SearchTaskResult.get().pageNumber != i) {
+ SearchTaskResult.set(null);
+ mDocView.resetupChildren();
+ }
+ }
+
+ protected void onSettle(View v) {
+ // When the layout has settled ask the page to render
+ // in HQ
+ ((PageView)v).addHq(false);
+ }
+
+ protected void onUnsettle(View v) {
+ // When something changes making the previous settled view
+ // no longer appropriate, tell the page to remove HQ
+ ((PageView)v).removeHq();
+ }
+
+ @Override
+ protected void onNotInUse(View v) {
+ ((PageView)v).releaseResources();
+ }
+ };
+ mDocView.setAdapter(new MuPDFPageAdapter(this, core));
+
+ // Make the buttons overlay, and store all its
+ // controls in variables
+ makeButtonsView();
+
+ // Set up the page slider
+ int smax = Math.max(core.countPages()-1,1);
+ mPageSliderRes = ((10 + smax - 1)/smax) * 2;
+
+ // Set the file-name text
+ mFilenameView.setText(mFileName);
+
+ // Activate the seekbar
+ mPageSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ mDocView.setDisplayedViewIndex((seekBar.getProgress()+mPageSliderRes/2)/mPageSliderRes);
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ public void onProgressChanged(SeekBar seekBar, int progress,
+ boolean fromUser) {
+ updatePageNumView((progress+mPageSliderRes/2)/mPageSliderRes);
+ }
+ });
+
+ // Activate the search-preparing button
+ mSearchButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ searchModeOn();
+ }
+ });
+
+ // Activate the select button
+ mSelectButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ mSelecting = true;
+ mTopBarSwitcher.setDisplayedChild(2);
+ }
+ });
+
+ mCancelSelectButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ PageView pageView = (PageView) mDocView.getDisplayedView();
+ if (pageView != null)
+ pageView.deselectText();
+ mSelecting = false;
+ mTopBarSwitcher.setDisplayedChild(0);
+ }
+ });
+
+ final Context context = this;
+ mCopySelectButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ PageView pageView = (PageView) mDocView.getDisplayedView();
+ boolean copied = false;
+ if (pageView != null)
+ copied = pageView.copySelection();
+ mSelecting = false;
+ mTopBarSwitcher.setDisplayedChild(0);
+ mInfoView.setText(copied?"Copied to clipboard":"No text selected");
+ AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(context, R.animator.info);
+ set.setTarget(mInfoView);
+ set.addListener(new Animator.AnimatorListener() {
+ public void onAnimationStart(Animator animation) {
+ mInfoView.setVisibility(View.VISIBLE);
+ }
+
+ public void onAnimationRepeat(Animator animation) {
+ }
+
+ public void onAnimationEnd(Animator animation) {
+ mInfoView.setVisibility(View.INVISIBLE);
+ }
+
+ public void onAnimationCancel(Animator animation) {
+ }
+ });
+ set.start();
+ }
+ });
+
+ mCancelButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ searchModeOff();
+ }
+ });
+
+ // Search invoking buttons are disabled while there is no text specified
+ mSearchBack.setEnabled(false);
+ mSearchFwd.setEnabled(false);
+ mSearchBack.setColorFilter(Color.argb(255, 128, 128, 128));
+ mSearchFwd.setColorFilter(Color.argb(255, 128, 128, 128));
+
+ // React to interaction with the text widget
+ mSearchText.addTextChangedListener(new TextWatcher() {
+
+ public void afterTextChanged(Editable s) {
+ boolean haveText = s.toString().length() > 0;
+ mSearchBack.setEnabled(haveText);
+ mSearchFwd.setEnabled(haveText);
+ if (haveText) {
+ mSearchBack.setColorFilter(Color.argb(255, 255, 255, 255));
+ mSearchFwd.setColorFilter(Color.argb(255, 255, 255, 255));
+ } else {
+ mSearchBack.setColorFilter(Color.argb(255, 128, 128, 128));
+ mSearchFwd.setColorFilter(Color.argb(255, 128, 128, 128));
+ }
+
+ // Remove any previous search results
+ if (SearchTaskResult.get() != null && !mSearchText.getText().toString().equals(SearchTaskResult.get().txt)) {
+ SearchTaskResult.set(null);
+ mDocView.resetupChildren();
+ }
+ }
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {}
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {}
+ });
+
+ //React to Done button on keyboard
+ mSearchText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_DONE)
+ search(1);
+ return false;
+ }
+ });
+
+ mSearchText.setOnKeyListener(new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER)
+ search(1);
+ return false;
+ }
+ });
+
+ // Activate search invoking buttons
+ mSearchBack.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ search(-1);
+ }
+ });
+ mSearchFwd.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ search(1);
+ }
+ });
+
+ mLinkButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ if (mLinkHighlight) {
+ mLinkButton.setColorFilter(Color.argb(0xFF, 255, 255, 255));
+ mLinkHighlight = false;
+ } else {
+ // LINK_COLOR tint
+ mLinkButton.setColorFilter(Color.argb(0xFF, 172, 114, 37));
+ mLinkHighlight = true;
+ }
+ // Inform pages of the change.
+ mDocView.resetupChildren();
+ }
+ });
+
+ if (core.hasOutline()) {
+ mOutlineButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ OutlineItem outline[] = core.getOutline();
+ if (outline != null) {
+ OutlineActivityData.get().items = outline;
+ Intent intent = new Intent(MuPDFActivity.this, OutlineActivity.class);
+ startActivityForResult(intent, 0);
+ }
+ }
+ });
+ } else {
+ mOutlineButton.setVisibility(View.GONE);
+ }
+
+ // Reenstate last state if it was recorded
+ SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
+ mDocView.setDisplayedViewIndex(prefs.getInt("page"+mFileName, 0));
+
+ if (savedInstanceState == null || !savedInstanceState.getBoolean("ButtonsHidden", false))
+ showButtons();
+
+ if(savedInstanceState != null && savedInstanceState.getBoolean("SearchMode", false))
+ searchModeOn();
+
+ // Stick the document view and the buttons overlay into a parent view
+ RelativeLayout layout = new RelativeLayout(this);
+ layout.addView(mDocView);
+ layout.addView(mButtonsView);
+ layout.setBackgroundResource(R.drawable.tiled_background);
+ //layout.setBackgroundResource(R.color.canvas);
+ setContentView(layout);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode >= 0)
+ mDocView.setDisplayedViewIndex(resultCode);
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ public Object onRetainNonConfigurationInstance()
+ {
+ MuPDFCore mycore = core;
+ core = null;
+ return mycore;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ if (mFileName != null && mDocView != null) {
+ outState.putString("FileName", mFileName);
+
+ // Store current page in the prefs against the file name,
+ // so that we can pick it up each time the file is loaded
+ // Other info is needed only for screen-orientation change,
+ // so it can go in the bundle
+ SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
+ SharedPreferences.Editor edit = prefs.edit();
+ edit.putInt("page"+mFileName, mDocView.getDisplayedViewIndex());
+ edit.commit();
+ }
+
+ if (!mButtonsVisible)
+ outState.putBoolean("ButtonsHidden", true);
+
+ if (mTopBarIsSearch)
+ outState.putBoolean("SearchMode", true);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ killSearch();
+
+ if (mFileName != null && mDocView != null) {
+ SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
+ SharedPreferences.Editor edit = prefs.edit();
+ edit.putInt("page"+mFileName, mDocView.getDisplayedViewIndex());
+ edit.commit();
+ }
+ }
+
+ public void onDestroy()
+ {
+ if (core != null)
+ core.onDestroy();
+ if (mAlertTask != null) {
+ mAlertTask.cancel(true);
+ mAlertTask = null;
+ }
+ core = null;
+ super.onDestroy();
+ }
+
+ void showButtons() {
+ if (core == null)
+ return;
+ if (!mButtonsVisible) {
+ mButtonsVisible = true;
+ // Update page number text and slider
+ int index = mDocView.getDisplayedViewIndex();
+ updatePageNumView(index);
+ mPageSlider.setMax((core.countPages()-1)*mPageSliderRes);
+ mPageSlider.setProgress(index*mPageSliderRes);
+ if (mTopBarIsSearch) {
+ mSearchText.requestFocus();
+ showKeyboard();
+ }
+
+ Animation anim = new TranslateAnimation(0, 0, -mTopBarSwitcher.getHeight(), 0);
+ anim.setDuration(200);
+ anim.setAnimationListener(new Animation.AnimationListener() {
+ public void onAnimationStart(Animation animation) {
+ mTopBarSwitcher.setVisibility(View.VISIBLE);
+ }
+ public void onAnimationRepeat(Animation animation) {}
+ public void onAnimationEnd(Animation animation) {}
+ });
+ mTopBarSwitcher.startAnimation(anim);
+
+ anim = new TranslateAnimation(0, 0, mPageSlider.getHeight(), 0);
+ anim.setDuration(200);
+ anim.setAnimationListener(new Animation.AnimationListener() {
+ public void onAnimationStart(Animation animation) {
+ mPageSlider.setVisibility(View.VISIBLE);
+ }
+ public void onAnimationRepeat(Animation animation) {}
+ public void onAnimationEnd(Animation animation) {
+ mPageNumberView.setVisibility(View.VISIBLE);
+ }
+ });
+ mPageSlider.startAnimation(anim);
+ }
+ }
+
+ void hideButtons() {
+ if (mButtonsVisible) {
+ mButtonsVisible = false;
+ hideKeyboard();
+
+ Animation anim = new TranslateAnimation(0, 0, 0, -mTopBarSwitcher.getHeight());
+ anim.setDuration(200);
+ anim.setAnimationListener(new Animation.AnimationListener() {
+ public void onAnimationStart(Animation animation) {}
+ public void onAnimationRepeat(Animation animation) {}
+ public void onAnimationEnd(Animation animation) {
+ mTopBarSwitcher.setVisibility(View.INVISIBLE);
+ }
+ });
+ mTopBarSwitcher.startAnimation(anim);
+
+ anim = new TranslateAnimation(0, 0, 0, mPageSlider.getHeight());
+ anim.setDuration(200);
+ anim.setAnimationListener(new Animation.AnimationListener() {
+ public void onAnimationStart(Animation animation) {
+ mPageNumberView.setVisibility(View.INVISIBLE);
+ }
+ public void onAnimationRepeat(Animation animation) {}
+ public void onAnimationEnd(Animation animation) {
+ mPageSlider.setVisibility(View.INVISIBLE);
+ }
+ });
+ mPageSlider.startAnimation(anim);
+ }
+ }
+
+ void searchModeOn() {
+ if (!mTopBarIsSearch) {
+ mTopBarIsSearch = true;
+ //Focus on EditTextWidget
+ mSearchText.requestFocus();
+ showKeyboard();
+ mTopBarSwitcher.setDisplayedChild(1);
+ }
+ }
+
+ void searchModeOff() {
+ if (mTopBarIsSearch) {
+ mTopBarIsSearch = false;
+ hideKeyboard();
+ mTopBarSwitcher.setDisplayedChild(0);
+ SearchTaskResult.set(null);
+ // Make the ReaderView act on the change to mSearchTaskResult
+ // via overridden onChildSetup method.
+ mDocView.resetupChildren();
+ }
+ }
+
+ void updatePageNumView(int index) {
+ if (core == null)
+ return;
+ mPageNumberView.setText(String.format("%d / %d", index+1, core.countPages()));
+ }
+
+ void makeButtonsView() {
+ mButtonsView = getLayoutInflater().inflate(R.layout.buttons,null);
+ mFilenameView = (TextView)mButtonsView.findViewById(R.id.docNameText);
+ mPageSlider = (SeekBar)mButtonsView.findViewById(R.id.pageSlider);
+ mPageNumberView = (TextView)mButtonsView.findViewById(R.id.pageNumber);
+ mInfoView = (TextView)mButtonsView.findViewById(R.id.info);
+ mSearchButton = (ImageButton)mButtonsView.findViewById(R.id.searchButton);
+ mSelectButton = (ImageButton)mButtonsView.findViewById(R.id.selectButton);
+ mCancelSelectButton = (ImageButton)mButtonsView.findViewById(R.id.cancelSelectButton);
+ mCopySelectButton = (ImageButton)mButtonsView.findViewById(R.id.copySelectButton);
+ mCancelButton = (ImageButton)mButtonsView.findViewById(R.id.cancel);
+ mOutlineButton = (ImageButton)mButtonsView.findViewById(R.id.outlineButton);
+ mTopBarSwitcher = (ViewAnimator)mButtonsView.findViewById(R.id.switcher);
+ mSearchBack = (ImageButton)mButtonsView.findViewById(R.id.searchBack);
+ mSearchFwd = (ImageButton)mButtonsView.findViewById(R.id.searchForward);
+ mSearchText = (EditText)mButtonsView.findViewById(R.id.searchText);
+ mLinkButton = (ImageButton)mButtonsView.findViewById(R.id.linkButton);
+ mTopBarSwitcher.setVisibility(View.INVISIBLE);
+ mPageNumberView.setVisibility(View.INVISIBLE);
+ mInfoView.setVisibility(View.INVISIBLE);
+ mPageSlider.setVisibility(View.INVISIBLE);
+ }
+
+ void showKeyboard() {
+ InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null)
+ imm.showSoftInput(mSearchText, 0);
+ }
+
+ void hideKeyboard() {
+ InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null)
+ imm.hideSoftInputFromWindow(mSearchText.getWindowToken(), 0);
+ }
+
+ void killSearch() {
+ if (mSearchTask != null) {
+ mSearchTask.cancel(true);
+ mSearchTask = null;
+ }
+ }
+
+ void search(int direction) {
+ hideKeyboard();
+ if (core == null)
+ return;
+ killSearch();
+
+ final int increment = direction;
+ final int startIndex = SearchTaskResult.get() == null ? mDocView.getDisplayedViewIndex() : SearchTaskResult.get().pageNumber + increment;
+
+ final ProgressDialogX progressDialog = new ProgressDialogX(this);
+ progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ progressDialog.setTitle(getString(R.string.searching_));
+ progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ killSearch();
+ }
+ });
+ progressDialog.setMax(core.countPages());
+
+ mSearchTask = new AsyncTask<Void,Integer,SearchTaskResult>() {
+ @Override
+ protected SearchTaskResult doInBackground(Void... params) {
+ int index = startIndex;
+
+ while (0 <= index && index < core.countPages() && !isCancelled()) {
+ publishProgress(index);
+ RectF searchHits[] = core.searchPage(index, mSearchText.getText().toString());
+
+ if (searchHits != null && searchHits.length > 0)
+ return new SearchTaskResult(mSearchText.getText().toString(), index, searchHits);
+
+ index += increment;
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(SearchTaskResult result) {
+ progressDialog.cancel();
+ if (result != null) {
+ // Ask the ReaderView to move to the resulting page
+ mDocView.setDisplayedViewIndex(result.pageNumber);
+ SearchTaskResult.set(result);
+ // Make the ReaderView act on the change to mSearchTaskResult
+ // via overridden onChildSetup method.
+ mDocView.resetupChildren();
+ } else {
+ mAlertBuilder.setTitle(SearchTaskResult.get() == null ? R.string.text_not_found : R.string.no_further_occurences_found);
+ AlertDialog alert = mAlertBuilder.create();
+ alert.setButton(AlertDialog.BUTTON_POSITIVE, "Dismiss",
+ (DialogInterface.OnClickListener)null);
+ alert.show();
+ }
+ }
+
+ @Override
+ protected void onCancelled() {
+ super.onCancelled();
+ progressDialog.cancel();
+ }
+
+ @Override
+ protected void onProgressUpdate(Integer... values) {
+ super.onProgressUpdate(values);
+ progressDialog.setProgress(values[0].intValue());
+ }
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ mHandler.postDelayed(new Runnable() {
+ public void run() {
+ if (!progressDialog.isCancelled())
+ {
+ progressDialog.show();
+ progressDialog.setProgress(startIndex);
+ }
+ }
+ }, SEARCH_PROGRESS_DELAY);
+ }
+ };
+
+ mSearchTask.execute();
+ }
+
+ @Override
+ public boolean onSearchRequested() {
+ if (mButtonsVisible && mTopBarIsSearch) {
+ hideButtons();
+ } else {
+ showButtons();
+ searchModeOn();
+ }
+ return super.onSearchRequested();
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if (mButtonsVisible && !mTopBarIsSearch) {
+ hideButtons();
+ } else {
+ showButtons();
+ searchModeOff();
+ }
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ protected void onStart() {
+ if (core != null)
+ core.startAlerts();
+
+ createAlertWaiter();
+ super.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ destroyAlertWaiter();
+ if (core != null)
+ core.stopAlerts();
+
+ super.onStop();
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (core.hasChanges()) {
+ DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == AlertDialog.BUTTON_POSITIVE)
+ core.save();
+
+ finish();
+ }
+ };
+ AlertDialog alert = mAlertBuilder.create();
+ alert.setTitle("MuPDF");
+ alert.setMessage("Document has changes. Save them?");
+ alert.setButton(AlertDialog.BUTTON_POSITIVE, "Yes", listener);
+ alert.setButton(AlertDialog.BUTTON_NEGATIVE, "No", listener);
+ alert.show();
+ } else {
+ super.onBackPressed();
+ }
+ }
+}