From d89a8d2158bec963eaf6b962df1cbbb7d6c9cbe4 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Mon, 3 Oct 2022 13:20:44 -0700 Subject: [PATCH] load highlights when pdf is loaded --- .../app/omnivore/omnivore/models/Highlight.kt | 46 +++++++++++++- .../omnivore/omnivore/ui/reader/PDFReader.kt | 54 +++++++++++++--- .../omnivore/ui/reader/PDFReaderViewModel.kt | 62 +------------------ .../omnivore/ui/reader/WebReaderContent.kt | 12 +++- .../omnivore/ui/reader/WebReaderViewModel.kt | 2 +- 5 files changed, 102 insertions(+), 74 deletions(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/models/Highlight.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/models/Highlight.kt index f7c4fe365..f2f78ab4d 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/models/Highlight.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/models/Highlight.kt @@ -1,5 +1,14 @@ package app.omnivore.omnivore.models +import android.graphics.Color +import android.graphics.Rect +import android.graphics.RectF +import app.omnivore.omnivore.networking.CreateHighlightParams +import com.google.gson.Gson +import com.pspdfkit.annotations.AnnotationType +import com.pspdfkit.annotations.BlendMode +import com.pspdfkit.annotations.HighlightAnnotation + data class Highlight( val id: String, val shortId: String, @@ -11,4 +20,39 @@ data class Highlight( val createdAt: Any?, val updatedAt: Any?, val createdByMe : Boolean, -) +) { + fun asHighlightAnnotation(): HighlightAnnotation { + val highlightPatch = Gson().fromJson(patch, HighlightPatch::class.java) + + var highlightAnnotation = HighlightAnnotation( + highlightPatch.pageIndex, + highlightPatch.rectList() + ) + + highlightAnnotation.color = Color.parseColor(highlightPatch.color) + highlightAnnotation.boundingBox = highlightPatch.boundingBox() + highlightAnnotation.blendMode = BlendMode.MULTIPLY + + return highlightAnnotation + + } +} + +data class HighlightPatch( + val bbox: List, + val blendMode: String, + val color: String, + val rects: List>, + val pageIndex: Int +) { + fun rectList(): List { + return rects.map { + RectF(it[0], it[1], it[2], it[3]) + } + } + + fun boundingBox(): RectF { + return RectF(bbox[0], bbox[1], bbox[2], bbox[3]) + } +} + 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 a93ba11ec..64dee631b 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 @@ -2,17 +2,24 @@ package app.omnivore.omnivore.ui.reader import android.net.Uri import android.os.Bundle +import android.util.Log import androidx.activity.viewModels +import androidx.annotation.UiThread import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import app.omnivore.omnivore.R +import com.google.gson.Gson +import com.pspdfkit.annotations.HighlightAnnotation import com.pspdfkit.configuration.PdfConfiguration import com.pspdfkit.configuration.page.PageScrollDirection +import com.pspdfkit.document.PdfDocument +import com.pspdfkit.listeners.DocumentListener import com.pspdfkit.ui.PdfFragment import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint -class PDFReaderActivity: AppCompatActivity() { +class PDFReaderActivity: AppCompatActivity(), DocumentListener { + private var hasLoadedHighlights = false private lateinit var fragment: PdfFragment val viewModel: PDFReaderViewModel by viewModels() @@ -26,14 +33,7 @@ class PDFReaderActivity: AppCompatActivity() { // Create the observer which updates the UI. val pdfParamsObserver = Observer { params -> if (params != null) { - val configuration = PdfConfiguration.Builder() - .scrollDirection(PageScrollDirection.HORIZONTAL) - .build() - - // First, try to restore a previously created fragment. - // If no fragment exists, create a new one. - fragment = supportFragmentManager.findFragmentById(R.id.fragmentContainer) as PdfFragment? - ?: createFragment(params.localFileUri, configuration) + load(params) } } @@ -41,6 +41,42 @@ class PDFReaderActivity: AppCompatActivity() { viewModel.pdfReaderParamsLiveData.observe(this, pdfParamsObserver) } + private fun load(params: PDFReaderParams) { + val configuration = PdfConfiguration.Builder() + .scrollDirection(PageScrollDirection.HORIZONTAL) + .build() + + // First, try to restore a previously created fragment. + // If no fragment exists, create a new one. + fragment = supportFragmentManager.findFragmentById(R.id.fragmentContainer) as PdfFragment? + ?: createFragment(params.localFileUri, configuration) + + fragment.apply { + addDocumentListener(this@PDFReaderActivity) + } + } + + @UiThread + override fun onDocumentLoaded(document: PdfDocument) { + if (hasLoadedHighlights) return + + hasLoadedHighlights = true + + val params = viewModel.pdfReaderParamsLiveData.value + + params?.let { + for (highlight in it.articleContent.highlights) { + Log.d("anno", "adding highlight: ${highlight.patch}") + + val highlightAnnotation = highlight.asHighlightAnnotation() + + Log.d("anno", "created highlight annotation: $highlightAnnotation") + + fragment.addAnnotationToPage(highlightAnnotation, true) + } + } + } + private fun createFragment(documentUri: Uri, configuration: PdfConfiguration): PdfFragment { val fragment = PdfFragment.newInstance(documentUri, configuration) supportFragmentManager.beginTransaction() 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 a316eee37..fed961558 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 @@ -7,7 +7,6 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import app.omnivore.omnivore.DatastoreRepository -import app.omnivore.omnivore.models.Highlight import app.omnivore.omnivore.models.LinkedItem import app.omnivore.omnivore.networking.Networker import app.omnivore.omnivore.networking.linkedItem @@ -33,6 +32,7 @@ class PDFReaderViewModel @Inject constructor( private val networker: Networker ): ViewModel() { val pdfReaderParamsLiveData = MutableLiveData(null) + var annotations: List = listOf() fun loadItem(slug: String, context: Context) { viewModelScope.launch { @@ -57,7 +57,7 @@ class PDFReaderViewModel @Inject constructor( val articleContent = ArticleContent( title = article.title, htmlContent = article.content ?: "", - highlightsJSONString = Gson().toJson(articleQueryResult.highlights), + highlights = articleQueryResult.highlights, contentStatus = "SUCCEEDED", objectID = "", labelsJSONString = Gson().toJson(articleQueryResult.labels) @@ -76,62 +76,4 @@ class PDFReaderViewModel @Inject constructor( fun reset() { pdfReaderParamsLiveData.value = null } - -// fun makePDFAnnotations(highlights: List): List { -// for (highlight in highlights) { -// Annotation() -// } -// } - -// private func applyHighlights(documentProvider: PDFDocumentProvider) { -// viewModel.loadHighlightPatches { [weak self] highlightPatches in -// var annnotations: [Annotation] = [] -// for patch in highlightPatches { -// guard let data = patch.data(using: String.Encoding.utf8) else { continue } -// let annotation = try? Annotation(fromInstantJSON: data, documentProvider: documentProvider) -// guard let annotation = annotation else { continue } -// annnotations.append(annotation) -// } -// self?.add(annotations: annnotations) -// -// self?.highlightsApplied = true -// } -// } } - - -//2022-10-03 08:39:47.903 22564-22564/app.omnivore.omnivore D/anno: highlights: -//[ - -//Highlight( -// id=877d4e93-a525-4749-83c1-f49b7045ed32, -// shortId=B30zlzGW, -// quote= especially children, men, and strangers, before he is twelve weeks old. Well-socialized puppies grow up to be wonderful companions, whereas antisocial dogs are difficult, time-consuming, and potentially dangerous. Your puppy needs to learn to enjoy the company of all people and to enjoy being handled by all people, especially children and strangers. As a rule of thumb, your puppy needs to meet at least a hundred people before he is three months old. Since your puppy is still too young to venture out on the, -// prefix=null, -// suffix=null, -// patch={ -// "bbox": [47.035999298095703, 361.49798583984375, 303.94073486328125, 145.78799438476562], -// "blendMode": "multiply", -// "color": "#FEE832", -// "createdAt": "2022-09-30T03:33:47Z", -// "creatorName": "satindar", -// "customData": { -// "omnivoreHighlight": { -// "articleId": "c2e87313-59ca-48d2-b008-166c16afee63", -// "id": "877d4e93-a525-4749-83c1-f49b7045ed32", -// "quote": " especially children, men, and strangers, before he is twelve weeks old. Well-socialized puppies grow up to be wonderful companions, whereas antisocial dogs are difficult, time-consuming, and potentially dangerous. Your puppy needs to learn to enjoy the company of all people and to enjoy being handled by all people, especially children and strangers. As a rule of thumb, your puppy needs to meet at least a hundred people before he is three months old. Since your puppy is still too young to venture out on the", -// "shortId": "B30zlzGW" -// } -// }, -// "name": "FF1F83A7-8D38-4AAA-A882-07A0DD4BAC86", -// "opacity": 1, -// "pageIndex": 5, -// "rects": [[139.03999328613281, 361.49798583984375, 211.30220031738281, 10.788009643554688], [350.34219360351562, 369.70599365234375, 0, 0], [350.34219360351562, 369.70599365234375, 0, 0], [47.035999298095703, 376.49798583984375, 303.61203002929688, 10.788009643554688], [350.64801025390625, 384.70599365234375, 0, 0], [350.64801025390625, 384.70599365234375, 0, 0], [47.155998229980469, 391.49798583984375, 303.5977783203125, 10.788009643554688], [350.75375366210938, 399.70599365234375, 0, 0], [350.75375366210938, 399.70599365234375, 0, 0], [47.324001312255859, 406.49798583984375, 303.64541625976562, 10.788009643554688], [350.96942138671875, 414.70599365234375, 0, 0], [350.96942138671875, 414.70599365234375, 0, 0], [47.057407379150391, 421.49798583984375, 303.53900146484375, 10.787994384765625], [350.59637451171875, 429.70599365234375, 0, 0], [350.59637451171875, 429.70599365234375, 0, 0], [47.297409057617188, 436.49798583984375, 303.57977294921875, 10.787994384765625], [350.87716674804688, 444.70599365234375, 0, 0], [350.87716674804688, 444.70599365234375, 0, 0], [47.609405517578125, 451.49798583984375, 44.86920166015625, 10.787994384765625], [92.478607177734375, 459.70599365234375, 0, 0], [92.478607177734375, 459.70599365234375, 0, 0], [61.577407836914062, 466.49798583984375, 289.39935302734375, 10.787994384765625], [350.97674560546875, 474.70599365234375, 0, 0], [350.97674560546875, 474.70599365234375, 0, 0], [47.10540771484375, 481.49798583984375, 303.55093383789062, 10.787994384765625], [350.65634155273438, 489.70599365234375, 0, 0], [350.65634155273438, 489.70599365234375, 0, 0], [47.189407348632812, 496.49798583984375, 193.45796203613281, 10.787994384765625]], "type": "pspdfkit/markup/highlight", "updatedAt": "2022-09-30T03:33:47Z", "v": 1}, -// annotation=null, -// createdAt=null, -// updatedAt=2022-09-30T03:33:48.152Z, -// createdByMe=true), -// - -// -//Highlight(id=4c720330-4ea3-41d4-9b7b-031c9b495312, shortId=dYGMMMbz, quote= So, you have your new puppy. Now what? Basically, you are at a fork in the road. The success of the relationship depends on your teaching your puppy the rules and regulations of domestic living. The most critical time in your dog's life is right now—puppyhood! First impressions are indelible and long-lasting. Consequently, prefix=null, suffix=null, patch={"bbox": [46.13800048828125, 194.449951171875, 303.64797973632812, 85.78802490234375], "blendMode": "multiply", "color": "#FEE832", "createdAt": "2022-10-03T15:35:50 diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderContent.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderContent.kt index a3342fb35..599c55078 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderContent.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderContent.kt @@ -1,7 +1,9 @@ package app.omnivore.omnivore.ui.reader import android.util.Log +import app.omnivore.omnivore.models.Highlight import app.omnivore.omnivore.models.LinkedItem +import com.google.gson.Gson enum class WebFont(val displayText: String, val rawValue: String) { INTER("Inter", "Inter"), @@ -26,11 +28,15 @@ enum class ArticleContentStatus(val rawValue: String) { data class ArticleContent( val title: String, val htmlContent: String, - val highlightsJSONString: String, + val highlights: List, val contentStatus: String, // ArticleContentStatus, val objectID: String?, // whatever the Room Equivalent of objectID is val labelsJSONString: String -) +) { + fun highlightsJSONString(): String { + return Gson().toJson(highlights) + } +} data class WebReaderContent( val textFontSize: Int, @@ -86,7 +92,7 @@ data class WebReaderContent( readingProgressPercent: ${item.readingProgress}, readingProgressAnchorIndex: ${item.readingProgressAnchor}, labels: ${articleContent.labelsJSONString}, - highlights: ${articleContent.highlightsJSONString}, + highlights: ${articleContent.highlightsJSONString()}, } window.fontSize = $textFontSize diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt index cb0908ed5..e9a6f4aaf 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt @@ -39,7 +39,7 @@ class WebReaderViewModel @Inject constructor( val articleContent = ArticleContent( title = article.title, htmlContent = article.content ?: "", - highlightsJSONString = Gson().toJson(articleQueryResult.highlights), + highlights = articleQueryResult.highlights, contentStatus = "SUCCEEDED", objectID = "", labelsJSONString = Gson().toJson(articleQueryResult.labels)