Implement Readability Highlighting in compose
This could still use some performance tweaks to make it run a bit better but at least it works
This commit is contained in:
parent
c7e54f21d9
commit
493444aaab
4 changed files with 322 additions and 279 deletions
|
@ -8,7 +8,7 @@ class Readability(private val content: String) {
|
|||
val list = ArrayList<Sentence>()
|
||||
var startOfSentance = 0
|
||||
var lineBuilder = StringBuilder()
|
||||
for (i in 0 until content.length) {
|
||||
for (i in content.indices) {
|
||||
val c = content[i] + ""
|
||||
if (DELIMS.contains(c)) {
|
||||
list.add(Sentence(content, startOfSentance, i))
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.wbrawner.simplemarkdown.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.text.SpannableString
|
||||
import android.text.style.BackgroundColorSpan
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
@ -48,16 +50,22 @@ import androidx.compose.ui.graphics.Brush
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import androidx.navigation.NavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.wbrawner.simplemarkdown.R
|
||||
import com.wbrawner.simplemarkdown.model.Readability
|
||||
import com.wbrawner.simplemarkdown.view.activity.Route
|
||||
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
|
@ -121,6 +129,11 @@ fun MainScreen(navController: NavController, viewModel: MarkdownViewModel) {
|
|||
}) { paddingValues ->
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val pagerState = rememberPagerState { 2 }
|
||||
val context = LocalContext.current
|
||||
val enableReadability = remember {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getBoolean(PREF_KEY_READABILITY, false)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
@ -139,14 +152,32 @@ fun MainScreen(navController: NavController, viewModel: MarkdownViewModel) {
|
|||
userScrollEnabled = !lockSwiping
|
||||
) { page ->
|
||||
val markdown by viewModel.markdownUpdates.collectAsState()
|
||||
var textFieldValue by remember {
|
||||
val annotatedMarkdown = if (enableReadability) {
|
||||
markdown.annotateReadability()
|
||||
} else {
|
||||
AnnotatedString(markdown)
|
||||
}
|
||||
mutableStateOf(TextFieldValue(annotatedMarkdown))
|
||||
}
|
||||
if (page == 0) {
|
||||
BasicTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(8.dp),
|
||||
value = markdown,
|
||||
onValueChange = { viewModel.updateMarkdown(it) },
|
||||
textStyle = TextStyle.Default.copy(fontFamily = FontFamily.Monospace, color = MaterialTheme.colorScheme.onSurface),
|
||||
value = textFieldValue,
|
||||
onValueChange = {
|
||||
textFieldValue = if (enableReadability) {
|
||||
it.copy(annotatedString = it.text.annotateReadability())
|
||||
} else {
|
||||
it
|
||||
}
|
||||
viewModel.updateMarkdown(it.text)
|
||||
},
|
||||
textStyle = TextStyle.Default.copy(
|
||||
fontFamily = FontFamily.Monospace,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences),
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface)
|
||||
)
|
||||
|
@ -159,6 +190,18 @@ fun MainScreen(navController: NavController, viewModel: MarkdownViewModel) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun String.annotateReadability(): AnnotatedString {
|
||||
val readability = Readability(this)
|
||||
val annotated = AnnotatedString.Builder(this)
|
||||
for (sentence in readability.sentences()) {
|
||||
var color = Color.Transparent
|
||||
if (sentence.syllableCount() > 25) color = Color(229, 232, 42, 100)
|
||||
if (sentence.syllableCount() > 35) color = Color(193, 66, 66, 100)
|
||||
annotated.addStyle(SpanStyle(background = color), sentence.start(), sentence.end())
|
||||
}
|
||||
return annotated.toAnnotatedString()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MarkdownNavigationDrawer(
|
||||
navigate: (Route) -> Unit, content: @Composable (drawerState: DrawerState) -> Unit
|
||||
|
|
|
@ -1,179 +1,179 @@
|
|||
package com.wbrawner.simplemarkdown.view.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.SpannableString
|
||||
import android.text.TextWatcher
|
||||
import android.text.style.BackgroundColorSpan
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.wbrawner.simplemarkdown.R
|
||||
import com.wbrawner.simplemarkdown.model.Readability
|
||||
import com.wbrawner.simplemarkdown.utility.hideKeyboard
|
||||
import com.wbrawner.simplemarkdown.utility.showKeyboard
|
||||
import com.wbrawner.simplemarkdown.view.ViewPagerPage
|
||||
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
|
||||
import kotlinx.coroutines.*
|
||||
import timber.log.Timber
|
||||
import kotlin.math.abs
|
||||
|
||||
class EditFragment : Fragment(), ViewPagerPage {
|
||||
private var markdownEditor: EditText? = null
|
||||
private var markdownEditorScroller: NestedScrollView? = null
|
||||
private val viewModel: MarkdownViewModel by viewModels({ requireParentFragment() })
|
||||
private var readabilityWatcher: TextWatcher? = null
|
||||
private val markdownWatcher = object : TextWatcher {
|
||||
private var searchFor = ""
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
val searchText = s.toString().trim()
|
||||
if (searchText == searchFor)
|
||||
return
|
||||
|
||||
searchFor = searchText
|
||||
|
||||
lifecycleScope.launch {
|
||||
delay(50)
|
||||
if (searchText != searchFor)
|
||||
return@launch
|
||||
viewModel.updateMarkdown(searchText)
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? =
|
||||
inflater.inflate(R.layout.fragment_edit, container, false)
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
markdownEditor = view.findViewById(R.id.markdown_edit)
|
||||
markdownEditorScroller = view.findViewById(R.id.markdown_edit_container)
|
||||
markdownEditor?.addTextChangedListener(markdownWatcher)
|
||||
|
||||
var touchDown = 0L
|
||||
var oldX = 0f
|
||||
var oldY = 0f
|
||||
markdownEditorScroller!!.setOnTouchListener { _, event ->
|
||||
// The ScrollView's onClickListener doesn't seem to be called, so I've had to
|
||||
// implement a sort of custom click listener that checks that the tap was both quick
|
||||
// and didn't drag.
|
||||
when (event?.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
touchDown = System.currentTimeMillis()
|
||||
oldX = event.rawX
|
||||
oldY = event.rawY
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
if (System.currentTimeMillis() - touchDown < 150
|
||||
&& abs(event.rawX - oldX) < 25
|
||||
&& abs(event.rawY - oldY) < 25)
|
||||
markdownEditor?.showKeyboard()
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
markdownEditor?.setText(viewModel.markdownUpdates.value)
|
||||
viewModel.editorActions.observe(viewLifecycleOwner, Observer {
|
||||
if (it.consumed.getAndSet(true)) return@Observer
|
||||
if (it is MarkdownViewModel.EditorAction.Load) {
|
||||
markdownEditor?.apply {
|
||||
removeTextChangedListener(markdownWatcher)
|
||||
setText(it.markdown)
|
||||
addTextChangedListener(markdownWatcher)
|
||||
}
|
||||
}
|
||||
})
|
||||
lifecycleScope.launch {
|
||||
val enableReadability = withContext(Dispatchers.IO) {
|
||||
context?.let {
|
||||
PreferenceManager.getDefaultSharedPreferences(it)
|
||||
.getBoolean(getString(R.string.readability_enabled), false)
|
||||
} ?: false
|
||||
}
|
||||
if (enableReadability) {
|
||||
if (readabilityWatcher == null) {
|
||||
readabilityWatcher = ReadabilityTextWatcher()
|
||||
}
|
||||
markdownEditor?.addTextChangedListener(readabilityWatcher)
|
||||
} else {
|
||||
readabilityWatcher?.let {
|
||||
markdownEditor?.removeTextChangedListener(it)
|
||||
}
|
||||
readabilityWatcher = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSelected() {
|
||||
markdownEditor?.showKeyboard()
|
||||
}
|
||||
|
||||
override fun onDeselected() {
|
||||
markdownEditor?.hideKeyboard()
|
||||
}
|
||||
|
||||
inner class ReadabilityTextWatcher : TextWatcher {
|
||||
private var previousValue = ""
|
||||
private var searchFor = ""
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
val searchText = s.toString().trim()
|
||||
if (searchText == searchFor)
|
||||
return
|
||||
|
||||
searchFor = searchText
|
||||
|
||||
lifecycleScope.launch {
|
||||
delay(250)
|
||||
if (searchText != searchFor)
|
||||
return@launch
|
||||
val start = System.currentTimeMillis()
|
||||
if (searchFor.isEmpty()) return@launch
|
||||
if (previousValue == searchFor) return@launch
|
||||
val readability = Readability(searchFor)
|
||||
val span = SpannableString(searchFor)
|
||||
for (sentence in readability.sentences()) {
|
||||
var color = Color.TRANSPARENT
|
||||
if (sentence.syllableCount() > 25) color = Color.argb(100, 229, 232, 42)
|
||||
if (sentence.syllableCount() > 35) color = Color.argb(100, 193, 66, 66)
|
||||
Timber.d("Sentence start: ${sentence.start()} end: ${
|
||||
sentence
|
||||
.end()
|
||||
}")
|
||||
span.setSpan(BackgroundColorSpan(color), sentence.start(), sentence.end(), 0)
|
||||
}
|
||||
markdownEditor?.setTextKeepState(span, TextView.BufferType.SPANNABLE)
|
||||
previousValue = searchFor
|
||||
val timeTakenMs = System.currentTimeMillis() - start
|
||||
Timber.d("Handled markdown in $timeTakenMs ms")
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.wbrawner.simplemarkdown.view.fragment
|
||||
//
|
||||
//import android.annotation.SuppressLint
|
||||
//import android.graphics.Color
|
||||
//import android.os.Bundle
|
||||
//import android.text.Editable
|
||||
//import android.text.SpannableString
|
||||
//import android.text.TextWatcher
|
||||
//import android.text.style.BackgroundColorSpan
|
||||
//import android.view.LayoutInflater
|
||||
//import android.view.MotionEvent
|
||||
//import android.view.View
|
||||
//import android.view.ViewGroup
|
||||
//import android.widget.EditText
|
||||
//import android.widget.TextView
|
||||
//import androidx.core.widget.NestedScrollView
|
||||
//import androidx.fragment.app.Fragment
|
||||
//import androidx.fragment.app.viewModels
|
||||
//import androidx.lifecycle.Observer
|
||||
//import androidx.lifecycle.lifecycleScope
|
||||
//import androidx.preference.PreferenceManager
|
||||
//import com.wbrawner.simplemarkdown.R
|
||||
//import com.wbrawner.simplemarkdown.model.Readability
|
||||
//import com.wbrawner.simplemarkdown.utility.hideKeyboard
|
||||
//import com.wbrawner.simplemarkdown.utility.showKeyboard
|
||||
//import com.wbrawner.simplemarkdown.view.ViewPagerPage
|
||||
//import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
|
||||
//import kotlinx.coroutines.*
|
||||
//import timber.log.Timber
|
||||
//import kotlin.math.abs
|
||||
//
|
||||
//class EditFragment : Fragment(), ViewPagerPage {
|
||||
// private var markdownEditor: EditText? = null
|
||||
// private var markdownEditorScroller: NestedScrollView? = null
|
||||
// private val viewModel: MarkdownViewModel by viewModels({ requireParentFragment() })
|
||||
// private var readabilityWatcher: TextWatcher? = null
|
||||
// private val markdownWatcher = object : TextWatcher {
|
||||
// private var searchFor = ""
|
||||
//
|
||||
// override fun afterTextChanged(s: Editable?) {
|
||||
// val searchText = s.toString().trim()
|
||||
// if (searchText == searchFor)
|
||||
// return
|
||||
//
|
||||
// searchFor = searchText
|
||||
//
|
||||
// lifecycleScope.launch {
|
||||
// delay(50)
|
||||
// if (searchText != searchFor)
|
||||
// return@launch
|
||||
// viewModel.updateMarkdown(searchText)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
||||
// }
|
||||
//
|
||||
// override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @SuppressLint("ClickableViewAccessibility")
|
||||
// override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
// savedInstanceState: Bundle?): View? =
|
||||
// inflater.inflate(R.layout.fragment_edit, container, false)
|
||||
//
|
||||
// @SuppressLint("ClickableViewAccessibility")
|
||||
// override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// super.onViewCreated(view, savedInstanceState)
|
||||
// markdownEditor = view.findViewById(R.id.markdown_edit)
|
||||
// markdownEditorScroller = view.findViewById(R.id.markdown_edit_container)
|
||||
// markdownEditor?.addTextChangedListener(markdownWatcher)
|
||||
//
|
||||
// var touchDown = 0L
|
||||
// var oldX = 0f
|
||||
// var oldY = 0f
|
||||
// markdownEditorScroller!!.setOnTouchListener { _, event ->
|
||||
// // The ScrollView's onClickListener doesn't seem to be called, so I've had to
|
||||
// // implement a sort of custom click listener that checks that the tap was both quick
|
||||
// // and didn't drag.
|
||||
// when (event?.action) {
|
||||
// MotionEvent.ACTION_DOWN -> {
|
||||
// touchDown = System.currentTimeMillis()
|
||||
// oldX = event.rawX
|
||||
// oldY = event.rawY
|
||||
// }
|
||||
// MotionEvent.ACTION_UP -> {
|
||||
// if (System.currentTimeMillis() - touchDown < 150
|
||||
// && abs(event.rawX - oldX) < 25
|
||||
// && abs(event.rawY - oldY) < 25)
|
||||
// markdownEditor?.showKeyboard()
|
||||
// }
|
||||
// }
|
||||
// false
|
||||
// }
|
||||
// markdownEditor?.setText(viewModel.markdownUpdates.value)
|
||||
// viewModel.editorActions.observe(viewLifecycleOwner, Observer {
|
||||
// if (it.consumed.getAndSet(true)) return@Observer
|
||||
// if (it is MarkdownViewModel.EditorAction.Load) {
|
||||
// markdownEditor?.apply {
|
||||
// removeTextChangedListener(markdownWatcher)
|
||||
// setText(it.markdown)
|
||||
// addTextChangedListener(markdownWatcher)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// lifecycleScope.launch {
|
||||
// val enableReadability = withContext(Dispatchers.IO) {
|
||||
// context?.let {
|
||||
// PreferenceManager.getDefaultSharedPreferences(it)
|
||||
// .getBoolean(getString(R.string.readability_enabled), false)
|
||||
// } ?: false
|
||||
// }
|
||||
// if (enableReadability) {
|
||||
// if (readabilityWatcher == null) {
|
||||
// readabilityWatcher = ReadabilityTextWatcher()
|
||||
// }
|
||||
// markdownEditor?.addTextChangedListener(readabilityWatcher)
|
||||
// } else {
|
||||
// readabilityWatcher?.let {
|
||||
// markdownEditor?.removeTextChangedListener(it)
|
||||
// }
|
||||
// readabilityWatcher = null
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onSelected() {
|
||||
// markdownEditor?.showKeyboard()
|
||||
// }
|
||||
//
|
||||
// override fun onDeselected() {
|
||||
// markdownEditor?.hideKeyboard()
|
||||
// }
|
||||
//
|
||||
// inner class ReadabilityTextWatcher : TextWatcher {
|
||||
// private var previousValue = ""
|
||||
// private var searchFor = ""
|
||||
//
|
||||
// override fun afterTextChanged(s: Editable?) {
|
||||
// val searchText = s.toString().trim()
|
||||
// if (searchText == searchFor)
|
||||
// return
|
||||
//
|
||||
// searchFor = searchText
|
||||
//
|
||||
// lifecycleScope.launch {
|
||||
// delay(250)
|
||||
// if (searchText != searchFor)
|
||||
// return@launch
|
||||
// val start = System.currentTimeMillis()
|
||||
// if (searchFor.isEmpty()) return@launch
|
||||
// if (previousValue == searchFor) return@launch
|
||||
// val readability = Readability(searchFor)
|
||||
// val span = SpannableString(searchFor)
|
||||
// for (sentence in readability.sentences()) {
|
||||
// var color = Color.TRANSPARENT
|
||||
// if (sentence.syllableCount() > 25) color = Color.argb(100, 229, 232, 42)
|
||||
// if (sentence.syllableCount() > 35) color = Color.argb(100, 193, 66, 66)
|
||||
// Timber.d("Sentence start: ${sentence.start()} end: ${
|
||||
// sentence
|
||||
// .end()
|
||||
// }")
|
||||
// span.setSpan(BackgroundColorSpan(color), sentence.start(), sentence.end(), 0)
|
||||
// }
|
||||
// markdownEditor?.setTextKeepState(span, TextView.BufferType.SPANNABLE)
|
||||
// previousValue = searchFor
|
||||
// val timeTakenMs = System.currentTimeMillis() - start
|
||||
// Timber.d("Handled markdown in $timeTakenMs ms")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
||||
// }
|
||||
//
|
||||
// override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -1,96 +1,96 @@
|
|||
package com.wbrawner.simplemarkdown.view.fragment
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebView
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.wbrawner.simplemarkdown.BuildConfig
|
||||
import com.wbrawner.simplemarkdown.R
|
||||
import com.wbrawner.simplemarkdown.utility.toHtml
|
||||
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class PreviewFragment : Fragment() {
|
||||
private val viewModel: MarkdownViewModel by viewModels({ requireParentFragment() })
|
||||
private var markdownPreview: WebView? = null
|
||||
private var style: String = ""
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? = inflater.inflate(R.layout.fragment_preview, container, false)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
markdownPreview = view.findViewById(R.id.markdown_view)
|
||||
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
|
||||
lifecycleScope.launch {
|
||||
val isNightMode = AppCompatDelegate.getDefaultNightMode() ==
|
||||
AppCompatDelegate.MODE_NIGHT_YES
|
||||
|| requireContext().resources.configuration.uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES
|
||||
val defaultCssId = if (isNightMode) {
|
||||
R.string.pref_custom_css_default_dark
|
||||
} else {
|
||||
R.string.pref_custom_css_default
|
||||
}
|
||||
val css = withContext(Dispatchers.IO) {
|
||||
val context = context ?: return@withContext null
|
||||
@Suppress("ConstantConditionIf")
|
||||
if (!BuildConfig.ENABLE_CUSTOM_CSS) {
|
||||
context.getString(defaultCssId)
|
||||
} else {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getString(
|
||||
getString(R.string.pref_custom_css),
|
||||
getString(defaultCssId)
|
||||
)
|
||||
}
|
||||
}
|
||||
style = String.format(FORMAT_CSS, css ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
updateWebContent(viewModel.markdownUpdates.value ?: "")
|
||||
// viewModel.markdownUpdates.observe(this, {
|
||||
// updateWebContent(it)
|
||||
// })
|
||||
}
|
||||
|
||||
private fun updateWebContent(markdown: String) {
|
||||
markdownPreview?.post {
|
||||
lifecycleScope.launch {
|
||||
markdownPreview?.loadDataWithBaseURL(null,
|
||||
style + markdown.toHtml(),
|
||||
"text/html",
|
||||
"UTF-8", null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
markdownPreview?.let {
|
||||
(it.parent as ViewGroup).removeView(it)
|
||||
it.destroy()
|
||||
markdownPreview = null
|
||||
}
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
companion object {
|
||||
var FORMAT_CSS = "<style>" +
|
||||
"%s" +
|
||||
"</style>"
|
||||
}
|
||||
}
|
||||
//package com.wbrawner.simplemarkdown.view.fragment
|
||||
//
|
||||
//import android.content.Context
|
||||
//import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
||||
//import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
//import android.os.Bundle
|
||||
//import android.view.LayoutInflater
|
||||
//import android.view.View
|
||||
//import android.view.ViewGroup
|
||||
//import android.webkit.WebView
|
||||
//import androidx.appcompat.app.AppCompatDelegate
|
||||
//import androidx.fragment.app.Fragment
|
||||
//import androidx.fragment.app.viewModels
|
||||
//import androidx.lifecycle.lifecycleScope
|
||||
//import androidx.preference.PreferenceManager
|
||||
//import com.wbrawner.simplemarkdown.BuildConfig
|
||||
//import com.wbrawner.simplemarkdown.R
|
||||
//import com.wbrawner.simplemarkdown.utility.toHtml
|
||||
//import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
|
||||
//import kotlinx.coroutines.*
|
||||
//
|
||||
//class PreviewFragment : Fragment() {
|
||||
// private val viewModel: MarkdownViewModel by viewModels({ requireParentFragment() })
|
||||
// private var markdownPreview: WebView? = null
|
||||
// private var style: String = ""
|
||||
//
|
||||
// override fun onCreateView(
|
||||
// inflater: LayoutInflater,
|
||||
// container: ViewGroup?,
|
||||
// savedInstanceState: Bundle?
|
||||
// ): View? = inflater.inflate(R.layout.fragment_preview, container, false)
|
||||
//
|
||||
// override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// markdownPreview = view.findViewById(R.id.markdown_view)
|
||||
// WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
|
||||
// lifecycleScope.launch {
|
||||
// val isNightMode = AppCompatDelegate.getDefaultNightMode() ==
|
||||
// AppCompatDelegate.MODE_NIGHT_YES
|
||||
// || requireContext().resources.configuration.uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES
|
||||
// val defaultCssId = if (isNightMode) {
|
||||
// R.string.pref_custom_css_default_dark
|
||||
// } else {
|
||||
// R.string.pref_custom_css_default
|
||||
// }
|
||||
// val css = withContext(Dispatchers.IO) {
|
||||
// val context = context ?: return@withContext null
|
||||
// @Suppress("ConstantConditionIf")
|
||||
// if (!BuildConfig.ENABLE_CUSTOM_CSS) {
|
||||
// context.getString(defaultCssId)
|
||||
// } else {
|
||||
// PreferenceManager.getDefaultSharedPreferences(context)
|
||||
// .getString(
|
||||
// getString(R.string.pref_custom_css),
|
||||
// getString(defaultCssId)
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// style = String.format(FORMAT_CSS, css ?: "")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onAttach(context: Context) {
|
||||
// super.onAttach(context)
|
||||
// updateWebContent(viewModel.markdownUpdates.value ?: "")
|
||||
//// viewModel.markdownUpdates.observe(this, {
|
||||
//// updateWebContent(it)
|
||||
//// })
|
||||
// }
|
||||
//
|
||||
// private fun updateWebContent(markdown: String) {
|
||||
// markdownPreview?.post {
|
||||
// lifecycleScope.launch {
|
||||
// markdownPreview?.loadDataWithBaseURL(null,
|
||||
// style + markdown.toHtml(),
|
||||
// "text/html",
|
||||
// "UTF-8", null
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onDestroyView() {
|
||||
// markdownPreview?.let {
|
||||
// (it.parent as ViewGroup).removeView(it)
|
||||
// it.destroy()
|
||||
// markdownPreview = null
|
||||
// }
|
||||
// super.onDestroyView()
|
||||
// }
|
||||
//
|
||||
// companion object {
|
||||
// var FORMAT_CSS = "<style>" +
|
||||
// "%s" +
|
||||
// "</style>"
|
||||
// }
|
||||
//}
|
||||
|
|
Loading…
Reference in a new issue