diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReader.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReader.kt index 34a8be717..61b10f493 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReader.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReader.kt @@ -103,43 +103,9 @@ class PDFReaderActivity: AppCompatActivity(), DocumentListener, TextSelectionMan addDocumentListener(modularSearchView) addDocumentListener(thumbnailBar.documentListener) isImmersive = true - - addOnAnnotationUpdatedListener(object: AnnotationProvider.OnAnnotationUpdatedListener { - override fun onAnnotationCreated(annotation: Annotation) { - if (isNewAnnotation(annotation)) { - viewModel.createHighlight(annotation, params.item.id) - } - } - - override fun onAnnotationUpdated(annotation: Annotation) { - if (!isNewAnnotation(annotation)) { return } - val highlightAnnotation = annotation as? HighlightAnnotation ?: return - viewModel.syncUpdatedAnnotationHighlight(highlightAnnotation, articleID = params.item.id) - } - - override fun onAnnotationRemoved(annotation: Annotation) { - viewModel.deleteHighlight(annotation) - } - - override fun onAnnotationZOrderChanged( - p0: Int, - p1: MutableList, - p2: MutableList - ) { - // Unimplemented - } - }) } } - // If created time is less than 2 seconds then we consider it a newly created annotation - private fun isNewAnnotation(annotation: Annotation): Boolean { - val currentTime = Calendar.getInstance().time.time - val createdTime = annotation.createdDate?.time ?: 0 - val duration = currentTime - createdTime - return duration < 2000 - } - override fun onDocumentLoaded(document: PdfDocument) { if (hasLoadedHighlights) return hasLoadedHighlights = true @@ -299,8 +265,6 @@ class PDFReaderActivity: AppCompatActivity(), DocumentListener, TextSelectionMan val textRects = p0?.textBlocks ?: return val pageIndex = p0.pageIndex pendingHighlightAnnotation = HighlightAnnotation(pageIndex, textRects) - Log.d("pdf", "created annotation: $pendingHighlightAnnotation") -// fragment.addAnnotationToPage(highlightAnnotation, false) } override fun onEnterTextSelectionMode(p0: TextSelectionController) { @@ -308,21 +272,32 @@ class PDFReaderActivity: AppCompatActivity(), DocumentListener, TextSelectionMan val pageIndex = p0.textSelection?.pageIndex ?: return pendingHighlightAnnotation = HighlightAnnotation(pageIndex, textRects) textSelectionController = p0 - Log.d("pdf", "updated annotation via mode listener: $pendingHighlightAnnotation") } override fun onExitTextSelectionMode(p0: TextSelectionController) { textSelectionController = null pendingHighlightAnnotation = null - Log.d("pdf", "destroyed pending highlight") } @SuppressLint("ResourceType") override fun onPrepareTextSelectionPopupToolbar(p0: PdfTextSelectionPopupToolbar) { - val onClickListener = PopupToolbar.OnPopupToolbarItemClickedListener { + val onClickListener = PopupToolbar.OnPopupToolbarItemClickedListener { it -> when (it.id) { 1 -> { - Log.d("pdf", "user selected highlight action") + pendingHighlightAnnotation?.let { annotation -> + val existingAnnotations = fragment.document?.annotationProvider?.getAnnotations(fragment.pageIndex) ?: listOf() + val overlappingAnnotations = viewModel.overlappingAnnotations(annotation, existingAnnotations) + val overlapIDs = overlappingAnnotations.mapNotNull { viewModel.pluckHighlightID(it) } + + for (overlappingAnnotation in overlappingAnnotations) { + fragment.document?.annotationProvider?.removeAnnotationFromPage(overlappingAnnotation) + } + + fragment.addAnnotationToPage(annotation, false) { + viewModel.syncHighlightUpdates(annotation, overlapIDs) + } + } + textSelectionController?.textSelection = null p0.dismiss() return@OnPopupToolbarItemClickedListener true diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReaderViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReaderViewModel.kt index 0fa7387e1..597523414 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReaderViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReaderViewModel.kt @@ -41,8 +41,6 @@ class PDFReaderViewModel @Inject constructor( private val networker: Networker ): ViewModel() { val pdfReaderParamsLiveData = MutableLiveData(null) - var annotations: List = listOf() - var documentHighlights: MutableList = mutableListOf() fun loadItem(slug: String, context: Context) { viewModelScope.launch { @@ -62,8 +60,6 @@ class PDFReaderViewModel @Inject constructor( } override fun onComplete(output: File) { - documentHighlights.addAll(0, articleQueryResult.highlights) - val articleContent = ArticleContent( title = article.title, htmlContent = article.content ?: "", @@ -87,26 +83,40 @@ class PDFReaderViewModel @Inject constructor( pdfReaderParamsLiveData.postValue(null) } - fun createHighlight(annotation: Annotation, articleID: String) { + fun syncHighlightUpdates(newAnnotation: Annotation, overlapIds: List) { + val itemID = pdfReaderParamsLiveData.value?.item?.id ?: return val highlightID = UUID.randomUUID().toString() val shortID = UUID.randomUUID().toString().replace("-","").substring(0,8) - val quote = annotation.contents ?: "" + val quote = newAnnotation.contents ?: "" - annotation.customData = createCustomData(highlightID, shortID, quote, articleID) + newAnnotation.customData = createCustomData(highlightID, shortID, quote, itemID) - val createHighlightInput = CreateHighlightInput( - annotation = Optional.presentIfNotNull(null), - articleId = articleID, - id = highlightID, - patch = annotation.toInstantJson(), - quote = quote, - shortId = shortID, - ) + if (overlapIds.isNotEmpty()) { + val input = MergeHighlightInput( + annotation = Optional.presentIfNotNull(newAnnotation.contents), + articleId = itemID, + id = highlightID, + overlapHighlightIdList = overlapIds, + patch = newAnnotation.toInstantJson(), + quote = quote, + shortId = shortID + ) - viewModelScope.launch { - val highlight = networker.createHighlight(createHighlightInput) - if (highlight != null) { - documentHighlights.add(highlight) + viewModelScope.launch { + networker.mergeHighlights(input) + } + } else { + val createHighlightInput = CreateHighlightInput( + annotation = Optional.presentIfNotNull(null), + articleId = itemID, + id = highlightID, + patch = newAnnotation.toInstantJson(), + quote = quote, + shortId = shortID, + ) + + viewModelScope.launch { + networker.createHighlight(createHighlightInput) } } } @@ -127,46 +137,6 @@ class PDFReaderViewModel @Inject constructor( .put("omnivoreHighlight", jsonValues) } - fun syncUpdatedAnnotationHighlight(annotation: HighlightAnnotation, articleID: String) { - val overlapList = overlappingHighlightIDs(annotation) - val highlightID = pluckHighlightID(annotation) ?: return - val shortID = pluckShortID(annotation) ?: return - - Log.d("Network", "overlaps: $overlapList") - - if (overlapList.isNotEmpty()) { - val quote = annotation.contents ?: "" - - annotation.customData = createCustomData( - highlightID = highlightID, - shortID = shortID, - quote = quote, - articleID = articleID - ) - - val input = MergeHighlightInput( - annotation = Optional.presentIfNotNull(annotation.contents), - articleId = articleID, - id = highlightID, - overlapHighlightIdList = overlapList, - patch = annotation.toInstantJson(), - quote = quote, - shortId = shortID - ) - - documentHighlights.removeAll { overlapList.contains(it.id) } - - viewModelScope.launch { - networker.mergeHighlights(input) - Log.d("network", "merged annotations with input: $input") - } - - return - } - -// createHighlight(annotation, articleID) - } - fun deleteHighlight(annotation: Annotation) { val highlightID = pluckHighlightID(annotation) ?: return viewModelScope.launch { @@ -175,34 +145,22 @@ class PDFReaderViewModel @Inject constructor( } } - private fun overlappingHighlightIDs(annotation: HighlightAnnotation): List { - val result: MutableList = mutableListOf() - val highlightID = pluckHighlightID(annotation) ?: return listOf() + fun overlappingAnnotations(newAnnotation: Annotation, existingAnnotations: List): List { + val result: MutableList = mutableListOf() - val pageHighlights = documentHighlights.filter { - Gson().fromJson(it.patch, HighlightPatch::class.java).pageIndex == annotation.pageIndex - } - - for (highlight in pageHighlights) { - if (highlight.id == highlightID) { - continue - } - - val rects = Gson().fromJson(highlight.patch, HighlightPatch::class.java).rects - if (hasOverlaps(annotation.rects, rects)) { - result.add(highlight.id) + for (existingAnnotation in existingAnnotations) { + if (hasOverlaps(newAnnotation, existingAnnotation)) { + result.add(existingAnnotation) } } return result } - private fun hasOverlaps(leftRects: List, rightRects: List>): Boolean { - for (leftRect in leftRects) { - for (rightRect in rightRects) { - Log.d("rect", "left: $leftRect, right: $rightRect") - val transformedRect = RectF(rightRect[0].toFloat(), rightRect[1].toFloat(), rightRect[2].toFloat(), rightRect[3].toFloat()) - if (transformedRect.intersect(leftRect)) { + private fun hasOverlaps(leftAnnotation: Annotation, rightAnnotation: Annotation): Boolean { + for (leftRect in (leftAnnotation as? HighlightAnnotation)?.rects ?: listOf()) { + for (rightRect in (rightAnnotation as? HighlightAnnotation)?.rects ?: listOf()) { + if (rightRect.intersect(leftRect)) { return true } } @@ -211,18 +169,8 @@ class PDFReaderViewModel @Inject constructor( return false } - private fun pluckHighlightID(annotation: Annotation): String? { + fun pluckHighlightID(annotation: Annotation): String? { val omnivoreHighlight = annotation.customData?.get("omnivoreHighlight") as? JSONObject return omnivoreHighlight?.get("id") as? String } - - private fun pluckShortID(annotation: Annotation): String? { - val omnivoreHighlight = annotation.customData?.get("omnivoreHighlight") as? JSONObject - return omnivoreHighlight?.get("shortId") as? String - } } - -data class HighlightPatch( - val rects: List>, - val pageIndex: Int -)