Skip to content

Commit 6e80f58

Browse files
committed
Add programmatic form filling example
Signed-off-by: Simone Arpe <[email protected]>
1 parent 7311adb commit 6e80f58

File tree

6 files changed

+337
-51
lines changed

6 files changed

+337
-51
lines changed

android/src/main/AndroidManifest.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414

1515
<application android:largeHeap="true">
1616
<activity
17-
android:name="com.pspdfkit.ui.PdfActivity"
17+
android:name=".FlutterPdfActivity"
1818
android:windowSoftInputMode="adjustNothing" />
19-
2019
</application>
2120
</manifest>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.pspdfkit.flutter.pspdfkit;
2+
3+
import android.os.Bundle;
4+
5+
import androidx.annotation.NonNull;
6+
7+
import com.pspdfkit.document.PdfDocument;
8+
import com.pspdfkit.ui.PdfActivity;
9+
10+
import java.util.concurrent.atomic.AtomicReference;
11+
12+
import io.flutter.plugin.common.MethodChannel.Result;
13+
14+
/**
15+
* For communication with the PSPDFKit plugin, we keep a static reference to the current
16+
* activity.
17+
*/
18+
public class FlutterPdfActivity extends PdfActivity {
19+
20+
private static FlutterPdfActivity currentActivity;
21+
private static AtomicReference<Result> loadedDocumentResult = new AtomicReference<>();
22+
23+
public static void setLoadedDocumentResult(Result result) {
24+
loadedDocumentResult.set(result);
25+
}
26+
27+
@Override
28+
protected void onCreate(Bundle bundle) {
29+
super.onCreate(bundle);
30+
bindActivity();
31+
}
32+
33+
@Override
34+
public void onDocumentLoaded(@NonNull PdfDocument pdfDocument) {
35+
super.onDocumentLoaded(pdfDocument);
36+
Result result = loadedDocumentResult.getAndSet(null);
37+
if (result != null) {
38+
result.success(true);
39+
}
40+
}
41+
42+
@Override
43+
public void onDocumentLoadFailed(@NonNull Throwable throwable) {
44+
super.onDocumentLoadFailed(throwable);
45+
Result result = loadedDocumentResult.getAndSet(null);
46+
if (result != null) {
47+
result.success(false);
48+
}
49+
}
50+
51+
private void bindActivity() {
52+
currentActivity = this;
53+
}
54+
55+
public static FlutterPdfActivity getCurrentActivity() {
56+
return currentActivity;
57+
}
58+
}

android/src/main/java/com/pspdfkit/flutter/pspdfkit/PspdfkitPlugin.java

Lines changed: 186 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,32 @@
1717
import android.provider.Settings;
1818
import android.util.Log;
1919

20+
import androidx.annotation.NonNull;
21+
import androidx.core.app.ActivityCompat;
22+
import androidx.core.content.ContextCompat;
23+
2024
import com.pspdfkit.PSPDFKit;
21-
import com.pspdfkit.ui.PdfActivity;
25+
import com.pspdfkit.document.PdfDocumentLoader;
26+
import com.pspdfkit.forms.ChoiceFormElement;
27+
import com.pspdfkit.forms.EditableButtonFormElement;
28+
import com.pspdfkit.forms.SignatureFormElement;
29+
import com.pspdfkit.forms.TextFormElement;
30+
import com.pspdfkit.ui.PdfActivityIntentBuilder;
2231

32+
import java.util.ArrayList;
2333
import java.util.HashMap;
34+
import java.util.Iterator;
35+
import java.util.List;
2436
import java.util.concurrent.atomic.AtomicReference;
2537

26-
import androidx.annotation.NonNull;
27-
import androidx.core.app.ActivityCompat;
28-
import androidx.core.content.ContextCompat;
29-
3038
import io.flutter.plugin.common.MethodCall;
3139
import io.flutter.plugin.common.MethodChannel;
3240
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
3341
import io.flutter.plugin.common.MethodChannel.Result;
3442
import io.flutter.plugin.common.PluginRegistry;
3543
import io.flutter.plugin.common.PluginRegistry.Registrar;
44+
import io.reactivex.android.schedulers.AndroidSchedulers;
45+
import io.reactivex.schedulers.Schedulers;
3646

3747
import static com.pspdfkit.flutter.pspdfkit.util.Preconditions.requireNotNullNotEmpty;
3848

@@ -44,10 +54,10 @@ public class PspdfkitPlugin implements MethodCallHandler, PluginRegistry.Request
4454
private static final String FILE_SCHEME = "file:///";
4555
private final Context context;
4656
private final Registrar registrar;
47-
/** Atomic reference that prevents sending twice the permission result and throw exception. */
57+
/** Atomic reference that prevents sending twice the permission result and throwing exception. */
4858
private AtomicReference<Result> permissionRequestResult;
4959

50-
public PspdfkitPlugin(Registrar registrar) {
60+
private PspdfkitPlugin(Registrar registrar) {
5161
this.context = registrar.activeContext();
5262
this.registrar = registrar;
5363
this.permissionRequestResult = new AtomicReference<>();
@@ -65,7 +75,8 @@ public static void registerWith(Registrar registrar) {
6575

6676
@Override
6777
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
68-
String permission;
78+
String fullyQualifiedName;
79+
FlutterPdfActivity flutterPdfActivity;
6980

7081
switch (call.method) {
7182
case "frameworkVersion":
@@ -76,44 +87,198 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
7687
requireNotNullNotEmpty(licenseKey, "License key");
7788
PSPDFKit.initialize(context, licenseKey);
7889
break;
90+
case "open":
91+
String documentPathToOpen = call.argument("document");
92+
requireNotNullNotEmpty(documentPathToOpen, "Document path");
93+
94+
final String documentPathToOpenFixedScheme = addFileSchemeIfMissing(documentPathToOpen);
95+
96+
//noinspection ResultOfMethodCallIgnored
97+
PdfDocumentLoader.openDocumentAsync(context, Uri.parse(documentPathToOpenFixedScheme))
98+
.subscribeOn(Schedulers.computation())
99+
.observeOn(AndroidSchedulers.mainThread())
100+
.subscribe(
101+
pdfDocument -> result.success(pdfDocument.getUid()),
102+
throwable -> result.error(LOG_TAG,
103+
String.format("Error while opening document %s", documentPathToOpenFixedScheme),
104+
throwable.getMessage())
105+
);
106+
break;
79107
case "present":
80108
String documentPath = call.argument("document");
81109
requireNotNullNotEmpty(documentPath, "Document path");
82110

83111
HashMap<String, Object> configurationMap = call.argument("configuration");
84112
ConfigurationAdapter configurationAdapter = new ConfigurationAdapter(context, configurationMap);
85-
if (Uri.parse(documentPath).getScheme() == null) {
86-
if (documentPath.startsWith("/")) {
87-
documentPath = documentPath.substring(1);
88-
}
89-
documentPath = FILE_SCHEME + documentPath;
90-
}
113+
114+
documentPath = addFileSchemeIfMissing(documentPath);
115+
116+
FlutterPdfActivity.setLoadedDocumentResult(result);
91117
boolean imageDocument = isImageDocument(documentPath);
92118
if (imageDocument) {
93-
PdfActivity.showImage(context, Uri.parse(documentPath), configurationAdapter.build());
119+
Intent intent = PdfActivityIntentBuilder.fromImageUri(context, Uri.parse(documentPath))
120+
.activityClass(FlutterPdfActivity.class)
121+
.configuration(configurationAdapter.build())
122+
.build();
123+
context.startActivity(intent);
124+
94125
} else {
95-
PdfActivity.showDocument(context, Uri.parse(documentPath), configurationAdapter.getPassword(), configurationAdapter.build());
126+
Intent intent = PdfActivityIntentBuilder.fromUri(context, Uri.parse(documentPath))
127+
.activityClass(FlutterPdfActivity.class)
128+
.configuration(configurationAdapter.build())
129+
.passwords(configurationAdapter.getPassword())
130+
.build();
131+
context.startActivity(intent);
96132
}
97133
break;
98134
case "checkPermission":
99-
permission = call.argument("permission");
100-
result.success(checkPermission(permission));
135+
final String permissionToCheck;
136+
permissionToCheck = call.argument("permission");
137+
result.success(checkPermission(permissionToCheck));
101138
break;
102139
case "requestPermission":
103-
permission = call.argument("permission");
104-
this.permissionRequestResult.set(result);
105-
requestPermission(permission);
140+
final String permissionToRequest;
141+
permissionToRequest = call.argument("permission");
142+
permissionRequestResult.set(result);
143+
requestPermission(permissionToRequest);
106144
break;
107145
case "openSettings":
108146
openSettings();
109147
result.success(true);
110148
break;
149+
case "setFormFieldValue":
150+
String value = call.argument("value");
151+
fullyQualifiedName = call.argument("fullyQualifiedName");
152+
153+
requireNotNullNotEmpty(value, "Value");
154+
requireNotNullNotEmpty(fullyQualifiedName, "Fully qualified name");
155+
flutterPdfActivity = FlutterPdfActivity.getCurrentActivity();
156+
if (flutterPdfActivity == null) {
157+
throw new IllegalStateException("Before using \"Pspdfkit.setFormFieldValue()\" " +
158+
"the document needs to be presented by calling \"Pspdfkit.present()\".");
159+
}
160+
161+
//noinspection ResultOfMethodCallIgnored,ConstantConditions
162+
flutterPdfActivity.getPdfFragment().getDocument().getFormProvider()
163+
.getFormElementWithNameAsync(fullyQualifiedName)
164+
.subscribeOn(Schedulers.computation())
165+
.observeOn(AndroidSchedulers.mainThread())
166+
.subscribe(
167+
formElement -> {
168+
if (formElement instanceof TextFormElement) {
169+
((TextFormElement) formElement).setText(value);
170+
result.success(true);
171+
} else if (formElement instanceof EditableButtonFormElement) {
172+
if (value.equals("selected")) {
173+
((EditableButtonFormElement) formElement).select();
174+
result.success(true);
175+
} else if (value.equals("deselected")) {
176+
((EditableButtonFormElement) formElement).deselect();
177+
result.success(true);
178+
} else {
179+
result.success(false);
180+
}
181+
} else if (formElement instanceof ChoiceFormElement) {
182+
List<Integer> selectedIndexes = new ArrayList<>();
183+
if (areValidIndexes(value, selectedIndexes)) {
184+
((ChoiceFormElement) formElement).setSelectedIndexes(selectedIndexes);
185+
result.success(true);
186+
} else {
187+
result.error(LOG_TAG, "\"value\" argument needs a list of " +
188+
"integers to set selected indexes for a choice " +
189+
"form element (e.g.: \"1, 3, 5\").", null);
190+
}
191+
} else if (formElement instanceof SignatureFormElement) {
192+
result.error("Signature form elements are not supported.", null, null);
193+
} else {
194+
result.success(false);
195+
}
196+
},
197+
throwable -> result.error(LOG_TAG,
198+
String.format("Error while searching for a form element with name %s", fullyQualifiedName),
199+
throwable.getMessage()),
200+
// Form element for the given name not found.
201+
() -> result.success(false)
202+
);
203+
break;
204+
case "getFormFieldValue":
205+
fullyQualifiedName = call.argument("fullyQualifiedName");
206+
207+
requireNotNullNotEmpty(fullyQualifiedName, "Fully qualified name");
208+
flutterPdfActivity = FlutterPdfActivity.getCurrentActivity();
209+
if (flutterPdfActivity == null) {
210+
throw new IllegalStateException("Before using \"Pspdfkit.setFormFieldValue()\" " +
211+
"the document needs to be presented by calling \"Pspdfkit.present()\".");
212+
}
213+
214+
//noinspection ResultOfMethodCallIgnored,ConstantConditions
215+
flutterPdfActivity.getPdfFragment().getDocument().getFormProvider()
216+
.getFormElementWithNameAsync(fullyQualifiedName)
217+
.subscribeOn(Schedulers.computation())
218+
.observeOn(AndroidSchedulers.mainThread())
219+
.subscribe(
220+
formElement -> {
221+
if (formElement instanceof TextFormElement) {
222+
String text = ((TextFormElement) formElement).getText();
223+
result.success(text);
224+
} else if (formElement instanceof EditableButtonFormElement) {
225+
boolean isSelected = ((EditableButtonFormElement) formElement).isSelected();
226+
result.success(isSelected ? "selected" : "deselected");
227+
} else if (formElement instanceof ChoiceFormElement) {
228+
List<Integer> selectedIndexes = ((ChoiceFormElement) formElement).getSelectedIndexes();
229+
StringBuilder stringBuilder = new StringBuilder();
230+
Iterator<Integer> iterator = selectedIndexes.iterator();
231+
while (iterator.hasNext()) {
232+
stringBuilder.append(iterator.next());
233+
if (iterator.hasNext()) {
234+
stringBuilder.append(",");
235+
}
236+
}
237+
result.success(stringBuilder.toString());
238+
} else if (formElement instanceof SignatureFormElement) {
239+
result.error("Signature form elements are not supported.", null, null);
240+
} else {
241+
result.success(false);
242+
}
243+
},
244+
throwable -> result.error(LOG_TAG,
245+
String.format("Error while searching for a form element with name %s", fullyQualifiedName),
246+
throwable.getMessage()),
247+
// Form element for the given name not found.
248+
() -> result.error(LOG_TAG,
249+
String.format("Form element not found with name %s", fullyQualifiedName),
250+
null)
251+
);
252+
break;
111253
default:
112254
result.notImplemented();
113255
break;
114256
}
115257
}
116258

259+
private boolean areValidIndexes(String value, List<Integer> selectedIndexes) {
260+
String[] indexes = value.split(",");
261+
try {
262+
for (String index : indexes) {
263+
if (index.trim().isEmpty()) continue;
264+
selectedIndexes.add(Integer.parseInt(index.trim()));
265+
}
266+
} catch (NumberFormatException e) {
267+
return false;
268+
}
269+
return true;
270+
}
271+
272+
private String addFileSchemeIfMissing(String documentPath) {
273+
if (Uri.parse(documentPath).getScheme() == null) {
274+
if (documentPath.startsWith("/")) {
275+
documentPath = documentPath.substring(1);
276+
}
277+
documentPath = FILE_SCHEME + documentPath;
278+
}
279+
return documentPath;
280+
}
281+
117282
private void requestPermission(String permission) {
118283
Activity activity = registrar.activity();
119284
permission = getManifestPermission(permission);

example/PDFs/Form_example.pdf

101 KB
Binary file not shown.

0 commit comments

Comments
 (0)