diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1a6ab597665..9b071c2bf10 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,6 +19,7 @@ + diff --git a/app/src/main/java/ani/dantotsu/Functions.kt b/app/src/main/java/ani/dantotsu/Functions.kt index abfebb3ab14..c41d22ced6c 100644 --- a/app/src/main/java/ani/dantotsu/Functions.kt +++ b/app/src/main/java/ani/dantotsu/Functions.kt @@ -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) ?: "" +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/MainActivity.kt b/app/src/main/java/ani/dantotsu/MainActivity.kt index 7b2e0b1d953..412ed67692c 100644 --- a/app/src/main/java/ani/dantotsu/MainActivity.kt +++ b/app/src/main/java/ani/dantotsu/MainActivity.kt @@ -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() fun startTorrent() { if (torrentManager.isAvailable() && PrefManager.getVal(PrefName.TorrentEnabled)) { diff --git a/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt b/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt index fc21805e67f..de1c76b8df1 100644 --- a/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/comments/CommentsFragment.kt @@ -353,6 +353,7 @@ class CommentsFragment : Fragment() { } } } + @SuppressLint("NotifyDataSetChanged") override fun onResume() { super.onResume() diff --git a/app/src/main/java/ani/dantotsu/others/AlignTagHandler.kt b/app/src/main/java/ani/dantotsu/others/AlignTagHandler.kt new file mode 100644 index 00000000000..1708ed0507c --- /dev/null +++ b/app/src/main/java/ani/dantotsu/others/AlignTagHandler.kt @@ -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 { + return setOf("align") + } +} diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt index 2022fac1013..7e58e870058 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt @@ -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) } diff --git a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt index 6f188b854c3..c71a6a9bab8 100644 --- a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt +++ b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt @@ -191,6 +191,7 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files SubscriptionNotificationStore(Pref(Location.Irrelevant, List::class, listOf())), 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 diff --git a/app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt b/app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt index fdc24937690..5548401e797 100644 --- a/app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt +++ b/app/src/main/java/ani/dantotsu/util/ActivityMarkdownCreator.kt @@ -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) diff --git a/app/src/main/java/ani/dantotsu/util/AniMarkdown.kt b/app/src/main/java/ani/dantotsu/util/AniMarkdown.kt index 3f8c04d7250..200b3883411 100644 --- a/app/src/main/java/ani/dantotsu/util/AniMarkdown.kt +++ b/app/src/main/java/ani/dantotsu/util/AniMarkdown.kt @@ -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] """$altText""" } + return anilistRegex.replace(markdownImage) { matchResult -> + val imageUrl = matchResult.groupValues[1] + """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] """$linkText""" } } - private fun replaceLeftovers(html: String): String { - return html.replace(" ", " ") + private fun String.convertYoutubeToHtml(): String { + val regex = """
""".toRegex() + return regex.replace(this) { matchResult -> + val url = matchResult.groupValues[1] + val id = getYoutubeId(url) + if (id.isNotEmpty()) { + """
+ $url + + Youtube Link + +
""".trimIndent() + } else { + """Youtube Video""" + } + } + } + + private fun String.replaceLeftovers(): String { + return this.replace(" ", " ") .replace("&", "&") .replace("<", "<") .replace(">", ">") @@ -46,18 +70,29 @@ class AniMarkdown { //istg anilist has the worst api .replace("\n", "
") } - private fun underlineToHtml(html: String): String { - return html.replace("(?s)___(.*?)___".toRegex(), "
$1
") + private fun String.underlineToHtml(): String { + return this.replace("(?s)___(.*?)___".toRegex(), "
$1
") .replace("(?s)__(.*?)__".toRegex(), "
$1
") .replace("(?s)\\s+_([^_]+)_\\s+".toRegex(), "$1") } + private fun String.convertCenterToHtml(): String { + val regex = """~~~(.*?)~~~""".toRegex() + return regex.replace(this) { matchResult -> + val centerText = matchResult.groupValues[1] + """$centerText""" + } + } + 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 { diff --git a/app/src/main/java/ani/dantotsu/util/AudioHelper.kt b/app/src/main/java/ani/dantotsu/util/AudioHelper.kt new file mode 100644 index 00000000000..bfd38be0a97 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/util/AudioHelper.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_markdown_creator.xml b/app/src/main/res/layout/activity_markdown_creator.xml index fb66d6f0362..548ad1771ea 100644 --- a/app/src/main/res/layout/activity_markdown_creator.xml +++ b/app/src/main/res/layout/activity_markdown_creator.xml @@ -92,6 +92,21 @@ android:textIsSelectable="true" android:textSize="18sp" tools:ignore="LabelFor" /> + + diff --git a/app/src/main/res/raw/audio.mp3 b/app/src/main/res/raw/audio.mp3 new file mode 100644 index 00000000000..49e1e430f00 Binary files /dev/null and b/app/src/main/res/raw/audio.mp3 differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 832cc9fac02..f47309a0eca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1042,5 +1042,7 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc Enable Forgot Password (hold clear button for 10 seconds) Hide Notification Dot Private + you have been Ǫ̴̺̙͎̤̫͓̮̰̿͝M̴͇̤͗́̾̈́̑̍̿̈͌͝Ȅ̴̡̨̛͉̣̙̩̲̣̤̟̪̣̎͗̎̆̒̉͆̆̕ͅͅǴ̸̯̬̗̠̙͛͐̀̈͋̀̈̽́̎̿͘͘͝ͅĀ̶̧̲̀ͅ ̴̢̟͕̜̓̾̓C̶̬̜̰̘̝̱̫͓͙̭̈́͐͋̓̏̈̍̓̀̌̾̚Ư̸̛̤̱̈́͆̽͊͛̐̓́̑͘̕̕͝R̸̨̨͈̬̱̺͕̪̪̘͕͎̂͛́̅̆̓̀͝ͅS̴̨̨̛̩̭̗̹̰̭̥͉̮̝̠̓̔͆̂͊͆̀̈́̅̕͘̚͝È̴̢̛̝͈̳͉͈͒͒̒̄̏̈̈́D̸̢̡̨̜̞̩̼̫̹̗̮͛̀̈̋̾̇̕̕͜ͅ + you have been freed