summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorPaul Gardiner <paulg.artifex@glidos.net>2013-08-27 14:49:15 +0100
committerPaul Gardiner <paulg.artifex@glidos.net>2013-08-27 14:49:15 +0100
commitdca49ac644409e9bed09812e03977e649519f5d7 (patch)
tree70c894021aa35b42fd075be7d3e041c14acc11b5 /platform
parentcf3ba3e213ce17bee76fa2fe02b43d5d3cc60732 (diff)
downloadmupdf-dca49ac644409e9bed09812e03977e649519f5d7.tar.xz
Android: support signing
Diffstat (limited to 'platform')
-rw-r--r--platform/android/jni/mupdf.c58
-rw-r--r--platform/android/src/com/artifex/mupdfdemo/ChoosePDFActivity.java76
-rw-r--r--platform/android/src/com/artifex/mupdfdemo/FilePicker.java21
-rw-r--r--platform/android/src/com/artifex/mupdfdemo/MuPDFActivity.java18
-rw-r--r--platform/android/src/com/artifex/mupdfdemo/MuPDFCore.java7
-rw-r--r--platform/android/src/com/artifex/mupdfdemo/MuPDFPageAdapter.java6
-rw-r--r--platform/android/src/com/artifex/mupdfdemo/MuPDFPageView.java109
7 files changed, 254 insertions, 41 deletions
diff --git a/platform/android/jni/mupdf.c b/platform/android/jni/mupdf.c
index a5bae5a7..3652595e 100644
--- a/platform/android/jni/mupdf.c
+++ b/platform/android/jni/mupdf.c
@@ -2177,7 +2177,15 @@ JNI_FN(MuPDFCore_getFocusedWidgetTypeInternal)(JNIEnv * env, jobject thiz)
return NONE;
}
-JNIEXPORT jboolean JNICALL
+/* This enum should be kept in line with SignatureState in MuPDFPageView.java */
+enum
+{
+ Signature_NoSupport,
+ Signature_Unsigned,
+ Signature_Signed
+};
+
+JNIEXPORT int JNICALL
JNI_FN(MuPDFCore_getFocusedWidgetSignatureState)(JNIEnv * env, jobject thiz)
{
globals *glo = get_globals(env, thiz);
@@ -2185,14 +2193,17 @@ JNI_FN(MuPDFCore_getFocusedWidgetSignatureState)(JNIEnv * env, jobject thiz)
pdf_widget *focus;
if (idoc == NULL)
- return JNI_FALSE;
+ return Signature_NoSupport;
focus = pdf_focused_widget(idoc);
if (focus == NULL)
- return JNI_FALSE;
+ return Signature_NoSupport;
+
+ if (!pdf_signatures_supported())
+ return Signature_NoSupport;
- return pdf_dict_gets(((pdf_annot *)focus)->obj, "V") ? JNI_TRUE : JNI_FALSE;
+ return pdf_dict_gets(((pdf_annot *)focus)->obj, "V") ? Signature_Signed : Signature_Unsigned;
}
JNIEXPORT jstring JNICALL
@@ -2220,6 +2231,45 @@ exit:
return (*env)->NewStringUTF(env, ebuf);
}
+JNIEXPORT jboolean JNICALL
+JNI_FN(MuPDFCore_signFocusedSignatureInternal)(JNIEnv * env, jobject thiz, jstring jkeyfile, jstring jpassword)
+{
+ globals *glo = get_globals(env, thiz);
+ fz_context *ctx = glo->ctx;
+ pdf_document *idoc = pdf_specifics(glo->doc);
+ pdf_widget *focus;
+ const char *keyfile;
+ const char *password;
+ jboolean res;
+
+ if (idoc == NULL)
+ return JNI_FALSE;
+
+ focus = pdf_focused_widget(idoc);
+
+ if (focus == NULL)
+ return JNI_FALSE;
+
+ keyfile = (*env)->GetStringUTFChars(env, jkeyfile, NULL);
+ password = (*env)->GetStringUTFChars(env, jpassword, NULL);
+ if (keyfile == NULL || password == NULL)
+ return JNI_FALSE;
+
+ fz_var(res);
+ fz_try(ctx)
+ {
+ pdf_sign_signature(idoc, focus, keyfile, password);
+ dump_annotation_display_lists(glo);
+ res = JNI_TRUE;
+ }
+ fz_catch(ctx)
+ {
+ res = JNI_FALSE;
+ }
+
+ return res;
+}
+
JNIEXPORT jobject JNICALL
JNI_FN(MuPDFCore_waitForAlertInternal)(JNIEnv * env, jobject thiz)
{
diff --git a/platform/android/src/com/artifex/mupdfdemo/ChoosePDFActivity.java b/platform/android/src/com/artifex/mupdfdemo/ChoosePDFActivity.java
index c1c9142c..f9428dc8 100644
--- a/platform/android/src/com/artifex/mupdfdemo/ChoosePDFActivity.java
+++ b/platform/android/src/com/artifex/mupdfdemo/ChoosePDFActivity.java
@@ -21,6 +21,11 @@ import android.os.Handler;
import android.view.View;
import android.widget.ListView;
+enum Purpose {
+ PickPDF,
+ PickKeyFile
+}
+
public class ChoosePDFActivity extends ListActivity {
static private File mDirectory;
static private Map<String, Integer> mPositions = new HashMap<String, Integer>();
@@ -30,11 +35,15 @@ public class ChoosePDFActivity extends ListActivity {
private Handler mHandler;
private Runnable mUpdateFiles;
private ChoosePDFAdapter adapter;
+ private Purpose mPurpose;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mPurpose = Intent.ACTION_MAIN.equals(getIntent().getAction()) ? Purpose.PickPDF : Purpose.PickKeyFile;
+
+
String storageState = Environment.getExternalStorageState();
if (!Environment.MEDIA_MOUNTED.equals(storageState)
@@ -88,29 +97,38 @@ public class ChoosePDFActivity extends ListActivity {
if (file.isDirectory())
return false;
String fname = file.getName().toLowerCase();
- if (fname.endsWith(".pdf"))
- return true;
- if (fname.endsWith(".xps"))
- return true;
- if (fname.endsWith(".cbz"))
- return true;
- if (fname.endsWith(".png"))
- return true;
- if (fname.endsWith(".jpe"))
- return true;
- if (fname.endsWith(".jpeg"))
- return true;
- if (fname.endsWith(".jpg"))
- return true;
- if (fname.endsWith(".jfif"))
- return true;
- if (fname.endsWith(".jfif-tbnl"))
- return true;
- if (fname.endsWith(".tif"))
- return true;
- if (fname.endsWith(".tiff"))
- return true;
- return false;
+ switch (mPurpose) {
+ case PickPDF:
+ if (fname.endsWith(".pdf"))
+ return true;
+ if (fname.endsWith(".xps"))
+ return true;
+ if (fname.endsWith(".cbz"))
+ return true;
+ if (fname.endsWith(".png"))
+ return true;
+ if (fname.endsWith(".jpe"))
+ return true;
+ if (fname.endsWith(".jpeg"))
+ return true;
+ if (fname.endsWith(".jpg"))
+ return true;
+ if (fname.endsWith(".jfif"))
+ return true;
+ if (fname.endsWith(".jfif-tbnl"))
+ return true;
+ if (fname.endsWith(".tif"))
+ return true;
+ if (fname.endsWith(".tiff"))
+ return true;
+ return false;
+ case PickKeyFile:
+ if (fname.endsWith(".pfx"))
+ return true;
+ return false;
+ default:
+ return false;
+ }
}
});
if (mFiles == null)
@@ -184,7 +202,17 @@ public class ChoosePDFActivity extends ListActivity {
Intent intent = new Intent(this,MuPDFActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.setData(uri);
- startActivity(intent);
+ switch (mPurpose) {
+ case PickPDF:
+ // Start an activity to display the PDF file
+ startActivity(intent);
+ break;
+ case PickKeyFile:
+ // Return the uri to the caller
+ setResult(RESULT_OK, intent);
+ finish();
+ break;
+ }
}
@Override
diff --git a/platform/android/src/com/artifex/mupdfdemo/FilePicker.java b/platform/android/src/com/artifex/mupdfdemo/FilePicker.java
new file mode 100644
index 00000000..65e2e901
--- /dev/null
+++ b/platform/android/src/com/artifex/mupdfdemo/FilePicker.java
@@ -0,0 +1,21 @@
+package com.artifex.mupdfdemo;
+
+import android.net.Uri;
+
+interface FilePickerSupport {
+ void performPickFor(FilePicker picker);
+}
+
+public abstract class FilePicker {
+ private final FilePickerSupport support;
+
+ FilePicker(FilePickerSupport _support) {
+ support = _support;
+ }
+
+ void pick() {
+ support.performPickFor(this);
+ }
+
+ abstract void onPick(Uri uri);
+}
diff --git a/platform/android/src/com/artifex/mupdfdemo/MuPDFActivity.java b/platform/android/src/com/artifex/mupdfdemo/MuPDFActivity.java
index 28aecd84..e38a3179 100644
--- a/platform/android/src/com/artifex/mupdfdemo/MuPDFActivity.java
+++ b/platform/android/src/com/artifex/mupdfdemo/MuPDFActivity.java
@@ -38,7 +38,7 @@ class ThreadPerTaskExecutor implements Executor {
}
}
-public class MuPDFActivity extends Activity
+public class MuPDFActivity extends Activity implements FilePickerSupport
{
/* The core rendering instance */
enum TopBarMode {Main, Search, Annot, Delete, More, Accept};
@@ -46,6 +46,7 @@ public class MuPDFActivity extends Activity
private final int OUTLINE_REQUEST=0;
private final int PRINT_REQUEST=1;
+ private final int FILEPICK_REQUEST=2;
private MuPDFCore core;
private String mFileName;
private MuPDFReaderView mDocView;
@@ -78,6 +79,7 @@ public class MuPDFActivity extends Activity
private boolean mReflow = false;
private AsyncTask<Void,Void,MuPDFAlert> mAlertTask;
private AlertDialog mAlertDialog;
+ private FilePicker mFilePicker;
public void createAlertWaiter() {
mAlertsActive = true;
@@ -418,7 +420,7 @@ public class MuPDFActivity extends Activity
}
}
};
- mDocView.setAdapter(new MuPDFPageAdapter(this, core));
+ mDocView.setAdapter(new MuPDFPageAdapter(this, this, core));
mSearchTask = new SearchTask(this, core) {
@Override
@@ -592,6 +594,9 @@ public class MuPDFActivity extends Activity
if (resultCode == RESULT_CANCELED)
showInfo(getString(R.string.print_failed));
break;
+ case FILEPICK_REQUEST:
+ if (mFilePicker != null && resultCode == RESULT_OK)
+ mFilePicker.onPick(data.getData());
}
super.onActivityResult(requestCode, resultCode, data);
}
@@ -606,7 +611,7 @@ public class MuPDFActivity extends Activity
private void reflowModeSet(boolean reflow)
{
mReflow = reflow;
- mDocView.setAdapter(mReflow ? new MuPDFReflowAdapter(this, core) : new MuPDFPageAdapter(this, core));
+ mDocView.setAdapter(mReflow ? new MuPDFReflowAdapter(this, core) : new MuPDFPageAdapter(this, this, core));
mReflowButton.setColorFilter(mReflow ? Color.argb(0xFF, 172, 114, 37) : Color.argb(0xFF, 255, 255, 255));
setButtonEnabled(mAnnotButton, !reflow);
setButtonEnabled(mSearchButton, !reflow);
@@ -1088,4 +1093,11 @@ public class MuPDFActivity extends Activity
super.onBackPressed();
}
}
+
+ @Override
+ public void performPickFor(FilePicker picker) {
+ mFilePicker = picker;
+ Intent intent = new Intent(this, ChoosePDFActivity.class);
+ startActivityForResult(intent, FILEPICK_REQUEST);
+ }
}
diff --git a/platform/android/src/com/artifex/mupdfdemo/MuPDFCore.java b/platform/android/src/com/artifex/mupdfdemo/MuPDFCore.java
index 7b1b8e41..c22f3fa9 100644
--- a/platform/android/src/com/artifex/mupdfdemo/MuPDFCore.java
+++ b/platform/android/src/com/artifex/mupdfdemo/MuPDFCore.java
@@ -49,8 +49,9 @@ public class MuPDFCore
private native void setFocusedWidgetChoiceSelectedInternal(String [] selected);
private native String [] getFocusedWidgetChoiceSelected();
private native String [] getFocusedWidgetChoiceOptions();
- private native boolean getFocusedWidgetSignatureState();
+ private native int getFocusedWidgetSignatureState();
private native String checkFocusedSignatureInternal();
+ private native boolean signFocusedSignatureInternal(String keyFile, String password);
private native int setFocusedWidgetTextInternal(String text);
private native String getFocusedWidgetTextInternal();
private native int getFocusedWidgetTypeInternal();
@@ -209,6 +210,10 @@ public class MuPDFCore
return checkFocusedSignatureInternal();
}
+ public synchronized boolean signFocusedSignature(String keyFile, String password) {
+ return signFocusedSignatureInternal(keyFile, password);
+ }
+
public synchronized LinkInfo [] getPageLinks(int page) {
return getPageLinksInternal(page);
}
diff --git a/platform/android/src/com/artifex/mupdfdemo/MuPDFPageAdapter.java b/platform/android/src/com/artifex/mupdfdemo/MuPDFPageAdapter.java
index 806d0830..83d6a3fc 100644
--- a/platform/android/src/com/artifex/mupdfdemo/MuPDFPageAdapter.java
+++ b/platform/android/src/com/artifex/mupdfdemo/MuPDFPageAdapter.java
@@ -10,11 +10,13 @@ import android.widget.BaseAdapter;
public class MuPDFPageAdapter extends BaseAdapter {
private final Context mContext;
+ private final FilePickerSupport mFilePickerSupport;
private final MuPDFCore mCore;
private final SparseArray<PointF> mPageSizes = new SparseArray<PointF>();
- public MuPDFPageAdapter(Context c, MuPDFCore core) {
+ public MuPDFPageAdapter(Context c, FilePickerSupport filePickerSupport, MuPDFCore core) {
mContext = c;
+ mFilePickerSupport = filePickerSupport;
mCore = core;
}
@@ -33,7 +35,7 @@ public class MuPDFPageAdapter extends BaseAdapter {
public View getView(final int position, View convertView, ViewGroup parent) {
final MuPDFPageView pageView;
if (convertView == null) {
- pageView = new MuPDFPageView(mContext, mCore, new Point(parent.getWidth(), parent.getHeight()));
+ pageView = new MuPDFPageView(mContext, mFilePickerSupport, mCore, new Point(parent.getWidth(), parent.getHeight()));
} else {
pageView = (MuPDFPageView) convertView;
}
diff --git a/platform/android/src/com/artifex/mupdfdemo/MuPDFPageView.java b/platform/android/src/com/artifex/mupdfdemo/MuPDFPageView.java
index 2cb3974b..774aafb7 100644
--- a/platform/android/src/com/artifex/mupdfdemo/MuPDFPageView.java
+++ b/platform/android/src/com/artifex/mupdfdemo/MuPDFPageView.java
@@ -10,10 +10,20 @@ import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.RectF;
+import android.net.Uri;
+import android.text.method.PasswordTransformationMethod;
import android.view.LayoutInflater;
import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
+/* This enum should be kept in line with the cooresponding C enum in mupdf.c */
+enum SignatureState {
+ NoSupport,
+ Unsigned,
+ Signed
+}
+
abstract class PassClickResultVisitor {
public abstract void visitText(PassClickResultText result);
public abstract void visitChoice(PassClickResultChoice result);
@@ -60,11 +70,11 @@ class PassClickResultChoice extends PassClickResult {
}
class PassClickResultSignature extends PassClickResult {
- public final boolean isSigned;
+ public final SignatureState state;
- public PassClickResultSignature(boolean _changed, boolean _isSigned) {
+ public PassClickResultSignature(boolean _changed, int _state) {
super(_changed);
- isSigned = _isSigned;
+ state = SignatureState.values()[_state];
}
public void acceptVisitor(PassClickResultVisitor visitor) {
@@ -73,6 +83,7 @@ class PassClickResultSignature extends PassClickResult {
}
public class MuPDFPageView extends PageView implements MuPDFView {
+ final private FilePickerSupport mFilePickerSupport;
private final MuPDFCore mCore;
private AsyncTask<Void,Void,PassClickResult> mPassClick;
private RectF mWidgetAreas[];
@@ -84,7 +95,10 @@ public class MuPDFPageView extends PageView implements MuPDFView {
private AlertDialog.Builder mChoiceEntryBuilder;
private AlertDialog.Builder mSigningDialogBuilder;
private AlertDialog.Builder mSignatureReportBuilder;
+ private AlertDialog.Builder mPasswordEntryBuilder;
+ private EditText mPasswordText;
private AlertDialog mTextEntry;
+ private AlertDialog mPasswordEntry;
private EditText mEditText;
private AsyncTask<String,Void,Boolean> mSetWidgetText;
private AsyncTask<String,Void,Void> mSetWidgetChoice;
@@ -92,10 +106,12 @@ public class MuPDFPageView extends PageView implements MuPDFView {
private AsyncTask<PointF[][],Void,Void> mAddInk;
private AsyncTask<Integer,Void,Void> mDeleteAnnotation;
private AsyncTask<Void,Void,String> mCheckSignature;
+ private AsyncTask<Void,Void,Boolean> mSign;
private Runnable changeReporter;
- public MuPDFPageView(Context c, MuPDFCore core, Point parentSize) {
+ public MuPDFPageView(Context c, FilePickerSupport filePickerSupport, MuPDFCore core, Point parentSize) {
super(c, parentSize);
+ mFilePickerSupport = filePickerSupport;
mCore = core;
mTextEntryBuilder = new AlertDialog.Builder(c);
mTextEntryBuilder.setTitle(getContext().getString(R.string.fill_out_text_field));
@@ -137,8 +153,21 @@ public class MuPDFPageView extends PageView implements MuPDFView {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
+ });
+ mSigningDialogBuilder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ FilePicker picker = new FilePicker(mFilePickerSupport) {
+ @Override
+ void onPick(Uri uri) {
+ signWithKeyFile(uri);
+ }
+ };
+ picker.pick();
+ }
});
+
mSignatureReportBuilder = new AlertDialog.Builder(c);
mSignatureReportBuilder.setTitle("Signature checked");
mSignatureReportBuilder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
@@ -147,6 +176,59 @@ public class MuPDFPageView extends PageView implements MuPDFView {
dialog.dismiss();
}
});
+
+ mPasswordText = new EditText(c);
+ mPasswordText.setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
+ mPasswordText.setTransformationMethod(new PasswordTransformationMethod());
+
+ mPasswordEntryBuilder = new AlertDialog.Builder(c);
+ mPasswordEntryBuilder.setTitle(R.string.enter_password);
+ mPasswordEntryBuilder.setView(mPasswordText);
+ mPasswordEntryBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+
+ mPasswordEntry = mPasswordEntryBuilder.create();
+ }
+
+ private void signWithKeyFile(final Uri uri) {
+ mPasswordEntry.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ mPasswordEntry.setButton(AlertDialog.BUTTON_POSITIVE, "Sign", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ signWithKeyFileAndPassword(uri, mPasswordText.getText().toString());
+ }
+ });
+
+ mPasswordEntry.show();
+ }
+
+ private void signWithKeyFileAndPassword(final Uri uri, final String password) {
+ mSign = new AsyncTask<Void,Void,Boolean>() {
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ return mCore.signFocusedSignature(Uri.decode(uri.getEncodedPath()), password);
+ }
+ @Override
+ protected void onPostExecute(Boolean result) {
+ if (result)
+ {
+ changeReporter.run();
+ }
+ else
+ {
+ mPasswordText.setText("");
+ signWithKeyFile(uri);
+ }
+ }
+
+ };
+
+ mSign.execute();
}
public LinkInfo hitLink(float x, float y) {
@@ -217,6 +299,12 @@ public class MuPDFPageView extends PageView implements MuPDFView {
dialog.show();
}
+ private void warnNoSignatureSupport() {
+ AlertDialog dialog = mSignatureReportBuilder.create();
+ dialog.setTitle("App built with no signature support");
+ dialog.show();
+ }
+
public void setChangeReporter(Runnable reporter) {
changeReporter = reporter;
}
@@ -287,10 +375,17 @@ public class MuPDFPageView extends PageView implements MuPDFView {
@Override
public void visitSignature(PassClickResultSignature result) {
- if (result.isSigned)
- invokeSignatureCheckingDialog();
- else
+ switch (result.state) {
+ case NoSupport:
+ warnNoSignatureSupport();
+ break;
+ case Unsigned:
invokeSigningDialog();
+ break;
+ case Signed:
+ invokeSignatureCheckingDialog();
+ break;
+ }
}
});
}