From 6bce5b5e19f55926aee67d977a3bae084208c72c Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 24 Oct 2023 12:36:54 +0800 Subject: [PATCH] Improve PDF downloading on Android --- android/Omnivore/app/build.gradle | 4 +- .../omnivore/networking/SavedItemQuery.kt | 22 +++++- .../omnivore/persistence/AppDatabase.kt | 2 +- .../persistence/entities/SavedItem.kt | 3 +- .../omnivore/ui/reader/PDFReaderViewModel.kt | 77 +++++++++++++------ .../omnivore/ui/reader/WebReaderContent.kt | 2 +- 6 files changed, 80 insertions(+), 30 deletions(-) diff --git a/android/Omnivore/app/build.gradle b/android/Omnivore/app/build.gradle index 1041a5ec9..beeda4ff1 100644 --- a/android/Omnivore/app/build.gradle +++ b/android/Omnivore/app/build.gradle @@ -17,8 +17,8 @@ android { applicationId "app.omnivore.omnivore" minSdk 26 targetSdk 33 - versionCode 118 - versionName "0.0.118" + versionCode 122 + versionName "0.0.122" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/SavedItemQuery.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/SavedItemQuery.kt index 2fd8e9298..cd2f50e35 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/SavedItemQuery.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/networking/SavedItemQuery.kt @@ -1,9 +1,15 @@ package app.omnivore.omnivore.networking +import android.util.Log import app.omnivore.omnivore.graphql.generated.GetArticleQuery +import app.omnivore.omnivore.graphql.generated.type.ContentReader import app.omnivore.omnivore.persistence.entities.SavedItem import app.omnivore.omnivore.persistence.entities.SavedItemLabel import app.omnivore.omnivore.persistence.entities.Highlight +import java.io.File +import java.net.URL +import java.nio.file.Files +import java.nio.file.StandardCopyOption data class SavedItemQueryResponse( val item: SavedItem?, @@ -60,7 +66,18 @@ suspend fun Networker.savedItem(slug: String): SavedItemQueryResponse { ) } - // TODO: handle errors + var localPDFPath: String? = null + if (article.articleFields.contentReader == ContentReader.PDF) { + // download the PDF and save it locally + // article.articleFields.url + + val localFile = File.createTempFile("pdf-" + article.articleFields.id, ".pdf", ) + val url = URL(article.articleFields.url) + Log.d("pdf", "creating local file: $localFile") + + url.openStream().use { Files.copy(it, localFile.toPath(), StandardCopyOption.REPLACE_EXISTING) } + localPDFPath = localFile.toPath().toString() + } val savedItem = SavedItem( savedItemId = article.articleFields.id, @@ -82,7 +99,8 @@ suspend fun Networker.savedItem(slug: String): SavedItemQueryResponse { isArchived = article.articleFields.isArchived, contentReader = article.articleFields.contentReader.rawValue, content = article.articleFields.content, - wordsCount = article.articleFields.wordsCount + wordsCount = article.articleFields.wordsCount, + localPDFPath = localPDFPath ) return SavedItemQueryResponse(item = savedItem, highlights, labels = savedItemLabels, state = article.articleFields.state?.rawValue ?: "") diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/AppDatabase.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/AppDatabase.kt index 07202993f..bf98747fd 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/AppDatabase.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/AppDatabase.kt @@ -13,7 +13,7 @@ import app.omnivore.omnivore.persistence.entities.* SavedItemAndSavedItemLabelCrossRef::class, SavedItemAndHighlightCrossRef::class ], - version = 11 + version = 12 ) abstract class AppDatabase : RoomDatabase() { abstract fun viewerDao(): ViewerDao diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt index 863ebe7ae..7dbd1dfd0 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt @@ -40,7 +40,8 @@ data class SavedItem( @ColumnInfo(typeAffinity = ColumnInfo.BLOB) val pdfData: ByteArray? = null, var serverSyncStatus: Int = 0, val tempPDFURL: String? = null, - val wordsCount: Int? = null + val wordsCount: Int? = null, + val localPDFPath: String? = null // hasMany highlights // hasMany labels 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 056fa559c..e674b89ac 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,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import app.omnivore.omnivore.DatastoreRepository -import app.omnivore.omnivore.EventTracker +import app.omnivore.omnivore.dataService.DataService import app.omnivore.omnivore.graphql.generated.type.CreateHighlightInput import app.omnivore.omnivore.graphql.generated.type.MergeHighlightInput import app.omnivore.omnivore.graphql.generated.type.UpdateHighlightInput @@ -21,11 +21,16 @@ import com.pspdfkit.document.download.DownloadJob import com.pspdfkit.document.download.DownloadRequest import com.pspdfkit.document.download.Progress import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.json.JSONObject import java.io.File import java.lang.Double.max import java.lang.Double.min +import java.lang.Exception +import java.net.URLEncoder +import java.nio.file.FileSystem import java.util.* import javax.inject.Inject @@ -38,8 +43,8 @@ data class PDFReaderParams( @HiltViewModel class PDFReaderViewModel @Inject constructor( private val datastoreRepo: DatastoreRepository, - private val networker: Networker, - private val eventTracker: EventTracker, + private val dataService: DataService, + private val networker: Networker ): ViewModel() { var annotationUnderNoteEdit: Annotation? = null val pdfReaderParamsLiveData = MutableLiveData(null) @@ -47,21 +52,51 @@ class PDFReaderViewModel @Inject constructor( fun loadItem(slug: String, context: Context) { viewModelScope.launch { + loadItemFromDB(slug) + loadItemFromNetwork(slug, context) + } + } + + private suspend fun loadItemFromDB(slug: String) { + withContext(Dispatchers.IO) { + val persistedItem = dataService.db.savedItemDao().getSavedItemWithLabelsAndHighlights(slug) + persistedItem?.let { persistedItem -> + persistedItem?.savedItem?.localPDF?.let { localPDF -> + val localFile = File(localPDF) + + if (localFile.exists()) { + val articleContent = ArticleContent( + title = persistedItem.savedItem.title, + htmlContent = "", + highlights = persistedItem.highlights, + contentStatus = "SUCCEEDED", + objectID = "", + labelsJSONString = Gson().toJson(persistedItem.labels) + ) + + pdfReaderParamsLiveData.postValue( + PDFReaderParams( + persistedItem.savedItem, + articleContent, + Uri.fromFile(localFile) + ) + ) + } + } + } + } + } + + private suspend fun loadItemFromNetwork(slug: String, context: Context) { + withContext(Dispatchers.IO) { val articleQueryResult = networker.savedItem(slug) - - val article = articleQueryResult.item ?: return@launch - + val article = articleQueryResult.item ?: return@withContext val request = DownloadRequest.Builder(context) .uri(article.pageURLString) .build() val job = DownloadJob.startDownload(request) - job.setProgressListener(object : DownloadJob.ProgressListenerAdapter() { - override fun onProgress(progress: Progress) { -// progressBar.setProgress((100f * progress.bytesReceived / progress.totalBytes).toInt()) - } - override fun onComplete(output: File) { val articleContent = ArticleContent( title = article.title, @@ -72,22 +107,18 @@ class PDFReaderViewModel @Inject constructor( labelsJSONString = Gson().toJson(articleQueryResult.labels) ) - val pdfReaderParams = PDFReaderParams(article, articleContent, Uri.fromFile(output)) - - eventTracker.track("link_read", - com.posthog.android.Properties() - .putValue("linkID", pdfReaderParams.item.savedItemId) - .putValue("slug", pdfReaderParams.item.slug) - .putValue("originalArticleURL", pdfReaderParams.item.pageURLString) - .putValue("loaded_from", "network") - ) - currentReadingProgress = article.readingProgress - pdfReaderParamsLiveData.postValue(pdfReaderParams) + pdfReaderParamsLiveData.postValue( + PDFReaderParams( + article, + articleContent, + Uri.fromFile(output) + ) + ) } override fun onError(exception: Throwable) { -// handleDownloadError(exception) +// handleDownloadError(exception) } }) } 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 571e423d7..e04f13591 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 @@ -91,7 +91,7 @@ data class WebReaderContent( url: `${item.pageURLString}`, title: `${articleContent.title.replace("`", "\\`")}`, content: document.getElementById('_omnivore-htmlContent').innerHTML, - originalArticleUrl: "${item.pageURLString}", + originalArticleUrl: "${item.publisherURLString}", contentReader: "WEB", readingProgressPercent: ${item.readingProgress}, readingProgressAnchorIndex: ${item.readingProgressAnchor},