From 7d42acddc4f03e7e99d7012ead554ea7fa93c1d0 Mon Sep 17 00:00:00 2001 From: Fred Ross-Perry Date: Sat, 20 Aug 2016 15:08:02 -0700 Subject: Android example - file toolbar icons, password dialog add five icons to the File toolbar (not yet functioning) add a dialog to collect the password from the user for protected documents. --- .../com/artifex/mupdf/android/DocActivityView.java | 107 +++++- .../com/artifex/mupdf/android/DocViewBase.java | 21 +- .../java/com/artifex/mupdf/android/Utilities.java | 383 +++++++++++++++++++++ .../mupdf/src/main/res/drawable/icon_open_in.xml | 22 ++ .../mupdf/src/main/res/drawable/icon_print.xml | 39 +++ .../mupdf/src/main/res/drawable/icon_save.xml | 0 .../mupdf/src/main/res/drawable/icon_save_as.xml | 44 +++ .../mupdf/src/main/res/drawable/icon_share.xml | 32 ++ .../mupdf/src/main/res/layout/file_toolbar.xml | 111 +++++- .../mupdf/src/main/res/layout/pages_toolbar.xml | 3 +- .../mupdf/src/main/res/layout/password_prompt.xml | 27 ++ .../example/mupdf/src/main/res/values/strings.xml | 2 + 12 files changed, 780 insertions(+), 11 deletions(-) create mode 100644 platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/Utilities.java create mode 100755 platform/android/example/mupdf/src/main/res/drawable/icon_open_in.xml create mode 100755 platform/android/example/mupdf/src/main/res/drawable/icon_print.xml mode change 100644 => 100755 platform/android/example/mupdf/src/main/res/drawable/icon_save.xml create mode 100644 platform/android/example/mupdf/src/main/res/drawable/icon_save_as.xml create mode 100755 platform/android/example/mupdf/src/main/res/drawable/icon_share.xml create mode 100644 platform/android/example/mupdf/src/main/res/layout/password_prompt.xml (limited to 'platform/android') diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocActivityView.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocActivityView.java index 5d5834be..6a2cca11 100644 --- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocActivityView.java +++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocActivityView.java @@ -19,7 +19,9 @@ import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TabHost; import android.widget.TextView; +import android.widget.Toast; +import com.artifex.mupdf.fitz.Document; import com.artifex.mupdf.fitz.Link; import com.artifex.mupdf.fitz.Outline; import com.artifex.mupdf.fitz.R; @@ -48,6 +50,12 @@ public class DocActivityView extends FrameLayout implements TabHost.OnTabChangeL private ImageButton mSearchPreviousButton; private ImageButton mBackButton; + private ImageButton mSaveButton; + private ImageButton mSaveAsButton; + private ImageButton mPrintButton; + private ImageButton mShareButton; + private ImageButton mOpenInButton; + public DocActivityView(Context context) { super(context); @@ -308,6 +316,21 @@ public class DocActivityView extends FrameLayout implements TabHost.OnTabChangeL mSearchText = (EditText) findViewById(R.id.search_text_input); mSearchText.setOnClickListener(this); + mSaveButton = (ImageButton)findViewById(R.id.save_button); + mSaveButton.setOnClickListener(this); + + mSaveAsButton = (ImageButton)findViewById(R.id.save_as_button); + mSaveAsButton.setOnClickListener(this); + + mPrintButton = (ImageButton)findViewById(R.id.print_button); + mPrintButton.setOnClickListener(this); + + mShareButton = (ImageButton)findViewById(R.id.share_button); + mShareButton.setOnClickListener(this); + + mOpenInButton = (ImageButton)findViewById(R.id.open_in_button); + mOpenInButton.setOnClickListener(this); + // this listener will mSearchText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @@ -329,8 +352,54 @@ public class DocActivityView extends FrameLayout implements TabHost.OnTabChangeL mSearchPreviousButton = (ImageButton)findViewById(R.id.search_previous_button); mSearchPreviousButton.setOnClickListener(this); + mDoc = new Document(path); + + if (mDoc.needsPassword()) + { + askForPassword(); + } + else + { + afterPassword(); + } + } + + private Document mDoc; + + private void askForPassword() + { + Utilities.passwordDialog((Activity) getContext(), new Utilities.passwordDialogListener() + { + @Override + public void onOK(String password) + { + // yes + boolean ok = mDoc.authenticatePassword(password); + if (ok) + { + afterPassword(); + mDocView.requestLayout(); + } + else + { + askForPassword(); + } + } + + @Override + public void onCancel() + { + mDoc.destroy(); + if (mDoneListener != null) + mDoneListener.done(); + } + }); + } + + private void afterPassword() + { // start the views - mDocView.start(path); + mDocView.start(mDoc); if (usePagesView()) { mDocPagesView.clone(mDocView); @@ -421,6 +490,17 @@ public class DocActivityView extends FrameLayout implements TabHost.OnTabChangeL onSearchPreviousButton(); if (v == mBackButton) onBackButton(); + + if (v == mSaveButton) + onSaveButton(); + if (v == mSaveAsButton) + onSaveAsButton(); + if (v == mPrintButton) + onPrintButton(); + if (v == mShareButton) + onShareButton(); + if (v == mOpenInButton) + onOpenInButton(); } public void onSearchNextButton() @@ -530,6 +610,31 @@ public class DocActivityView extends FrameLayout implements TabHost.OnTabChangeL mDoneListener.done(); } + private void onSaveButton() + { + Toast.makeText(getContext(),"onSaveButton", Toast.LENGTH_SHORT).show(); + } + + private void onSaveAsButton() + { + Toast.makeText(getContext(),"onSaveAsButton", Toast.LENGTH_SHORT).show(); + } + + private void onPrintButton() + { + Toast.makeText(getContext(),"onPrintButton", Toast.LENGTH_SHORT).show(); + } + + private void onShareButton() + { + Toast.makeText(getContext(),"onShareButton", Toast.LENGTH_SHORT).show(); + } + + private void onOpenInButton() + { + Toast.makeText(getContext(),"onOpenInButton", Toast.LENGTH_SHORT).show(); + } + private OnDoneListener mDoneListener = null; public void setOnDoneListener(OnDoneListener l) {mDoneListener = l;} public interface OnDoneListener diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocViewBase.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocViewBase.java index 9c8ab998..6152851c 100755 --- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocViewBase.java +++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocViewBase.java @@ -153,11 +153,11 @@ public class DocViewBase DocPageView.bitmapMarginY = (h - screenH) / 2; } - public void start(final String path) + public void start (Document doc) { + mDoc = doc; mAdapter = new PageAdapter(mContext); mAdapter.setWidth(getWidth()); - mDoc = new Document(path); mAdapter.setDocument(mDoc); mScale = 1.0f; mStarted = true; @@ -501,7 +501,10 @@ public class DocViewBase protected int getPageCount() { - return getAdapter().getCount(); + Adapter adapter = getAdapter(); + if (null != adapter) + return adapter.getCount(); + return 0; } protected void onLayout(boolean changed, int left, int top, int right, int bottom) @@ -921,10 +924,14 @@ public class DocViewBase } // get rid of bitmaps - bitmaps[0].recycle(); - bitmaps[0] = null; - bitmaps[1].recycle(); - bitmaps[1] = null; + for (int i=0;i 0){ + outStream.write(bucket, 0, bytesRead); + } + } + } + finally + { + if (inStream != null) + inStream.close(); + if (outStream != null) + outStream.close(); + } + } + catch (FileNotFoundException ex){ + } + catch (IOException ex){ + } + } + + public static boolean deleteFile (String path) + { + try + { + File fileToDelete = new File(path); + if (fileToDelete.exists()) { + fileToDelete.delete(); + } + } + catch(Exception e) + { + return false; + } + + return true; + } + + public static boolean renameFile (String oldPath, String newPath) + { + File fOld = new File(oldPath); + File fNew = new File(newPath); + return fOld.renameTo(fNew); + } + + // this function safely replaces a file by first renaming it + // and, if the copy fails, renaming it back. + public static boolean replaceFile (String srcPath, String dstPath) + { + // source file must exist + File srcFile = new File(srcPath); + if (!srcFile.exists()) + return false; + + // destination file may or may not exist + File dstFile = new File(dstPath); + boolean dstExists = dstFile.exists(); + String tmp = dstPath+"xxx"; + + // if tmp exists, error + File tmpFile = new File(tmp); + if (tmpFile.exists()) + return false; + + // rename the destination temporarily + if (dstExists) { + if (!renameFile(dstPath,tmp)) { + // rename error, do nothing else. + return false; + } + } + + // copy the file + if (!copyFile(srcPath,dstPath,true)) { + // copy failed, put the destination back + if (dstExists) { + if (!renameFile(tmp,dstPath)) { + // bad mojo here. Can't rename back, + // file appears lost. + } + } + return false; + } + + // copy succeeded, now delete the tmp file + deleteFile(tmp); + + return true; + } + + // get the extension part of the filename, not including the "." + public static String getExtension(String filename) + { + String filenameArray[] = filename.split("\\."); + + if (filenameArray.length<=1) + { + // no extension + return ""; + } + + String extension = filenameArray[filenameArray.length-1]; + extension = extension.toLowerCase(); + return extension; + } + + public static void showMessage(final Activity activity, final String title, final String body) + { + showMessage(activity, title, body, activity.getResources().getString(R.string.ok)); + } + public static void showMessage(final Activity activity, final String title, final String body, final String okLabel) + { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + new AlertDialog.Builder(activity) + .setTitle(title) + .setMessage(body) + .setCancelable(false) + .setPositiveButton(okLabel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }).create().show(); + } + }); + } + + public static void showMessageAndFinish(final Activity activity, final String title, final String body) + { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + new AlertDialog.Builder(activity) + .setTitle(title) + .setMessage(body) + .setCancelable(false) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + activity.finish(); + } + }).create().show(); + } + }); + } + + public static void passwordDialog( final Activity activity, final passwordDialogListener listener) + { + activity.runOnUiThread(new Runnable() + { + @Override + public void run() + { + AlertDialog.Builder dialog = new AlertDialog.Builder(activity); + LayoutInflater li = LayoutInflater.from(activity); + View promptsView = li.inflate(R.layout.password_prompt, null); + + final EditText et = (EditText)(promptsView.findViewById(R.id.editTextDialogUserInput)); + + dialog.setView(promptsView); + + dialog.setTitle(""); + + dialog.setPositiveButton(activity.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + if (listener!=null) + { + String password = et.getText().toString(); + listener.onOK(password); + } + } + }); + + dialog.setNegativeButton(activity.getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + if (listener!=null) + listener.onCancel(); + } + }); + + dialog.create().show(); + } + }); + } + + public interface passwordDialogListener + { + void onOK(String password); + void onCancel(); + } + + public static void yesNoMessage(final Activity activity, final String title, final String body, + final String yesButtonLabel, final String noButtonLabel, + final Runnable yesRunnable, final Runnable noRunnable) + { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + AlertDialog.Builder dialog = new AlertDialog.Builder(activity); + + dialog.setTitle(title); + dialog.setMessage(body); + + dialog.setPositiveButton(yesButtonLabel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + if (yesRunnable!=null) + yesRunnable.run(); + } + }); + + dialog.setNegativeButton(noButtonLabel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + if (noRunnable!=null) + noRunnable.run(); + } + }); + + dialog.create(); + dialog.show(); + } + }); + } + + public static File extractAssetToFile(Context context, String file) + { + File cacheFile = new File(context.getCacheDir(), file); + try + { + InputStream inputStream = context.getAssets().open(file); + try + { + FileOutputStream outputStream = new FileOutputStream(cacheFile); + try + { + byte[] buf = new byte[1024]; + int len; + while ((len = inputStream.read(buf)) > 0) + { + outputStream.write(buf, 0, len); + } + } finally + { + outputStream.close(); + } + } finally + { + inputStream.close(); + } + } + catch (IOException e) + { + e.printStackTrace(); + return null; + } + return cacheFile; + } + + public static String extractAssetToString(Context context, String file) + { + String json; + try { + InputStream is = context.getAssets().open(file); + int size = is.available(); + byte[] buffer = new byte[size]; + is.read(buffer); + is.close(); + json = new String(buffer, "UTF-8"); + } catch (IOException ex) { + ex.printStackTrace(); + return null; + } + return json; + } + + public static String removeExtention(String filePath) + { + File f = new File(filePath); + + // if it's a directory, don't remove the extention + if (f.isDirectory()) + return filePath; + + String name = f.getName(); + + // Now we know it's a file - don't need to do any special hidden + // checking or contains() checking because of: + final int lastPeriodPos = name.lastIndexOf('.'); + if (lastPeriodPos <= 0) + { + // No period after first character - return name as it was passed in + return filePath; + } + else + { + // Remove the last period and everything after it + File renamed = new File(f.getParent(), name.substring(0, lastPeriodPos)); + return renamed.getPath(); + } + } + + public static String getMimeType (String path) + { + String ext = Utilities.getExtension(path); + String mime = null; + if (ext != null) { + mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext); + } + return mime; + } +} diff --git a/platform/android/example/mupdf/src/main/res/drawable/icon_open_in.xml b/platform/android/example/mupdf/src/main/res/drawable/icon_open_in.xml new file mode 100755 index 00000000..ba08fa0a --- /dev/null +++ b/platform/android/example/mupdf/src/main/res/drawable/icon_open_in.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/platform/android/example/mupdf/src/main/res/drawable/icon_print.xml b/platform/android/example/mupdf/src/main/res/drawable/icon_print.xml new file mode 100755 index 00000000..2ea50da6 --- /dev/null +++ b/platform/android/example/mupdf/src/main/res/drawable/icon_print.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/platform/android/example/mupdf/src/main/res/drawable/icon_save.xml b/platform/android/example/mupdf/src/main/res/drawable/icon_save.xml old mode 100644 new mode 100755 diff --git a/platform/android/example/mupdf/src/main/res/drawable/icon_save_as.xml b/platform/android/example/mupdf/src/main/res/drawable/icon_save_as.xml new file mode 100644 index 00000000..88b0c64d --- /dev/null +++ b/platform/android/example/mupdf/src/main/res/drawable/icon_save_as.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/platform/android/example/mupdf/src/main/res/drawable/icon_share.xml b/platform/android/example/mupdf/src/main/res/drawable/icon_share.xml new file mode 100755 index 00000000..4dd1cb69 --- /dev/null +++ b/platform/android/example/mupdf/src/main/res/drawable/icon_share.xml @@ -0,0 +1,32 @@ + + + + + + + + + + \ No newline at end of file diff --git a/platform/android/example/mupdf/src/main/res/layout/file_toolbar.xml b/platform/android/example/mupdf/src/main/res/layout/file_toolbar.xml index e0d8ecb4..6468c9d0 100644 --- a/platform/android/example/mupdf/src/main/res/layout/file_toolbar.xml +++ b/platform/android/example/mupdf/src/main/res/layout/file_toolbar.xml @@ -18,7 +18,9 @@ + android:orientation="vertical" + android:paddingLeft="10dp" + android:paddingRight="10dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + android:paddingRight="10dp"> + + + + + + + + + + + diff --git a/platform/android/example/mupdf/src/main/res/values/strings.xml b/platform/android/example/mupdf/src/main/res/values/strings.xml index be1a518a..3709da70 100644 --- a/platform/android/example/mupdf/src/main/res/values/strings.xml +++ b/platform/android/example/mupdf/src/main/res/values/strings.xml @@ -10,5 +10,7 @@ NEXT PREVIOUS + OK + Cancel -- cgit v1.2.3