diff --git a/android/Omnivore/app/src/main/AndroidManifest.xml b/android/Omnivore/app/src/main/AndroidManifest.xml index dfdb231bc..0bbedccff 100644 --- a/android/Omnivore/app/src/main/AndroidManifest.xml +++ b/android/Omnivore/app/src/main/AndroidManifest.xml @@ -39,5 +39,15 @@ + + + + diff --git a/android/Omnivore/app/src/main/assets/test.pdf b/android/Omnivore/app/src/main/assets/test.pdf new file mode 100644 index 000000000..2887e5b76 Binary files /dev/null and b/android/Omnivore/app/src/main/assets/test.pdf differ diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt index 2a07a427b..ff2d4f155 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt @@ -16,6 +16,7 @@ import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import app.omnivore.omnivore.ui.auth.LoginViewModel import app.omnivore.omnivore.ui.home.HomeViewModel +import app.omnivore.omnivore.ui.reader.PDFReaderViewModel import app.omnivore.omnivore.ui.reader.WebReaderViewModel import app.omnivore.omnivore.ui.root.RootView import dagger.hilt.android.AndroidEntryPoint diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/models/LinkedItem.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/models/LinkedItem.kt index 0d5a75efd..960348b8b 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/models/LinkedItem.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/models/LinkedItem.kt @@ -26,4 +26,9 @@ data class LinkedItem( fun publisherDisplayName(): String? { return publisherURLString?.toUri()?.host } + + fun isPDF(): Boolean { + val hasPDFSuffix = pageURLString.endsWith("pdf") + return contentReader == "PDF" || hasPDFSuffix + } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt index ed17d858a..54e6ab667 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt @@ -1,5 +1,6 @@ package app.omnivore.omnivore.ui.home +import android.content.Intent import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -11,10 +12,12 @@ import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import app.omnivore.omnivore.Routes import app.omnivore.omnivore.models.LinkedItem +import app.omnivore.omnivore.ui.reader.PDFReaderActivity import kotlinx.coroutines.flow.distinctUntilChanged @@ -53,6 +56,7 @@ fun HomeViewContent( navController: NavHostController, modifier: Modifier ) { + val context = LocalContext.current val listState = rememberLazyListState() val linkedItems: List by homeViewModel.itemsLiveData.observeAsState(listOf()) @@ -69,7 +73,13 @@ fun HomeViewContent( LinkedItemCard( item = item, onClickHandler = { - navController.navigate("WebReader/${item.slug}") + if (item.isPDF()) { + val intent = Intent(context, PDFReaderActivity::class.java) + intent.putExtra("LINKED_ITEM_SLUG", item.slug) + context.startActivity(intent) + } else { + navController.navigate("WebReader/${item.slug}") + } } ) } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt index e07e2c09d..6fc76e977 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt @@ -24,7 +24,7 @@ class HomeViewModel @Inject constructor( private var receivedIdx = 0 // Live Data - val searchTextLiveData = MutableLiveData("") + val searchTextLiveData = MutableLiveData("") val itemsLiveData = MutableLiveData>(listOf()) fun updateSearchText(text: String) { 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 new file mode 100644 index 000000000..91a134234 --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReader.kt @@ -0,0 +1,76 @@ +package app.omnivore.omnivore.ui.reader + +import android.net.Uri +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import com.pspdfkit.configuration.activity.PdfActivityConfiguration +import com.pspdfkit.configuration.activity.UserInterfaceViewMode +import com.pspdfkit.jetpack.compose.DocumentView +import com.pspdfkit.jetpack.compose.ExperimentalPSPDFKitApi +import com.pspdfkit.jetpack.compose.rememberDocumentState +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class PDFReaderActivity: AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + val viewModel: PDFReaderViewModel by viewModels() + + super.onCreate(savedInstanceState) + + val slug = intent.getStringExtra("LINKED_ITEM_SLUG") ?: "" + + setContent { + PDFReaderLoadingContainer(slug = slug, pdfReaderViewModel = viewModel) + } + } +} + +@Composable +fun PDFReaderLoadingContainer(slug: String, pdfReaderViewModel: PDFReaderViewModel) { + val pdfReaderParams: PDFReaderParams? by pdfReaderViewModel.pdfReaderParamsLiveData.observeAsState(null) + + if (pdfReaderParams == null) { + pdfReaderViewModel.loadItem(slug = slug) + } + + if (pdfReaderParams != null) { + PDFDocumentView(urlString = pdfReaderParams!!.item.pageURLString) + } else { + // TODO: add a proper loading view + Text("Loading...") + } +} + +@OptIn(ExperimentalPSPDFKitApi::class) +@Composable +fun PDFDocumentView(urlString: String) { + val context = LocalContext.current + + val pdfActivityConfiguration = remember { + PdfActivityConfiguration + .Builder(context) + .setUserInterfaceViewMode(UserInterfaceViewMode.USER_INTERFACE_VIEW_MODE_HIDDEN) + .build() + } + + val pdfDocumentState = rememberDocumentState( + documentUri = Uri.parse("file:///android_asset/test.pdf"), +// documentUri = Uri.parse(urlString), + configuration = pdfActivityConfiguration + ) + + DocumentView( + documentState = pdfDocumentState, + modifier = Modifier.fillMaxSize() + ) +} 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 new file mode 100644 index 000000000..b5c1370ad --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/PDFReaderViewModel.kt @@ -0,0 +1,49 @@ +package app.omnivore.omnivore.ui.reader + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import app.omnivore.omnivore.DatastoreRepository +import app.omnivore.omnivore.models.LinkedItem +import app.omnivore.omnivore.networking.Networker +import app.omnivore.omnivore.networking.linkedItem +import com.google.gson.Gson +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +data class PDFReaderParams( + val item: LinkedItem, + val articleContent: ArticleContent +) + +@HiltViewModel +class PDFReaderViewModel @Inject constructor( + private val datastoreRepo: DatastoreRepository, + private val networker: Networker +): ViewModel() { + val pdfReaderParamsLiveData = MutableLiveData(null) + + fun loadItem(slug: String) { + viewModelScope.launch { + val articleQueryResult = networker.linkedItem(slug) + + val article = articleQueryResult.item ?: return@launch + + val articleContent = ArticleContent( + title = article.title, + htmlContent = article.content ?: "", + highlightsJSONString = Gson().toJson(articleQueryResult.highlights), + contentStatus = "SUCCEEDED", + objectID = "", + labelsJSONString = Gson().toJson(articleQueryResult.labels) + ) + + pdfReaderParamsLiveData.value = PDFReaderParams(article, articleContent) + } + } + + fun reset() { + pdfReaderParamsLiveData.value = null + } +} diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReader.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReader.kt index c141a78ef..20e3e0439 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReader.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReader.kt @@ -17,9 +17,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.viewinterop.AndroidView import app.omnivore.omnivore.R -import app.omnivore.omnivore.networking.ReadingProgressParams import com.google.gson.Gson -import org.json.JSONObject @Composable diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/root/RootView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/root/RootView.kt index 60619015c..c669be8b6 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/root/RootView.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/root/RootView.kt @@ -18,10 +18,7 @@ import app.omnivore.omnivore.ui.auth.LoginViewModel import app.omnivore.omnivore.ui.auth.WelcomeScreen import app.omnivore.omnivore.ui.home.HomeView import app.omnivore.omnivore.ui.home.HomeViewModel -import app.omnivore.omnivore.ui.reader.ArticleWebView -import app.omnivore.omnivore.ui.reader.WebReader -import app.omnivore.omnivore.ui.reader.WebReaderLoadingContainer -import app.omnivore.omnivore.ui.reader.WebReaderViewModel +import app.omnivore.omnivore.ui.reader.* import com.google.accompanist.systemuicontroller.rememberSystemUiController @Composable