Add Save as... and improve preview/edit performance
This commit is contained in:
parent
19e678f150
commit
fa1dfb9141
6 changed files with 116 additions and 85 deletions
|
@ -15,11 +15,11 @@ class MarkdownApplication : Application() {
|
||||||
.detectAll()
|
.detectAll()
|
||||||
.penaltyDeath()
|
.penaltyDeath()
|
||||||
.build())
|
.build())
|
||||||
// StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
|
StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
|
||||||
// .detectAll()
|
.detectAll()
|
||||||
// .penaltyLog()
|
.penaltyLog()
|
||||||
// .penaltyDeath()
|
.penaltyDeath()
|
||||||
// .build())
|
.build())
|
||||||
}
|
}
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,9 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
R.id.action_save_as -> {
|
||||||
|
requestFileOp(REQUEST_SAVE_FILE)
|
||||||
|
}
|
||||||
R.id.action_share -> {
|
R.id.action_share -> {
|
||||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
val shareIntent = Intent(Intent.ACTION_SEND)
|
||||||
shareIntent.putExtra(Intent.EXTRA_TEXT, viewModel.markdownUpdates.value)
|
shareIntent.putExtra(Intent.EXTRA_TEXT, viewModel.markdownUpdates.value)
|
||||||
|
|
|
@ -35,6 +35,7 @@ class EditFragment : Fragment(), ViewPagerPage, CoroutineScope {
|
||||||
private var markdownEditorScroller: ScrollView? = null
|
private var markdownEditorScroller: ScrollView? = null
|
||||||
private lateinit var viewModel: MarkdownViewModel
|
private lateinit var viewModel: MarkdownViewModel
|
||||||
override val coroutineContext: CoroutineContext = Dispatchers.Main
|
override val coroutineContext: CoroutineContext = Dispatchers.Main
|
||||||
|
private var readabilityWatcher: TextWatcher? = null
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
@ -71,56 +72,6 @@ class EditFragment : Fragment(), ViewPagerPage, CoroutineScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
var enableReadability = false
|
|
||||||
launch {
|
|
||||||
enableReadability = withContext(Dispatchers.IO) {
|
|
||||||
context?.let {
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(it)
|
|
||||||
.getBoolean(getString(R.string.readability_enabled), false)
|
|
||||||
}?: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (enableReadability) {
|
|
||||||
markdownEditor?.addTextChangedListener(object : TextWatcher {
|
|
||||||
private var previousValue = ""
|
|
||||||
private var searchFor = ""
|
|
||||||
|
|
||||||
override fun afterTextChanged(s: Editable?) {
|
|
||||||
val searchText = s.toString().trim()
|
|
||||||
if (searchText == searchFor)
|
|
||||||
return
|
|
||||||
|
|
||||||
searchFor = searchText
|
|
||||||
|
|
||||||
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)
|
|
||||||
span.setSpan(BackgroundColorSpan(color), sentence.start(), sentence.end(), 0)
|
|
||||||
}
|
|
||||||
markdownEditor?.setTextKeepState(span, TextView.BufferType.SPANNABLE)
|
|
||||||
previousValue = searchFor
|
|
||||||
val timeTakenMs = System.currentTimeMillis() - start
|
|
||||||
Log.d("SimpleMarkdown", "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) {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var touchDown = 0L
|
var touchDown = 0L
|
||||||
var oldX = 0f
|
var oldX = 0f
|
||||||
|
@ -157,6 +108,30 @@ class EditFragment : Fragment(), ViewPagerPage, CoroutineScope {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
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 onDestroy() {
|
override fun onDestroy() {
|
||||||
coroutineContext[Job]?.let {
|
coroutineContext[Job]?.let {
|
||||||
cancel()
|
cancel()
|
||||||
|
@ -171,4 +146,45 @@ class EditFragment : Fragment(), ViewPagerPage, CoroutineScope {
|
||||||
override fun onDeselected() {
|
override fun onDeselected() {
|
||||||
markdownEditor?.hideKeyboard()
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
Log.d("SimpleMarkdown", "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
|
||||||
|
Log.d("SimpleMarkdown", "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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ class PreviewFragment : Fragment(), CoroutineScope {
|
||||||
override val coroutineContext: CoroutineContext = Dispatchers.Main
|
override val coroutineContext: CoroutineContext = Dispatchers.Main
|
||||||
lateinit var viewModel: MarkdownViewModel
|
lateinit var viewModel: MarkdownViewModel
|
||||||
private var markdownPreview: WebView? = null
|
private var markdownPreview: WebView? = null
|
||||||
|
private var style: String = ""
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -42,8 +43,7 @@ class PreviewFragment : Fragment(), CoroutineScope {
|
||||||
this,
|
this,
|
||||||
(requireActivity().application as MarkdownApplication).viewModelFactory
|
(requireActivity().application as MarkdownApplication).viewModelFactory
|
||||||
).get(MarkdownViewModel::class.java)
|
).get(MarkdownViewModel::class.java)
|
||||||
viewModel.markdownUpdates.observe(this, Observer<String> {
|
launch {
|
||||||
markdownPreview?.post {
|
|
||||||
val isNightMode = AppCompatDelegate.getDefaultNightMode() ==
|
val isNightMode = AppCompatDelegate.getDefaultNightMode() ==
|
||||||
AppCompatDelegate.MODE_NIGHT_YES
|
AppCompatDelegate.MODE_NIGHT_YES
|
||||||
|| context!!.resources.configuration.uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES
|
|| context!!.resources.configuration.uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES
|
||||||
|
@ -52,7 +52,6 @@ class PreviewFragment : Fragment(), CoroutineScope {
|
||||||
} else {
|
} else {
|
||||||
R.string.pref_custom_css_default
|
R.string.pref_custom_css_default
|
||||||
}
|
}
|
||||||
launch {
|
|
||||||
val css = withContext(Dispatchers.IO) {
|
val css = withContext(Dispatchers.IO) {
|
||||||
@Suppress("ConstantConditionIf")
|
@Suppress("ConstantConditionIf")
|
||||||
if (!BuildConfig.ENABLE_CUSTOM_CSS) {
|
if (!BuildConfig.ENABLE_CUSTOM_CSS) {
|
||||||
|
@ -65,15 +64,24 @@ class PreviewFragment : Fragment(), CoroutineScope {
|
||||||
) ?: ""
|
) ?: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val style = String.format(FORMAT_CSS, css)
|
style = String.format(FORMAT_CSS, css)
|
||||||
|
updateWebContent(viewModel.markdownUpdates.value ?: "")
|
||||||
|
viewModel.markdownUpdates.observe(this@PreviewFragment, Observer<String> {
|
||||||
|
updateWebContent(it)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateWebContent(markdown: String) {
|
||||||
|
markdownPreview?.post {
|
||||||
|
launch {
|
||||||
markdownPreview?.loadDataWithBaseURL(null,
|
markdownPreview?.loadDataWithBaseURL(null,
|
||||||
style + it.toHtml(),
|
style + markdown.toHtml(),
|
||||||
"text/html",
|
"text/html",
|
||||||
"UTF-8", null
|
"UTF-8", null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_save"
|
android:id="@+id/action_save"
|
||||||
android:title="@string/action_save" />
|
android:title="@string/action_save" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_save_as"
|
||||||
|
android:title="@string/action_save_as" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_lock_swipe"
|
android:id="@+id/action_lock_swipe"
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
<string name="save_changes">Save Changes</string>
|
<string name="save_changes">Save Changes</string>
|
||||||
<string name="prompt_save_changes">Would you like to save your changes?</string>
|
<string name="prompt_save_changes">Would you like to save your changes?</string>
|
||||||
<string name="action_discard">Discard</string>
|
<string name="action_discard">Discard</string>
|
||||||
|
<string name="action_save_as">Save as...</string>
|
||||||
<string-array name="pref_entries_dark_mode">
|
<string-array name="pref_entries_dark_mode">
|
||||||
<item>@string/pref_value_light</item>
|
<item>@string/pref_value_light</item>
|
||||||
<item>@string/pref_value_dark</item>
|
<item>@string/pref_value_dark</item>
|
||||||
|
|
Loading…
Reference in a new issue