Skip to content

Commit 38ef9b3

Browse files
authored
Merge pull request #39 from PSPDFKit/simone/forms-api
Forms API for Android
2 parents 61083da + a86da29 commit 38ef9b3

File tree

7 files changed

+437
-54
lines changed

7 files changed

+437
-54
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: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
protected void onDestroy() {
35+
super.onDestroy();
36+
releaseActivity();
37+
}
38+
39+
@Override
40+
public void onDocumentLoaded(@NonNull PdfDocument pdfDocument) {
41+
super.onDocumentLoaded(pdfDocument);
42+
Result result = loadedDocumentResult.getAndSet(null);
43+
if (result != null) {
44+
result.success(true);
45+
}
46+
}
47+
48+
@Override
49+
public void onDocumentLoadFailed(@NonNull Throwable throwable) {
50+
super.onDocumentLoadFailed(throwable);
51+
Result result = loadedDocumentResult.getAndSet(null);
52+
if (result != null) {
53+
result.success(false);
54+
}
55+
}
56+
57+
private void bindActivity() {
58+
currentActivity = this;
59+
}
60+
61+
private void releaseActivity() {
62+
Result result = loadedDocumentResult.getAndSet(null);
63+
if (result != null) {
64+
result.success(false);
65+
}
66+
currentActivity = null;
67+
}
68+
69+
public static FlutterPdfActivity getCurrentActivity() {
70+
return currentActivity;
71+
}
72+
}

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

Lines changed: 181 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,31 @@
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.forms.ChoiceFormElement;
26+
import com.pspdfkit.forms.EditableButtonFormElement;
27+
import com.pspdfkit.forms.SignatureFormElement;
28+
import com.pspdfkit.forms.TextFormElement;
29+
import com.pspdfkit.ui.PdfActivityIntentBuilder;
2230

31+
import java.util.ArrayList;
2332
import java.util.HashMap;
33+
import java.util.Iterator;
34+
import java.util.List;
2435
import java.util.concurrent.atomic.AtomicReference;
2536

26-
import androidx.annotation.NonNull;
27-
import androidx.core.app.ActivityCompat;
28-
import androidx.core.content.ContextCompat;
29-
3037
import io.flutter.plugin.common.MethodCall;
3138
import io.flutter.plugin.common.MethodChannel;
3239
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
3340
import io.flutter.plugin.common.MethodChannel.Result;
3441
import io.flutter.plugin.common.PluginRegistry;
3542
import io.flutter.plugin.common.PluginRegistry.Registrar;
43+
import io.reactivex.android.schedulers.AndroidSchedulers;
44+
import io.reactivex.schedulers.Schedulers;
3645

3746
import static com.pspdfkit.flutter.pspdfkit.util.Preconditions.requireNotNullNotEmpty;
3847

@@ -44,10 +53,10 @@ public class PspdfkitPlugin implements MethodCallHandler, PluginRegistry.Request
4453
private static final String FILE_SCHEME = "file:///";
4554
private final Context context;
4655
private final Registrar registrar;
47-
/** Atomic reference that prevents sending twice the permission result and throw exception. */
56+
/** Atomic reference that prevents sending twice the permission result and throwing exception. */
4857
private AtomicReference<Result> permissionRequestResult;
4958

