Skip to content

Commit

Permalink
Merge pull request #4129 from omnivore-app/feat/android-offline-pdf
Browse files Browse the repository at this point in the history
WIP: download android PDFs for offline to internal storage
  • Loading branch information
jacksonh authored Jul 2, 2024
2 parents e90a02b + 09db09f commit 2f9e107
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ suspend fun DataService.sync(context: Context, since: String, cursor: String?, l
}

val savedItems = syncResult.items.map {
if (!saveLibraryItemContentToFile(context, it.id, it.content)) {
if (!saveLibraryItemContentToFile(context, it.id, it.contentReader, it.content, it.url)) {
return SavedItemSyncResult(
hasError = true,
errorString = "Error saving page content",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ class LibraryRepositoryImpl @Inject constructor(
}

val savedItems = syncResult.items.map {
saveLibraryItemContentToFile(context, it.id, it.content)
saveLibraryItemContentToFile(context, it.id, it.contentReader, it.content, it.url)
val savedItem = SavedItem(
savedItemId = it.id,
title = it.title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import app.omnivore.omnivore.core.database.entities.SavedItem
import app.omnivore.omnivore.core.database.entities.SavedItemLabel
import app.omnivore.omnivore.graphql.generated.GetArticleQuery
import app.omnivore.omnivore.graphql.generated.type.ContentReader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.net.URL
import java.nio.file.Files
Expand Down Expand Up @@ -67,21 +69,7 @@ suspend fun Networker.savedItem(context: Context, slug: String): SavedItemQueryR
)
}

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()
}

saveLibraryItemContentToFile(context, article.articleFields.id, article.articleFields.content)
saveLibraryItemContentToFile(context, article.articleFields.id, article.articleFields.contentReader, article.articleFields.content, article.articleFields.url)

val savedItem = SavedItem(
savedItemId = article.articleFields.id,
Expand All @@ -104,7 +92,6 @@ suspend fun Networker.savedItem(context: Context, slug: String): SavedItemQueryR
isArchived = article.articleFields.isArchived,
contentReader = article.articleFields.contentReader.rawValue,
wordsCount = article.articleFields.wordsCount,
localPDFPath = localPDFPath
)

return SavedItemQueryResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,18 @@ import java.io.FileOutputStream
import android.Manifest
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.net.Uri
import android.util.Log
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import app.omnivore.omnivore.graphql.generated.type.ContentReader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.net.URL
import java.nio.file.Files
import java.nio.file.StandardCopyOption

data class LibrarySearchQueryResponse(
val cursor: String?, val items: List<LibrarySearchItem>
Expand All @@ -43,7 +53,7 @@ suspend fun Networker.search(
val itemList = result.data?.search?.onSearchSuccess?.edges ?: listOf()

val searchItems = itemList.map {
saveLibraryItemContentToFile(context, it.node.id, it.node.content)
saveLibraryItemContentToFile(context, it.node.id, it.node.contentReader, it.node.content, it.node.url)
LibrarySearchItem(item = SavedItem(
savedItemId = it.node.id,
title = it.node.title,
Expand Down Expand Up @@ -129,14 +139,41 @@ private fun readFromInternalStorage(context: Context, fileName: String): String?
}
}

fun getUriForInternalFile(context: Context, fileName: String): Uri {
val file = File(context.filesDir, fileName)
return file.toUri()
}


fun saveLibraryItemContentToFile(context: Context, libraryItemId: String, content: String?): Boolean {
suspend fun saveLibraryItemContentToFile(context: Context, libraryItemId: String, contentReader: ContentReader, content: String?, contentUrl: String?): Boolean {
return try {
content?.let { content ->
writeToInternalStorage(context, content = content, fileName = "${libraryItemId}.html", )
return false
var localPDFPath: String? = null
if (contentReader == ContentReader.PDF) {
val localPDFPath = "${libraryItemId}.pdf"
val file = File(context.filesDir, localPDFPath)
if (file.exists()) {
// TODO: there should really be a checksum check here
Log.d("PDF", "SKIPPING DOWNLOAD FOR LOCAL PDF PATH: ${localPDFPath}")
return true
}
withContext(Dispatchers.IO) {
val urlStream: InputStream = URL(contentUrl).openStream()
context.openFileOutput(localPDFPath, Context.MODE_PRIVATE).use { outputStream ->
urlStream.use { inputStream ->
inputStream.copyTo(outputStream)
}
}
Log.d("PDF", "File written successfully to internal storage.")
}
Log.d("PDF", "DOWNLOADING PDF TO LOCAL PDF PATH: ${localPDFPath}")
true
} else {
content?.let { content ->
writeToInternalStorage(context, content = content, fileName = "${libraryItemId}.html", )
return true
}
false
}
false
} catch (e: Exception) {
e.printStackTrace()
false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package app.omnivore.omnivore.feature.reader
import android.content.Context
import android.net.Uri
import android.util.Log
import androidx.core.content.FileProvider
import androidx.core.net.toFile
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
Expand All @@ -20,6 +22,7 @@ import app.omnivore.omnivore.graphql.generated.type.CreateHighlightInput
import app.omnivore.omnivore.graphql.generated.type.MergeHighlightInput
import app.omnivore.omnivore.graphql.generated.type.UpdateHighlightInput
import app.omnivore.omnivore.core.database.entities.SavedItem
import app.omnivore.omnivore.core.network.getUriForInternalFile
import com.apollographql.apollo3.api.Optional
import com.google.gson.Gson
import com.pspdfkit.annotations.Annotation
Expand Down Expand Up @@ -57,17 +60,22 @@ class PDFReaderViewModel @Inject constructor(

fun loadItem(slug: String, context: Context) {
viewModelScope.launch {
loadItemFromDB(slug)
loadItemFromDB(slug, context)
loadItemFromNetwork(slug, context)
}
}

private suspend fun loadItemFromDB(slug: String) {


private suspend fun loadItemFromDB(slug: String, context: Context) {
withContext(Dispatchers.IO) {
val persistedItem = dataService.db.savedItemDao().getSavedItemWithLabelsAndHighlights(slug)
persistedItem?.let { item ->
item.savedItem.localPDF?.let { localPDF ->
val localFile = File(localPDF)
Log.d("PDF", " - persistedItem?.let { item -> ${item}")
Log.d("PDF", " - item.savedItem.localPDF -> ${item.savedItem.localPDF}")

val localPdf = getUriForInternalFile(context,"${item.savedItem.savedItemId}.pdf")
val localFile = localPdf.toFile()

if (localFile.exists()) {
val articleContent = ArticleContent(
Expand All @@ -82,10 +90,9 @@ class PDFReaderViewModel @Inject constructor(
PDFReaderParams(
item.savedItem,
articleContent,
Uri.fromFile(localFile)
localPdf
)
)
}
}
}
}
Expand Down

0 comments on commit 2f9e107

Please sign in to comment.