diff --git a/android/Omnivore/app/build.gradle b/android/Omnivore/app/build.gradle
index a8c001e18..25258b9bb 100644
--- a/android/Omnivore/app/build.gradle
+++ b/android/Omnivore/app/build.gradle
@@ -148,6 +148,8 @@ dependencies {
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
kapt "androidx.room:room-compiler:$room_version"
+
+ implementation 'com.github.jeziellago:compose-markdown:0.3.3'
}
apollo {
diff --git a/android/Omnivore/app/src/main/AndroidManifest.xml b/android/Omnivore/app/src/main/AndroidManifest.xml
index 3983989ed..a721ffdc7 100644
--- a/android/Omnivore/app/src/main/AndroidManifest.xml
+++ b/android/Omnivore/app/src/main/AndroidManifest.xml
@@ -57,5 +57,10 @@
android:exported="true"
android:theme="@style/Theme.Omnivore"/>
+
+
diff --git a/android/Omnivore/app/src/main/assets/test.pdf b/android/Omnivore/app/src/main/assets/test.pdf
deleted file mode 100644
index 2887e5b76..000000000
Binary files a/android/Omnivore/app/src/main/assets/test.pdf and /dev/null differ
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt
index 6608a30f6..01b4b9a6f 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt
@@ -7,4 +7,5 @@ sealed class Routes(val route: String) {
object Documentation: Routes("Documentation")
object PrivacyPolicy: Routes("PrivacyPolicy")
object TermsAndConditions: Routes("TermsAndConditions")
+ object Notebook: Routes("Notebook")
}
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 38cd552d6..eff79b6f0 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
@@ -162,6 +162,22 @@ interface SavedItemDao {
)
fun getLibraryLiveDataSortedByRecentlyPublished(archiveFilter: Int): LiveData>
+ @Transaction
+ @Query(
+ "SELECT ${SavedItemQueryConstants.libraryColumns} " +
+ "FROM SavedItem " +
+ "LEFT OUTER JOIN SavedItemAndSavedItemLabelCrossRef on SavedItem.savedItemId = SavedItemAndSavedItemLabelCrossRef.savedItemId " +
+ "LEFT OUTER JOIN SavedItemAndHighlightCrossRef on SavedItem.savedItemId = SavedItemAndHighlightCrossRef.savedItemId " +
+
+ "LEFT OUTER JOIN SavedItemLabel on SavedItemLabel.savedItemLabelId = SavedItemAndSavedItemLabelCrossRef.savedItemLabelId " +
+ "LEFT OUTER JOIN Highlight on highlight.highlightId = SavedItemAndHighlightCrossRef.highlightId " +
+
+ "WHERE SavedItem.savedItemId != :savedItemId " +
+
+ "GROUP BY SavedItem.savedItemId "
+ )
+ fun getLibraryItemById(savedItemId: String): LiveData
+
@Transaction
@Query(
"SELECT ${SavedItemQueryConstants.libraryColumns} " +
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/notebook/NotebookView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/notebook/NotebookView.kt
new file mode 100644
index 000000000..200688191
--- /dev/null
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/notebook/NotebookView.kt
@@ -0,0 +1,327 @@
+package app.omnivore.omnivore.ui.notebook
+
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import android.view.ContextMenu
+import android.view.View
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
+import androidx.activity.compose.setContent
+import androidx.activity.viewModels
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.LocalContentColor
+import androidx.compose.material.TopAppBar
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.rememberNavController
+import app.omnivore.omnivore.MainActivity
+import app.omnivore.omnivore.R
+import app.omnivore.omnivore.ui.components.WebReaderLabelsSelectionSheet
+import app.omnivore.omnivore.ui.savedItemViews.SavedItemContextMenu
+import app.omnivore.omnivore.ui.theme.OmnivoreTheme
+import com.google.accompanist.systemuicontroller.rememberSystemUiController
+import dagger.hilt.android.AndroidEntryPoint
+import kotlin.math.roundToInt
+import androidx.navigation.compose.rememberNavController
+import app.omnivore.omnivore.persistence.entities.SavedItemWithLabelsAndHighlights
+import app.omnivore.omnivore.ui.components.LabelChipColors
+import app.omnivore.omnivore.ui.library.SavedItemFilter
+import app.omnivore.omnivore.ui.library.SearchField
+import app.omnivore.omnivore.ui.library.SearchViewContent
+import app.omnivore.omnivore.ui.library.TypeaheadSearchViewContent
+import app.omnivore.omnivore.ui.reader.WebReaderViewModel
+import app.omnivore.omnivore.ui.theme.md_theme_dark_outline
+import dev.jeziellago.compose.markdowntext.MarkdownText
+
+
+@AndroidEntryPoint
+class NotebookActivity: ComponentActivity() {
+ val viewModel: NotebookViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val savedItemId = intent.getStringExtra("SAVED_ITEM_ID")
+
+ setContent {
+ val systemUiController = rememberSystemUiController()
+ val useDarkIcons = !isSystemInDarkTheme()
+
+ DisposableEffect(systemUiController, useDarkIcons) {
+ systemUiController.setSystemBarsColor(
+ color = Color.Black,
+ darkIcons = false
+ )
+
+ onDispose {}
+ }
+
+ OmnivoreTheme {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(color = Color.Black)
+ ) {
+ savedItemId?.let {
+ NotebookView(
+ savedItemId = savedItemId,
+ viewModel = viewModel
+ )
+ }
+ }
+ }
+ }
+ }
+
+// // animate the view up when keyboard appears
+// WindowCompat.setDecorFitsSystemWindows(window, false)
+// val rootView = findViewById(android.R.id.content).rootView
+// ViewCompat.setOnApplyWindowInsetsListener(rootView) { _, insets ->
+// val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
+// rootView.setPadding(0, 0, 0, imeHeight)
+// insets
+// }
+// }
+
+ private fun startMainActivity() {
+ val intent = Intent(this, MainActivity::class.java)
+ this.startActivity(intent)
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun NotebookView(savedItemId: String, viewModel: NotebookViewModel) {
+ val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
+ val savedItem = viewModel.getLibraryItemById(savedItemId).observeAsState()
+
+// var isMenuExpanded by remember { mutableStateOf(false) }
+// var showWebPreferencesDialog by remember { mutableStateOf(false ) }
+
+
+ val noteMarkdown = "This is some *markdown* for a note."
+
+ OmnivoreTheme() {
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = { Text("Notebook") },
+ colors = TopAppBarDefaults.topAppBarColors(
+ containerColor = MaterialTheme.colorScheme.surfaceVariant
+ ),
+ navigationIcon = {
+ IconButton(onClick = {
+ onBackPressedDispatcher?.onBackPressed()
+ }) {
+ Icon(
+ imageVector = androidx.compose.material.icons.Icons.Filled.ArrowBack,
+ modifier = Modifier,
+ contentDescription = "Back"
+ )
+ }
+ },
+ actions = {
+ IconButton(onClick = { }) {
+ Icon(
+ imageVector = Icons.Default.MoreVert,
+ contentDescription = null
+ )
+ }
+ }
+ )
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ ) {
+ savedItem.value?.let {
+ ArticleNotes(it)
+ HighlightsList(it)
+ }
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ArticleNotes(item: SavedItemWithLabelsAndHighlights) {
+ val notes = item.highlights?.filter { it.type == "NOTE" } ?: listOf()
+ val listState = rememberLazyListState()
+
+ Column(modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = 15.dp)
+ .padding(top = 40.dp, bottom = 10.dp)
+ ) {
+ Text("Article Notes")
+ Divider(modifier = Modifier.padding(bottom= 15.dp))
+ Surface(
+ modifier = Modifier
+ .padding(0.dp, end = 15.dp)
+ .fillMaxWidth(),
+ shape = androidx.compose.material.MaterialTheme.shapes.medium,
+ color = MaterialTheme.colorScheme.surfaceVariant
+ ) {
+ LazyColumn(
+ state = listState,
+ verticalArrangement = Arrangement.Top,
+ horizontalAlignment = Alignment.CenterHorizontally,
+// modifier = Modifier
+// .fillMaxSize()
+ ) {
+ items(notes) { note ->
+ MarkdownText(
+ // modifier = Modifier.padding(paddingValues),
+ markdown = note.annotation ?: "",
+ fontSize = 12.sp,
+ )
+ }
+ }
+ if (notes.isEmpty()) {
+ Text(
+ text = "Add Notes...",
+ style = androidx.compose.material.MaterialTheme.typography.subtitle2,
+ modifier = Modifier.padding(vertical = 10.dp, horizontal = 10.dp)
+ )
+ }
+ }
+ }
+
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun HighlightsList(item: SavedItemWithLabelsAndHighlights) {
+ val highlights = item.highlights?.filter { it.type == "HIGHLIGHT" } ?: listOf()
+ val listState = rememberLazyListState()
+ val yellowColor = colorResource(R.color.cta_yellow)
+
+ Column(modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = 15.dp)
+ .padding(top = 40.dp)
+ ) {
+ Text("Highlights")
+ Divider(modifier = Modifier.padding(bottom= 10.dp))
+ LazyColumn(
+ state = listState,
+ verticalArrangement = Arrangement.Top,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ items(highlights) { highlight ->
+ var isMenuOpen by remember { mutableStateOf(false) }
+
+ Row(modifier = Modifier
+ .fillMaxWidth()
+ .align(Alignment.End)) {
+ IconButton(onClick = { isMenuOpen = true }) {
+ Icon(
+ imageVector = Icons.Default.MoreVert,
+ contentDescription = null
+ )
+ }
+ DropdownMenu(
+ expanded = isMenuOpen,
+ onDismissRequest = { isMenuOpen = false }
+ ) {
+ DropdownMenuItem(
+ text = { Text("Copy") },
+ onClick = {
+ // actionHandler(it)
+ // onDismiss()
+ }
+ )
+ }
+ }
+
+ highlight.quote?.let {
+ Row(modifier = Modifier
+ .padding(start = 2.dp, end = 15.dp)
+ .fillMaxWidth()
+ .drawWithCache {
+ onDrawWithContent {
+ // draw behind the content the vertical line on the left
+ drawLine(
+ color = yellowColor,
+ start = Offset.Zero,
+ end = Offset(0f, this.size.height),
+ strokeWidth = 10f
+ )
+
+ // draw the content
+ drawContent()
+ }
+ }) {
+
+ MarkdownText(
+ modifier = Modifier
+ .padding(start = 15.dp, end = 15.dp),
+ markdown = it,
+ fontSize = 12.sp,
+ color = MaterialTheme.colorScheme.onPrimaryContainer,
+
+ )
+ }
+ }
+ highlight.annotation?.let {
+ MarkdownText(
+ // modifier = Modifier.padding(paddingValues),
+ markdown = it,
+ fontSize = 12.sp,
+ )
+ } ?: run {
+ Surface(
+ modifier = Modifier
+ .padding(0.dp, end = 15.dp, top = 15.dp, bottom = 30.dp)
+ .fillMaxWidth(),
+ shape = androidx.compose.material.MaterialTheme.shapes.medium,
+ color = MaterialTheme.colorScheme.surfaceVariant
+ ) {
+ Row {
+ Text(
+ text = "Add Notes...",
+ style = androidx.compose.material.MaterialTheme.typography.subtitle2,
+ modifier = Modifier.padding(vertical = 10.dp, horizontal = 10.dp)
+ )
+ }
+ }
+ }
+ }
+ }
+ if (highlights.isEmpty()) {
+ Text(
+ text = "You have not added any highlights to this page.",
+ style = androidx.compose.material.MaterialTheme.typography.subtitle2,
+ modifier = Modifier.padding(vertical = 10.dp, horizontal = 10.dp)
+ )
+ }
+ }
+}
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/notebook/NotebookViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/notebook/NotebookViewModel.kt
new file mode 100644
index 000000000..e1095d1ab
--- /dev/null
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/notebook/NotebookViewModel.kt
@@ -0,0 +1,23 @@
+package app.omnivore.omnivore.ui.notebook
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.ViewModel
+import app.omnivore.omnivore.DatastoreRepository
+import app.omnivore.omnivore.dataService.DataService
+import app.omnivore.omnivore.networking.Networker
+import app.omnivore.omnivore.persistence.entities.SavedItemWithLabelsAndHighlights
+import app.omnivore.omnivore.ui.library.SavedItemViewModel
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+
+@HiltViewModel
+class NotebookViewModel @Inject constructor(
+ private val networker: Networker,
+ private val dataService: DataService,
+ private val datastoreRepo: DatastoreRepository
+): ViewModel() {
+
+ fun getLibraryItemById(savedItemId: String): LiveData {
+ return dataService.db.savedItemDao().getLibraryItemById(savedItemId)
+ }
+}
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt
index 5230b381e..7d64466ce 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt
@@ -26,6 +26,7 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
@@ -33,8 +34,6 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.viewmodel.compose.viewModel
-import androidx.navigation.NavHostController
-import androidx.navigation.compose.rememberNavController
import app.omnivore.omnivore.MainActivity
import app.omnivore.omnivore.R
import app.omnivore.omnivore.ui.components.WebReaderLabelsSelectionSheet
@@ -44,6 +43,8 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
import dagger.hilt.android.AndroidEntryPoint
import kotlin.math.roundToInt
import androidx.navigation.compose.rememberNavController
+import app.omnivore.omnivore.Routes
+import app.omnivore.omnivore.ui.notebook.NotebookActivity
@AndroidEntryPoint
@@ -55,7 +56,6 @@ class WebReaderLoadingContainerActivity: ComponentActivity() {
val requestID = intent.getStringExtra("SAVED_ITEM_REQUEST_ID")
val slug = intent.getStringExtra("SAVED_ITEM_SLUG")
-
setContent {
val systemUiController = rememberSystemUiController()
val useDarkIcons = !isSystemInDarkTheme()
@@ -122,6 +122,8 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o
webReaderViewModel.maxToolbarHeightPx = with(LocalDensity.current) { maxToolbarHeight.roundToPx().toFloat() }
webReaderViewModel.loadItem(slug = slug, requestID = requestID)
+ val context = LocalContext.current
+
val styledContent = webReaderParams?.let {
val webReaderContent = WebReaderContent(
preferences = webReaderViewModel.storedWebPreferences(isSystemInDarkTheme()),
@@ -171,6 +173,18 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o
)
}
}
+ webReaderParams?.let {
+ IconButton(onClick = {
+ val intent = Intent(context, NotebookActivity::class.java)
+ intent.putExtra("SAVED_ITEM_ID", it.item.savedItemId)
+ context.startActivity(intent)
+ }) {
+ Icon(
+ painter = painterResource(id = R.drawable.notebook),
+ contentDescription = null
+ )
+ }
+ }
IconButton(onClick = { showWebPreferencesDialog = true }) {
Icon(
painter = painterResource(id = R.drawable.format_letter_case),
diff --git a/android/Omnivore/app/src/main/res/drawable/notebook.xml b/android/Omnivore/app/src/main/res/drawable/notebook.xml
new file mode 100644
index 000000000..c39b20458
--- /dev/null
+++ b/android/Omnivore/app/src/main/res/drawable/notebook.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/Omnivore/app/src/main/res/values/colors.xml b/android/Omnivore/app/src/main/res/values/colors.xml
index d81888f7f..fdb6f43db 100644
--- a/android/Omnivore/app/src/main/res/values/colors.xml
+++ b/android/Omnivore/app/src/main/res/values/colors.xml
@@ -10,4 +10,8 @@
#EBEBEB
#E6E4BF
+ #F8D457
+
+
+
diff --git a/android/Omnivore/settings.gradle b/android/Omnivore/settings.gradle
index 8a45271fe..d3111f0f7 100644
--- a/android/Omnivore/settings.gradle
+++ b/android/Omnivore/settings.gradle
@@ -13,6 +13,7 @@ dependencyResolutionManagement {
maven {
url = uri("https://customers.pspdfkit.com/maven")
}
+ maven { url 'https://jitpack.io' }
}
}
rootProject.name = "Omnivore"