50-
public PspdfkitPlugin(Registrar registrar) {
59+
private PspdfkitPlugin(Registrar registrar) {
5160
this.context = registrar.activeContext();
5261
this.registrar = registrar;
5362
this.permissionRequestResult = new AtomicReference<>();
@@ -65,7 +74,8 @@ public static void registerWith(Registrar registrar) {
6574

6675
@Override
6776
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
68-
String permission;
77+
String fullyQualifiedName;
78+
FlutterPdfActivity flutterPdfActivity;
6979

7080
switch (call.method) {
7181
case "frameworkVersion":
@@ -82,38 +92,188 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
8292

8393
HashMap<String, Object> configurationMap = call.argument("configuration");
8494
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-
}
95+
96+
documentPath = addFileSchemeIfMissing(documentPath);
97+
98+
FlutterPdfActivity.setLoadedDocumentResult(result);
9199
boolean imageDocument = isImageDocument(documentPath);
92100
if (imageDocument) {
93-
PdfActivity.showImage(context, Uri.parse(documentPath), configurationAdapter.build());
101+
Intent intent = PdfActivityIntentBuilder.fromImageUri(context, Uri.parse(documentPath))
102+
.activityClass(FlutterPdfActivity.class)
103+
.configuration(configurationAdapter.build())
104+
.build();
105+
context.startActivity(intent);
106+
94107
} else {
95-
PdfActivity.showDocument(context, Uri.parse(documentPath), configurationAdapter.getPassword(), configurationAdapter.build());
108+
Intent intent = PdfActivityIntentBuilder.fromUri(context, Uri.parse(documentPath))
109+
.activityClass(FlutterPdfActivity.class)
110+
.configuration(configurationAdapter.build())
111+
.passwords(configurationAdapter.getPassword())
112+
.build();
113+
context.startActivity(intent);
96114
}
97115
break;
98116
case "checkPermission":
99-
permission = call.argument("permission");
100-
result.success(checkPermission(permission));
117+
final String permissionToCheck;
118+
permissionToCheck = call.argument("permission");
119+
result.success(checkPermission(permissionToCheck));
101120
break;
102121
case "requestPermission":
103-
permission = call.argument("permission");
104-
this.permissionRequestResult.set(result);
105-
requestPermission(permission);
122+
final String permissionToRequest;
123+
permissionToRequest = call.argument("permission");
124+
permissionRequestResult.set(result);
125+
requestPermission(permissionToRequest);
106126
break;
107127
case "openSettings":
108128
openSettings();
109129
result.success(true);
130+
break;
131+
case "setFormFieldValue":
132+
String value = call.argument("value");
133+
fullyQualifiedName = call.argument("fullyQualifiedName");
134+
135+
requireNotNullNotEmpty(value, "Value");
136+
requireNotNullNotEmpty(fullyQualifiedName, "Fully qualified name");
137+
flutterPdfActivity = FlutterPdfActivity.getCurrentActivity();
138+
if (flutterPdfActivity == null) {
139+
throw new IllegalStateException("Before using \"Pspdfkit.setFormFieldValue()\" " +
140+
"the document needs to be presented by calling \"Pspdfkit.present()\".");
141+
}
142+
143+
//noinspection ResultOfMethodCallIgnored,ConstantConditions
144+
flutterPdfActivity.getPdfFragment().getDocument().getFormProvider()
145+
.getFormElementWithNameAsync(fullyQualifiedName)
146+
.subscribeOn(Schedulers.computation())
147+
.observeOn(AndroidSchedulers.mainThread())
148+
.subscribe(
149+
formElement -> {
150+
if (formElement instanceof TextFormElement) {
151+
((TextFormElement) formElement).setText(value);
152+
result.success(true);
153+
} else if (formElement instanceof EditableButtonFormElement) {
154+
if (value.equals("selected")) {
155+
((EditableButtonFormElement) formElement).select();
156+
result.success(true);
157+
} else if (value.equals("deselected")) {
158+
((EditableButtonFormElement) formElement).deselect();
159+
result.success(true);
160+
} else {
161+
result.success(false);
162+
}
163+
} else if (formElement instanceof ChoiceFormElement) {
164+
List<Integer> selectedIndexes = new ArrayList<>();
165+
if (areValidIndexes(value, selectedIndexes)) {
166+
((ChoiceFormElement) formElement).setSelectedIndexes(selectedIndexes);
167+
result.success(true);
168+
} else {
169+
result.error(LOG_TAG, "\"value\" argument needs a list of " +
170+
"integers to set selected indexes for a choice " +
171+
"form element (e.g.: \"1, 3, 5\").", null);
172+
}
173+
} else if (formElement instanceof SignatureFormElement) {
174+
result.error("Signature form elements are not supported.", null, null);
175+
} else {
176+
result.success(false);
177+
}
178+
},
179+
throwable -> result.error(LOG_TAG,
180+
String.format("Error while searching for a form element with name %s", fullyQualifiedName),
181+
throwable.getMessage()),
182+
// Form element for the given name not found.
183+
() -> result.success(false)
184+
);
185+
break;
186+
case "getFormFieldValue":
187+
fullyQualifiedName = call.argument("fullyQualifiedName");
188+
189+
requireNotNullNotEmpty(fullyQualifiedName, "Fully qualified name");
190+
flutterPdfActivity = FlutterPdfActivity.getCurrentActivity();
191+
if (flutterPdfActivity == null) {
192+
throw new IllegalStateException("Before using \"Pspdfkit.setFormFieldValue()\" " +
193+
"the document needs to be presented by calling \"Pspdfkit.present()\".");
194+
}
195+
196+
//noinspection ResultOfMethodCallIgnored,ConstantConditions
197+
flutterPdfActivity.getPdfFragment().getDocument().getFormProvider()
198+
.getFormElementWithNameAsync(fullyQualifiedName)
199+
.subscribeOn(Schedulers.computation())
200+
.observeOn(AndroidSchedulers.mainThread())
201+
.subscribe(
202+
formElement -> {
203+
if (formElement instanceof TextFormElement) {
204+
String text = ((TextFormElement) formElement).getText();
205+
result.success(text);
206+
} else if (formElement instanceof EditableButtonFormElement) {
207+
boolean isSelected = ((EditableButtonFormElement) formElement).isSelected();
208+
result.success(isSelected ? "selected" : "deselected");
209+
} else if (formElement instanceof ChoiceFormElement) {
210+
List<Integer> selectedIndexes = ((ChoiceFormElement) formElement).getSelectedIndexes();
211+
StringBuilder stringBuilder = new StringBuilder();
212+
Iterator<Integer> iterator = selectedIndexes.iterator();
213+
while (iterator.hasNext()) {
214+
stringBuilder.append(iterator.next());
215+
if (iterator.hasNext()) {
216+
stringBuilder.append(",");
217+
}
218+
}
219+
result.success(stringBuilder.toString());
220+
} else if (formElement instanceof SignatureFormElement) {
221+
result.error("Signature form elements are not supported.", null, null);
222+
} else {
223+
result.success(false);
224+
}
225+
},
226+
throwable -> result.error(LOG_TAG,
227+
String.format("Error while searching for a form element with name %s", fullyQualifiedName),
228+
throwable.getMessage()),
229+
// Form element for the given name not found.
230+
() -> result.error(LOG_TAG,
231+
String.format("Form element not found with name %s", fullyQualifiedName),
232+
null)
233+
);
234+
break;
235+
case "save":
236+
flutterPdfActivity = FlutterPdfActivity.getCurrentActivity();
237+
if (flutterPdfActivity == null) {
238+
throw new IllegalStateException("Before using \"Pspdfkit.save()\" " +
239+
"the document needs to be presented by calling \"Pspdfkit.present()\".");
240+
}
241+
//noinspection ResultOfMethodCallIgnored,ConstantConditions
242+
flutterPdfActivity.getPdfFragment().getDocument().saveIfModifiedAsync()
243+
.subscribeOn(Schedulers.computation())
244+
.observeOn(AndroidSchedulers.mainThread())
245+
.subscribe(result::success);
246+
110247
break;
111248
default:
112249
result.notImplemented();
113250
break;
114251
}
115252
}
116253

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

example/PDFs/Form_example.pdf

101 KB
Binary file not shown.

0 commit comments

Comments
 (0)