diff --git a/app/src/main/java/org/zotero/android/architecture/Defaults.kt b/app/src/main/java/org/zotero/android/architecture/Defaults.kt index b032c7c2..7cbbfc64 100644 --- a/app/src/main/java/org/zotero/android/architecture/Defaults.kt +++ b/app/src/main/java/org/zotero/android/architecture/Defaults.kt @@ -58,6 +58,8 @@ open class Defaults @Inject constructor( private val webDavScheme = "webDavScheme" private val webDavPassword = "webDavPassword" + private val needsZeroWidthOrHeightAnnotationsFix = "needsZeroWidthOrHeightAnnotationsFix2" + private val sharedPreferences: SharedPreferences by lazy { context.getSharedPreferences( sharedPrefsFile, @@ -395,6 +397,14 @@ open class Defaults @Inject constructor( return sharedPreferences.getInt(performFullSyncGuardKey, 1) } + fun needsZeroWidthOrHeightAnnotationsFix(): Boolean { + return sharedPreferences.getBoolean(needsZeroWidthOrHeightAnnotationsFix, true) + } + + fun setNeedsZeroWidthOrHeightAnnotationsFix(value: Boolean) { + sharedPreferences.edit { putBoolean(needsZeroWidthOrHeightAnnotationsFix, value) } + } + fun reset() { setUsername("") setDisplayName("") diff --git a/app/src/main/java/org/zotero/android/database/requests/FixSquareAnnotationsWithZeroWidthOrHeightDbRequest.kt b/app/src/main/java/org/zotero/android/database/requests/FixSquareAnnotationsWithZeroWidthOrHeightDbRequest.kt new file mode 100644 index 00000000..3f02c501 --- /dev/null +++ b/app/src/main/java/org/zotero/android/database/requests/FixSquareAnnotationsWithZeroWidthOrHeightDbRequest.kt @@ -0,0 +1,36 @@ +package org.zotero.android.database.requests + +import io.realm.Realm +import io.realm.kotlin.where +import org.zotero.android.database.DbRequest +import org.zotero.android.database.objects.AnnotationType +import org.zotero.android.database.objects.ItemTypes +import org.zotero.android.database.objects.RItem + +class FixSquareAnnotationsWithZeroWidthOrHeightDbRequest : DbRequest { + override val needsWrite: Boolean + get() = true + + override fun process(database: Realm) { + val targetTypes = listOf(AnnotationType.image.name) + val results = database.where() + .item(type = ItemTypes.annotation) + .deleted(false) + .`in`("annotationType", targetTypes.toTypedArray()) + .findAll() + + for (rItem in results) { + val rect = rItem.rects.getOrNull(0) ?: continue + val libraryId = rItem.libraryId ?: continue + val width = rect.maxX - rect.minX + val height = rect.maxY - rect.minY + if (width == 0.0 || height == 0.0) { + MarkObjectsAsDeletedDbRequest( + clazz = RItem::class, + keys = listOf(rItem.key), + libraryId = libraryId + ).process(database) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/zotero/android/pdf/data/AnnotationPreviewManager.kt b/app/src/main/java/org/zotero/android/pdf/data/AnnotationPreviewManager.kt index a5bc03e5..1a33b4a9 100644 --- a/app/src/main/java/org/zotero/android/pdf/data/AnnotationPreviewManager.kt +++ b/app/src/main/java/org/zotero/android/pdf/data/AnnotationPreviewManager.kt @@ -180,6 +180,10 @@ class AnnotationPreviewManager @Inject constructor( val resultScale = scaleX.coerceAtLeast(scaleY) val resultVideoViewWidth = (width / resultScale).toInt() val resultVideoViewHeight = (height / resultScale).toInt() + if (resultVideoViewWidth == 0 || resultVideoViewHeight == 0) { + throw Exception("An attempt to create an AnnotationPreview for annotation of type ${annotation.type} resulting in a zero scale dimension. width = $width, height = $height, maxSide = $maxSide, resultVideoViewWidth = $resultVideoViewWidth, resultVideoViewHeight = $resultVideoViewHeight") + } + return rawDocumentBitmap.scale(resultVideoViewWidth, resultVideoViewHeight, true) } diff --git a/app/src/main/java/org/zotero/android/pdf/reader/PdfReaderViewModel.kt b/app/src/main/java/org/zotero/android/pdf/reader/PdfReaderViewModel.kt index 3772f1f8..22d62323 100644 --- a/app/src/main/java/org/zotero/android/pdf/reader/PdfReaderViewModel.kt +++ b/app/src/main/java/org/zotero/android/pdf/reader/PdfReaderViewModel.kt @@ -916,6 +916,18 @@ class PdfReaderViewModel @Inject constructor( onAnnotationUpdatedListener = object : AnnotationProvider.OnAnnotationUpdatedListener { override fun onAnnotationCreated(annotation: Annotation) { + //Don't allow Square Annotations with zero width or height from being added. + val annotationRect = annotation.boundingBox + val width = (annotationRect.right - annotationRect.left).toInt() + val height = (annotationRect.top - annotationRect.bottom).toInt() + if (annotation.type == AnnotationType.SQUARE && (width == 0 || height == 0)) { + Timber.w("PdfReaderViewModel: Prevented an annotation of type ${annotation.type} from being created with width=$width and height=$height") + this@PdfReaderViewModel.document.annotationProvider.removeAnnotationFromPage( + annotation + ) + return + } + processAnnotationObserving(annotation, emptyList(), PdfReaderNotification.PSPDFAnnotationsAdded) } diff --git a/app/src/main/java/org/zotero/android/sync/UserControllers.kt b/app/src/main/java/org/zotero/android/sync/UserControllers.kt index c41f7d22..39c4d405 100644 --- a/app/src/main/java/org/zotero/android/sync/UserControllers.kt +++ b/app/src/main/java/org/zotero/android/sync/UserControllers.kt @@ -14,6 +14,8 @@ import org.zotero.android.architecture.coroutines.Dispatchers import org.zotero.android.attachmentdownloader.AttachmentDownloader import org.zotero.android.database.DbWrapperMain import org.zotero.android.database.requests.CleanupUnusedTags +import org.zotero.android.database.requests.FixSquareAnnotationsWithZeroWidthOrHeightDbRequest +import org.zotero.android.files.FileStore import org.zotero.android.websocket.ChangeWsResponse import timber.log.Timber import javax.inject.Inject @@ -32,6 +34,7 @@ class UserControllers @Inject constructor( private val changeWsResponseKindEventStream: ChangeWsResponseKindEventStream, private val fileDownloader: AttachmentDownloader, private val defaults: Defaults, + private val fileStore: FileStore ) { private lateinit var changeObserver: ObjectUserChangeObserver @@ -55,6 +58,14 @@ class UserControllers @Inject constructor( dbWrapperMain.realmDbStorage.perform(coordinatorAction = { coordinator -> isFirstLaunch = coordinator.perform(InitializeCustomLibrariesDbRequest()) coordinator.perform(CleanupUnusedTags()) + + if (defaults.needsZeroWidthOrHeightAnnotationsFix()) { + fileStore.annotationPreviews.deleteRecursively() + fileStore.pageThumbnails.deleteRecursively() + coordinator.perform(FixSquareAnnotationsWithZeroWidthOrHeightDbRequest()) + defaults.setNeedsZeroWidthOrHeightAnnotationsFix(false) + } + coordinator.invalidate() }) } diff --git a/buildSrc/src/main/kotlin/BuildConfig.kt b/buildSrc/src/main/kotlin/BuildConfig.kt index 60e2d7bf..c08fe14a 100644 --- a/buildSrc/src/main/kotlin/BuildConfig.kt +++ b/buildSrc/src/main/kotlin/BuildConfig.kt @@ -4,7 +4,7 @@ object BuildConfig { const val compileSdkVersion = 34 const val targetSdk = 34 - val versionCode = 125 // Must be updated on every build + val versionCode = 126 // Must be updated on every build val version = Version( major = 1, minor = 0,