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