mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
finally get highlight merging working right
This commit is contained in:
parent
0d32117c75
commit
c102bcfc7c
2 changed files with 54 additions and 131 deletions
|
|
@ -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<Annotation>,
|
||||
p2: MutableList<Annotation>
|
||||
) {
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -41,8 +41,6 @@ class PDFReaderViewModel @Inject constructor(
|
|||
private val networker: Networker
|
||||
): ViewModel() {
|
||||
val pdfReaderParamsLiveData = MutableLiveData<PDFReaderParams?>(null)
|
||||
var annotations: List<HighlightAnnotation> = listOf()
|
||||
var documentHighlights: MutableList<Highlight> = 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<String>) {
|
||||
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<String> {
|
||||
val result: MutableList<String> = mutableListOf()
|
||||
val highlightID = pluckHighlightID(annotation) ?: return listOf()
|
||||
fun overlappingAnnotations(newAnnotation: Annotation, existingAnnotations: List<Annotation>): List<Annotation> {
|
||||
val result: MutableList<Annotation> = 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<RectF>, rightRects: List<List<Double>>): 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<List<Double>>,
|
||||
val pageIndex: Int
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue