Skip to content

Commit 2d1025a

Browse files
author
PSPDFKit
committed
Release 4.4.0
1 parent ac08358 commit 2d1025a

Some content is hidden

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

43 files changed

+1544
-301
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 4.4.0 - 10 Jun 2025
2+
3+
- Adds AI Assistant support. (J#HYB-742)
4+
- Adds `addWebEventListener` and `removeWebEventListener` APIs to `PspdfkitWidgetController` for Flutter Web. (J#HYB-760)
5+
- Fixes missing resource ID issue with Android Gradle Plugin (AGP) version 8.9.2. (J#HYB-808)
6+
- Fixes issue where PDF viewer fails to load when the mounting container has zero width on Flutter Web. (J#HYB-812)
7+
18
## 4.3.0 - 11 Apr 2025
29

310
- Adds setting custom stamp items support. Only custom text stamps are supported, and this is available only on iOS and Android for now. (J#HYB-715)

android/build.gradle

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ buildscript {
1919
}
2020
}
2121

22-
2322
group 'com.pspdfkit.flutter.pspdfkit'
2423
version pspdfkitFlutterVersion
2524

@@ -65,10 +64,17 @@ dependencies {
6564
implementation "io.nutrient:$pspdfkitMavenModuleName:$pspdfkitVersion"
6665
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
6766
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
68-
implementation "androidx.compose.material:material:1.7.8"
69-
implementation "androidx.compose.material:material:1.7.8"
67+
implementation "androidx.compose.material:material:1.8.1"
7068
implementation "androidx.constraintlayout:constraintlayout:2.2.1"
7169
implementation "androidx.constraintlayout:constraintlayout-compose:1.1.1"
72-
implementation "androidx.compose.foundation:foundation:1.7.8"
73-
implementation "androidx.compose.ui:ui:1.7.8"
70+
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.9.0") // Use the latest stable version
71+
implementation "androidx.compose.foundation:foundation:1.8.1"
72+
implementation "androidx.compose.ui:ui:1.8.1"
73+
implementation("io.noties.markwon:core:4.6.2")
74+
implementation("io.noties.markwon:html:4.6.2")
75+
implementation("io.noties.markwon:linkify:4.6.2")
76+
implementation("io.noties.markwon:ext-tables:4.6.2")
77+
implementation("io.noties.markwon:ext-strikethrough:4.6.2")
78+
implementation("io.socket:socket.io-client:2.1.1")
79+
implementation("com.squareup.okhttp3:logging-interceptor:4.9.3") // Replace 4.x.x with the latest stable version
7480
}

android/config.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,5 @@ ext.pspdfkitFlutterVersion = pubspecYaml.version
5555
ext.androidCompileSdkVersion = 35
5656
ext.androidMinSdkVersion = 21
5757
ext.androidTargetSdkVersion = 35
58-
ext.androidGradlePluginVersion = '8.7.3'
58+
ext.androidGradlePluginVersion = '8.9.2'
5959
ext.kotlinVersion = "1.9.20"

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ class ConfigurationAdapter {
204204
private static final String MINIMUM_ZOOM_SCALE = "minimumZoomScale";
205205
private static final String DEFAULT_ZOOM_SCALE = "defaultZoomScale";
206206

207+
private static final String ENABLE_AI_ASSISTANT = "enableAiAssistant";
208+
207209
private static final Map<String, SignatureSavingStrategy> signatureSavingStrategyMap = new HashMap<>(){{
208210
put("alwaysSave", SignatureSavingStrategy.ALWAYS_SAVE);
209211
put("saveIfSelected", SignatureSavingStrategy.SAVE_IF_SELECTED);
@@ -426,6 +428,10 @@ class ConfigurationAdapter {
426428
if (key !=null){
427429
configureSignatureCreationConfiguration((HashMap<String, Object>) configurationMap.get(key));
428430
}
431+
key = getKeyOfType(configurationMap, ENABLE_AI_ASSISTANT, Boolean.class);
432+
if (key != null) {
433+
configureAiAssistant((Boolean) configurationMap.get(key));
434+
}
429435
}
430436
}
431437

@@ -884,6 +890,10 @@ private void configureMeasurementToolSnappingEnabled(Context context,Boolean aBo
884890
PSPDFKitPreferences.get(context).setMeasurementSnappingEnabled(aBoolean);
885891
}
886892

893+
private void configureAiAssistant(Boolean aBoolean) {
894+
configuration.setAiAssistantEnabled(aBoolean);
895+
}
896+
887897
private <T> boolean containsKeyOfType(@NonNull HashMap<String, Object> configurationMap,
888898
@NonNull String key,
889899
@NonNull Class<T> clazz) {

android/src/main/java/com/pspdfkit/flutter/pspdfkit/FlutterPdfUiFragment.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
package com.pspdfkit.flutter.pspdfkit
1111

1212
import android.content.Context
13-
import android.graphics.Color
1413
import android.graphics.drawable.Drawable
15-
import android.graphics.drawable.Icon
1614
import android.os.Bundle
1715
import android.util.Log
1816
import android.view.LayoutInflater
@@ -25,13 +23,14 @@ import androidx.appcompat.app.AppCompatActivity
2523
import androidx.appcompat.widget.Toolbar
2624
import androidx.core.content.ContextCompat
2725
import androidx.core.graphics.drawable.DrawableCompat
26+
import androidx.core.graphics.toColorInt
2827
import androidx.core.view.MenuHost
2928
import androidx.core.view.MenuProvider
3029
import androidx.lifecycle.Lifecycle
3130
import com.pspdfkit.document.PdfDocument
3231
import com.pspdfkit.flutter.pspdfkit.api.CustomToolbarCallbacks
3332
import com.pspdfkit.ui.PdfUiFragment
34-
33+
import com.pspdfkit.R
3534

3635
class FlutterPdfUiFragment : PdfUiFragment(), MenuProvider {
3736

@@ -40,7 +39,6 @@ class FlutterPdfUiFragment : PdfUiFragment(), MenuProvider {
4039
private var customToolbarCallbacks: CustomToolbarCallbacks? = null
4140
private var customToolbarItems: List<Map<String, Any>>? = null
4241

43-
4442
override fun onCreate(savedInstanceState: Bundle?) {
4543
super.onCreate(savedInstanceState)
4644

@@ -52,7 +50,6 @@ class FlutterPdfUiFragment : PdfUiFragment(), MenuProvider {
5250
}
5351
}
5452

55-
5653
override fun onCreateView(
5754
inflater: LayoutInflater,
5855
container: ViewGroup?,
@@ -227,7 +224,7 @@ class FlutterPdfUiFragment : PdfUiFragment(), MenuProvider {
227224
// Apply tint if specified
228225
if (drawable != null && iconColorHex != null) {
229226
try {
230-
val color = Color.parseColor(iconColorHex)
227+
val color = iconColorHex.toColorInt()
231228
DrawableCompat.setTint(drawable, color)
232229
Log.d(
233230
"FlutterPdfUiFragment",

android/src/main/java/com/pspdfkit/flutter/pspdfkit/FlutterPdfUiFragmentCallbacks.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.pspdfkit.listeners.DocumentListener
3131
import com.pspdfkit.ui.PdfFragment
3232
import io.flutter.plugin.common.BinaryMessenger
3333
import io.flutter.plugin.common.MethodChannel
34+
import io.nutrient.domain.ai.AiAssistant
3435

3536
private const val LOG_TAG = "FlutterPdfUiCallbacks"
3637

@@ -47,7 +48,9 @@ class FlutterPdfUiFragmentCallbacks(
4748
private val methodChannel: MethodChannel,
4849
private val measurementConfigurations: List<Map<String, Any>>?,
4950
private val binaryMessenger: BinaryMessenger,
50-
private val flutterWidgetCallback: FlutterWidgetCallback
51+
private val flutterWidgetCallback: FlutterWidgetCallback,
52+
private val aiAssistant: AiAssistant?
53+
5154
) : FragmentManager.FragmentLifecycleCallbacks(), DocumentListener {
5255

5356
private var pdfFragment: PdfFragment? = null
@@ -111,6 +114,14 @@ class FlutterPdfUiFragmentCallbacks(
111114
} catch (e: Exception) {
112115
Log.e(LOG_TAG, "Error sending direct event notification", e)
113116
}
117+
118+
aiAssistant?.let {
119+
try {
120+
document.setAiAssistant(it)
121+
} catch (e: Exception) {
122+
Log.e(LOG_TAG, "Error setting AiAssistant on loaded document", e)
123+
}
124+
}
114125
}
115126

116127
override fun onPageChanged(document: PdfDocument, pageIndex: Int) {

android/src/main/java/com/pspdfkit/flutter/pspdfkit/PSPDFKitView.kt

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ package com.pspdfkit.flutter.pspdfkit
1111
import android.content.Context
1212
import android.content.ContextWrapper
1313
import android.content.MutableContextWrapper
14-
import android.net.Uri
1514
import android.util.Log
1615
import android.view.View
16+
import androidx.core.net.toUri
1717
import androidx.fragment.app.Fragment
1818
import androidx.fragment.app.FragmentActivity
1919
import androidx.fragment.app.FragmentContainerView
@@ -39,6 +39,9 @@ import io.flutter.plugin.common.MethodChannel
3939
import io.flutter.plugin.common.StandardMessageCodec
4040
import io.flutter.plugin.platform.PlatformView
4141
import io.flutter.plugin.platform.PlatformViewFactory
42+
import io.nutrient.data.models.AiAssistantConfiguration
43+
import io.nutrient.domain.ai.AiAssistant
44+
import io.nutrient.domain.ai.standaloneAiAssistant
4245

4346
internal class PSPDFKitView(
4447
val context: Context,
@@ -59,6 +62,7 @@ internal class PSPDFKitView(
5962
private val customToolbarCallbacks: CustomToolbarCallbacks = CustomToolbarCallbacks(messenger, "customToolbar.callbacks.$id")
6063
private var isFragmentAttached = false
6164
private var methodCallHandler: PSPDFKitWidgetMethodCallHandler? = null
65+
private var aiAssistant: AiAssistant? = null
6266

6367
init {
6468
fragmentContainerView?.id = View.generateViewId()
@@ -70,6 +74,7 @@ internal class PSPDFKitView(
7074
val toolbarGroupingItems: List<Any>? = configurationMap?.get("toolbarItemGrouping") as List<Any>?
7175
val measurementValueConfigurations =
7276
configurationMap?.get("measurementValueConfigurations") as List<Map<String, Any>>?
77+
val aiAssistantConfigurationMap = configurationMap?.get("aiAssistant") as Map<String, Any>?
7378

7479
try {
7580
//noinspection pspdfkit-experimental
@@ -79,17 +84,17 @@ internal class PSPDFKitView(
7984
FlutterPdfUiFragment::class.java
8085
).configuration(pdfConfiguration).build()
8186
} else {
82-
val uri = Uri.parse(addFileSchemeIfMissing(documentPath))
87+
val uri = addFileSchemeIfMissing(documentPath).toUri()
8388
Log.d(LOG_TAG, "Loading document from URI: $uri")
84-
89+
8590
// Validate that the URI is accessible
8691
try {
8792
context.contentResolver.openInputStream(uri)?.close()
8893
} catch (e: Exception) {
8994
Log.w(LOG_TAG, "Document URI may not be accessible: $uri", e)
9095
// Continue anyway as PSPDFKit might handle this differently
9196
}
92-
97+
9398
val isImageDocument = isImageDocument(documentPath)
9499
if (isImageDocument) {
95100
Log.d(LOG_TAG, "Initializing PdfUiFragment with image document")
@@ -105,10 +110,10 @@ internal class PSPDFKitView(
105110
.build()
106111
}
107112
}
108-
Log.d(LOG_TAG, "PdfUiFragment initialized successfully")
113+
setupAiAssistant(context, aiAssistantConfigurationMap)
109114

110115
fragmentCallbacks = FlutterPdfUiFragmentCallbacks(methodChannel, measurementValueConfigurations,
111-
messenger,FlutterWidgetCallback(widgetCallbacks))
116+
messenger,FlutterWidgetCallback(widgetCallbacks),aiAssistant)
112117

113118
fragmentCallbacks?.let { callbacks ->
114119
getFragmentActivity(context).supportFragmentManager.registerFragmentLifecycleCallbacks(callbacks, true)
@@ -158,7 +163,7 @@ internal class PSPDFKitView(
158163
}
159164
}, true)
160165

161-
fragmentContainerView?.let { it ->
166+
fragmentContainerView?.let {
162167
it.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
163168
override fun onViewAttachedToWindow(view: View) {
164169
try {
@@ -208,7 +213,7 @@ internal class PSPDFKitView(
208213
if (isFragmentAttached) {
209214
try {
210215
val fragmentActivity = getFragmentActivity(context)
211-
if (!fragmentActivity.isFinishing && !fragmentActivity.isDestroyed
216+
if (!fragmentActivity.isFinishing && !fragmentActivity.isDestroyed
212217
&& fragmentActivity.supportFragmentManager.isDestroyed.not()) {
213218
fragmentActivity.supportFragmentManager.commit {
214219
pdfUiFragment.let { if (it.isAdded) remove(it) }
@@ -220,11 +225,11 @@ internal class PSPDFKitView(
220225
}
221226
isFragmentAttached = false
222227
}
223-
228+
224229
// Cleanup other resources
225230
pspdfkitViewImpl.setPdfFragment(null)
226231
pspdfkitViewImpl.dispose()
227-
232+
228233
// Unregister callbacks and listeners
229234
fragmentCallbacks?.let {
230235
try {
@@ -233,14 +238,14 @@ internal class PSPDFKitView(
233238
Log.e(LOG_TAG, "Error unregistering fragment lifecycle callbacks", e)
234239
}
235240
}
236-
241+
237242
// Null out references
238243
fragmentCallbacks = null
239244
fragmentContainerView = null
240-
245+
241246
// Unregister method channel
242247
PspdfkitWidgetControllerApi.setUp(messenger, null, id.toString())
243-
248+
244249
Log.d(LOG_TAG, "PSPDFKitView disposed successfully")
245250
} catch (e: Exception) {
246251
Log.e(LOG_TAG, "Error during PSPDFKitView disposal", e)
@@ -302,6 +307,29 @@ internal class PSPDFKitView(
302307
}
303308
}
304309

310+
private fun setupAiAssistant(
311+
context: Context,
312+
configuration: Map<String, Any>?
313+
) {
314+
// Initialize the AiAssistant with the provided parameters
315+
val serverUrl = configuration?.get("serverUrl") as String?
316+
val jwt = configuration?.get("jwt") as String?
317+
val sessionId = configuration?.get("sessionId") as String?
318+
val userId = configuration?.get("userId") as String?
319+
320+
if (serverUrl != null && jwt != null) {
321+
val aiAssistantConfiguration = AiAssistantConfiguration(
322+
serverUrl = serverUrl,
323+
jwt = jwt ,
324+
sessionId = sessionId?: "",
325+
userId = userId ?: ""
326+
)
327+
aiAssistant = standaloneAiAssistant(context, aiAssistantConfiguration)
328+
} else {
329+
Log.e(LOG_TAG, "Invalid AI Assistant configuration")
330+
}
331+
}
332+
305333
companion object {
306334
private const val LOG_TAG = "PSPDFKitPlugin"
307335
}

android/src/main/java/com/pspdfkit/flutter/pspdfkit/document/FlutterPdfDocument.kt

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99

1010
package com.pspdfkit.flutter.pspdfkit.document
1111

12-
import android.util.Log
13-
import com.pspdfkit.annotations.Annotation
1412
import com.pspdfkit.document.PdfDocument
1513
import com.pspdfkit.document.formatters.DocumentJsonFormatter
1614
import com.pspdfkit.document.formatters.XfdfFormatter
@@ -37,6 +35,9 @@ import java.io.File
3735
import java.io.FileOutputStream
3836
import java.nio.charset.StandardCharsets
3937
import java.util.EnumSet
38+
import android.util.Log
39+
import com.pspdfkit.annotations.Annotation
40+
import kotlinx.serialization.json.Json
4041

4142
class FlutterPdfDocument(
4243
private val pdfDocument: PdfDocument
@@ -375,8 +376,7 @@ class FlutterPdfDocument(
375376

376377
override fun removeAnnotation(jsonAnnotation: String, callback: (Result<Boolean?>) -> Unit) {
377378
try {
378-
val annotationObject = JSONObject(jsonAnnotation).toMap()
379-
379+
val annotationObject = Json.decodeFromString<Map<String,Any>>(jsonAnnotation)
380380
// Get name or UUID
381381
val name = annotationObject["name"] as? String?
382382
val uuid = annotationObject["id"] as? String?
@@ -597,20 +597,4 @@ class FlutterPdfDocument(
597597
pdfVersionMap[options.pdfVersion]
598598
)
599599
}
600-
}
601-
602-
fun JSONObject.toMap(): Map<String, Any> {
603-
val map = mutableMapOf<String, Any>()
604-
val keys = this.keys()
605-
while (keys.hasNext()) {
606-
val key = keys.next()
607-
var value = this.get(key)
608-
609-
if (value is JSONObject) {
610-
value = value.toMap()
611-
}
612-
613-
map[key] = value
614-
}
615-
return map
616-
}
600+
}

android/src/main/java/com/pspdfkit/flutter/pspdfkit/util/Utilities.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package com.pspdfkit.flutter.pspdfkit.util
44
import android.net.Uri
55
import java.lang.NumberFormatException
66
import java.util.Locale
7+
import androidx.core.net.toUri
78

89
private const val FILE_SCHEME = "file:///"
910

@@ -14,7 +15,7 @@ private const val FILE_SCHEME = "file:///"
1415
*/
1516
fun addFileSchemeIfMissing(documentPath: String): String {
1617
var result = documentPath
17-
if (Uri.parse(result).scheme == null) {
18+
if (result.toUri().scheme == null) {
1819
if (result.startsWith("/")) {
1920
result = documentPath.substring(1)
2021
}

0 commit comments

Comments
 (0)