Skip to content

Commit 401df05

Browse files
author
PSPDFKit
committed
Release 2.18.0
1 parent 0350e99 commit 401df05

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1766
-915
lines changed

CHANGELOG.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
1+
## 2.18.0 - 20 May 2025
2+
3+
- Adds the new `AIAssistantConfiguration` class to configure AI Assistant and also add new `aiAssistantButtonItem` option to Toolbar configuration. (J#HYB-743)
4+
- Adds the new `getPageInfo` API to the `PDFDocument` class. (J#HYB-801)
5+
- Updates to Nutrient Android SDK 10.2.0.
6+
- Updates to Nutrient iOS SDK 14.8.0.
7+
- Fixes an issue where the `setLicenseKeys` API could cause a crash on Android if called too early during the application lifecycle. (J#HYB-790)
8+
- Fixes an issue where `NotificationCenter` events are not always delivered when running Release build configuration on iOS. (J#HYB-793)
9+
- Fixes an issue where toolbar button customization was not persisted on Android during component reload. (J#HYB-800)
10+
- Fixes an issue where the `onAnnotationTapped` callback wasn't called reliably on Android. (J#HYB-805)
11+
112
## 2.17.0 - 14 Apr 2025
213

3-
- Adds new `setPageIndex` API to the `PDFDocument` class. (J#HYB-699)
14+
- Adds the new `setPageIndex` API to the `PDFDocument` class. (J#HYB-699)
415
- Adds the `documentTapped` event to `NotificationCenter` to receive document tap events. (J#HYB-771)
516
- Adds support for using multiple `PSPDFKitView` components in the same `View`. (J#HYB-692)
6-
- Adds TypeScript types for `FormElement` and `FormField` objects and also introduce new APIs to retrieve and update Form annotation data. (J#HYB-612)
7-
- Adds new `applyTemplate` property to `ToolbarItem` to manage toolbar icon color preservation when being displayed. (J#HYB-741)
17+
- Adds TypeScript types for `FormElement` and `FormField` objects, and introduces new APIs to retrieve and update form annotation data. (J#HYB-612)
18+
- Adds the new `applyTemplate` property to `ToolbarItem` to manage toolbar icon color preservation when being displayed. (J#HYB-741)
819
- Updates the `addAnnotations` API to accept optional annotation attachments. (J#HYB-782)
920
- Updates to Nutrient Android SDK 10.1.1.
1021
- Updates to Nutrient iOS SDK 14.6.0.
11-
- Fixes an issue where `selection_tool` was not being added to the `menuItemGrouping` configuration on iOS. (J#HYB-705)
22+
- Fixes an issue where `selection_tool` wasn't being added to the `menuItemGrouping` configuration on iOS. (J#HYB-705)
1223

1324
## 2.16.1 - 27 Mar 2025
1425

android/build.gradle

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Contains gradle configuration constants
1616
*/
1717
ext {
18-
PSPDFKIT_VERSION = '10.1.1'
18+
PSPDFKIT_VERSION = '10.2.0'
1919
}
2020

2121
buildscript {
@@ -109,8 +109,18 @@ dependencies {
109109

110110
implementation "com.facebook.react:react-native:+"
111111
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
112+
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
112113
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
113114
implementation "androidx.compose.material:material:1.7.8"
114115
implementation "androidx.compose.material3:material3:1.3.1"
115116
implementation "androidx.recyclerview:recyclerview:1.3.2"
117+
118+
// AI Assistant
119+
implementation("io.noties.markwon:core:4.6.2")
120+
implementation("io.noties.markwon:html:4.6.2")
121+
implementation("io.noties.markwon:linkify:4.6.2")
122+
implementation("io.noties.markwon:ext-tables:4.6.2")
123+
implementation("io.noties.markwon:ext-strikethrough:4.6.2")
124+
implementation("io.socket:socket.io-client:2.1.1")
125+
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0"
116126
}

android/src/main/java/com/pspdfkit/react/PDFDocumentModule.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@ class PDFDocumentModule(reactContext: ReactApplicationContext) : ReactContextBas
8686
}
8787
}
8888

89+
@ReactMethod fun getPageInfo(reference: Int, pageIndex: Int, promise: Promise) {
90+
try {
91+
val rotation = this.getDocument(reference)?.document?.getPageRotation(pageIndex);
92+
val result = Arguments.createMap()
93+
if (rotation != null) {
94+
result.putInt("savedRotation", rotation)
95+
}
96+
promise.resolve(result);
97+
} catch (e: Throwable) {
98+
promise.reject("getPageInfo error", e)
99+
}
100+
}
101+
89102
@ReactMethod fun getPageCount(reference: Int, promise: Promise) {
90103
try {
91104
promise.resolve(this.getDocument(reference)?.document?.pageCount)

android/src/main/java/com/pspdfkit/react/PSPDFKitModule.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -248,18 +248,19 @@ public void run() {
248248
public void setLicenseKey(@Nullable String licenseKey, @Nullable Promise promise) {
249249
try {
250250
InitializationOptions options = new InitializationOptions(licenseKey, emptyList(), CrossPlatformTechnology.ReactNative, null);
251-
Nutrient.initialize(getCurrentActivity(), options);
251+
Nutrient.initialize(getReactApplicationContext(), options);
252252
promise.resolve("Initialised Nutrient");
253253
} catch (InvalidNutrientLicenseException e) {
254254
promise.reject(e);
255255
}
256256
}
257257

258-
@ReactMethod
259-
public void getDocumentProperties(@Nullable String documentPath, @Nullable Promise promise) {
258+
@ReactMethod(isBlockingSynchronousMethod = true)
259+
public WritableMap getDocumentProperties(@Nullable String documentPath) {
260+
WritableMap properties = Arguments.createMap();
261+
260262
try {
261263
if (Uri.parse(documentPath).getScheme() == null) {
262-
// If there is no scheme it might be a raw path.
263264
try {
264265
File file = new File(documentPath);
265266
documentPath = Uri.fromFile(file).toString();
@@ -269,22 +270,22 @@ public void getDocumentProperties(@Nullable String documentPath, @Nullable Promi
269270
}
270271

271272
PdfDocument document = PdfDocumentLoader.openDocument(getReactApplicationContext(), Uri.parse(documentPath));
272-
WritableMap properties = Arguments.createMap();
273+
properties.putString("documentId", document.getDocumentIdString());
273274
properties.putInt("pageCount", document.getPageCount());
274275
properties.putBoolean("isEncrypted", document.isEncrypted());
275276

276-
promise.resolve(properties);
277277
} catch (IOException e) {
278-
// If the document is password protected, return this information to the caller.
279278
if (e instanceof InvalidPasswordException) {
280-
WritableMap properties = Arguments.createMap();
281279
properties.putInt("pageCount", 0);
282280
properties.putBoolean("isEncrypted", true);
283-
promise.resolve(properties);
284281
} else {
285-
promise.reject(e);
282+
properties.putString("documentId", null);
283+
properties.putInt("pageCount", 0);
284+
properties.putBoolean("isEncrypted", false);
286285
}
287286
}
287+
288+
return properties;
288289
}
289290

290291
@ReactMethod
@@ -293,7 +294,7 @@ public void setLicenseKeys(@Nullable String androidLicenseKey, @Nullable String
293294
// `iOSLicenseKey` will be used to activate the license on iOS.
294295
try {
295296
InitializationOptions options = new InitializationOptions(androidLicenseKey, emptyList(), CrossPlatformTechnology.ReactNative, null);
296-
Nutrient.initialize(getCurrentActivity(), options);
297+
Nutrient.initialize(getReactApplicationContext(), options);
297298
promise.resolve("Initialised Nutrient");
298299
} catch (InvalidNutrientLicenseException e) {
299300
promise.reject(e);

android/src/main/java/com/pspdfkit/react/ReactPdfViewManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ public void setConfiguration(PdfView view, @NonNull ReadableMap configuration) {
169169
if (configuration.getArray("measurementValueConfigurations") != null) {
170170
view.setMeasurementValueConfigurations(configuration.getArray("measurementValueConfigurations"));
171171
}
172+
if (configuration.getMap("aiAssistantConfiguration") != null) {
173+
view.setAIAConfiguration(configuration.getMap("aiAssistantConfiguration"));
174+
}
172175
}
173176

174177
@ReactProp(name = "annotationPresets")

android/src/main/java/com/pspdfkit/react/ToolbarMenuItemsAdapter.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class ToolbarMenuItemsAdapter {
2828
private static final String TOOLBAR_ITEM_BOOKMARKS = "bookmarkButtonItem";
2929
private static final String TOOLBAR_ITEM_PRINT = "printButtonItem";
3030
private static final String TOOLBAR_ITEM_ANNOTATION_LIST = "annotationListButtonItem";
31+
private static final String TOOLBAR_ITEM_AI_ASSISTANT = "aiAssistantButtonItem";
3132

3233
private final PdfActivityConfiguration.Builder newConfigurations;
3334

@@ -48,19 +49,22 @@ public ToolbarMenuItemsAdapter(@NonNull final PdfActivityConfiguration currentCo
4849
switch (toolbarItem) {
4950
case TOOLBAR_ITEM_BOOKMARKS:
5051
if (initialConfiguration.isBookmarkListEnabled()) {
51-
configuration.enableBookmarkList();
52+
configuration.bookmarkListEnabled(true);
5253
}
5354
break;
5455
case TOOLBAR_ITEM_PRINT:
5556
if (initialConfiguration.isPrintingEnabled()) {
56-
configuration.enablePrinting();
57+
configuration.printingEnabled(true);
5758
}
5859
break;
5960
case TOOLBAR_ITEM_ANNOTATION_LIST:
6061
if (initialConfiguration.isAnnotationListEnabled()) {
61-
configuration.enableAnnotationList();
62+
configuration.annotationListEnabled(true);
6263
}
6364
break;
65+
case TOOLBAR_ITEM_AI_ASSISTANT:
66+
configuration.setAiAssistantEnabled(true);
67+
break;
6468
}
6569
}
6670
newConfigurations = configuration;

android/src/main/java/com/pspdfkit/views/PdfView.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,12 @@
117117
import java.util.Locale;
118118
import java.util.Map;
119119
import java.util.NoSuchElementException;
120+
import java.util.Objects;
120121
import java.util.concurrent.atomic.AtomicBoolean;
121122

123+
import io.nutrient.data.models.AiAssistantConfiguration;
124+
import io.nutrient.domain.ai.AiAssistant;
125+
import io.nutrient.domain.ai.AiAssistantKt;
122126
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
123127
import io.reactivex.rxjava3.core.Completable;
124128
import io.reactivex.rxjava3.core.Maybe;
@@ -153,6 +157,7 @@ public class PdfView extends FrameLayout {
153157
private String documentPath;
154158
private String documentPassword;
155159
private ReadableMap remoteDocumentConfiguration;
160+
private ReadableMap aiaConfiguration;
156161
private int pageIndex = 0;
157162
private PdfActivityConfiguration initialConfiguration;
158163
private ReadableArray pendingToolbarItems;
@@ -316,6 +321,10 @@ public void setRemoteDocumentConfiguration(@Nullable ReadableMap remoteDocumentC
316321
this.remoteDocumentConfiguration = remoteDocumentConfig;
317322
}
318323

324+
public void setAIAConfiguration(@Nullable ReadableMap aiaConfiguration) {
325+
this.aiaConfiguration = aiaConfiguration;
326+
}
327+
319328
public void setDocument(@Nullable String documentPath, ReactApplicationContext reactApplicationContext) {
320329
if (documentPath == null) {
321330
this.document = null;
@@ -585,6 +594,21 @@ private void postFragmentSetup(PdfUiFragment pdfFragment) {
585594
pdfFragment.setPageIndex(pageIndex, true);
586595
}
587596
}
597+
if (aiaConfiguration != null) {
598+
try {
599+
if (aiaConfiguration.getString("serverURL") != null && aiaConfiguration.getString("jwt") != null && aiaConfiguration.getString("sessionID") != null) {
600+
AiAssistantConfiguration aiaConfig = new AiAssistantConfiguration(
601+
aiaConfiguration.getString("serverURL"),
602+
aiaConfiguration.getString("jwt"),
603+
aiaConfiguration.getString("sessionID"),
604+
aiaConfiguration.getString("userID"));
605+
AiAssistant aiAssistant = AiAssistantKt.standaloneAiAssistant(reactApplicationContext, aiaConfig);
606+
Objects.requireNonNull(pdfFragment.getDocument()).setAiAssistant(aiAssistant);
607+
}
608+
} catch (Exception e) {
609+
Log.w(TAG, "Failed to set AIA Configuration: " + e.getMessage());
610+
}
611+
}
588612
pdfUiFragmentGetter.onNext(Collections.singletonList(pdfFragment));
589613
}
590614

android/src/main/java/com/pspdfkit/views/PdfViewDocumentListener.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,17 @@ public void onDocumentSaveCancelled(PdfDocument pdfDocument) {
102102
@SuppressLint("CheckResult")
103103
@Override
104104
public boolean onPageClick(@NonNull PdfDocument pdfDocument, int pageIndex, @Nullable MotionEvent motionEvent, @Nullable PointF pointF, @Nullable Annotation annotation) {
105+
String documentID = pdfDocument.getDocumentIdString();
105106
if (NutrientNotificationCenter.INSTANCE.getIsNotificationCenterInUse()) {
106-
String documentID = pdfDocument.getDocumentIdString();
107107
NutrientNotificationCenter.INSTANCE.didTapDocument(pointF, documentID);
108-
if (annotation != null) {
108+
}
109+
if (annotation != null) {
110+
if (NutrientNotificationCenter.INSTANCE.getIsNotificationCenterInUse()) {
109111
NutrientNotificationCenter.INSTANCE.didTapAnnotation(annotation, pointF, documentID);
110-
eventDispatcher.dispatchEvent(new PdfViewAnnotationTappedEvent(parent.getId(), annotation));
111112
}
113+
eventDispatcher.dispatchEvent(new PdfViewAnnotationTappedEvent(parent.getId(), annotation));
112114
}
115+
113116
return false;
114117
}
115118

android/src/main/java/com/pspdfkit/views/ReactPdfUiFragment.java

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313

1414
package com.pspdfkit.views;
1515

16-
import android.content.Context;
1716
import android.content.res.TypedArray;
1817
import android.graphics.drawable.Drawable;
1918
import android.os.Bundle;
2019
import android.view.Menu;
2120
import android.view.MenuInflater;
2221
import android.view.MenuItem;
22+
import android.view.View;
2323

2424
import androidx.annotation.NonNull;
2525
import androidx.annotation.Nullable;
@@ -39,6 +39,7 @@
3939
import java.util.Arrays;
4040
import java.util.HashMap;
4141
import java.util.List;
42+
import java.util.Map;
4243

4344
/**
4445
* This {@link PdfUiFragment} provides additional callbacks to improve integration into react native.
@@ -59,6 +60,24 @@ public class ReactPdfUiFragment extends PdfUiFragment {
5960
private static final String TOOLBAR_ITEM_OUTLINE = "outlineButtonItem";
6061
private static final String TOOLBAR_ITEM_DOCUMENT_INFO_VIEW = "documentInfoViewButtonItem";
6162

63+
// Static map to store configurations
64+
private static final Map<String, ToolbarConfig> configMap = new HashMap<>();
65+
66+
// Inner class to hold all three properties together
67+
private static class ToolbarConfig {
68+
final ArrayList<String> stockToolbarItems;
69+
final ArrayList<HashMap> customToolbarItems;
70+
final MenuItemListener menuItemListener;
71+
72+
ToolbarConfig(ArrayList<String> stockToolbarItems,
73+
ArrayList<HashMap> customToolbarItems,
74+
MenuItemListener menuItemListener) {
75+
this.stockToolbarItems = new ArrayList<>(stockToolbarItems);
76+
this.customToolbarItems = new ArrayList<>(customToolbarItems);
77+
this.menuItemListener = menuItemListener;
78+
}
79+
}
80+
6281
private ArrayList<String> stockToolbarItems = new ArrayList<>();
6382
private ArrayList<HashMap> customToolbarItems = new ArrayList<>();
6483
private MenuItemListener menuItemListener;
@@ -75,6 +94,8 @@ public class ReactPdfUiFragment extends PdfUiFragment {
7594

7695
@Nullable private ReactPdfUiFragmentListener reactPdfUiFragmentListener;
7796

97+
@Nullable private int configSaveId;
98+
7899
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks = new FragmentManager.FragmentLifecycleCallbacks() {
79100
@Override
80101
public void onFragmentCreated(@NonNull FragmentManager fm, @NonNull Fragment f, @Nullable Bundle savedInstanceState) {
@@ -140,13 +161,36 @@ void setCustomToolbarItems(@NonNull ArrayList<String> stockToolbarItems, @NonNul
140161
this.stockToolbarItems = stockToolbarItems;
141162
this.customToolbarItems = customToolbarItems;
142163
this.menuItemListener = listener;
164+
165+
int fragmentId = this.configSaveId;
166+
167+
if (fragmentId != View.NO_ID) {
168+
configMap.put(String.valueOf(fragmentId), new ToolbarConfig(stockToolbarItems, customToolbarItems, listener)
169+
);
170+
}
143171
}
144172

145173
@Override
146-
public List<Integer> onGenerateMenuItemIds(@NonNull List<Integer> menuItems) {
174+
public void onCreate(@Nullable Bundle savedInstanceState) {
175+
super.onCreate(savedInstanceState);
176+
this.configSaveId = getId();
177+
178+
int viewId = getId();
179+
if (viewId != View.NO_ID) {
180+
ToolbarConfig config = configMap.get(String.valueOf(viewId));
181+
if (config != null) {
182+
this.stockToolbarItems = new ArrayList<>(config.stockToolbarItems);
183+
this.customToolbarItems = new ArrayList<>(config.customToolbarItems);
184+
this.menuItemListener = config.menuItemListener;
185+
}
186+
}
187+
}
147188

189+
@NonNull
190+
@Override
191+
public List<Integer> onGenerateMenuItemIds(@NonNull List<Integer> menuItems) {
148192
// No items should be removed / added
149-
if (this.stockToolbarItems.size() == 0 && this.customToolbarItems.size() == 0) {
193+
if (this.stockToolbarItems.isEmpty() && this.customToolbarItems.isEmpty()) {
150194
return menuItems;
151195
}
152196

@@ -198,10 +242,7 @@ public List<Integer> onGenerateMenuItemIds(@NonNull List<Integer> menuItems) {
198242
return menuItems;
199243
}
200244

201-
@Override
202-
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
203-
super.onCreateOptionsMenu(menu, inflater);
204-
245+
void setupOptionsMenu(@NonNull Menu menu) {
205246
for (HashMap item : customToolbarItems) {
206247
String customId = item.get("id").toString();
207248
String image = item.get("image").toString();
@@ -238,4 +279,16 @@ public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflat
238279
}
239280
}
240281
}
282+
283+
@Override
284+
public void onPrepareOptionsMenu(@NonNull Menu menu) {
285+
super.onPrepareOptionsMenu(menu);
286+
this.setupOptionsMenu(menu);
287+
}
288+
289+
@Override
290+
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
291+
super.onCreateOptionsMenu(menu, inflater);
292+
this.setupOptionsMenu(menu);
293+
}
241294
}

0 commit comments

Comments
 (0)