diff --git a/android/Omnivore/app/build.gradle b/android/Omnivore/app/build.gradle index a8c001e18..c20cfc63f 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 60 - versionName "0.0.60" + versionCode 61 + versionName "0.0.61" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -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..68f4b40f5 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,24 @@ 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 " + + "AND SavedItem.serverSyncStatus != 2 " + + "AND Highlight.serverSyncStatus != 2 " + + + "GROUP BY SavedItem.savedItemId " + ) + fun getLibraryItemById(savedItemId: String): LiveData + @Transaction @Query( "SELECT ${SavedItemQueryConstants.libraryColumns} " + @@ -173,7 +191,7 @@ interface SavedItemDao { "LEFT OUTER JOIN Highlight on highlight.highlightId = SavedItemAndHighlightCrossRef.highlightId " + "WHERE SavedItem.serverSyncStatus != 2 " + - "AND SavedItem.isArchived != :archiveFilter " + + "AND SavedItem.isArchived IN (:allowedArchiveStates) " + "AND SavedItem.contentReader IN (:allowedContentReaders) " + "AND CASE WHEN :hasRequiredLabels THEN SavedItemLabel.name in (:requiredLabels) ELSE 1 END " + "AND CASE WHEN :hasExcludedLabels THEN SavedItemLabel.name is NULL OR SavedItemLabel.name not in (:excludedLabels) ELSE 1 END " + @@ -187,11 +205,11 @@ interface SavedItemDao { "CASE WHEN :sortKey = 'recentlyRead' THEN SavedItem.readAt END DESC,\n" + "CASE WHEN :sortKey = 'recentlyPublished' THEN SavedItem.publishDate END DESC" ) - fun _filteredLibraryData(archiveFilter: Int, sortKey: String, hasRequiredLabels: Int, hasExcludedLabels: Int, requiredLabels: List, excludedLabels: List, allowedContentReaders: List): LiveData> + fun _filteredLibraryData(allowedArchiveStates: List, sortKey: String, hasRequiredLabels: Int, hasExcludedLabels: Int, requiredLabels: List, excludedLabels: List, allowedContentReaders: List): LiveData> - fun filteredLibraryData(archiveFilter: Int, sortKey: String, requiredLabels: List, excludedLabels: List, allowedContentReaders: List): LiveData> { + fun filteredLibraryData(allowedArchiveStates: List, sortKey: String, requiredLabels: List, excludedLabels: List, allowedContentReaders: List): LiveData> { return _filteredLibraryData( - archiveFilter = archiveFilter, + allowedArchiveStates = allowedArchiveStates, sortKey = sortKey, hasRequiredLabels = requiredLabels.size, hasExcludedLabels = excludedLabels.size, diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryViewModel.kt index cc4bb85ed..577526da6 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/library/LibraryViewModel.kt @@ -36,7 +36,7 @@ class LibraryViewModel @Inject constructor( // Live Data private var itemsLiveDataInternal = dataService.db.savedItemDao().filteredLibraryData( - archiveFilter = 1, + allowedArchiveStates = listOf(0), sortKey = "newest", requiredLabels = listOf(), excludedLabels = listOf(), @@ -65,28 +65,6 @@ class LibraryViewModel @Inject constructor( } } -// runBlocking { -// datastoreRepo.getString(DatastoreKeys.lastUsedSavedItemFilter)?.let { str -> -// try { -// val filter = SavedItemFilter.values().first { it.rawValue == str } -// appliedFilterLiveData.postValue(filter) -// } catch (e: Exception) { -// Log.d("error", "invalid filter value stored in datastore repo: $e") -// } -// -// datastoreRepo.getString(DatastoreKeys.lastUsedSavedItemSortFilter)?.let { str -> -// try { -// val filter = SavedItemSortFilter.values().first { it.rawValue == str } -// appliedSortFilterLiveData.postValue(filter) -// } catch (e: Exception) { -// Log.d("error", "invalid sort filter value stored in datastore repo: $e") -// } -// -// handleFilterChanges() -// } -// } -// } - viewModelScope.launch { handleFilterChanges() for (slug in contentRequestChannel) { @@ -196,9 +174,10 @@ class LibraryViewModel @Inject constructor( else -> "newest" } - val archiveFilter = when (appliedFilterLiveData.value) { - SavedItemFilter.ARCHIVED -> 0 - else -> 1 + val allowedArchiveStates = when (appliedFilterLiveData.value) { + SavedItemFilter.ALL -> listOf(0, 1) + SavedItemFilter.ARCHIVED -> listOf(1) + else -> listOf(0) } val allowedContentReaders = when(appliedFilterLiveData.value) { @@ -221,7 +200,7 @@ class LibraryViewModel @Inject constructor( } val newData = dataService.db.savedItemDao().filteredLibraryData( - archiveFilter = archiveFilter, + allowedArchiveStates = allowedArchiveStates, sortKey = sortKey, requiredLabels = requiredLabels, excludedLabels = excludeLabels, 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..cb7834c6c --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/notebook/NotebookView.kt @@ -0,0 +1,403 @@ +package app.omnivore.omnivore.ui.notebook + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.LocalOnBackPressedDispatcherOwner +import androidx.activity.compose.setContent +import androidx.activity.viewModels +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +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.clip +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import app.omnivore.omnivore.MainActivity +import app.omnivore.omnivore.R +import app.omnivore.omnivore.persistence.entities.SavedItemWithLabelsAndHighlights +import app.omnivore.omnivore.ui.library.* +import app.omnivore.omnivore.ui.theme.OmnivoreTheme +import com.google.accompanist.systemuicontroller.rememberSystemUiController +import dagger.hilt.android.AndroidEntryPoint +import dev.jeziellago.compose.markdowntext.MarkdownText +import kotlinx.coroutines.launch + + +@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, ExperimentalMaterialApi::class) +@Composable +fun NotebookView(savedItemId: String, viewModel: NotebookViewModel) { + val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher + val savedItem = viewModel.getLibraryItemById(savedItemId).observeAsState() + val scrollState = rememberScrollState() + val modalBottomSheetState = rememberModalBottomSheetState( + ModalBottomSheetValue.Hidden, + ) + val notes = savedItem.value?.highlights?.filter { it.type == "NOTE" } ?: listOf() + val highlights = savedItem.value?.highlights?.filter { it.type == "HIGHLIGHT" } ?: listOf() + + ModalBottomSheetLayout( + modifier = Modifier.statusBarsPadding(), + sheetBackgroundColor = Color.Transparent, + sheetState = modalBottomSheetState, + sheetContent = { + EditNoteModal() + } + ) { + Scaffold( + topBar = { + TopAppBar( + title = { Text("Notebook") }, + modifier = Modifier.statusBarsPadding(), + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.background + ), + 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) + .verticalScroll(scrollState) + .fillMaxSize() + ) { + savedItem.value?.let { + if (notes.isNotEmpty()) { + ArticleNotes(it) + } + HighlightsList(it) + } + Spacer(Modifier.weight(100f)) + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class) +@Composable +fun EditNoteModal() { + val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher + val annotation = remember { mutableStateOf("") } + + BottomSheetUI() { + Scaffold( + topBar = { + TopAppBar( + title = { Text("Note") }, + modifier = Modifier.statusBarsPadding(), + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.background + ), + navigationIcon = { + IconButton(onClick = { + onBackPressedDispatcher?.onBackPressed() + }) { + Icon( + imageVector = androidx.compose.material.icons.Icons.Filled.ArrowBack, + modifier = Modifier, + contentDescription = "Back" + ) + } + } + ) + } + ) { paddingValues -> + TextField( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize(), + value = annotation.value, onValueChange = { annotation.value = it } + ) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class) +@Composable +fun ArticleNotes(item: SavedItemWithLabelsAndHighlights) { + val notes = item.highlights?.filter { it.type == "NOTE" } ?: listOf() + val showDialog = remember { mutableStateOf(false) } + val modalBottomSheetState = rememberModalBottomSheetState( + ModalBottomSheetValue.Expanded, + ) + val annotation = remember { mutableStateOf("") } + + Column(modifier = Modifier + .fillMaxWidth() + .padding(start = 15.dp) + ) { + Text("Article Notes") + Divider(modifier = Modifier.padding(bottom= 15.dp)) + notes.forEach { note -> + MarkdownText( + markdown = note.annotation ?: "", + fontSize = 14.sp, + style = TextStyle(lineHeight = 18.sp), + color = MaterialTheme.colorScheme.onPrimaryContainer, + ) + } + if (notes.isEmpty()) { + Button( + onClick = { +// viewModelScope.launch { +// datastoreRepo.clearValue(DatastoreKeys.omnivorePendingUserToken) +// } + }, + modifier = Modifier + .padding(0.dp, end = 15.dp) + .fillMaxWidth(), + shape = androidx.compose.material.MaterialTheme.shapes.medium, + colors = ButtonDefaults.buttonColors( + contentColor = MaterialTheme.colorScheme.onPrimaryContainer, + containerColor = MaterialTheme.colorScheme.surfaceVariant + ) + ) { +// Text( +// text = "Add Notes...", +// style = androidx.compose.material.MaterialTheme.typography.subtitle2, +// modifier = Modifier +// .padding(vertical = 2.dp, horizontal = 0.dp), +// ) + Spacer(Modifier.weight(1f)) + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HighlightsList(item: SavedItemWithLabelsAndHighlights) { + val highlights = item.highlights?.filter { it.type == "HIGHLIGHT" } ?: listOf() + val yellowColor = colorResource(R.color.cta_yellow) + + val coroutineScope = rememberCoroutineScope() + val snackBarHostState = remember { SnackbarHostState() } + val clipboard: ClipboardManager? = + LocalContext.current.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager? + + Column(modifier = Modifier + .fillMaxWidth() + .padding(start = 15.dp) + .padding(top = 40.dp, bottom = 100.dp) + ) { + Text("Highlights") + Divider(modifier = Modifier.padding(bottom= 10.dp)) + highlights.forEach { highlight -> + var isMenuOpen by remember { mutableStateOf(false) } + + Row(modifier = Modifier + .fillMaxWidth() + .align(Alignment.End) + .padding(0.dp) + ) { + Spacer(Modifier.weight(1f)) + Box { + IconButton(onClick = { isMenuOpen = true }) { + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = null + ) + } + if (isMenuOpen) { + DropdownMenu( + expanded = isMenuOpen, + onDismissRequest = { isMenuOpen = false } + ) { + DropdownMenuItem( + text = { Text("Copy") }, + onClick = { + val clip = ClipData.newPlainText("highlight", highlight.quote) + clipboard?.let { + it + clipboard?.setPrimaryClip(clip) + } ?: run { + coroutineScope.launch { + snackBarHostState + .showSnackbar("Highlight copied") + } + } + isMenuOpen = false + } + ) + } + } + } + } + + 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 = 14.sp, + color = MaterialTheme.colorScheme.onPrimaryContainer, + ) + } + } + highlight.annotation?.let { + MarkdownText( + // modifier = Modifier.padding(paddingValues), + markdown = it, + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onPrimaryContainer, + ) + } ?: 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) + ) + } + } +} + +@Composable +private fun BottomSheetUI(content: @Composable () -> Unit) { + Box( + modifier = Modifier + .wrapContentHeight() + .fillMaxWidth() + .clip(RoundedCornerShape(topEnd = 20.dp, topStart = 20.dp)) + .background(Color.White) + .statusBarsPadding() + .padding(top = 20.dp) + ) { + content() + } +} + 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"