Merge pull request #2874 from remychantenay/android-highlight-color-palette

Android: Add Highlight color palette for web reader
This commit is contained in:
Jackson Harper
2023-10-09 09:58:12 +08:00
committed by GitHub
7 changed files with 166 additions and 11 deletions

View File

@ -12,7 +12,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.* import java.util.*
suspend fun DataService.createWebHighlight(jsonString: String) { suspend fun DataService.createWebHighlight(jsonString: String, colorName: String?) {
val createHighlightInput = Gson().fromJson(jsonString, CreateHighlightParams::class.java).asCreateHighlightInput() val createHighlightInput = Gson().fromJson(jsonString, CreateHighlightParams::class.java).asCreateHighlightInput()
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@ -28,7 +28,7 @@ suspend fun DataService.createWebHighlight(jsonString: String) {
createdAt = null, createdAt = null,
updatedAt = null, updatedAt = null,
createdByMe = false, createdByMe = false,
color = null, color = colorName ?: createHighlightInput.color.getOrNull(),
) )
highlight.serverSyncStatus = ServerSyncStatus.NEEDS_CREATION.rawValue highlight.serverSyncStatus = ServerSyncStatus.NEEDS_CREATION.rawValue
@ -80,13 +80,13 @@ suspend fun DataService.createNoteHighlight(savedItemId: String, note: String):
db.savedItemAndHighlightCrossRefDao().insertAll(listOf(crossRef)) db.savedItemAndHighlightCrossRefDao().insertAll(listOf(crossRef))
val newHighlight = networker.createHighlight(input = CreateHighlightParams( val newHighlight = networker.createHighlight(input = CreateHighlightParams(
type = HighlightType.NOTE, type = HighlightType.NOTE,
articleId = savedItemId, articleId = savedItemId,
id = createHighlightId, id = createHighlightId,
shortId = shortId, shortId = shortId,
quote = null, quote = null,
patch = null, patch = null,
annotation = note, annotation = note,
).asCreateHighlightInput()) ).asCreateHighlightInput())
newHighlight?.let { newHighlight?.let {

View File

@ -0,0 +1,8 @@
package app.omnivore.omnivore.ui.components
import androidx.compose.ui.graphics.Color
data class HighlightColor(
val name: String = "yellow",
val color: Color = Color(0xFFFFD234),
)

View File

@ -0,0 +1,48 @@
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
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 app.omnivore.omnivore.ui.components.HighlightColor
import app.omnivore.omnivore.ui.components.HighlightColorPaletteMode
@Composable
fun HighlightColorPalette(
mode: HighlightColorPaletteMode = HighlightColorPaletteMode.Light,
selectedColorName: String,
onColorSelected: (color: HighlightColor) -> Unit,
modifier: Modifier = Modifier,
) {
Surface(
modifier = modifier,
shape = RoundedCornerShape(8.dp),
color = mode.backgroundColor,
shadowElevation = 9.dp
) {
Row(modifier = Modifier.padding(8.dp, 2.dp, 8.dp, 2.dp)) {
HighlightColorPaletteItem(
color = HighlightColor(name = "yellow", Color(0xFFFFD234)),
isSelected = "yellow" == selectedColorName,
onClick = onColorSelected
)
HighlightColorPaletteItem(
color = HighlightColor(name = "red", Color(0xFFFB9A9A)),
isSelected = "red" == selectedColorName,
onClick = onColorSelected
)
HighlightColorPaletteItem(
color = HighlightColor(name = "green", Color(0xFF55C689)),
isSelected = "green" == selectedColorName,
onClick = onColorSelected
)
HighlightColorPaletteItem(
color = HighlightColor(name = "blue", Color(0xFF6AB1FF)),
isSelected = "blue" == selectedColorName,
onClick = onColorSelected
)
}
}
}

View File

@ -0,0 +1,47 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Check
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import app.omnivore.omnivore.ui.components.HighlightColor
@Composable
fun HighlightColorPaletteItem(
color: HighlightColor,
isSelected: Boolean,
onClick: (color: HighlightColor) -> Unit,
modifier: Modifier = Modifier.padding(6.dp)
) {
Column (
modifier = modifier,
) {
Box(
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.background(color.color)
.clickable { onClick(color) }
)
{
if (isSelected) {
Icon(
Icons.Rounded.Check,
contentDescription = "checkIcon",
tint = Color.DarkGray,
modifier = Modifier.align(Alignment.Center)
)
}
}
}
}

View File

@ -0,0 +1,7 @@
package app.omnivore.omnivore.ui.components
import androidx.compose.ui.graphics.Color
enum class HighlightColorPaletteMode(val backgroundColor: Color) {
Light(Color.White),
Dark(Color.Black),
}

View File

@ -1,5 +1,6 @@
package app.omnivore.omnivore.ui.reader package app.omnivore.omnivore.ui.reader
import HighlightColorPalette
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
@ -18,8 +19,12 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import app.omnivore.omnivore.R import app.omnivore.omnivore.R
import app.omnivore.omnivore.ui.components.HighlightColorPaletteMode
import com.google.gson.Gson import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -40,6 +45,9 @@ fun WebReader(
WebView.setWebContentsDebuggingEnabled(true) WebView.setWebContentsDebuggingEnabled(true)
val showHighlightColorPalette = webReaderViewModel.showHighlightColorPalette.observeAsState()
val highlightColor = webReaderViewModel.highlightColor.observeAsState()
Box { Box {
AndroidView(factory = { AndroidView(factory = {
OmnivoreWebView(it).apply { OmnivoreWebView(it).apply {
@ -144,6 +152,18 @@ fun WebReader(
webReaderViewModel.resetJavascriptDispatchQueue() webReaderViewModel.resetJavascriptDispatchQueue()
} }
}) })
if (showHighlightColorPalette.value == true) {
HighlightColorPalette(
mode = if (isDarkMode) HighlightColorPaletteMode.Dark else HighlightColorPaletteMode.Light,
selectedColorName = highlightColor.value?.name ?: "yellow",
onColorSelected = {
webReaderViewModel.setHighlightColor(it)
},
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(12.dp, 12.dp, 12.dp, 36.dp)
)
}
} }
} }
@ -164,6 +184,7 @@ class OmnivoreWebView(context: Context) : WebView(context), OnScrollChangeListen
Log.d("wv", "inflating existing highlight menu") Log.d("wv", "inflating existing highlight menu")
mode.menuInflater.inflate(R.menu.highlight_selection_menu, menu) mode.menuInflater.inflate(R.menu.highlight_selection_menu, menu)
} else { } else {
viewModel?.showHighlightColorPalette()
mode.menuInflater.inflate(R.menu.text_selection_menu, menu) mode.menuInflater.inflate(R.menu.text_selection_menu, menu)
} }
return true return true
@ -233,6 +254,7 @@ class OmnivoreWebView(context: Context) : WebView(context), OnScrollChangeListen
override fun onDestroyActionMode(mode: ActionMode) { override fun onDestroyActionMode(mode: ActionMode) {
Log.d("wv", "destroying menu: $mode") Log.d("wv", "destroying menu: $mode")
viewModel?.hasTappedExistingHighlight = false viewModel?.hasTappedExistingHighlight = false
viewModel?.hideHighlightColorPalette()
actionMode = null actionMode = null
} }

View File

@ -21,6 +21,7 @@ import app.omnivore.omnivore.networking.*
import app.omnivore.omnivore.persistence.entities.SavedItem import app.omnivore.omnivore.persistence.entities.SavedItem
import app.omnivore.omnivore.persistence.entities.SavedItemAndSavedItemLabelCrossRef import app.omnivore.omnivore.persistence.entities.SavedItemAndSavedItemLabelCrossRef
import app.omnivore.omnivore.persistence.entities.SavedItemLabel import app.omnivore.omnivore.persistence.entities.SavedItemLabel
import app.omnivore.omnivore.ui.components.HighlightColor
import app.omnivore.omnivore.ui.library.SavedItemAction import app.omnivore.omnivore.ui.library.SavedItemAction
import com.apollographql.apollo3.api.Optional import com.apollographql.apollo3.api.Optional
import com.apollographql.apollo3.api.Optional.Companion.presentIfNotNull import com.apollographql.apollo3.api.Optional.Companion.presentIfNotNull
@ -77,7 +78,10 @@ class WebReaderViewModel @Inject constructor(
var lastTapCoordinates: TapCoordinates? = null var lastTapCoordinates: TapCoordinates? = null
private var isLoading = false private var isLoading = false
private var slug: String? = null private var slug: String? = null
val showHighlightColorPalette = MutableLiveData(false)
val highlightColor = MutableLiveData(HighlightColor())
fun loadItem(slug: String?, requestID: String?) { fun loadItem(slug: String?, requestID: String?) {
this.slug = slug this.slug = slug
if (isLoading || webReaderParamsLiveData.value != null) { return } if (isLoading || webReaderParamsLiveData.value != null) { return }
@ -297,11 +301,30 @@ class WebReaderViewModel @Inject constructor(
} }
} }
fun showHighlightColorPalette() {
CoroutineScope(Dispatchers.Main).launch {
showHighlightColorPalette.postValue(true)
}
}
fun hideHighlightColorPalette() {
CoroutineScope(Dispatchers.Main).launch {
showHighlightColorPalette.postValue(false)
}
}
fun setHighlightColor(color: HighlightColor) {
CoroutineScope(Dispatchers.Main).launch {
highlightColor.postValue(color)
}
}
fun handleIncomingWebMessage(actionID: String, jsonString: String) { fun handleIncomingWebMessage(actionID: String, jsonString: String) {
when (actionID) { when (actionID) {
"createHighlight" -> { "createHighlight" -> {
viewModelScope.launch { viewModelScope.launch {
dataService.createWebHighlight(jsonString) dataService.createWebHighlight(jsonString, highlightColor.value?.name)
} }
} }
"deleteHighlight" -> { "deleteHighlight" -> {