fix: some markdown fixes

This commit is contained in:
rebelonion 2024-06-29 10:59:18 -05:00
parent 6eb654bf51
commit b09f26ed34
13 changed files with 194 additions and 27 deletions

View file

@ -19,6 +19,7 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />

View file

@ -68,7 +68,6 @@ import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
@ -98,6 +97,7 @@ import ani.dantotsu.databinding.ItemCountDownBinding
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.notifications.IncognitoNotificationClickReceiver
import ani.dantotsu.others.AlignTagHandler
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.others.SpoilerPlugin
import ani.dantotsu.parsers.ShowResponse
@ -119,8 +119,8 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withC
import com.bumptech.glide.load.resource.gif.GifDrawable
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.target.ViewTarget
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.bottomsheet.BottomSheetBehavior
@ -1448,6 +1448,8 @@ fun openOrCopyAnilistLink(link: String) {
} else {
copyToClipboard(link, true)
}
} else if (getYoutubeId(link).isNotEmpty()) {
openLinkInYouTube(link)
} else {
copyToClipboard(link, true)
}
@ -1484,6 +1486,7 @@ fun buildMarkwon(
TagHandlerNoOp.create("h1", "h2", "h3", "h4", "h5", "h6", "hr", "pre", "a")
)
}
plugin.addHandler(AlignTagHandler())
})
.usePlugin(GlideImagesPlugin.create(object : GlideImagesPlugin.GlideStore {
@ -1527,3 +1530,11 @@ fun buildMarkwon(
.build()
return markwon
}
fun getYoutubeId(url: String): String {
val regex = """(?:youtube\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|(?:youtu\.be|youtube\.com)/)([^"&?/\s]{11})|youtube\.com/""".toRegex()
val matchResult = regex.find(url)
return matchResult?.groupValues?.getOrNull(1) ?: ""
}

View file

@ -2,7 +2,6 @@ package ani.dantotsu
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Intent
import android.content.res.Configuration
import android.graphics.drawable.Animatable
@ -60,11 +59,11 @@ import ani.dantotsu.settings.saving.SharedPreferenceBooleanLiveData
import ani.dantotsu.settings.saving.internal.PreferenceKeystore
import ani.dantotsu.settings.saving.internal.PreferencePackager
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.AudioHelper
import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputEditText
import eu.kanade.domain.source.service.SourcePreferences
import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin
@ -455,7 +454,10 @@ class MainActivity : AppCompatActivity() {
}
}
}
if (PrefManager.getVal(PrefName.OC)) {
AudioHelper.run(this, R.raw.audio)
PrefManager.setVal(PrefName.OC, false)
}
val torrentManager = Injekt.get<TorrentAddonManager>()
fun startTorrent() {
if (torrentManager.isAvailable() && PrefManager.getVal(PrefName.TorrentEnabled)) {

View file

@ -353,6 +353,7 @@ class CommentsFragment : Fragment() {
}
}
}
@SuppressLint("NotifyDataSetChanged")
override fun onResume() {
super.onResume()

View file

@ -0,0 +1,32 @@
package ani.dantotsu.others
import android.text.Layout
import android.text.style.AlignmentSpan
import io.noties.markwon.MarkwonConfiguration
import io.noties.markwon.RenderProps
import io.noties.markwon.html.HtmlTag
import io.noties.markwon.html.tag.SimpleTagHandler
class AlignTagHandler : SimpleTagHandler() {
override fun getSpans(
configuration: MarkwonConfiguration,
renderProps: RenderProps,
tag: HtmlTag
): Any {
val alignment: Layout.Alignment = if (tag.attributes().containsKey("center")) {
Layout.Alignment.ALIGN_CENTER
} else if (tag.attributes().containsKey("end")) {
Layout.Alignment.ALIGN_OPPOSITE
} else {
Layout.Alignment.ALIGN_NORMAL
}
return AlignmentSpan.Standard(alignment)
}
override fun supportedTags(): Collection<String> {
return setOf("align")
}
}

View file

@ -23,12 +23,12 @@ import ani.dantotsu.databinding.ActivitySettingsBinding
import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.openLinkInYouTube
import ani.dantotsu.others.AppUpdater
import ani.dantotsu.others.CustomBottomDialog
import ani.dantotsu.pop
import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import ani.dantotsu.startMainActivity
import ani.dantotsu.statusBarHeight
@ -217,10 +217,14 @@ class SettingsActivity : AppCompatActivity() {
settingsLogo.setSafeOnClickListener {
cursedCounter++
(settingsLogo.drawable as Animatable).start()
if (cursedCounter % 7 == 0) {
toast(R.string.you_cursed)
openLinkInYouTube(getString(R.string.cursed_yt))
//PrefManager.setVal(PrefName.ImageUrl, !PrefManager.getVal(PrefName.ImageUrl, false))
if (cursedCounter % 16 == 0) {
val oldVal: Boolean = PrefManager.getVal(PrefName.OC)
if (!oldVal) {
toast(R.string.omega_cursed)
} else {
toast(R.string.omega_freed)
}
PrefManager.setVal(PrefName.OC, !oldVal)
} else {
snackString(array[(Math.random() * array.size).toInt()], context)
}

View file

@ -191,6 +191,7 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
SubscriptionNotificationStore(Pref(Location.Irrelevant, List::class, listOf<SubscriptionStore>())),
UnreadCommentNotifications(Pref(Location.Irrelevant, Int::class, 0)),
DownloadsDir(Pref(Location.Irrelevant, String::class, "")),
OC(Pref(Location.Irrelevant, Boolean::class, false)),
RefreshStatus(Pref(Location.Irrelevant, Boolean::class, false)),
//Protected

View file

@ -6,6 +6,7 @@ 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
@ -42,7 +43,7 @@ class ActivityMarkdownCreator : AppCompatActivity() {
BOLD("****", 2, R.id.formatBold),
ITALIC("**", 1, R.id.formatItalic),
STRIKETHROUGH("~~~~", 2, R.id.formatStrikethrough),
SPOILER("~||~", 2, R.id.formatSpoiler),
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),
@ -267,9 +268,13 @@ class ActivityMarkdownCreator : AppCompatActivity() {
private fun previewMarkdown(preview: Boolean) {
val markwon = buildMarkwon(this, false, anilist = true)
if (preview) {
binding.editText.isVisible = false
binding.editText.isEnabled = false
markwon.setMarkdown(binding.editText, text)
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)

View file

@ -1,12 +1,13 @@
package ani.dantotsu.util
import ani.dantotsu.getYoutubeId
import ani.dantotsu.util.ColorEditor.Companion.toCssColor
class AniMarkdown { //istg anilist has the worst api
companion object {
private fun convertNestedImageToHtml(markdown: String): String {
private fun String.convertNestedImageToHtml(): String {
val regex = """\[!\[(.*?)]\((.*?)\)]\((.*?)\)""".toRegex()
return regex.replace(markdown) { matchResult ->
return regex.replace(this) { matchResult ->
val altText = matchResult.groupValues[1]
val imageUrl = matchResult.groupValues[2]
val linkUrl = matchResult.groupValues[3]
@ -14,26 +15,49 @@ class AniMarkdown { //istg anilist has the worst api
}
}
private fun convertImageToHtml(markdown: String): String {
private fun String.convertImageToHtml(): String {
val regex = """!\[(.*?)]\((.*?)\)""".toRegex()
return regex.replace(markdown) { matchResult ->
val anilistRegex = """img\(.*?\)""".toRegex()
val markdownImage = regex.replace(this) { matchResult ->
val altText = matchResult.groupValues[1]
val imageUrl = matchResult.groupValues[2]
"""<img src="$imageUrl" alt="$altText">"""
}
return anilistRegex.replace(markdownImage) { matchResult ->
val imageUrl = matchResult.groupValues[1]
"""<img src="$imageUrl" alt="Image">"""
}
}
private fun convertLinkToHtml(markdown: String): String {
private fun String.convertLinkToHtml(): String {
val regex = """\[(.*?)]\((.*?)\)""".toRegex()
return regex.replace(markdown) { matchResult ->
return regex.replace(this) { matchResult ->
val linkText = matchResult.groupValues[1]
val linkUrl = matchResult.groupValues[2]
"""<a href="$linkUrl">$linkText</a>"""
}
}
private fun replaceLeftovers(html: String): String {
return html.replace("&nbsp;", " ")
private fun String.convertYoutubeToHtml(): String {
val regex = """<div class='youtube' id='(.*?)'></div>""".toRegex()
return regex.replace(this) { matchResult ->
val url = matchResult.groupValues[1]
val id = getYoutubeId(url)
if (id.isNotEmpty()) {
"""<div>
<a href="https://www.youtube.com/watch?v=$id"><img src="https://i3.ytimg.com/vi/$id/maxresdefault.jpg" alt="$url"></a>
<align center>
<a href="https://www.youtube.com/watch?v=$id">Youtube Link</a>
</align>
</div>""".trimIndent()
} else {
"""<a href="$url">Youtube Video</a>"""
}
}
}
private fun String.replaceLeftovers(): String {
return this.replace("&nbsp;", " ")
.replace("&amp;", "&")
.replace("&lt;", "<")
.replace("&gt;", ">")
@ -46,18 +70,29 @@ class AniMarkdown { //istg anilist has the worst api
.replace("\n", "<br>")
}
private fun underlineToHtml(html: String): String {
return html.replace("(?s)___(.*?)___".toRegex(), "<br><em><strong>$1</strong></em><br>")
private fun String.underlineToHtml(): String {
return this.replace("(?s)___(.*?)___".toRegex(), "<br><em><strong>$1</strong></em><br>")
.replace("(?s)__(.*?)__".toRegex(), "<br><strong>$1</strong><br>")
.replace("(?s)\\s+_([^_]+)_\\s+".toRegex(), "<em>$1</em>")
}
private fun String.convertCenterToHtml(): String {
val regex = """~~~(.*?)~~~""".toRegex()
return regex.replace(this) { matchResult ->
val centerText = matchResult.groupValues[1]
"""<align center>$centerText</align>"""
}
}
fun getBasicAniHTML(html: String): String {
val step0 = convertNestedImageToHtml(html)
val step1 = convertImageToHtml(step0)
val step2 = convertLinkToHtml(step1)
val step3 = replaceLeftovers(step2)
return underlineToHtml(step3)
return html
.convertNestedImageToHtml()
.convertImageToHtml()
.convertLinkToHtml()
.convertYoutubeToHtml()
.convertCenterToHtml()
.replaceLeftovers()
.underlineToHtml()
}
fun getFullAniHTML(html: String, textColor: Int): String {

View file

@ -0,0 +1,58 @@
package ani.dantotsu.util
import android.content.Context
import android.media.AudioManager
import android.media.MediaPlayer
class AudioHelper(private val context: Context) {
private val audioManager: AudioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
private var mediaPlayer: MediaPlayer? = null
fun routeAudioToSpeaker() {
audioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
audioManager.isSpeakerphoneOn = true
}
private val maxVolume: Int
get() = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
private var oldVolume: Int = 0
fun setVolume(percentage: Int) {
oldVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
val volume = (maxVolume * percentage) / 100
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0)
}
fun playAudio(audio: Int) {
mediaPlayer?.release()
mediaPlayer = MediaPlayer.create(context, audio)
mediaPlayer?.setOnCompletionListener {
setVolume(oldVolume)
audioManager.abandonAudioFocus(null)
it.release()
}
mediaPlayer?.setOnPreparedListener {
it.start()
}
}
fun stopAudio() {
mediaPlayer?.let {
if (it.isPlaying) {
it.stop()
}
it.release()
mediaPlayer = null
}
}
companion object {
fun run(context: Context, audio: Int) {
val audioHelper = AudioHelper(context)
audioHelper.routeAudioToSpeaker()
audioHelper.setVolume(90)
audioHelper.playAudio(audio)
}
}
}

View file

@ -92,6 +92,21 @@
android:textIsSelectable="true"
android:textSize="18sp"
tools:ignore="LabelFor" />
<TextView
android:id="@+id/markdownPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fontFamily="@font/poppins_semi_bold"
android:gravity="top|start"
android:hint="@string/reply_hint"
android:nestedScrollingEnabled="true"
android:padding="16dp"
android:textColor="?attr/colorOnBackground"
android:textIsSelectable="true"
android:textSize="18sp"
android:visibility="gone"
tools:ignore="LabelFor" />
</LinearLayout>
</ScrollView>

Binary file not shown.

View file

@ -1042,5 +1042,7 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
<string name="enable_forgot_password">Enable Forgot Password (hold clear button for 10 seconds)</string>
<string name="hide_notification_dot">Hide Notification Dot</string>
<string name="private_mode">Private</string>
<string name="omega_cursed">you have been Ǫ̴̺̙͎̤̫͓̮̰̿͝M̴͇̤͗́̾̈́̑̍̿̈͌͝Ȅ̴̡̨̛͉̣̙̩̲̣̤̟̪̣̎͗̎̆̒̉͆̆̕ͅͅǴ̸̯̬̗̠̙͛͐̀̈͋̀̈̽́̎̿͘͘͝ͅĀ̶̧̲̀ͅ ̴̢̟͕̜̓̾̓C̶̬̜̰̘̝̱̫͓͙̭̈́͐͋̓̏̈̍̓̀̌̾̚Ư̸̛̤̱̈́͆̽͊͛̐̓́̑͘̕̕͝R̸̨̨͈̬̱̺͕̪̪̘͕͎̂͛́̅̆̓̀͝ͅS̴̨̨̛̩̭̗̹̰̭̥͉̮̝̠̓̔͆̂͊͆̀̈́̅̕͘̚͝È̴̢̛̝͈̳͉͈͒͒̒̄̏̈̈́D̸̢̡̨̜̞̩̼̫̹̗̮͛̀̈̋̾̇̕̕͜ͅ</string>
<string name="omega_freed">you have been freed</string>
</resources>