Files
Dantotsu/app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt
rebel onion f9f9767ecc chore: clean
2025-01-06 08:22:44 -06:00

287 lines
11 KiB
Kotlin

package ani.dantotsu.util
import android.os.Bundle
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.widget.addTextChangedListener
import ani.dantotsu.R
import ani.dantotsu.buildMarkwon
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.databinding.ActivityMarkdownCreatorBinding
import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.others.AndroidBug5497Workaround
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.toast
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import io.noties.markwon.editor.MarkwonEditor
import io.noties.markwon.editor.MarkwonEditorTextWatcher
import kotlinx.coroutines.DelicateCoroutinesApi
import tachiyomi.core.util.lang.launchIO
class ActivityMarkdownCreator : AppCompatActivity() {
private lateinit var binding: ActivityMarkdownCreatorBinding
private lateinit var type: String
private var text: String = ""
private var ping: String? = null
private var parentId: Int = 0
private var isPreviewMode: Boolean = false
enum class MarkdownFormat(
val syntax: String,
val selectionOffset: Int,
val imageViewId: Int
) {
BOLD("****", 2, R.id.formatBold),
ITALIC("**", 1, R.id.formatItalic),
STRIKETHROUGH("~~~~", 2, R.id.formatStrikethrough),
SPOILER("~!!~", 2, R.id.formatSpoiler),
LINK("[Placeholder](%s)", 0, R.id.formatLink),
IMAGE("img(%s)", 0, R.id.formatImage),
YOUTUBE("youtube(%s)", 0, R.id.formatYoutube),
VIDEO("webm(%s)", 0, R.id.formatVideo),
ORDERED_LIST("1. ", 3, R.id.formatListOrdered),
UNORDERED_LIST("- ", 2, R.id.formatListUnordered),
HEADING("# ", 2, R.id.formatTitle),
CENTERED("~~~~~~", 3, R.id.formatCenter),
QUOTE("> ", 2, R.id.formatQuote),
CODE("``", 1, R.id.formatCode)
}
@OptIn(DelicateCoroutinesApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ThemeManager(this).applyTheme()
initActivity(this)
binding = ActivityMarkdownCreatorBinding.inflate(layoutInflater)
binding.markdownCreatorToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = statusBarHeight
}
binding.markdownOptionsContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin += navBarHeight
}
setContentView(binding.root)
AndroidBug5497Workaround.assistActivity(this) {}
val params = binding.createButton.layoutParams as ViewGroup.MarginLayoutParams
params.marginEnd = 16 * resources.displayMetrics.density.toInt()
binding.createButton.layoutParams = params
if (intent.hasExtra("type")) {
type = intent.getStringExtra("type")!!
} else {
toast("Error: No type")
finish()
return
}
val editId = intent.getIntExtra("edit", -1)
val userId = intent.getIntExtra("userId", -1)
parentId = intent.getIntExtra("parentId", -1)
when (type) {
"replyActivity" -> if (parentId == -1) {
toast("Error: No parent ID")
finish()
return
}
"message" -> {
if (editId == -1) {
binding.privateCheckbox.visibility = ViewGroup.VISIBLE
}
}
}
var private = false
binding.privateCheckbox.setOnCheckedChangeListener { _, isChecked ->
private = isChecked
}
ping = intent.getStringExtra("other")
text = ping ?: ""
binding.editText.setText(text)
binding.editText.addTextChangedListener {
if (!isPreviewMode) {
text = it.toString()
}
}
previewMarkdown(false)
binding.markdownCreatorBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
binding.createButton.setOnClickListener {
if (text.isBlank()) {
toast(getString(R.string.cannot_be_empty))
return@setOnClickListener
}
customAlertDialog().apply {
setTitle(R.string.warning)
setMessage(R.string.post_to_anilist_warning)
setPosButton(R.string.ok) {
launchIO {
val isEdit = editId != -1
val success = when (type) {
"activity" -> if (isEdit) {
Anilist.mutation.postActivity(text, editId)
} else {
Anilist.mutation.postActivity(text)
}
//"review" -> Anilist.mutation.postReview(text)
"replyActivity" -> if (isEdit) {
Anilist.mutation.postReply(parentId, text, editId)
} else {
Anilist.mutation.postReply(parentId, text)
}
"message" -> if (isEdit) {
Anilist.mutation.postMessage(userId, text, editId)
} else {
Anilist.mutation.postMessage(userId, text, isPrivate = private)
}
else -> "Error: Unknown type"
}
toast(success)
finish()
}
}
setNeutralButton(R.string.open_rules) {
openLinkInBrowser("https://anilist.co/forum/thread/14")
}
setNegButton(R.string.cancel)
show()
}
}
binding.previewCheckbox.setOnClickListener {
isPreviewMode = !isPreviewMode
previewMarkdown(isPreviewMode)
if (isPreviewMode) {
toast("Preview enabled")
} else {
toast("Preview disabled")
}
}
binding.editText.requestFocus()
setupMarkdownButtons()
}
private fun setupMarkdownButtons() {
MarkdownFormat.entries.forEach { format ->
findViewById<ImageView>(format.imageViewId)?.setOnClickListener {
applyMarkdownFormat(format)
}
}
}
private fun applyMarkdownFormat(format: MarkdownFormat) {
val start = binding.editText.selectionStart
val end = binding.editText.selectionEnd
if (start != end) {
val selectedText = binding.editText.text?.substring(start, end) ?: ""
val lines = selectedText.split("\n")
val newText = when (format) {
MarkdownFormat.UNORDERED_LIST -> {
lines.joinToString("\n") { "- $it" }
}
MarkdownFormat.ORDERED_LIST -> {
lines.mapIndexed { index, line -> "${index + 1}. $line" }.joinToString("\n")
}
else -> {
if (format.syntax.contains("%s")) {
String.format(format.syntax, selectedText)
} else {
format.syntax.substring(0, format.selectionOffset) +
selectedText +
format.syntax.substring(format.selectionOffset)
}
}
}
binding.editText.text?.replace(start, end, newText)
binding.editText.setSelection(start + newText.length)
} else {
if (format.syntax.contains("%s")) {
showInputDialog(format, start)
} else {
val newText = format.syntax
binding.editText.text?.insert(start, newText)
binding.editText.setSelection(start + format.selectionOffset)
}
}
}
private fun showInputDialog(format: MarkdownFormat, position: Int) {
val inputLayout = TextInputLayout(this).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
boxBackgroundMode = TextInputLayout.BOX_BACKGROUND_OUTLINE
isHintEnabled = true
}
val inputEditText = TextInputEditText(this).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
}
inputLayout.addView(inputEditText)
val container = FrameLayout(this).apply {
addView(inputLayout)
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
setPadding(0, 0, 0, 0)
}
customAlertDialog().apply {
setTitle("Paste your link here")
setCustomView(container)
setPosButton(getString(R.string.ok)) {
val input = inputEditText.text.toString()
val formattedText = String.format(format.syntax, input)
binding.editText.text?.insert(position, formattedText)
binding.editText.setSelection(position + formattedText.length)
}
setNegButton(getString(R.string.cancel))
}.show()
inputEditText.requestFocus()
}
private fun previewMarkdown(preview: Boolean) {
val markwon = buildMarkwon(this, false, anilist = true)
if (preview) {
binding.editText.isVisible = false
binding.editText.isEnabled = false
binding.markdownPreview.isVisible = true
markwon.setMarkdown(binding.markdownPreview, AniMarkdown.getBasicAniHTML(text))
} else {
binding.editText.isVisible = true
binding.markdownPreview.isVisible = false
binding.editText.setText(text)
binding.editText.isEnabled = true
val markwonEditor = MarkwonEditor.create(markwon)
binding.editText.addTextChangedListener(
MarkwonEditorTextWatcher.withProcess(markwonEditor)
)
}
}
}