From 541719bc598cd2c12a71c0c46fe1de151e69490f Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Mon, 10 Oct 2022 21:12:25 -0700 Subject: [PATCH 01/17] bump android version --- android/Omnivore/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/Omnivore/app/build.gradle b/android/Omnivore/app/build.gradle index 66cccad8f..4bfbabe93 100644 --- a/android/Omnivore/app/build.gradle +++ b/android/Omnivore/app/build.gradle @@ -17,8 +17,8 @@ android { applicationId "app.omnivore.omnivore" minSdk 23 targetSdk 32 - versionCode 7 - versionName "0.0.7" + versionCode 8 + versionName "0.0.8" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { From 9d741ff0d9e9b4feca453764f779c4c6b71c01dd Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 11 Oct 2022 11:17:26 -0700 Subject: [PATCH 02/17] change navbar offset on scroll --- .../omnivore/omnivore/ui/reader/WebReader.kt | 74 ++++++++++++++++++- .../omnivore/ui/reader/WebReaderViewModel.kt | 3 + 2 files changed, 74 insertions(+), 3 deletions(-) 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 20e3e0439..2927040e4 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 @@ -8,28 +8,96 @@ import android.view.* import android.webkit.JavascriptInterface import android.webkit.WebView import android.webkit.WebViewClient -import androidx.compose.foundation.layout.Box -import androidx.compose.material3.Text +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Settings +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.viewmodel.compose.viewModel import app.omnivore.omnivore.R import com.google.gson.Gson +import kotlin.math.roundToInt @Composable fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewModel) { val webReaderParams: WebReaderParams? by webReaderViewModel.webReaderParamsLiveData.observeAsState(null) + val toolbarHeight = 48.dp + val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() } + + // Offset to collapse toolbar + val toolbarOffsetHeightPx = remember { mutableStateOf(0f) } + + // Create a connection to the nested scroll system and listen to the scroll happening inside child Column + val nestedScrollConnection = remember { + object : NestedScrollConnection { + override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { + val delta = available.y + val newOffset = toolbarOffsetHeightPx.value + delta + toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f) + return Offset.Zero + } + } + } + if (webReaderParams == null) { webReaderViewModel.loadItem(slug = slug) } if (webReaderParams != null) { - WebReader(webReaderParams!!, webReaderViewModel) + Box( + modifier = Modifier + .fillMaxSize() + .nestedScroll(nestedScrollConnection) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(webReaderViewModel.scrollState) + + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .requiredHeight(height = toolbarHeight) + ) { + } + WebReader(webReaderParams!!, webReaderViewModel) + } + + TopAppBar( + modifier = Modifier + .height(height = toolbarHeight) + .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) }, + backgroundColor = MaterialTheme.colorScheme.surfaceVariant, + elevation = (-10).dp, + title = {}, + navigationIcon = { + IconButton(onClick = {}) { + Icon( + imageVector = Icons.Filled.Settings, + contentDescription = null + ) + } + } + ) + } } else { // TODO: add a proper loading view Text("Loading...") diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt index e9a6f4aaf..7a5bc60c6 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt @@ -1,6 +1,7 @@ package app.omnivore.omnivore.ui.reader import android.util.Log +import androidx.compose.foundation.ScrollState import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -27,6 +28,7 @@ class WebReaderViewModel @Inject constructor( private val datastoreRepo: DatastoreRepository, private val networker: Networker ): ViewModel() { + var scrollState = ScrollState(0) val webReaderParamsLiveData = MutableLiveData(null) val annotationLiveData = MutableLiveData(null) @@ -90,6 +92,7 @@ class WebReaderViewModel @Inject constructor( fun reset() { webReaderParamsLiveData.value = null annotationLiveData.value = null + scrollState = ScrollState(0) } fun cancelAnnotationEdit() { From 80ba68768e3c9362745e16c071093168b6413d70 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 11 Oct 2022 14:12:53 -0700 Subject: [PATCH 03/17] scale nav bar height as web reader is scrolled --- .../omnivore/omnivore/ui/reader/WebReader.kt | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) 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 2927040e4..8e30c80c4 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 @@ -25,10 +25,8 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView -import androidx.lifecycle.viewmodel.compose.viewModel import app.omnivore.omnivore.R import com.google.gson.Gson import kotlin.math.roundToInt @@ -38,19 +36,17 @@ import kotlin.math.roundToInt fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewModel) { val webReaderParams: WebReaderParams? by webReaderViewModel.webReaderParamsLiveData.observeAsState(null) - val toolbarHeight = 48.dp - val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() } - - // Offset to collapse toolbar - val toolbarOffsetHeightPx = remember { mutableStateOf(0f) } + val maxToolbarHeight = 48.dp + val maxToolbarHeightPx = with(LocalDensity.current) { maxToolbarHeight.roundToPx().toFloat() } + val toolbarHeightPx = remember { mutableStateOf(maxToolbarHeightPx) } // Create a connection to the nested scroll system and listen to the scroll happening inside child Column val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val delta = available.y - val newOffset = toolbarOffsetHeightPx.value + delta - toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f) + val newHeight = toolbarHeightPx.value + delta + toolbarHeightPx.value = newHeight.coerceIn(0f, maxToolbarHeightPx) return Offset.Zero } } @@ -75,7 +71,7 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod Row( modifier = Modifier .fillMaxWidth() - .requiredHeight(height = toolbarHeight) + .requiredHeight(height = maxToolbarHeight) ) { } WebReader(webReaderParams!!, webReaderViewModel) @@ -83,12 +79,12 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod TopAppBar( modifier = Modifier - .height(height = toolbarHeight) - .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) }, + .height(height = with(LocalDensity.current) { + toolbarHeightPx.value.roundToInt().toDp() + } ), backgroundColor = MaterialTheme.colorScheme.surfaceVariant, - elevation = (-10).dp, title = {}, - navigationIcon = { + actions = { IconButton(onClick = {}) { Icon( imageVector = Icons.Filled.Settings, From 883ee2a431e7beb694a5543f2fee472a463ac2a0 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 11 Oct 2022 15:56:07 -0700 Subject: [PATCH 04/17] structure web preerences into their own data class --- .../omnivore/omnivore/ui/reader/WebReader.kt | 46 ++++++++++++++----- .../omnivore/ui/reader/WebReaderContent.kt | 15 +++--- 2 files changed, 40 insertions(+), 21 deletions(-) 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 8e30c80c4..f0176c790 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 @@ -14,11 +14,8 @@ import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue +import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection @@ -27,6 +24,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.viewmodel.compose.viewModel import app.omnivore.omnivore.R import com.google.gson.Gson import kotlin.math.roundToInt @@ -34,6 +32,19 @@ import kotlin.math.roundToInt @Composable fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewModel) { + // TODO: maybe move to web reader view model? + val defaultWebPreferences = WebPreferences( + textFontSize = 12, + lineHeight = 150, + maxWidthPercentage = 100, + themeKey = "LightGray", + fontFamily = WebFont.SYSTEM, + prefersHighContrastText = false + ) + + var showWebPreferencesDialog by remember { mutableStateOf(false ) } + var webPreferences by remember { mutableStateOf(defaultWebPreferences ) } + val webReaderParams: WebReaderParams? by webReaderViewModel.webReaderParamsLiveData.observeAsState(null) val maxToolbarHeight = 48.dp @@ -74,7 +85,7 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod .requiredHeight(height = maxToolbarHeight) ) { } - WebReader(webReaderParams!!, webReaderViewModel) + WebReader(webReaderParams!!, webPreferences, webReaderViewModel) } TopAppBar( @@ -85,7 +96,7 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod backgroundColor = MaterialTheme.colorScheme.surfaceVariant, title = {}, actions = { - IconButton(onClick = {}) { + IconButton(onClick = { showWebPreferencesDialog = true }) { Icon( imageVector = Icons.Filled.Settings, contentDescription = null @@ -93,6 +104,17 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod } } ) + + if (showWebPreferencesDialog) { + WebPreferencesDialog { preferences -> + if (preferences != null) { + webPreferences = preferences!! + showWebPreferencesDialog = false + } + + showWebPreferencesDialog = false + } + } } } else { // TODO: add a proper loading view @@ -102,7 +124,11 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod @SuppressLint("SetJavaScriptEnabled") @Composable -fun WebReader(params: WebReaderParams, webReaderViewModel: WebReaderViewModel) { +fun WebReader( + params: WebReaderParams, + preferences: WebPreferences, + webReaderViewModel: WebReaderViewModel +) { // TODO: maybe handle cases where js can be queued up? val javascriptToExecute = remember { mutableStateOf(null) } @@ -111,14 +137,10 @@ fun WebReader(params: WebReaderParams, webReaderViewModel: WebReaderViewModel) { WebView.setWebContentsDebuggingEnabled(true) val webReaderContent = WebReaderContent( - textFontSize = 12, - lineHeight = 150, - maxWidthPercentage = 100, + preferences = preferences, item = params.item, themeKey = "LightGray", - fontFamily = WebFont.SYSTEM , articleContent = params.articleContent, - prefersHighContrastText = false, ) val styledContent = webReaderContent.styledContent() 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 599c55078..45aee447a 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 @@ -39,20 +39,17 @@ data class ArticleContent( } data class WebReaderContent( - val textFontSize: Int, - val lineHeight: Int, - val maxWidthPercentage: Int, + val preferences: WebPreferences, val item: LinkedItem, val themeKey: String, - val fontFamily: WebFont, val articleContent: ArticleContent, - val prefersHighContrastText: Boolean ) { fun styledContent(): String { // TODO: Kotlinize these three values (pasted from Swift) val savedAt = "new Date(1662571290735.0).toISOString()" val createdAt = "new Date().toISOString()" val publishedAt = "new Date().toISOString()" //if (item.publishDate != null) "new Date((item.publishDate!.timeIntervalSince1970 * 1000)).toISOString()" else "undefined" + val textFontSize = preferences.textFontSize val content = """ @@ -96,11 +93,11 @@ data class WebReaderContent( } window.fontSize = $textFontSize - window.fontFamily = "${fontFamily.rawValue}" - window.maxWidthPercentage = $maxWidthPercentage - window.lineHeight = $lineHeight + window.fontFamily = "${preferences.fontFamily.rawValue}" + window.maxWidthPercentage = $preferences.maxWidthPercentage + window.lineHeight = $preferences.lineHeight window.localStorage.setItem("theme", "$themeKey") - window.prefersHighContrastFont = $prefersHighContrastText + window.prefersHighContrastFont = $preferences.prefersHighContrastText window.enableHighlightBar = false From 3142ff82c52ab576fc29402f249e1ea68fbf3b63 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 11 Oct 2022 15:56:21 -0700 Subject: [PATCH 05/17] add a web prefs dialog --- .../ui/reader/WebPreferencesDialog.kt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt new file mode 100644 index 000000000..d2d1e31e4 --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt @@ -0,0 +1,35 @@ +package app.omnivore.omnivore.ui.reader + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog + +@Composable +fun WebPreferencesDialog(onDismiss: (WebPreferences?) -> Unit) { + Dialog(onDismissRequest = { onDismiss(null) }) { + Surface( + shape = RoundedCornerShape(16.dp), + color = Color.White + ) { + WebPreferencesView(onDismiss) + } + } +} + +@Composable +fun WebPreferencesView(onDismiss: (WebPreferences?) -> Unit) { + Text("Web Prefs") +} + +data class WebPreferences( + val textFontSize: Int, + val lineHeight: Int, + val maxWidthPercentage: Int, + val themeKey: String, + val fontFamily: WebFont, + val prefersHighContrastText: Boolean +) From 50681697156e9c9e70777894a4121d9149048f94 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 11 Oct 2022 22:09:10 -0700 Subject: [PATCH 06/17] use queue of js ops in web reader view model --- .../omnivore/omnivore/ui/reader/WebReader.kt | 19 +++++++++++-------- .../omnivore/ui/reader/WebReaderViewModel.kt | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) 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 f0176c790..d08c4b857 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 @@ -27,6 +27,7 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.viewmodel.compose.viewModel import app.omnivore.omnivore.R import com.google.gson.Gson +import java.util.* import kotlin.math.roundToInt @@ -129,11 +130,12 @@ fun WebReader( preferences: WebPreferences, webReaderViewModel: WebReaderViewModel ) { - // TODO: maybe handle cases where js can be queued up? - val javascriptToExecute = remember { mutableStateOf(null) } - val annotation: String? by webReaderViewModel.annotationLiveData.observeAsState(null) + val javascriptActionLoopUUID: UUID by webReaderViewModel + .javascriptActionLoopUUIDLiveData + .observeAsState(UUID.randomUUID()) + WebView.setWebContentsDebuggingEnabled(true) val webReaderContent = WebReaderContent( @@ -186,8 +188,11 @@ fun WebReader( ) } }, update = { - if (javascriptToExecute.value != null) { - it.evaluateJavascript(javascriptToExecute.value!!, null) + if (javascriptActionLoopUUID != webReaderViewModel.lastJavascriptActionLoopUUID) { + for (script in webReaderViewModel.javascriptDispatchQueue) { + it.evaluateJavascript(script, null) + } + webReaderViewModel.resetJavascriptDispatchQueue() } }) @@ -195,9 +200,7 @@ fun WebReader( AnnotationEditView( initialAnnotation = annotation!!, onSave = { - val script = "var event = new Event('saveAnnotation');event.annotation = '$it';document.dispatchEvent(event);" - javascriptToExecute.value = script - webReaderViewModel.cancelAnnotationEdit() + webReaderViewModel.saveAnnotation(it) }, onCancel = { webReaderViewModel.cancelAnnotationEdit() diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt index 7a5bc60c6..d1f2c7ed9 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt @@ -12,6 +12,7 @@ import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import org.json.JSONObject +import java.util.* import javax.inject.Inject data class WebReaderParams( @@ -28,9 +29,13 @@ class WebReaderViewModel @Inject constructor( private val datastoreRepo: DatastoreRepository, private val networker: Networker ): ViewModel() { + var lastJavascriptActionLoopUUID = UUID.randomUUID() + var javascriptDispatchQueue: MutableList = mutableListOf() var scrollState = ScrollState(0) + val webReaderParamsLiveData = MutableLiveData(null) val annotationLiveData = MutableLiveData(null) + val javascriptActionLoopUUIDLiveData = MutableLiveData(lastJavascriptActionLoopUUID) fun loadItem(slug: String) { viewModelScope.launch { @@ -93,6 +98,19 @@ class WebReaderViewModel @Inject constructor( webReaderParamsLiveData.value = null annotationLiveData.value = null scrollState = ScrollState(0) + javascriptDispatchQueue = mutableListOf() + } + + fun resetJavascriptDispatchQueue() { + lastJavascriptActionLoopUUID = javascriptActionLoopUUIDLiveData.value + javascriptDispatchQueue = mutableListOf() + } + + fun saveAnnotation(annotation: String) { + val script = "var event = new Event('saveAnnotation');event.annotation = '$annotation';document.dispatchEvent(event);" + javascriptDispatchQueue.add(script) + javascriptActionLoopUUIDLiveData.value = UUID.randomUUID() + cancelAnnotationEdit() } fun cancelAnnotationEdit() { From 84de7ea640b8ee0debeed54a34fd8b458cfd509a Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 11 Oct 2022 22:13:31 -0700 Subject: [PATCH 07/17] move annotation view into web reader container --- .../omnivore/omnivore/ui/reader/WebReader.kt | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) 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 d08c4b857..8aeb8e619 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 @@ -47,6 +47,7 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod var webPreferences by remember { mutableStateOf(defaultWebPreferences ) } val webReaderParams: WebReaderParams? by webReaderViewModel.webReaderParamsLiveData.observeAsState(null) + val annotation: String? by webReaderViewModel.annotationLiveData.observeAsState(null) val maxToolbarHeight = 48.dp val maxToolbarHeightPx = with(LocalDensity.current) { maxToolbarHeight.roundToPx().toFloat() } @@ -116,6 +117,18 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod showWebPreferencesDialog = false } } + + if (annotation != null) { + AnnotationEditView( + initialAnnotation = annotation!!, + onSave = { + webReaderViewModel.saveAnnotation(it) + }, + onCancel = { + webReaderViewModel.cancelAnnotationEdit() + } + ) + } } } else { // TODO: add a proper loading view @@ -130,8 +143,6 @@ fun WebReader( preferences: WebPreferences, webReaderViewModel: WebReaderViewModel ) { - val annotation: String? by webReaderViewModel.annotationLiveData.observeAsState(null) - val javascriptActionLoopUUID: UUID by webReaderViewModel .javascriptActionLoopUUIDLiveData .observeAsState(UUID.randomUUID()) @@ -195,18 +206,6 @@ fun WebReader( webReaderViewModel.resetJavascriptDispatchQueue() } }) - - if (annotation != null) { - AnnotationEditView( - initialAnnotation = annotation!!, - onSave = { - webReaderViewModel.saveAnnotation(it) - }, - onCancel = { - webReaderViewModel.cancelAnnotationEdit() - } - ) - } } } From 41e5d3000b6ecba8c8448ce54868886590493b93 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 12 Oct 2022 08:04:14 -0700 Subject: [PATCH 08/17] remove param from web pref dialog onDismiss call --- .../omnivore/ui/reader/WebPreferencesDialog.kt | 8 ++++---- .../java/app/omnivore/omnivore/ui/reader/WebReader.kt | 10 ++-------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt index d2d1e31e4..d576e2164 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt @@ -9,19 +9,19 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @Composable -fun WebPreferencesDialog(onDismiss: (WebPreferences?) -> Unit) { - Dialog(onDismissRequest = { onDismiss(null) }) { +fun WebPreferencesDialog(onDismiss: () -> Unit) { + Dialog(onDismissRequest = { onDismiss() }) { Surface( shape = RoundedCornerShape(16.dp), color = Color.White ) { - WebPreferencesView(onDismiss) + WebPreferencesView() } } } @Composable -fun WebPreferencesView(onDismiss: (WebPreferences?) -> Unit) { +fun WebPreferencesView() { Text("Web Prefs") } 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 8aeb8e619..9a0b6bd36 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 @@ -44,7 +44,6 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod ) var showWebPreferencesDialog by remember { mutableStateOf(false ) } - var webPreferences by remember { mutableStateOf(defaultWebPreferences ) } val webReaderParams: WebReaderParams? by webReaderViewModel.webReaderParamsLiveData.observeAsState(null) val annotation: String? by webReaderViewModel.annotationLiveData.observeAsState(null) @@ -87,7 +86,7 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod .requiredHeight(height = maxToolbarHeight) ) { } - WebReader(webReaderParams!!, webPreferences, webReaderViewModel) + WebReader(webReaderParams!!, defaultWebPreferences, webReaderViewModel) } TopAppBar( @@ -108,12 +107,7 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod ) if (showWebPreferencesDialog) { - WebPreferencesDialog { preferences -> - if (preferences != null) { - webPreferences = preferences!! - showWebPreferencesDialog = false - } - + WebPreferencesDialog { showWebPreferencesDialog = false } } From 86337d5d0125d3570129d3f1c74518acf757d107 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Mon, 17 Oct 2022 21:23:39 -0700 Subject: [PATCH 09/17] add steppers on web prefs dialog --- .../ui/reader/WebPreferencesDialog.kt | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt index d576e2164..ad925c59f 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt @@ -1,9 +1,16 @@ package app.omnivore.omnivore.ui.reader +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material3.Divider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @@ -22,7 +29,61 @@ fun WebPreferencesDialog(onDismiss: () -> Unit) { @Composable fun WebPreferencesView() { - Text("Web Prefs") + Column { + Text("Web Preferences") + // Font Size: Stepper + Stepper( + label = "Font Size:", + onIncrease = {}, + onDecrease = {} + ) + + // Margin: Slider + Stepper( + label = "Margin:", + onIncrease = {}, + onDecrease = {} + ) + + // Line Spacing: Slider + Stepper( + label = "Line Spacing:", + onIncrease = {}, + onDecrease = {} + ) + + // High Contrast Text: Switch + // Reader Font: List of Fonts + } +} + +@Composable +fun Stepper(label: String, onIncrease: () -> Unit, onDecrease: () -> Unit) { + Row { + Text(text = label) + Spacer(modifier = Modifier.weight(1.0F)) + + IconButton(onClick = { onDecrease() }) { + Icon( + imageVector = Icons.Filled.Add, + contentDescription = null + ) + } + + Divider( + color = Color.Black, + modifier = Modifier + .height(40.dp) + .width(1.dp) + ) + + IconButton(onClick = { onIncrease() }) { + Icon( + imageVector = Icons.Filled.Add, + contentDescription = null + ) + } + } } data class WebPreferences( From 7a1e66ba5f8d10056b795bf654de8b70773f99d8 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Mon, 17 Oct 2022 21:37:38 -0700 Subject: [PATCH 10/17] style web prefs popover --- .../ui/reader/WebPreferencesDialog.kt | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt index ad925c59f..346011b72 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt @@ -4,16 +4,19 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Text import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.KeyboardArrowDown +import androidx.compose.material.icons.filled.KeyboardArrowUp import androidx.compose.material3.Divider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog +import com.pspdfkit.ui.note.AlignedAnnotationHinterDrawable @Composable fun WebPreferencesDialog(onDismiss: () -> Unit) { @@ -29,8 +32,19 @@ fun WebPreferencesDialog(onDismiss: () -> Unit) { @Composable fun WebPreferencesView() { - Column { - Text("Web Preferences") + Column( + modifier = Modifier + .padding(top = 6.dp, start = 6.dp, end = 6.dp, bottom = 6.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp, bottom = 12.dp), + horizontalArrangement = Arrangement.Center + ) { + Text("Web Preferences") + } + // Font Size: Stepper Stepper( label = "Font Size:", @@ -59,13 +73,18 @@ fun WebPreferencesView() { @Composable fun Stepper(label: String, onIncrease: () -> Unit, onDecrease: () -> Unit) { - Row { - Text(text = label) + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = label, + modifier = Modifier + .padding(bottom = 6.dp) + ) + Spacer(modifier = Modifier.weight(1.0F)) IconButton(onClick = { onDecrease() }) { Icon( - imageVector = Icons.Filled.Add, + imageVector = Icons.Filled.KeyboardArrowDown, contentDescription = null ) } @@ -73,13 +92,13 @@ fun Stepper(label: String, onIncrease: () -> Unit, onDecrease: () -> Unit) { Divider( color = Color.Black, modifier = Modifier - .height(40.dp) + .height(20.dp) .width(1.dp) ) IconButton(onClick = { onIncrease() }) { Icon( - imageVector = Icons.Filled.Add, + imageVector = Icons.Filled.KeyboardArrowUp, contentDescription = null ) } From a341475438006226a22f1de210c463d42a037a36 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 18 Oct 2022 16:25:40 -0700 Subject: [PATCH 11/17] store web prefs in dataStore. dispatch changes to webview --- .../java/app/omnivore/omnivore/Constants.kt | 7 ++ .../ui/reader/WebPreferencesDialog.kt | 19 +++-- .../omnivore/omnivore/ui/reader/WebReader.kt | 22 ++---- .../omnivore/ui/reader/WebReaderViewModel.kt | 76 ++++++++++++++++++- 4 files changed, 96 insertions(+), 28 deletions(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Constants.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Constants.kt index 3b219d6a3..075eb297b 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Constants.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Constants.kt @@ -1,5 +1,7 @@ package app.omnivore.omnivore +import app.omnivore.omnivore.ui.reader.WebFont + object Constants { const val apiURL = BuildConfig.OMNIVORE_API_URL const val webURL = BuildConfig.OMNIVORE_WEB_URL @@ -10,6 +12,11 @@ object DatastoreKeys { const val omnivoreAuthToken = "omnivoreAuthToken" const val omnivoreAuthCookieString = "omnivoreAuthCookieString" const val omnivorePendingUserToken = "omnivorePendingUserToken" + const val preferredWebFontSize = "preferredWebFontSize" + const val preferredWebLineHeight = "preferredWebLineHeight" + const val preferredWebMaxWidthPercentage = "preferredWebMaxWidthPercentage" + const val preferredWebFontFamily = "preferredWebFontFamily" + const val prefersWebHighContrastText = "prefersWebHighContrastText" } object AppleConstants { diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt index 346011b72..550162d53 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt @@ -16,22 +16,21 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog -import com.pspdfkit.ui.note.AlignedAnnotationHinterDrawable @Composable -fun WebPreferencesDialog(onDismiss: () -> Unit) { +fun WebPreferencesDialog(onDismiss: () -> Unit, webReaderViewModel: WebReaderViewModel) { Dialog(onDismissRequest = { onDismiss() }) { Surface( shape = RoundedCornerShape(16.dp), color = Color.White ) { - WebPreferencesView() + WebPreferencesView(webReaderViewModel) } } } @Composable -fun WebPreferencesView() { +fun WebPreferencesView(webReaderViewModel: WebReaderViewModel) { Column( modifier = Modifier .padding(top = 6.dp, start = 6.dp, end = 6.dp, bottom = 6.dp) @@ -48,22 +47,22 @@ fun WebPreferencesView() { // Font Size: Stepper Stepper( label = "Font Size:", - onIncrease = {}, - onDecrease = {} + onIncrease = { webReaderViewModel.updateFontSize(isIncrease = true) }, + onDecrease = { webReaderViewModel.updateFontSize(isIncrease = false) } ) // Margin: Slider Stepper( label = "Margin:", - onIncrease = {}, - onDecrease = {} + onIncrease = { webReaderViewModel.updateMaxWidthPercentage(isIncrease = false) }, + onDecrease = { webReaderViewModel.updateMaxWidthPercentage(isIncrease = true) } ) // Line Spacing: Slider Stepper( label = "Line Spacing:", - onIncrease = {}, - onDecrease = {} + onIncrease = { webReaderViewModel.updateLineSpacing(isIncrease = true) }, + onDecrease = { webReaderViewModel.updateLineSpacing(isIncrease = false) } ) // High Contrast Text: Switch 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 9a0b6bd36..afff5de0f 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 @@ -33,16 +33,6 @@ import kotlin.math.roundToInt @Composable fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewModel) { - // TODO: maybe move to web reader view model? - val defaultWebPreferences = WebPreferences( - textFontSize = 12, - lineHeight = 150, - maxWidthPercentage = 100, - themeKey = "LightGray", - fontFamily = WebFont.SYSTEM, - prefersHighContrastText = false - ) - var showWebPreferencesDialog by remember { mutableStateOf(false ) } val webReaderParams: WebReaderParams? by webReaderViewModel.webReaderParamsLiveData.observeAsState(null) @@ -86,7 +76,7 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod .requiredHeight(height = maxToolbarHeight) ) { } - WebReader(webReaderParams!!, defaultWebPreferences, webReaderViewModel) + WebReader(webReaderParams!!, webReaderViewModel.storedWebPreferences(), webReaderViewModel) } TopAppBar( @@ -107,9 +97,12 @@ fun WebReaderLoadingContainer(slug: String, webReaderViewModel: WebReaderViewMod ) if (showWebPreferencesDialog) { - WebPreferencesDialog { - showWebPreferencesDialog = false - } + WebPreferencesDialog( + onDismiss = { + showWebPreferencesDialog = false + }, + webReaderViewModel = webReaderViewModel + ) } if (annotation != null) { @@ -195,6 +188,7 @@ fun WebReader( }, update = { if (javascriptActionLoopUUID != webReaderViewModel.lastJavascriptActionLoopUUID) { for (script in webReaderViewModel.javascriptDispatchQueue) { + Log.d("js", "executing script: $script") it.evaluateJavascript(script, null) } webReaderViewModel.resetJavascriptDispatchQueue() diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt index d1f2c7ed9..4ce99a090 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt @@ -5,12 +5,14 @@ import androidx.compose.foundation.ScrollState import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import app.omnivore.omnivore.DatastoreKeys import app.omnivore.omnivore.DatastoreRepository import app.omnivore.omnivore.models.LinkedItem import app.omnivore.omnivore.networking.* import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import org.json.JSONObject import java.util.* import javax.inject.Inject @@ -29,7 +31,7 @@ class WebReaderViewModel @Inject constructor( private val datastoreRepo: DatastoreRepository, private val networker: Networker ): ViewModel() { - var lastJavascriptActionLoopUUID = UUID.randomUUID() + var lastJavascriptActionLoopUUID: UUID = UUID.randomUUID() var javascriptDispatchQueue: MutableList = mutableListOf() var scrollState = ScrollState(0) @@ -102,18 +104,84 @@ class WebReaderViewModel @Inject constructor( } fun resetJavascriptDispatchQueue() { - lastJavascriptActionLoopUUID = javascriptActionLoopUUIDLiveData.value + lastJavascriptActionLoopUUID = javascriptActionLoopUUIDLiveData.value ?: UUID.randomUUID() javascriptDispatchQueue = mutableListOf() } fun saveAnnotation(annotation: String) { val script = "var event = new Event('saveAnnotation');event.annotation = '$annotation';document.dispatchEvent(event);" - javascriptDispatchQueue.add(script) - javascriptActionLoopUUIDLiveData.value = UUID.randomUUID() + enqueueScript(script) cancelAnnotationEdit() } fun cancelAnnotationEdit() { annotationLiveData.value = null } + + private fun enqueueScript(javascript: String) { + javascriptDispatchQueue.add(javascript) + javascriptActionLoopUUIDLiveData.value = UUID.randomUUID() + } + + fun storedWebPreferences(): WebPreferences = runBlocking { + val storedFontSize = datastoreRepo.getInt(DatastoreKeys.preferredWebFontSize) + val storedLineHeight = datastoreRepo.getInt(DatastoreKeys.preferredWebLineHeight) + val storedMaxWidth = datastoreRepo.getInt(DatastoreKeys.preferredWebMaxWidthPercentage) + val storedFontFamily = datastoreRepo.getString(DatastoreKeys.preferredWebFontFamily) + val prefersHighContrastFont = datastoreRepo.getString(DatastoreKeys.prefersWebHighContrastText) == "true" + + WebPreferences( + textFontSize = storedFontSize ?: 12, + lineHeight = storedLineHeight ?: 150, + maxWidthPercentage = storedMaxWidth ?: 100, + themeKey = "LightGray", + fontFamily = WebFont.SYSTEM, + prefersHighContrastText = prefersHighContrastFont + ) + } + + fun updateFontSize(isIncrease: Boolean) { + val delta = if (isIncrease) 2 else -2 + var newFontSize: Int + + runBlocking { + val storedFontSize = datastoreRepo.getInt(DatastoreKeys.preferredWebFontSize) + newFontSize = ((storedFontSize ?: 12) + delta).coerceIn(8, 28) + datastoreRepo.putInt(DatastoreKeys.preferredWebFontSize, newFontSize) + } + + // Get value from data store and then update it + val script = "var event = new Event('updateFontSize');event.fontSize = '$newFontSize';document.dispatchEvent(event);" + enqueueScript(script) + } + + fun updateMaxWidthPercentage(isIncrease: Boolean) { + val delta = if (isIncrease) 10 else -10 + var newMaxWidthPercentageValue: Int + + runBlocking { + val storedWidth = datastoreRepo.getInt(DatastoreKeys.preferredWebMaxWidthPercentage) + newMaxWidthPercentageValue = ((storedWidth ?: 100) + delta).coerceIn(40, 100) + datastoreRepo.putInt(DatastoreKeys.preferredWebMaxWidthPercentage, newMaxWidthPercentageValue) + } + + // Get value from data store and then update it + val script = "var event = new Event('updateMaxWidthPercentage');event.maxWidthPercentage = '$newMaxWidthPercentageValue';document.dispatchEvent(event);" + enqueueScript(script) + } + + fun updateLineSpacing(isIncrease: Boolean) { + val delta = if (isIncrease) 25 else -25 + var newLineHeight: Int + + runBlocking { + val storedHeight = datastoreRepo.getInt(DatastoreKeys.preferredWebLineHeight) + newLineHeight = ((storedHeight ?: 150) + delta).coerceIn(100, 300) + datastoreRepo.putInt(DatastoreKeys.preferredWebLineHeight, newLineHeight) + } + + // Get value from data store and then update it + val script = "var event = new Event('updateLineHeight');event.lineHeight = '$newLineHeight';document.dispatchEvent(event);" + enqueueScript(script) + } } From 3513e5e97599bb192e8e01379b65d1504bd35dd8 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 19 Oct 2022 12:44:18 -0700 Subject: [PATCH 12/17] add plus and minus icons for steppers --- .../app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt | 6 ++++-- android/Omnivore/app/src/main/res/drawable-v24/minus.xml | 1 + android/Omnivore/app/src/main/res/drawable-v24/plus.xml | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 android/Omnivore/app/src/main/res/drawable-v24/minus.xml create mode 100644 android/Omnivore/app/src/main/res/drawable-v24/plus.xml diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt index 550162d53..0b89c1c62 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt @@ -14,8 +14,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog +import app.omnivore.omnivore.R @Composable fun WebPreferencesDialog(onDismiss: () -> Unit, webReaderViewModel: WebReaderViewModel) { @@ -83,7 +85,7 @@ fun Stepper(label: String, onIncrease: () -> Unit, onDecrease: () -> Unit) { IconButton(onClick = { onDecrease() }) { Icon( - imageVector = Icons.Filled.KeyboardArrowDown, + painter = painterResource(id = R.drawable.minus), contentDescription = null ) } @@ -97,7 +99,7 @@ fun Stepper(label: String, onIncrease: () -> Unit, onDecrease: () -> Unit) { IconButton(onClick = { onIncrease() }) { Icon( - imageVector = Icons.Filled.KeyboardArrowUp, + painter = painterResource(id = R.drawable.plus), contentDescription = null ) } diff --git a/android/Omnivore/app/src/main/res/drawable-v24/minus.xml b/android/Omnivore/app/src/main/res/drawable-v24/minus.xml new file mode 100644 index 000000000..7ac604f6a --- /dev/null +++ b/android/Omnivore/app/src/main/res/drawable-v24/minus.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/android/Omnivore/app/src/main/res/drawable-v24/plus.xml b/android/Omnivore/app/src/main/res/drawable-v24/plus.xml new file mode 100644 index 000000000..a476f560f --- /dev/null +++ b/android/Omnivore/app/src/main/res/drawable-v24/plus.xml @@ -0,0 +1 @@ + \ No newline at end of file From 6c0676dbc9a90773522fe202751fd1955198c18b Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 19 Oct 2022 13:01:55 -0700 Subject: [PATCH 13/17] add switch for toggling high contrast font on android --- .../omnivore/ui/reader/WebPreferencesDialog.kt | 18 ++++++++++++++++++ .../omnivore/ui/reader/WebReaderViewModel.kt | 9 +++++++++ 2 files changed, 27 insertions(+) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt index 0b89c1c62..ee50009ea 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt @@ -2,6 +2,7 @@ package app.omnivore.omnivore.ui.reader import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Switch import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.KeyboardArrowDown @@ -11,6 +12,8 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -33,6 +36,9 @@ fun WebPreferencesDialog(onDismiss: () -> Unit, webReaderViewModel: WebReaderVie @Composable fun WebPreferencesView(webReaderViewModel: WebReaderViewModel) { + val currentWebPreferences = webReaderViewModel.storedWebPreferences() + val highContrastTextSwitchState = remember { mutableStateOf(currentWebPreferences.prefersHighContrastText) } + Column( modifier = Modifier .padding(top = 6.dp, start = 6.dp, end = 6.dp, bottom = 6.dp) @@ -68,6 +74,18 @@ fun WebPreferencesView(webReaderViewModel: WebReaderViewModel) { ) // High Contrast Text: Switch + Row(verticalAlignment = Alignment.CenterVertically) { + Text("High Contrast Text") + Spacer(modifier = Modifier.weight(1.0F)) + Switch( + checked = highContrastTextSwitchState.value, + onCheckedChange = { + highContrastTextSwitchState.value = it + webReaderViewModel.updateHighContrastTextPreference(it) + } + ) + } + // Reader Font: List of Fonts } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt index 4ce99a090..639b7714e 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt @@ -184,4 +184,13 @@ class WebReaderViewModel @Inject constructor( val script = "var event = new Event('updateLineHeight');event.lineHeight = '$newLineHeight';document.dispatchEvent(event);" enqueueScript(script) } + + fun updateHighContrastTextPreference(prefersHighContrastText: Boolean) { + runBlocking { + datastoreRepo.putString(DatastoreKeys.prefersWebHighContrastText, prefersHighContrastText.toString()) + } + val fontContrastValue = if (prefersHighContrastText) "high" else "normal" + val script = "var event = new Event('handleFontContrastChange');event.fontContrast = '$fontContrastValue';document.dispatchEvent(event);" + enqueueScript(script) + } } From 370649573fbfe2022d4e24d279007de891542a62 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 19 Oct 2022 13:32:34 -0700 Subject: [PATCH 14/17] add vertical scroll to web prefs dialog --- .../ui/reader/WebPreferencesDialog.kt | 77 +++++++++++-------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt index ee50009ea..4e8c10b7c 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt @@ -1,7 +1,10 @@ package app.omnivore.omnivore.ui.reader import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.Switch import androidx.compose.material.Text import androidx.compose.material.icons.Icons @@ -27,7 +30,9 @@ fun WebPreferencesDialog(onDismiss: () -> Unit, webReaderViewModel: WebReaderVie Dialog(onDismissRequest = { onDismiss() }) { Surface( shape = RoundedCornerShape(16.dp), - color = Color.White + color = Color.White, + modifier = Modifier + .height(250.dp) ) { WebPreferencesView(webReaderViewModel) } @@ -52,41 +57,47 @@ fun WebPreferencesView(webReaderViewModel: WebReaderViewModel) { Text("Web Preferences") } - // Font Size: Stepper - Stepper( - label = "Font Size:", - onIncrease = { webReaderViewModel.updateFontSize(isIncrease = true) }, - onDecrease = { webReaderViewModel.updateFontSize(isIncrease = false) } - ) + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()) + ) { - // Margin: Slider - Stepper( - label = "Margin:", - onIncrease = { webReaderViewModel.updateMaxWidthPercentage(isIncrease = false) }, - onDecrease = { webReaderViewModel.updateMaxWidthPercentage(isIncrease = true) } - ) - - // Line Spacing: Slider - Stepper( - label = "Line Spacing:", - onIncrease = { webReaderViewModel.updateLineSpacing(isIncrease = true) }, - onDecrease = { webReaderViewModel.updateLineSpacing(isIncrease = false) } - ) - - // High Contrast Text: Switch - Row(verticalAlignment = Alignment.CenterVertically) { - Text("High Contrast Text") - Spacer(modifier = Modifier.weight(1.0F)) - Switch( - checked = highContrastTextSwitchState.value, - onCheckedChange = { - highContrastTextSwitchState.value = it - webReaderViewModel.updateHighContrastTextPreference(it) - } + // Font Size: Stepper + Stepper( + label = "Font Size:", + onIncrease = { webReaderViewModel.updateFontSize(isIncrease = true) }, + onDecrease = { webReaderViewModel.updateFontSize(isIncrease = false) } ) - } - // Reader Font: List of Fonts + // Margin: Slider + Stepper( + label = "Margin:", + onIncrease = { webReaderViewModel.updateMaxWidthPercentage(isIncrease = false) }, + onDecrease = { webReaderViewModel.updateMaxWidthPercentage(isIncrease = true) } + ) + + // Line Spacing: Slider + Stepper( + label = "Line Spacing:", + onIncrease = { webReaderViewModel.updateLineSpacing(isIncrease = true) }, + onDecrease = { webReaderViewModel.updateLineSpacing(isIncrease = false) } + ) + + // High Contrast Text: Switch + Row(verticalAlignment = Alignment.CenterVertically) { + Text("High Contrast Text") + Spacer(modifier = Modifier.weight(1.0F)) + Switch( + checked = highContrastTextSwitchState.value, + onCheckedChange = { + highContrastTextSwitchState.value = it + webReaderViewModel.updateHighContrastTextPreference(it) + } + ) + } + + // Reader Font: List of Fonts + } } } From 49df480e2b990996d43fa13620298f43ca2cd10e Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 19 Oct 2022 16:05:23 -0700 Subject: [PATCH 15/17] display list of font families in web pref dialog --- .../ui/reader/WebPreferencesDialog.kt | 61 +++++++++++++++---- .../omnivore/ui/reader/WebReaderViewModel.kt | 5 ++ 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt index 4e8c10b7c..4f9d01c4a 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt @@ -1,5 +1,7 @@ package app.omnivore.omnivore.ui.reader +import android.util.Log +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.rememberScrollState @@ -8,12 +10,11 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.Switch import androidx.compose.material.Text import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.KeyboardArrowDown +import androidx.compose.material.icons.filled.KeyboardArrowRight import androidx.compose.material.icons.filled.KeyboardArrowUp -import androidx.compose.material3.Divider -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Surface +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -32,7 +33,7 @@ fun WebPreferencesDialog(onDismiss: () -> Unit, webReaderViewModel: WebReaderVie shape = RoundedCornerShape(16.dp), color = Color.White, modifier = Modifier - .height(250.dp) + .height(300.dp) ) { WebPreferencesView(webReaderViewModel) } @@ -42,7 +43,9 @@ fun WebPreferencesDialog(onDismiss: () -> Unit, webReaderViewModel: WebReaderVie @Composable fun WebPreferencesView(webReaderViewModel: WebReaderViewModel) { val currentWebPreferences = webReaderViewModel.storedWebPreferences() + val isFontListExpanded = remember { mutableStateOf(false) } val highContrastTextSwitchState = remember { mutableStateOf(currentWebPreferences.prefersHighContrastText) } + val selectedWebFontRawValue = remember { mutableStateOf(currentWebPreferences.fontFamily.rawValue) } Column( modifier = Modifier @@ -61,29 +64,24 @@ fun WebPreferencesView(webReaderViewModel: WebReaderViewModel) { modifier = Modifier .verticalScroll(rememberScrollState()) ) { - - // Font Size: Stepper Stepper( label = "Font Size:", onIncrease = { webReaderViewModel.updateFontSize(isIncrease = true) }, onDecrease = { webReaderViewModel.updateFontSize(isIncrease = false) } ) - // Margin: Slider Stepper( label = "Margin:", onIncrease = { webReaderViewModel.updateMaxWidthPercentage(isIncrease = false) }, onDecrease = { webReaderViewModel.updateMaxWidthPercentage(isIncrease = true) } ) - // Line Spacing: Slider Stepper( label = "Line Spacing:", onIncrease = { webReaderViewModel.updateLineSpacing(isIncrease = true) }, onDecrease = { webReaderViewModel.updateLineSpacing(isIncrease = false) } ) - // High Contrast Text: Switch Row(verticalAlignment = Alignment.CenterVertically) { Text("High Contrast Text") Spacer(modifier = Modifier.weight(1.0F)) @@ -96,7 +94,48 @@ fun WebPreferencesView(webReaderViewModel: WebReaderViewModel) { ) } - // Reader Font: List of Fonts + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clickable(onClick = { isFontListExpanded.value = !isFontListExpanded.value }) + ) { + Text("Font Family") + Spacer(modifier = Modifier.weight(1.0F)) + Icon( + imageVector = + if (isFontListExpanded.value) + Icons.Filled.KeyboardArrowDown + else + Icons.Filled.KeyboardArrowRight, + contentDescription = null + ) + } + + if (isFontListExpanded.value) { + WebFont.values().forEach { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clickable(onClick = { + selectedWebFontRawValue.value = it.rawValue + webReaderViewModel.applyWebFont(it) + }) + ) { + Text( + it.displayText, + modifier = Modifier + .padding(top = 6.dp, start = 6.dp, end = 6.dp, bottom = 6.dp) + ) + Spacer(modifier = Modifier.weight(1.0F)) + if (it.rawValue == selectedWebFontRawValue.value) { + Icon( + imageVector = Icons.Filled.Check, + contentDescription = null + ) + } + } + } + } } } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt index 639b7714e..355bcb760 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt @@ -193,4 +193,9 @@ class WebReaderViewModel @Inject constructor( val script = "var event = new Event('handleFontContrastChange');event.fontContrast = '$fontContrastValue';document.dispatchEvent(event);" enqueueScript(script) } + + fun applyWebFont(font: WebFont) { + // TODO: update value in datastore and dispatch update to web view + Log.d("Font", "Web Font selected: ${font.displayText}") + } } From 0ec409cad4ead70ef53ecc893b9a11813177cd0c Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Thu, 20 Oct 2022 13:02:22 -0700 Subject: [PATCH 16/17] save font preference in datastore --- .../omnivore/ui/reader/WebPreferencesDialog.kt | 2 +- .../omnivore/ui/reader/WebReaderContent.kt | 12 +++++------- .../omnivore/ui/reader/WebReaderViewModel.kt | 17 ++++++++++++----- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt index 4f9d01c4a..c2466dbe0 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebPreferencesDialog.kt @@ -117,8 +117,8 @@ fun WebPreferencesView(webReaderViewModel: WebReaderViewModel) { verticalAlignment = Alignment.CenterVertically, modifier = Modifier .clickable(onClick = { - selectedWebFontRawValue.value = it.rawValue webReaderViewModel.applyWebFont(it) + selectedWebFontRawValue.value = it.rawValue }) ) { Text( 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 45aee447a..69bfb484f 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 @@ -1,6 +1,5 @@ package app.omnivore.omnivore.ui.reader -import android.util.Log import app.omnivore.omnivore.models.Highlight import app.omnivore.omnivore.models.LinkedItem import com.google.gson.Gson @@ -15,7 +14,6 @@ enum class WebFont(val displayText: String, val rawValue: String) { ROBOTO("Roboto", "Roboto"), CRIMSON_TEXT("Crimson Text", "Crimson Text"), SOURCE_SERIF_PRO("Source Serif Pro", "Source Serif Pro"), - Inter("Inter", "Inter"), } enum class ArticleContentStatus(val rawValue: String) { @@ -48,17 +46,19 @@ data class WebReaderContent( // TODO: Kotlinize these three values (pasted from Swift) val savedAt = "new Date(1662571290735.0).toISOString()" val createdAt = "new Date().toISOString()" - val publishedAt = "new Date().toISOString()" //if (item.publishDate != null) "new Date((item.publishDate!.timeIntervalSince1970 * 1000)).toISOString()" else "undefined" + val publishedAt = + "new Date().toISOString()" //if (item.publishDate != null) "new Date((item.publishDate!.timeIntervalSince1970 * 1000)).toISOString()" else "undefined" val textFontSize = preferences.textFontSize + val highlightCssFilePath = "highlight${if (themeKey == "Gray") "-dark" else ""}.css" - val content = """ + return """ @@ -106,7 +106,5 @@ data class WebReaderContent( """ - - return content } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt index 355bcb760..fa816a96f 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderViewModel.kt @@ -127,15 +127,18 @@ class WebReaderViewModel @Inject constructor( val storedFontSize = datastoreRepo.getInt(DatastoreKeys.preferredWebFontSize) val storedLineHeight = datastoreRepo.getInt(DatastoreKeys.preferredWebLineHeight) val storedMaxWidth = datastoreRepo.getInt(DatastoreKeys.preferredWebMaxWidthPercentage) - val storedFontFamily = datastoreRepo.getString(DatastoreKeys.preferredWebFontFamily) + + val storedFontFamily = datastoreRepo.getString(DatastoreKeys.preferredWebFontFamily) ?: WebFont.SYSTEM.rawValue + val storedWebFont = WebFont.values().first { it.rawValue == storedFontFamily } + val prefersHighContrastFont = datastoreRepo.getString(DatastoreKeys.prefersWebHighContrastText) == "true" WebPreferences( textFontSize = storedFontSize ?: 12, lineHeight = storedLineHeight ?: 150, maxWidthPercentage = storedMaxWidth ?: 100, - themeKey = "LightGray", - fontFamily = WebFont.SYSTEM, + themeKey = "LightGray", // TODO: match system value + fontFamily = storedWebFont, prefersHighContrastText = prefersHighContrastFont ) } @@ -195,7 +198,11 @@ class WebReaderViewModel @Inject constructor( } fun applyWebFont(font: WebFont) { - // TODO: update value in datastore and dispatch update to web view - Log.d("Font", "Web Font selected: ${font.displayText}") + runBlocking { + datastoreRepo.putString(DatastoreKeys.preferredWebFontFamily, font.rawValue) + } + + val script = "var event = new Event('updateFontFamily');event.fontFamily = '${font.rawValue}';document.dispatchEvent(event);" + enqueueScript(script) } } From 18819b205a0a270d928719cacc0f5f3a8ce41efb Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Thu, 20 Oct 2022 13:06:16 -0700 Subject: [PATCH 17/17] update android build number to 9 --- android/Omnivore/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/Omnivore/app/build.gradle b/android/Omnivore/app/build.gradle index 4bfbabe93..ef847fd4d 100644 --- a/android/Omnivore/app/build.gradle +++ b/android/Omnivore/app/build.gradle @@ -17,8 +17,8 @@ android { applicationId "app.omnivore.omnivore" minSdk 23 targetSdk 32 - versionCode 8 - versionName "0.0.8" + versionCode 9 + versionName "0.0.9" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables {