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.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" /> android:maxSdkVersion="29" />

View file

@ -68,7 +68,6 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
@ -98,6 +97,7 @@ import ani.dantotsu.databinding.ItemCountDownBinding
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.notifications.IncognitoNotificationClickReceiver import ani.dantotsu.notifications.IncognitoNotificationClickReceiver
import ani.dantotsu.others.AlignTagHandler
import ani.dantotsu.others.ImageViewDialog import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.others.SpoilerPlugin import ani.dantotsu.others.SpoilerPlugin
import ani.dantotsu.parsers.ShowResponse 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.load.resource.gif.GifDrawable
import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions 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.Target
import com.bumptech.glide.request.target.ViewTarget
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
@ -1448,6 +1448,8 @@ fun openOrCopyAnilistLink(link: String) {
} else { } else {
copyToClipboard(link, true) copyToClipboard(link, true)
} }
} else if (getYoutubeId(link).isNotEmpty()) {
openLinkInYouTube(link)
} else { } else {
copyToClipboard(link, true) copyToClipboard(link, true)
} }
@ -1484,6 +1486,7 @@ fun buildMarkwon(
TagHandlerNoOp.create("h1", "h2", "h3", "h4", "h5", "h6", "hr", "pre", "a") TagHandlerNoOp.create("h1", "h2", "h3", "h4", "h5", "h6", "hr", "pre", "a")
) )
} }
plugin.addHandler(AlignTagHandler())
}) })
.usePlugin(GlideImagesPlugin.create(object : GlideImagesPlugin.GlideStore { .usePlugin(GlideImagesPlugin.create(object : GlideImagesPlugin.GlideStore {
@ -1527,3 +1530,11 @@ fun buildMarkwon(
.build() .build()
return markwon 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.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.drawable.Animatable 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.PreferenceKeystore
import ani.dantotsu.settings.saving.internal.PreferencePackager import ani.dantotsu.settings.saving.internal.PreferencePackager
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.AudioHelper
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog import ani.dantotsu.util.customAlertDialog
import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputEditText
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import io.noties.markwon.Markwon import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin 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>() val torrentManager = Injekt.get<TorrentAddonManager>()
fun startTorrent() { fun startTorrent() {
if (torrentManager.isAvailable() && PrefManager.getVal(PrefName.TorrentEnabled)) { if (torrentManager.isAvailable() && PrefManager.getVal(PrefName.TorrentEnabled)) {

View file

@ -353,6 +353,7 @@ class CommentsFragment : Fragment() {
} }
} }
} }
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
override fun onResume() { override fun onResume() {
super.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.initActivity
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.openLinkInBrowser import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.openLinkInYouTube
import ani.dantotsu.others.AppUpdater import ani.dantotsu.others.AppUpdater
import ani.dantotsu.others.CustomBottomDialog import ani.dantotsu.others.CustomBottomDialog
import ani.dantotsu.pop import ani.dantotsu.pop
import ani.dantotsu.setSafeOnClickListener import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.startMainActivity import ani.dantotsu.startMainActivity
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
@ -217,10 +217,14 @@ class SettingsActivity : AppCompatActivity() {
settingsLogo.setSafeOnClickListener { settingsLogo.setSafeOnClickListener {
cursedCounter++ cursedCounter++
(settingsLogo.drawable as Animatable).start() (settingsLogo.drawable as Animatable).start()
if (cursedCounter % 7 == 0) { if (cursedCounter % 16 == 0) {
toast(R.string.you_cursed) val oldVal: Boolean = PrefManager.getVal(PrefName.OC)
openLinkInYouTube(getString(R.string.cursed_yt)) if (!oldVal) {
//PrefManager.setVal(PrefName.ImageUrl, !PrefManager.getVal(PrefName.ImageUrl, false)) toast(R.string.omega_cursed)
} else {
toast(R.string.omega_freed)
}
PrefManager.setVal(PrefName.OC, !oldVal)
} else { } else {
snackString(array[(Math.random() * array.size).toInt()], context) 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>())), SubscriptionNotificationStore(Pref(Location.Irrelevant, List::class, listOf<SubscriptionStore>())),
UnreadCommentNotifications(Pref(Location.Irrelevant, Int::class, 0)), UnreadCommentNotifications(Pref(Location.Irrelevant, Int::class, 0)),
DownloadsDir(Pref(Location.Irrelevant, String::class, "")), DownloadsDir(Pref(Location.Irrelevant, String::class, "")),
OC(Pref(Location.Irrelevant, Boolean::class, false)),
RefreshStatus(Pref(Location.Irrelevant, Boolean::class, false)), RefreshStatus(Pref(Location.Irrelevant, Boolean::class, false)),
//Protected //Protected

View file

@ -6,6 +6,7 @@ import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import ani.dantotsu.R import ani.dantotsu.R
@ -42,7 +43,7 @@ class ActivityMarkdownCreator : AppCompatActivity() {
BOLD("****", 2, R.id.formatBold), BOLD("****", 2, R.id.formatBold),
ITALIC("**", 1, R.id.formatItalic), ITALIC("**", 1, R.id.formatItalic),
STRIKETHROUGH("~~~~", 2, R.id.formatStrikethrough), STRIKETHROUGH("~~~~", 2, R.id.formatStrikethrough),
SPOILER("~||~", 2, R.id.formatSpoiler), SPOILER("~!!~", 2, R.id.formatSpoiler),
LINK("[Placeholder](%s)", 0, R.id.formatLink), LINK("[Placeholder](%s)", 0, R.id.formatLink),
IMAGE("img(%s)", 0, R.id.formatImage), IMAGE("img(%s)", 0, R.id.formatImage),
YOUTUBE("youtube(%s)", 0, R.id.formatYoutube), YOUTUBE("youtube(%s)", 0, R.id.formatYoutube),
@ -267,9 +268,13 @@ class ActivityMarkdownCreator : AppCompatActivity() {
private fun previewMarkdown(preview: Boolean) { private fun previewMarkdown(preview: Boolean) {
val markwon = buildMarkwon(this, false, anilist = true) val markwon = buildMarkwon(this, false, anilist = true)
if (preview) { if (preview) {
binding.editText.isVisible = false
binding.editText.isEnabled = false binding.editText.isEnabled = false
markwon.setMarkdown(binding.editText, text) binding.markdownPreview.isVisible = true
markwon.setMarkdown(binding.markdownPreview, AniMarkdown.getBasicAniHTML(text))
} else { } else {
binding.editText.isVisible = true
binding.markdownPreview.isVisible = false
binding.editText.setText(text) binding.editText.setText(text)
binding.editText.isEnabled = true binding.editText.isEnabled = true
val markwonEditor = MarkwonEditor.create(markwon) val markwonEditor = MarkwonEditor.create(markwon)

View file

@ -1,12 +1,13 @@
package ani.dantotsu.util package ani.dantotsu.util
import ani.dantotsu.getYoutubeId
import ani.dantotsu.util.ColorEditor.Companion.toCssColor import ani.dantotsu.util.ColorEditor.Companion.toCssColor
class AniMarkdown { //istg anilist has the worst api class AniMarkdown { //istg anilist has the worst api
companion object { companion object {
private fun convertNestedImageToHtml(markdown: String): String { private fun String.convertNestedImageToHtml(): String {
val regex = """\[!\[(.*?)]\((.*?)\)]\((.*?)\)""".toRegex() val regex = """\[!\[(.*?)]\((.*?)\)]\((.*?)\)""".toRegex()
return regex.replace(markdown) { matchResult -> return regex.replace(this) { matchResult ->
val altText = matchResult.groupValues[1] val altText = matchResult.groupValues[1]
val imageUrl = matchResult.groupValues[2] val imageUrl = matchResult.groupValues[2]
val linkUrl = matchResult.groupValues[3] 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() val regex = """!\[(.*?)]\((.*?)\)""".toRegex()
return regex.replace(markdown) { matchResult -> val anilistRegex = """img\(.*?\)""".toRegex()
val markdownImage = regex.replace(this) { matchResult ->
val altText = matchResult.groupValues[1] val altText = matchResult.groupValues[1]
val imageUrl = matchResult.groupValues[2] val imageUrl = matchResult.groupValues[2]
"""<img src="$imageUrl" alt="$altText">""" """<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() val regex = """\[(.*?)]\((.*?)\)""".toRegex()
return regex.replace(markdown) { matchResult -> return regex.replace(this) { matchResult ->
val linkText = matchResult.groupValues[1] val linkText = matchResult.groupValues[1]
val linkUrl = matchResult.groupValues[2] val linkUrl = matchResult.groupValues[2]
"""<a href="$linkUrl">$linkText</a>""" """<a href="$linkUrl">$linkText</a>"""
} }
} }
private fun replaceLeftovers(html: String): String { private fun String.convertYoutubeToHtml(): String {
return html.replace("&nbsp;", " ") 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("&amp;", "&")
.replace("&lt;", "<") .replace("&lt;", "<")
.replace("&gt;", ">") .replace("&gt;", ">")
@ -46,18 +70,29 @@ class AniMarkdown { //istg anilist has the worst api
.replace("\n", "<br>") .replace("\n", "<br>")
} }
private fun underlineToHtml(html: String): String { private fun String.underlineToHtml(): String {
return html.replace("(?s)___(.*?)___".toRegex(), "<br><em><strong>$1</strong></em><br>") return this.replace("(?s)___(.*?)___".toRegex(), "<br><em><strong>$1</strong></em><br>")
.replace("(?s)__(.*?)__".toRegex(), "<br><strong>$1</strong><br>") .replace("(?s)__(.*?)__".toRegex(), "<br><strong>$1</strong><br>")
.replace("(?s)\\s+_([^_]+)_\\s+".toRegex(), "<em>$1</em>") .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 { fun getBasicAniHTML(html: String): String {
val step0 = convertNestedImageToHtml(html) return html
val step1 = convertImageToHtml(step0) .convertNestedImageToHtml()
val step2 = convertLinkToHtml(step1) .convertImageToHtml()
val step3 = replaceLeftovers(step2) .convertLinkToHtml()
return underlineToHtml(step3) .convertYoutubeToHtml()
.convertCenterToHtml()
.replaceLeftovers()
.underlineToHtml()
} }
fun getFullAniHTML(html: String, textColor: Int): String { 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:textIsSelectable="true"
android:textSize="18sp" android:textSize="18sp"
tools:ignore="LabelFor" /> 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> </LinearLayout>
</ScrollView> </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="enable_forgot_password">Enable Forgot Password (hold clear button for 10 seconds)</string>
<string name="hide_notification_dot">Hide Notification Dot</string> <string name="hide_notification_dot">Hide Notification Dot</string>
<string name="private_mode">Private</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> </resources>