Compare commits

...

19 Commits

Author SHA1 Message Date
Ankit Grai
c3f6d0ecee fix : dialog key names (#605) 2025-05-22 12:51:18 +05:30
aayush262
5124d6a2d8 fix: discord login 2025-05-22 12:50:06 +05:30
Ankit Grai
e83a0fe7da fix : long press progress dialog reset not working (#603) 2025-05-19 17:11:24 +05:30
rebel onion
61a8350043 fix: avoid waiting on network for local exts 2025-05-15 01:47:06 -05:00
rebel onion
baffbc845c fix: help bounds check /w custom speeds 2025-05-15 01:16:23 -05:00
rebel onion
afd9f6b884 fix: subtitles not showing 2025-05-15 01:13:47 -05:00
rebel onion
7d0894cd92 chore: bump extension interface 2025-05-14 22:35:50 -05:00
Rishvaish
dec2ed7959 hope for the best
* Update README.md

* To install multiple mangas

users can enter the value required to install as there is an EditText field instead of the Text View

* Issues

1)Installation of many mangas at same time now made to one to increase the installation efficiency
2)Installation order from the latest progresses chapter to the limit index
3)Tried to resolve the app crash bug

* Issues

1)Installation of many mangas at same time now made to one to increase the installation efficiency
2)Installation order from the latest progresses chapter to the limit index
3)Tried to resolve the app crash bug

---------

Co-authored-by: rebel onion <87634197+rebelonion@users.noreply.github.com>
2025-04-23 14:58:42 +05:30
aayush262
e4630df3e0 move stuff to dev (#587)
* Update README.md

* Fixed missing manga pages when downloading (#586)

* To install multiple mangas (#582)

users can enter the value required to install as there is an EditText field instead of the Text View

---------

Co-authored-by: rebel onion <87634197+rebelonion@users.noreply.github.com>
Co-authored-by: Daniele Santoru <30676094+danyev3@users.noreply.github.com>
Co-authored-by: Rishvaish <68911202+rishabpuranika@users.noreply.github.com>
2025-04-02 10:52:58 +05:30
Sadwhy
6fd3515d2c stuff (#567)
* Add blur to dialog for devices that support it

* More adjustable seek time

* Bump exo player
2025-01-17 13:03:01 +05:30
rebel onion
6fa2f11db2 Merge branch 'main' into dev 2025-01-16 00:15:21 -06:00
rebel onion
a5babea27c chore: version bump 2025-01-16 00:14:25 -06:00
rebelonion
8a9b8cca7e fix: Serializable 2025-01-13 14:23:02 -06:00
rebel onion
7479f5f43b Update stable.md 2025-01-09 19:58:00 -06:00
Sadwhy
3ac9307329 Use custom alert builder for all dialogs [skip ci] 2025-01-09 18:04:22 +05:30
rebel onion
f606bef2a5 Merge pull request #559 from rebelonion/dev
Dev
2025-01-06 08:29:48 -06:00
rebel onion
f9f9767ecc chore: clean 2025-01-06 08:22:44 -06:00
rebel onion
31a67c8edb Merge branch 'dev' of https://github.com/rebelonion/Dantotsu into dev 2025-01-05 20:23:59 -06:00
rebel onion
7fc69b4edd feat: alt update 2025-01-05 20:23:50 -06:00
123 changed files with 2888 additions and 2062 deletions

View File

@@ -14,8 +14,6 @@ Dantotsu is an [Anilist](https://anilist.co/) only client.
> **Dantotsu (断トツ; Dan-totsu)** literally means "the best of the best" in Japanese. Try it out for yourself and be the judge! > **Dantotsu (断トツ; Dan-totsu)** literally means "the best of the best" in Japanese. Try it out for yourself and be the judge!
<a href="https://www.buymeacoffee.com/rebelonion"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=rebelonion&button_colour=FFDD00&font_colour=030201&font_family=Poppins&outline_colour=000000&coffee_colour=ffffff" /></a>
## Terms of Use ## Terms of Use
By downloading, installing, or using this application, you agree to: By downloading, installing, or using this application, you agree to:
- Use the application in compliance with all applicable laws - Use the application in compliance with all applicable laws

View File

@@ -18,8 +18,8 @@ android {
minSdk 21 minSdk 21
targetSdk 35 targetSdk 35
versionCode((System.currentTimeMillis() / 60000).toInteger()) versionCode((System.currentTimeMillis() / 60000).toInteger())
versionName "3.2.0" versionName "3.2.1"
versionCode 300200000 versionCode versionName.split("\\.").collect { it.toInteger() * 100 }.join("") as Integer
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
} }
@@ -48,6 +48,10 @@ android {
manifestPlaceholders.icon_placeholder_round = "@mipmap/ic_launcher_alpha_round" manifestPlaceholders.icon_placeholder_round = "@mipmap/ic_launcher_alpha_round"
debuggable System.getenv("CI") == null debuggable System.getenv("CI") == null
isDefault true isDefault true
debuggable true
jniDebuggable true
minifyEnabled false
shrinkResources false
} }
debug { debug {
applicationIdSuffix ".beta" applicationIdSuffix ".beta"
@@ -81,25 +85,26 @@ android {
dependencies { dependencies {
// FireBase // FireBase
googleImplementation platform('com.google.firebase:firebase-bom:33.0.0') googleImplementation platform('com.google.firebase:firebase-bom:33.13.0')
googleImplementation 'com.google.firebase:firebase-analytics-ktx:22.0.0' googleImplementation 'com.google.firebase:firebase-analytics-ktx:22.4.0'
googleImplementation 'com.google.firebase:firebase-crashlytics-ktx:19.0.0' googleImplementation 'com.google.firebase:firebase-crashlytics-ktx:19.4.3'
// Core // Core
implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.browser:browser:1.8.0' implementation 'androidx.browser:browser:1.8.0'
implementation 'androidx.core:core-ktx:1.13.1' implementation 'androidx.core:core-ktx:1.16.0'
implementation 'androidx.fragment:fragment-ktx:1.6.2' implementation 'androidx.fragment:fragment-ktx:1.8.6'
implementation 'androidx.activity:activity-ktx:1.10.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation "androidx.work:work-runtime-ktx:2.9.0" implementation "androidx.work:work-runtime-ktx:2.10.1"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.github.Blatzar:NiceHttp:0.4.4' implementation 'com.github.Blatzar:NiceHttp:0.4.4'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3' implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3'
implementation 'androidx.preference:preference-ktx:1.2.1' implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'androidx.webkit:webkit:1.11.0' implementation 'androidx.webkit:webkit:1.13.0'
implementation "com.anggrayudi:storage:1.5.5" implementation "com.anggrayudi:storage:1.5.5"
implementation "androidx.biometric:biometric:1.1.0" implementation "androidx.biometric:biometric:1.1.0"
@@ -113,7 +118,7 @@ dependencies {
implementation 'jp.wasabeef:glide-transformations:4.3.0' implementation 'jp.wasabeef:glide-transformations:4.3.0'
// Exoplayer // Exoplayer
ext.exo_version = '1.5.0' ext.exo_version = '1.6.1'
implementation "androidx.media3:media3-exoplayer:$exo_version" implementation "androidx.media3:media3-exoplayer:$exo_version"
implementation "androidx.media3:media3-ui:$exo_version" implementation "androidx.media3:media3-ui:$exo_version"
implementation "androidx.media3:media3-exoplayer-hls:$exo_version" implementation "androidx.media3:media3-exoplayer-hls:$exo_version"
@@ -124,7 +129,7 @@ dependencies {
implementation "androidx.media3:media3-cast:$exo_version" implementation "androidx.media3:media3-cast:$exo_version"
implementation "androidx.mediarouter:mediarouter:1.7.0" implementation "androidx.mediarouter:mediarouter:1.7.0"
// Media3 extension // Media3 extension
implementation "com.github.anilbeesetti.nextlib:nextlib-media3ext:0.8.3" implementation "com.github.anilbeesetti.nextlib:nextlib-media3ext:0.8.4"
// UI // UI
implementation 'com.google.android.material:material:1.12.0' implementation 'com.google.android.material:material:1.12.0'
@@ -133,7 +138,7 @@ dependencies {
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0' implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
implementation 'com.alexvasilkov:gesture-views:2.8.3' implementation 'com.alexvasilkov:gesture-views:2.8.3'
implementation 'com.github.VipulOG:ebook-reader:0.1.6' implementation 'com.github.VipulOG:ebook-reader:0.1.6'
implementation 'androidx.paging:paging-runtime-ktx:3.2.1' implementation 'androidx.paging:paging-runtime-ktx:3.3.6'
implementation 'com.github.eltos:simpledialogfragments:v3.7' implementation 'com.github.eltos:simpledialogfragments:v3.7'
implementation 'com.github.AAChartModel:AAChartCore-Kotlin:7.2.3' implementation 'com.github.AAChartModel:AAChartCore-Kotlin:7.2.3'
@@ -162,13 +167,13 @@ dependencies {
implementation 'ca.gosyer:voyager-navigator:1.0.0-rc07' implementation 'ca.gosyer:voyager-navigator:1.0.0-rc07'
implementation 'com.squareup.logcat:logcat:0.1' implementation 'com.squareup.logcat:logcat:0.1'
implementation 'uy.kohesive.injekt:injekt-core:1.16.+' implementation 'uy.kohesive.injekt:injekt-core:1.16.+'
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.12' implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.14'
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.12' implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.14'
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps' implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps'
implementation 'com.squareup.okio:okio:3.8.0' implementation 'com.squareup.okio:okio:3.9.1'
implementation 'com.squareup.okhttp3:okhttp-brotli:5.0.0-alpha.12' implementation 'com.squareup.okhttp3:okhttp-brotli:5.0.0-alpha.14'
implementation 'org.jsoup:jsoup:1.16.1' implementation 'org.jsoup:jsoup:1.18.1'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.6.3' implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.7.3'
implementation 'com.jakewharton.rxrelay:rxrelay:1.2.0' implementation 'com.jakewharton.rxrelay:rxrelay:1.2.0'
implementation 'com.github.tachiyomiorg:unifile:17bec43' implementation 'com.github.tachiyomiorg:unifile:17bec43'
implementation 'com.github.gpanther:java-nat-sort:natural-comparator-1.1' implementation 'com.github.gpanther:java-nat-sort:natural-comparator-1.1'

View File

@@ -18,7 +18,9 @@ import ani.dantotsu.Mapper
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.buildMarkwon import ani.dantotsu.buildMarkwon
import ani.dantotsu.client import ani.dantotsu.client
import ani.dantotsu.connections.comments.CommentsAPI
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.decodeBase64ToString
import ani.dantotsu.logError import ani.dantotsu.logError
import ani.dantotsu.openLinkInBrowser import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
@@ -37,11 +39,34 @@ import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
object AppUpdater { object AppUpdater {
suspend fun check(activity: FragmentActivity, post: Boolean = false) { private val fallbackStableUrl: String
if (post) snackString(currContext()?.getString(R.string.checking_for_update)) get() = "aHR0cHM6Ly9hcGkuZGFudG90c3UuYXBwL3VwZGF0ZXMvc3RhYmxl".decodeBase64ToString()
val repo = activity.getString(R.string.repo) private val fallbackBetaUrl: String
tryWithSuspend { get() = "aHR0cHM6Ly9hcGkuZGFudG90c3UuYXBwL3VwZGF0ZXMvYmV0YQ==".decodeBase64ToString()
val (md, version) = if (BuildConfig.DEBUG) {
@Serializable
data class FallbackResponse(
val version: String,
val changelog: String,
val downloadUrl: String? = null
)
private suspend fun fetchUpdateInfo(repo: String, isDebug: Boolean): Pair<String, String>? {
return try {
fetchFromGithub(repo, isDebug)
} catch (e: Exception) {
Logger.log("Github fetch failed, trying fallback: ${e.message}")
try {
fetchFromFallback(isDebug)
} catch (e: Exception) {
Logger.log("Fallback fetch failed: ${e.message}")
null
}
}
}
private suspend fun fetchFromGithub(repo: String, isDebug: Boolean): Pair<String, String> {
return if (isDebug) {
val res = client.get("https://api.github.com/repos/$repo/releases") val res = client.get("https://api.github.com/repos/$repo/releases")
.parsed<JsonArray>().map { .parsed<JsonArray>().map {
Mapper.json.decodeFromJsonElement<GithubResponse>(it) Mapper.json.decodeFromJsonElement<GithubResponse>(it)
@@ -53,10 +78,49 @@ object AppUpdater {
val v = r.tagName.substringAfter("v", "") val v = r.tagName.substringAfter("v", "")
(r.body ?: "") to v.ifEmpty { throw Exception("Weird Version : ${r.tagName}") } (r.body ?: "") to v.ifEmpty { throw Exception("Weird Version : ${r.tagName}") }
} else { } else {
val res = val res = client.get("https://raw.githubusercontent.com/$repo/main/stable.md").text
client.get("https://raw.githubusercontent.com/$repo/main/stable.md").text
res to res.substringAfter("# ").substringBefore("\n") res to res.substringAfter("# ").substringBefore("\n")
} }
}
private suspend fun fetchFromFallback(isDebug: Boolean): Pair<String, String> {
val url = if (isDebug) fallbackBetaUrl else fallbackStableUrl
val response = CommentsAPI.requestBuilder().get(url).parsed<FallbackResponse>()
return response.changelog to response.version
}
private suspend fun fetchApkUrl(repo: String, version: String, isDebug: Boolean): String? {
return try {
fetchApkUrlFromGithub(repo, version)
} catch (e: Exception) {
Logger.log("Github APK fetch failed, trying fallback: ${e.message}")
try {
fetchApkUrlFromFallback(version, isDebug)
} catch (e: Exception) {
Logger.log("Fallback APK fetch failed: ${e.message}")
null
}
}
}
private suspend fun fetchApkUrlFromGithub(repo: String, version: String): String? {
val apks = client.get("https://api.github.com/repos/$repo/releases/tags/v$version")
.parsed<GithubResponse>().assets?.filter {
it.browserDownloadURL.endsWith(".apk")
}
return apks?.firstOrNull()?.browserDownloadURL
}
private suspend fun fetchApkUrlFromFallback(version: String, isDebug: Boolean): String? {
val url = if (isDebug) fallbackBetaUrl else fallbackStableUrl
return CommentsAPI.requestBuilder().get("$url/$version").parsed<FallbackResponse>().downloadUrl
}
suspend fun check(activity: FragmentActivity, post: Boolean = false) {
if (post) snackString(currContext()?.getString(R.string.checking_for_update))
val repo = activity.getString(R.string.repo)
tryWithSuspend {
val (md, version) = fetchUpdateInfo(repo, BuildConfig.DEBUG) ?: return@tryWithSuspend
Logger.log("Git Version : $version") Logger.log("Git Version : $version")
val dontShow = PrefManager.getCustomVal("dont_ask_for_update_$version", false) val dontShow = PrefManager.getCustomVal("dont_ask_for_update_$version", false)
@@ -69,7 +133,7 @@ object AppUpdater {
) )
addView( addView(
TextView(activity).apply { TextView(activity).apply {
val markWon = try { //slower phones can destroy the activity before this is done val markWon = try {
buildMarkwon(activity, false) buildMarkwon(activity, false)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
return@runOnUiThread return@runOnUiThread
@@ -89,17 +153,11 @@ object AppUpdater {
setPositiveButton(currContext()!!.getString(R.string.lets_go)) { setPositiveButton(currContext()!!.getString(R.string.lets_go)) {
MainScope().launch(Dispatchers.IO) { MainScope().launch(Dispatchers.IO) {
try { try {
val apks = val apkUrl = fetchApkUrl(repo, version, BuildConfig.DEBUG)
client.get("https://api.github.com/repos/$repo/releases/tags/v$version") if (apkUrl != null) {
.parsed<GithubResponse>().assets?.filter { activity.downloadUpdate(version, apkUrl)
it.browserDownloadURL.endsWith( } else {
".apk" openLinkInBrowser("https://github.com/repos/$repo/releases/tag/v$version")
)
}
val apkToDownload = apks?.first()
apkToDownload?.browserDownloadURL.apply {
if (this != null) activity.downloadUpdate(version, this)
else openLinkInBrowser("https://github.com/repos/$repo/releases/tag/v$version")
} }
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
@@ -112,8 +170,7 @@ object AppUpdater {
} }
show(activity.supportFragmentManager, "dialog") show(activity.supportFragmentManager, "dialog")
} }
} } else {
else {
if (post) snackString(currContext()?.getString(R.string.no_update_found)) if (post) snackString(currContext()?.getString(R.string.no_update_found))
} }
} }
@@ -144,8 +201,7 @@ object AppUpdater {
//Blatantly kanged from https://github.com/LagradOst/CloudStream-3/blob/master/app/src/main/java/com/lagradost/cloudstream3/utils/InAppUpdater.kt //Blatantly kanged from https://github.com/LagradOst/CloudStream-3/blob/master/app/src/main/java/com/lagradost/cloudstream3/utils/InAppUpdater.kt
private fun Activity.downloadUpdate(version: String, url: String): Boolean { private fun Activity.downloadUpdate(version: String, url: String) {
toast(getString(R.string.downloading_update, version)) toast(getString(R.string.downloading_update, version))
val downloadManager = this.getSystemService<DownloadManager>()!! val downloadManager = this.getSystemService<DownloadManager>()!!
@@ -167,7 +223,7 @@ object AppUpdater {
logError(e) logError(e)
-1 -1
} }
if (id == -1L) return true if (id == -1L) return
ContextCompat.registerReceiver( ContextCompat.registerReceiver(
this, this,
object : BroadcastReceiver() { object : BroadcastReceiver() {
@@ -188,7 +244,6 @@ object AppUpdater {
}, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), }, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
ContextCompat.RECEIVER_EXPORTED ContextCompat.RECEIVER_EXPORTED
) )
return true
} }
private fun openApk(context: Context, uri: Uri) { private fun openApk(context: Context, uri: Uri) {

View File

@@ -113,10 +113,8 @@
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:host="*"/>
<data android:mimeType="application/epub+zip"/> <data android:mimeType="application/epub+zip"/>
<data android:mimeType="application/x-mobipocket-ebook" /> <data android:mimeType="application/x-mobipocket-ebook" />
<data android:mimeType="application/vnd.amazon.ebook" /> <data android:mimeType="application/vnd.amazon.ebook" />
@@ -385,10 +383,10 @@
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" /> <data android:scheme="content" />
<data android:scheme="file" />
<data android:mimeType="*/*" /> <data android:mimeType="*/*" />
<data android:pathPattern=".*\\.ani" /> <data android:pathPattern=".*\\.ani" />
<data android:pathPattern=".*\\.sani" /> <data android:pathPattern=".*\\.sani" />
<data android:host="*" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />

View File

@@ -113,21 +113,28 @@ class App : MultiDexApplication() {
} }
} }
CoroutineScope(Dispatchers.IO).launch { val scope = CoroutineScope(Dispatchers.IO)
scope.launch {
animeExtensionManager = Injekt.get() animeExtensionManager = Injekt.get()
launch {
animeExtensionManager.findAvailableExtensions() animeExtensionManager.findAvailableExtensions()
}
Logger.log("Anime Extensions: ${animeExtensionManager.installedExtensionsFlow.first()}") Logger.log("Anime Extensions: ${animeExtensionManager.installedExtensionsFlow.first()}")
AnimeSources.init(animeExtensionManager.installedExtensionsFlow) AnimeSources.init(animeExtensionManager.installedExtensionsFlow)
} }
CoroutineScope(Dispatchers.IO).launch { scope.launch {
mangaExtensionManager = Injekt.get() mangaExtensionManager = Injekt.get()
launch {
mangaExtensionManager.findAvailableExtensions() mangaExtensionManager.findAvailableExtensions()
}
Logger.log("Manga Extensions: ${mangaExtensionManager.installedExtensionsFlow.first()}") Logger.log("Manga Extensions: ${mangaExtensionManager.installedExtensionsFlow.first()}")
MangaSources.init(mangaExtensionManager.installedExtensionsFlow) MangaSources.init(mangaExtensionManager.installedExtensionsFlow)
} }
CoroutineScope(Dispatchers.IO).launch { scope.launch {
novelExtensionManager = Injekt.get() novelExtensionManager = Injekt.get()
launch {
novelExtensionManager.findAvailableExtensions() novelExtensionManager.findAvailableExtensions()
}
Logger.log("Novel Extensions: ${novelExtensionManager.installedExtensionsFlow.first()}") Logger.log("Novel Extensions: ${novelExtensionManager.installedExtensionsFlow.first()}")
NovelSources.init(novelExtensionManager.installedExtensionsFlow) NovelSources.init(novelExtensionManager.installedExtensionsFlow)
} }

View File

@@ -117,7 +117,6 @@ 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.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
@@ -138,12 +137,9 @@ import io.noties.markwon.html.TagHandlerNoOp
import io.noties.markwon.image.AsyncDrawable import io.noties.markwon.image.AsyncDrawable
import io.noties.markwon.image.glide.GlideImagesPlugin import io.noties.markwon.image.glide.GlideImagesPlugin
import jp.wasabeef.glide.transformations.BlurTransformation import jp.wasabeef.glide.transformations.BlurTransformation
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import nl.joery.animatedbottombar.AnimatedBottomBar import nl.joery.animatedbottombar.AnimatedBottomBar
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@@ -157,6 +153,8 @@ import java.util.TimeZone
import java.util.Timer import java.util.Timer
import java.util.TimerTask import java.util.TimerTask
import kotlin.collections.set import kotlin.collections.set
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.math.log2 import kotlin.math.log2
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@@ -854,6 +852,7 @@ fun savePrefsToDownloads(
} }
) )
} }
@SuppressLint("StringFormatMatches") @SuppressLint("StringFormatMatches")
fun savePrefs(serialized: String, path: String, title: String, context: Context): File? { fun savePrefs(serialized: String, path: String, title: String, context: Context): File? {
var file = File(path, "$title.ani") var file = File(path, "$title.ani")
@@ -921,6 +920,7 @@ fun shareImage(title: String, bitmap: Bitmap, context: Context) {
intent.putExtra(Intent.EXTRA_STREAM, contentUri) intent.putExtra(Intent.EXTRA_STREAM, contentUri)
context.startActivity(Intent.createChooser(intent, "Share $title")) context.startActivity(Intent.createChooser(intent, "Share $title"))
} }
@SuppressLint("StringFormatMatches") @SuppressLint("StringFormatMatches")
fun saveImage(image: Bitmap, path: String, imageFileName: String): File? { fun saveImage(image: Bitmap, path: String, imageFileName: String): File? {
val imageFile = File(path, "$imageFileName.png") val imageFile = File(path, "$imageFileName.png")
@@ -1467,6 +1467,7 @@ fun buildMarkwon(
} }
return false return false
} }
override fun onLoadFailed( override fun onLoadFailed(
e: GlideException?, e: GlideException?,
model: Any?, model: Any?,
@@ -1495,9 +1496,9 @@ fun buildMarkwon(
} }
fun getYoutubeId(url: String): String { fun getYoutubeId(url: String): String {
val regex = """(?:youtube\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|(?:youtu\.be|youtube\.com)/)([^"&?/\s]{11})|youtube\.com/""".toRegex() val regex =
"""(?:youtube\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|(?:youtu\.be|youtube\.com)/)([^"&?/\s]{11})|youtube\.com/""".toRegex()
val matchResult = regex.find(url) val matchResult = regex.find(url)
return matchResult?.groupValues?.getOrNull(1) ?: "" return matchResult?.groupValues?.getOrNull(1) ?: ""
} }
@@ -1524,3 +1525,13 @@ fun getLanguageName(language: String): String? {
} }
return null return null
} }
@OptIn(ExperimentalEncodingApi::class)
fun String.decodeBase64ToString(): String {
return try {
String(Base64.decode(this), Charsets.UTF_8)
} catch (e: Exception) {
Logger.log(e)
""
}
}

View File

@@ -26,9 +26,17 @@ interface DownloadAddonApiV2 {
statCallback: (Double) -> Unit statCallback: (Double) -> Unit
): Long ): Long
suspend fun customFFMpeg(command: String, videoUrls: List<String>, logCallback: (String) -> Unit): Long suspend fun customFFMpeg(
command: String,
videoUrls: List<String>,
logCallback: (String) -> Unit
): Long
suspend fun customFFProbe(command: String, videoUrls: List<String>, logCallback: (String) -> Unit) suspend fun customFFProbe(
command: String,
videoUrls: List<String>,
logCallback: (String) -> Unit
)
fun getState(sessionId: Long): String fun getState(sessionId: Long): String

View File

@@ -6,11 +6,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.addons.AddonDownloader.Companion.hasUpdate import ani.dantotsu.addons.AddonDownloader.Companion.hasUpdate
import ani.dantotsu.addons.AddonInstallReceiver
import ani.dantotsu.addons.AddonListener import ani.dantotsu.addons.AddonListener
import ani.dantotsu.addons.AddonLoader import ani.dantotsu.addons.AddonLoader
import ani.dantotsu.addons.AddonManager import ani.dantotsu.addons.AddonManager
import ani.dantotsu.addons.LoadResult import ani.dantotsu.addons.LoadResult
import ani.dantotsu.addons.AddonInstallReceiver
import ani.dantotsu.media.AddonType import ani.dantotsu.media.AddonType
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName

View File

@@ -41,7 +41,8 @@ class TorrentServerService : Service() {
flags: Int, flags: Int,
startId: Int, startId: Int,
): Int { ): Int {
extension = Injekt.get<TorrentAddonManager>().extension?.extension ?: return START_NOT_STICKY extension =
Injekt.get<TorrentAddonManager>().extension?.extension ?: return START_NOT_STICKY
intent?.let { intent?.let {
if (it.action != null) { if (it.action != null) {
when (it.action) { when (it.action) {

View File

@@ -8,7 +8,6 @@ import androidx.media3.common.util.UnstableApi
import androidx.media3.database.StandaloneDatabaseProvider import androidx.media3.database.StandaloneDatabaseProvider
import ani.dantotsu.addons.download.DownloadAddonManager import ani.dantotsu.addons.download.DownloadAddonManager
import ani.dantotsu.addons.torrent.TorrentAddonManager import ani.dantotsu.addons.torrent.TorrentAddonManager
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.media.manga.MangaCache import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.parsers.novel.NovelExtensionManager import ani.dantotsu.parsers.novel.NovelExtensionManager

View File

@@ -145,7 +145,10 @@ class AnilistMutations {
return result?.get("errors") == null return result?.get("errors") == null
} }
suspend fun updateCustomLists(animeCustomLists: List<String>?, mangaCustomLists: List<String>?): Boolean { suspend fun updateCustomLists(
animeCustomLists: List<String>?,
mangaCustomLists: List<String>?
): Boolean {
val query = """ val query = """
mutation (${"$"}animeListOptions: MediaListOptionsInput, ${"$"}mangaListOptions: MediaListOptionsInput) { mutation (${"$"}animeListOptions: MediaListOptionsInput, ${"$"}mangaListOptions: MediaListOptionsInput) {
UpdateUser(animeListOptions: ${"$"}animeListOptions, mangaListOptions: ${"$"}mangaListOptions) { UpdateUser(animeListOptions: ${"$"}animeListOptions, mangaListOptions: ${"$"}mangaListOptions) {
@@ -291,7 +294,8 @@ class AnilistMutations {
isFollower isFollower
} }
} }
""".trimIndent()) """.trimIndent()
)
} }
suspend fun toggleLike(id: Int, type: String): ToggleLike? { suspend fun toggleLike(id: Int, type: String): ToggleLike? {
@@ -302,7 +306,8 @@ class AnilistMutations {
__typename __typename
} }
} }
""".trimIndent()) """.trimIndent()
)
} }
suspend fun postActivity(text: String, edit: Int? = null): String { suspend fun postActivity(text: String, edit: Int? = null): String {
@@ -316,10 +321,16 @@ class AnilistMutations {
""".trimIndent() """.trimIndent()
val result = executeQuery<JsonObject>(query) val result = executeQuery<JsonObject>(query)
val errors = result?.get("errors") val errors = result?.get("errors")
return errors?.toString() ?: (currContext()?.getString(ani.dantotsu.R.string.success) ?: "Success") return errors?.toString() ?: (currContext()?.getString(ani.dantotsu.R.string.success)
?: "Success")
} }
suspend fun postMessage(userId: Int, text: String, edit: Int? = null, isPrivate: Boolean = false): String { suspend fun postMessage(
userId: Int,
text: String,
edit: Int? = null,
isPrivate: Boolean = false
): String {
val encodedText = text.replace("", "").stringSanitizer() val encodedText = text.replace("", "").stringSanitizer()
val query = """ val query = """
mutation { mutation {
@@ -335,7 +346,8 @@ class AnilistMutations {
""".trimIndent() """.trimIndent()
val result = executeQuery<JsonObject>(query) val result = executeQuery<JsonObject>(query)
val errors = result?.get("errors") val errors = result?.get("errors")
return errors?.toString() ?: (currContext()?.getString(ani.dantotsu.R.string.success) ?: "Success") return errors?.toString() ?: (currContext()?.getString(ani.dantotsu.R.string.success)
?: "Success")
} }
suspend fun postReply(activityId: Int, text: String, edit: Int? = null): String { suspend fun postReply(activityId: Int, text: String, edit: Int? = null): String {
@@ -353,7 +365,8 @@ class AnilistMutations {
""".trimIndent() """.trimIndent()
val result = executeQuery<JsonObject>(query) val result = executeQuery<JsonObject>(query)
val errors = result?.get("errors") val errors = result?.get("errors")
return errors?.toString() ?: (currContext()?.getString(ani.dantotsu.R.string.success) ?: "Success") return errors?.toString() ?: (currContext()?.getString(ani.dantotsu.R.string.success)
?: "Success")
} }
suspend fun postReview(summary: String, body: String, mediaId: Int, score: Int): String { suspend fun postReview(summary: String, body: String, mediaId: Int, score: Int): String {
@@ -373,7 +386,8 @@ class AnilistMutations {
""".trimIndent() """.trimIndent()
val result = executeQuery<JsonObject>(query) val result = executeQuery<JsonObject>(query)
val errors = result?.get("errors") val errors = result?.get("errors")
return errors?.toString() ?: (currContext()?.getString(ani.dantotsu.R.string.success) ?: "Success") return errors?.toString() ?: (currContext()?.getString(ani.dantotsu.R.string.success)
?: "Success")
} }
suspend fun deleteActivityReply(activityId: Int): Boolean { suspend fun deleteActivityReply(activityId: Int): Boolean {

View File

@@ -359,19 +359,24 @@ class AnilistSearch : ViewModel() {
var searched = false var searched = false
var notSet = true var notSet = true
lateinit var aniMangaSearchResults: AniMangaSearchResults lateinit var aniMangaSearchResults: AniMangaSearchResults
private val aniMangaResult: MutableLiveData<AniMangaSearchResults?> = MutableLiveData<AniMangaSearchResults?>(null) private val aniMangaResult: MutableLiveData<AniMangaSearchResults?> =
MutableLiveData<AniMangaSearchResults?>(null)
lateinit var characterSearchResults: CharacterSearchResults lateinit var characterSearchResults: CharacterSearchResults
private val characterResult: MutableLiveData<CharacterSearchResults?> = MutableLiveData<CharacterSearchResults?>(null) private val characterResult: MutableLiveData<CharacterSearchResults?> =
MutableLiveData<CharacterSearchResults?>(null)
lateinit var studioSearchResults: StudioSearchResults lateinit var studioSearchResults: StudioSearchResults
private val studioResult: MutableLiveData<StudioSearchResults?> = MutableLiveData<StudioSearchResults?>(null) private val studioResult: MutableLiveData<StudioSearchResults?> =
MutableLiveData<StudioSearchResults?>(null)
lateinit var staffSearchResults: StaffSearchResults lateinit var staffSearchResults: StaffSearchResults
private val staffResult: MutableLiveData<StaffSearchResults?> = MutableLiveData<StaffSearchResults?>(null) private val staffResult: MutableLiveData<StaffSearchResults?> =
MutableLiveData<StaffSearchResults?>(null)
lateinit var userSearchResults: UserSearchResults lateinit var userSearchResults: UserSearchResults
private val userResult: MutableLiveData<UserSearchResults?> = MutableLiveData<UserSearchResults?>(null) private val userResult: MutableLiveData<UserSearchResults?> =
MutableLiveData<UserSearchResults?>(null)
fun <T> getSearch(type: SearchType): MutableLiveData<T?> { fun <T> getSearch(type: SearchType): MutableLiveData<T?> {
return when (type) { return when (type) {
@@ -517,7 +522,8 @@ class AnilistSearch : ViewModel() {
) )
) )
private suspend fun loadNextCharacterPage(r: CharacterSearchResults) = characterResult.postValue( private suspend fun loadNextCharacterPage(r: CharacterSearchResults) =
characterResult.postValue(
Anilist.query.searchCharacters( Anilist.query.searchCharacters(
r.page + 1, r.page + 1,
r.search, r.search,

View File

@@ -34,7 +34,8 @@ fun characterInformation(includeMediaInfo: Boolean) = """
month month
day day
} }
${if (includeMediaInfo) """ ${
if (includeMediaInfo) """
media(page: 0,sort:[POPULARITY_DESC,SCORE_DESC]) { media(page: 0,sort:[POPULARITY_DESC,SCORE_DESC]) {
$standardPageInformation $standardPageInformation
edges { edges {
@@ -79,7 +80,8 @@ fun characterInformation(includeMediaInfo: Boolean) = """
} }
} }
} }
}""".prepare() else ""} }""".prepare() else ""
}
""".prepare() """.prepare()
fun studioInformation(page: Int, perPage: Int) = """ fun studioInformation(page: Int, perPage: Int) = """

View File

@@ -14,6 +14,7 @@ data class FeedResponse(
val page: ActivityPage val page: ActivityPage
) : java.io.Serializable ) : java.io.Serializable
} }
@Serializable @Serializable
data class ActivityPage( data class ActivityPage(
@SerialName("activities") @SerialName("activities")

View File

@@ -239,6 +239,7 @@ data class AiringSchedule(
// The associate media of the airing episode // The associate media of the airing episode
@SerialName("media") var media: Media?, @SerialName("media") var media: Media?,
) )
@Serializable @Serializable
data class MediaStreamingEpisode( data class MediaStreamingEpisode(
// The title of the episode // The title of the episode
@@ -252,7 +253,8 @@ data class MediaStreamingEpisode(
// The site location of the streaming episode // The site location of the streaming episode
@SerialName("site") var site: String?, @SerialName("site") var site: String?,
) ) : java.io.Serializable
@Serializable @Serializable
data class MediaCoverImage( data class MediaCoverImage(
// The cover image url of the media at its largest size. If this size isn't available, large will be provided instead. // The cover image url of the media at its largest size. If this size isn't available, large will be provided instead.

View File

@@ -123,8 +123,10 @@ data class UserStatisticTypes(
enum class UserTitleLanguage { enum class UserTitleLanguage {
@SerialName("ENGLISH") @SerialName("ENGLISH")
ENGLISH, ENGLISH,
@SerialName("ROMAJI") @SerialName("ROMAJI")
ROMAJI, ROMAJI,
@SerialName("NATIVE") @SerialName("NATIVE")
NATIVE NATIVE
} }
@@ -133,8 +135,10 @@ enum class UserTitleLanguage {
enum class UserStaffNameLanguage { enum class UserStaffNameLanguage {
@SerialName("ROMAJI_WESTERN") @SerialName("ROMAJI_WESTERN")
ROMAJI_WESTERN, ROMAJI_WESTERN,
@SerialName("ROMAJI") @SerialName("ROMAJI")
ROMAJI, ROMAJI,
@SerialName("NATIVE") @SerialName("NATIVE")
NATIVE NATIVE
} }
@@ -143,12 +147,16 @@ enum class UserStaffNameLanguage {
enum class ScoreFormat { enum class ScoreFormat {
@SerialName("POINT_100") @SerialName("POINT_100")
POINT_100, POINT_100,
@SerialName("POINT_10_DECIMAL") @SerialName("POINT_10_DECIMAL")
POINT_10_DECIMAL, POINT_10_DECIMAL,
@SerialName("POINT_10") @SerialName("POINT_10")
POINT_10, POINT_10,
@SerialName("POINT_5") @SerialName("POINT_5")
POINT_5, POINT_5,
@SerialName("POINT_3") @SerialName("POINT_3")
POINT_3, POINT_3,
} }

View File

@@ -372,6 +372,7 @@ object CommentsAPI {
} }
errorMessage("Failed to login after multiple attempts") errorMessage("Failed to login after multiple attempts")
} }
private fun errorMessage(reason: String) { private fun errorMessage(reason: String) {
if (commentsEnabled) Logger.log(reason) if (commentsEnabled) Logger.log(reason)
if (isOnline && commentsEnabled) snackString(reason) if (isOnline && commentsEnabled) snackString(reason)
@@ -410,7 +411,7 @@ object CommentsAPI {
return map return map
} }
private fun requestBuilder(client: OkHttpClient = Injekt.get<NetworkHelper>().client): Requests { fun requestBuilder(client: OkHttpClient = Injekt.get<NetworkHelper>().client): Requests {
return Requests( return Requests(
client, client,
headerBuilder() headerBuilder()

View File

@@ -47,8 +47,8 @@ class Login : AppCompatActivity() {
view.evaluateJavascript( view.evaluateJavascript(
""" """
(function() { (function() {
const wreq = (webpackChunkdiscord_app.push([[''],{},e=>{m=[];for(let c in e.c)m.push(e.c[c])}]),m).find(m=>m?.exports?.default?.getToken!==void 0).exports.default.getToken(); const m = []; webpackChunkdiscord_app.push([[""], {}, e => {for (let c in e.c)m.push(e.c[c])}]);
return wreq; return m.find(n => n?.exports?.default?.getToken !== void 0)?.exports?.default?.getToken();
})() })()
""".trimIndent() """.trimIndent()
) { result -> ) { result ->

View File

@@ -8,8 +8,8 @@ import ani.dantotsu.settings.saving.PrefName
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import kotlin.coroutines.CoroutineContext
import java.util.concurrent.TimeUnit.SECONDS import java.util.concurrent.TimeUnit.SECONDS
import kotlin.coroutines.CoroutineContext
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
open class RPC(val token: String, val coroutineContext: CoroutineContext) { open class RPC(val token: String, val coroutineContext: CoroutineContext) {
@@ -34,6 +34,7 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) {
val stopTimestamp: Long? = null, val stopTimestamp: Long? = null,
val buttons: MutableList<Link> = mutableListOf() val buttons: MutableList<Link> = mutableListOf()
) )
suspend fun createPresence(data: RPCData): String { suspend fun createPresence(data: RPCData): String {
val json = Json { val json = Json {
encodeDefaults = true encodeDefaults = true

View File

@@ -30,7 +30,6 @@ import ani.dantotsu.bottomBar
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.currActivity import ani.dantotsu.currActivity
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadCompat
import ani.dantotsu.download.DownloadCompat.Companion.loadMediaCompat import ani.dantotsu.download.DownloadCompat.Companion.loadMediaCompat
import ani.dantotsu.download.DownloadCompat.Companion.loadOfflineAnimeModelCompat import ani.dantotsu.download.DownloadCompat.Companion.loadOfflineAnimeModelCompat
import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadedType
@@ -209,7 +208,9 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
setMessage("Are you sure you want to delete ${item.title}?") setMessage("Are you sure you want to delete ${item.title}?")
setPosButton(R.string.yes) { setPosButton(R.string.yes) {
downloadManager.removeMedia(item.title, type) downloadManager.removeMedia(item.title, type)
val mediaIds = PrefManager.getAnimeDownloadPreferences().all?.filter { it.key.contains(item.title) }?.values ?: emptySet() val mediaIds =
PrefManager.getAnimeDownloadPreferences().all?.filter { it.key.contains(item.title) }?.values
?: emptySet()
if (mediaIds.isEmpty()) { if (mediaIds.isEmpty()) {
snackString("No media found") // if this happens, terrible things have happened snackString("No media found") // if this happens, terrible things have happened
} }
@@ -287,10 +288,12 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
} }
downloadsJob = Job() downloadsJob = Job()
CoroutineScope(Dispatchers.IO + downloadsJob).launch { CoroutineScope(Dispatchers.IO + downloadsJob).launch {
val animeTitles = downloadManager.animeDownloadedTypes.map { it.titleName.findValidName() }.distinct() val animeTitles =
downloadManager.animeDownloadedTypes.map { it.titleName.findValidName() }.distinct()
val newAnimeDownloads = mutableListOf<OfflineAnimeModel>() val newAnimeDownloads = mutableListOf<OfflineAnimeModel>()
for (title in animeTitles) { for (title in animeTitles) {
val tDownloads = downloadManager.animeDownloadedTypes.filter { it.titleName.findValidName() == title } val tDownloads =
downloadManager.animeDownloadedTypes.filter { it.titleName.findValidName() == title }
val download = tDownloads.firstOrNull() ?: continue val download = tDownloads.firstOrNull() ?: continue
val offlineAnimeModel = loadOfflineAnimeModel(download) val offlineAnimeModel = loadOfflineAnimeModel(download)
if (offlineAnimeModel.title == "unknown") offlineAnimeModel.title = title if (offlineAnimeModel.title == "unknown") offlineAnimeModel.title = title

View File

@@ -232,12 +232,18 @@ class MangaDownloaderService : Service() {
image.page, image.page,
image.source image.source
) )
if (bitmap == null) {
snackString("${task.chapter} - Retrying to download page ${index.ofLength(3)}, attempt ${retryCount + 1}.")
}
retryCount++ retryCount++
} }
if (bitmap != null) { if (bitmap == null) {
saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap) outputDir.deleteRecursively(this@MangaDownloaderService, false)
throw Exception("${task.chapter} - Unable to download all pages after $retryCount attempts. Try again.")
} }
saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap)
farthest++ farthest++
builder.setProgress(task.imageData.size, farthest, false) builder.setProgress(task.imageData.size, farthest, false)

View File

@@ -177,6 +177,7 @@ object Helper {
downloadManager downloadManager
} }
} }
@Deprecated("exoplayer download manager is no longer used") @Deprecated("exoplayer download manager is no longer used")
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
fun getSimpleCache(context: Context): SimpleCache { fun getSimpleCache(context: Context): SimpleCache {
@@ -189,6 +190,7 @@ object Helper {
simpleCache!! simpleCache!!
} }
} }
@Synchronized @Synchronized
@Deprecated("exoplayer download manager is no longer used") @Deprecated("exoplayer download manager is no longer used")
private fun getDownloadDirectory(context: Context): File { private fun getDownloadDirectory(context: Context): File {
@@ -200,12 +202,16 @@ object Helper {
} }
return downloadDirectory!! return downloadDirectory!!
} }
@Deprecated("exoplayer download manager is no longer used") @Deprecated("exoplayer download manager is no longer used")
private var download: DownloadManager? = null private var download: DownloadManager? = null
@Deprecated("exoplayer download manager is no longer used") @Deprecated("exoplayer download manager is no longer used")
private const val DOWNLOAD_CONTENT_DIRECTORY = "Anime_Downloads" private const val DOWNLOAD_CONTENT_DIRECTORY = "Anime_Downloads"
@Deprecated("exoplayer download manager is no longer used") @Deprecated("exoplayer download manager is no longer used")
private var simpleCache: SimpleCache? = null private var simpleCache: SimpleCache? = null
@Deprecated("exoplayer download manager is no longer used") @Deprecated("exoplayer download manager is no longer used")
private var downloadDirectory: File? = null private var downloadDirectory: File? = null
} }

View File

@@ -22,9 +22,9 @@ import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.Refresh import ani.dantotsu.Refresh
import ani.dantotsu.bottomBar import ani.dantotsu.bottomBar
import ani.dantotsu.connections.anilist.AniMangaSearchResults
import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistAnimeViewModel import ani.dantotsu.connections.anilist.AnilistAnimeViewModel
import ani.dantotsu.connections.anilist.AniMangaSearchResults
import ani.dantotsu.connections.anilist.getUserId import ani.dantotsu.connections.anilist.getUserId
import ani.dantotsu.databinding.FragmentAnimeBinding import ani.dantotsu.databinding.FragmentAnimeBinding
import ani.dantotsu.media.MediaAdaptor import ani.dantotsu.media.MediaAdaptor
@@ -277,7 +277,8 @@ class AnimeFragment : Fragment() {
running = true running = true
scope.launch { scope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
Anilist.userid = PrefManager.getNullableVal<String>(PrefName.AnilistUserId, null) Anilist.userid =
PrefManager.getNullableVal<String>(PrefName.AnilistUserId, null)
?.toIntOrNull() ?.toIntOrNull()
if (Anilist.userid == null) { if (Anilist.userid == null) {
getUserId(requireContext()) { getUserId(requireContext()) {

View File

@@ -20,7 +20,6 @@ import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.MediaPageTransformer import ani.dantotsu.MediaPageTransformer
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType.Companion.toAnilistString
import ani.dantotsu.databinding.ItemAnimePageBinding import ani.dantotsu.databinding.ItemAnimePageBinding
import ani.dantotsu.databinding.LayoutTrendingBinding import ani.dantotsu.databinding.LayoutTrendingBinding
import ani.dantotsu.getAppString import ani.dantotsu.getAppString
@@ -266,7 +265,15 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
} }
} }
fun init(adaptor: MediaAdaptor, recyclerView: RecyclerView, progress: View, title: View , more: View , string: String, media : MutableList<Media>) { fun init(
adaptor: MediaAdaptor,
recyclerView: RecyclerView,
progress: View,
title: View,
more: View,
string: String,
media: MutableList<Media>
) {
progress.visibility = View.GONE progress.visibility = View.GONE
recyclerView.adapter = adaptor recyclerView.adapter = adaptor
recyclerView.layoutManager = recyclerView.layoutManager =

View File

@@ -469,7 +469,9 @@ class HomeFragment : Fragment() {
scope.launch { scope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
// Get user data first // Get user data first
Anilist.userid = PrefManager.getNullableVal<String>(PrefName.AnilistUserId, null)?.toIntOrNull() Anilist.userid =
PrefManager.getNullableVal<String>(PrefName.AnilistUserId, null)
?.toIntOrNull()
if (Anilist.userid == null) { if (Anilist.userid == null) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
getUserId(requireContext()) { getUserId(requireContext()) {

View File

@@ -20,9 +20,9 @@ import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.Refresh import ani.dantotsu.Refresh
import ani.dantotsu.bottomBar import ani.dantotsu.bottomBar
import ani.dantotsu.connections.anilist.AniMangaSearchResults
import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistMangaViewModel import ani.dantotsu.connections.anilist.AnilistMangaViewModel
import ani.dantotsu.connections.anilist.AniMangaSearchResults
import ani.dantotsu.connections.anilist.getUserId import ani.dantotsu.connections.anilist.getUserId
import ani.dantotsu.databinding.FragmentMangaBinding import ani.dantotsu.databinding.FragmentMangaBinding
import ani.dantotsu.media.MediaAdaptor import ani.dantotsu.media.MediaAdaptor
@@ -169,7 +169,10 @@ class MangaFragment : Fragment() {
} }
model.getPopularManga().observe(viewLifecycleOwner) { model.getPopularManga().observe(viewLifecycleOwner) {
if (it != null) { if (it != null) {
mangaPageAdapter.updateTrendingManga(MediaAdaptor(0, it, requireActivity()), it) mangaPageAdapter.updateTrendingManga(
MediaAdaptor(0, it, requireActivity()),
it
)
} }
} }
model.getPopularManhwa().observe(viewLifecycleOwner) { model.getPopularManhwa().observe(viewLifecycleOwner) {
@@ -262,7 +265,8 @@ class MangaFragment : Fragment() {
running = true running = true
scope.launch { scope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
Anilist.userid = PrefManager.getNullableVal<String>(PrefName.AnilistUserId, null) Anilist.userid =
PrefManager.getNullableVal<String>(PrefName.AnilistUserId, null)
?.toIntOrNull() ?.toIntOrNull()
if (Anilist.userid == null) { if (Anilist.userid == null) {
getUserId(requireContext()) { getUserId(requireContext()) {

View File

@@ -58,6 +58,7 @@ class StatusActivity : AppCompatActivity(), StoriesCallback {
} }
} }
private fun findFirstNonMatch(watchedActivity: Set<Int>, activity: List<Activity>): Int { private fun findFirstNonMatch(watchedActivity: Set<Int>, activity: List<Activity>): Int {
for (activityItem in activity) { for (activityItem in activity) {
if (activityItem.id !in watchedActivity) { if (activityItem.id !in watchedActivity) {
@@ -86,6 +87,7 @@ class StatusActivity : AppCompatActivity(), StoriesCallback {
binding.stories.pause() binding.stories.pause()
} }
} }
override fun onStoriesEnd() { override fun onStoriesEnd() {
position += 1 position += 1
if (position < activity.size) { if (position < activity.size) {
@@ -115,6 +117,7 @@ class StatusActivity : AppCompatActivity(), StoriesCallback {
finish() finish()
} }
} }
companion object { companion object {
var user: ArrayList<User> = arrayListOf() var user: ArrayList<User> = arrayListOf()
} }

View File

@@ -262,8 +262,6 @@ class Stories @JvmOverloads constructor(
} }
private fun rightPanelTouch() { private fun rightPanelTouch() {
Logger.log("rightPanelTouch: $storyIndex") Logger.log("rightPanelTouch: $storyIndex")
if (storyIndex == activityList.size) { if (storyIndex == activityList.size) {
@@ -469,6 +467,7 @@ class Stories @JvmOverloads constructor(
} }
} }
} }
private var startClickTime = 0L private var startClickTime = 0L
private var startX = 0f private var startX = 0f
private var startY = 0f private var startY = 0f
@@ -478,6 +477,7 @@ class Stories @JvmOverloads constructor(
onTouchView(view, event) onTouchView(view, event)
return true return true
} }
private fun onTouchView(view: View, event: MotionEvent, isText: Boolean = false) { private fun onTouchView(view: View, event: MotionEvent, isText: Boolean = false) {
val maxClickDuration = 200 val maxClickDuration = 200
val screenWidth = view.width val screenWidth = view.width
@@ -492,6 +492,7 @@ class Stories @JvmOverloads constructor(
pause() pause()
isLongPress = false isLongPress = false
} }
MotionEvent.ACTION_MOVE -> { MotionEvent.ACTION_MOVE -> {
val deltaX = event.x - startX val deltaX = event.x - startX
val deltaY = event.y - startY val deltaY = event.y - startY
@@ -499,6 +500,7 @@ class Stories @JvmOverloads constructor(
isLongPress = true isLongPress = true
} }
} }
MotionEvent.ACTION_UP -> { MotionEvent.ACTION_UP -> {
val clickDuration = Calendar.getInstance().timeInMillis - startClickTime val clickDuration = Calendar.getInstance().timeInMillis - startClickTime
if (isText) { if (isText) {

View File

@@ -76,10 +76,15 @@ class UserStatusAdapter(private val user: ArrayList<User>) :
setAnimation(b.root.context, b.root) setAnimation(b.root.context, b.root)
val user = user[position] val user = user[position]
b.profileUserAvatar.loadImage(user.pfp) b.profileUserAvatar.loadImage(user.pfp)
b.profileUserName.text = if (Anilist.userid == user.id) getAppString(R.string.your_story) else user.name b.profileUserName.text =
if (Anilist.userid == user.id) getAppString(R.string.your_story) else user.name
val watchedActivity = PrefManager.getCustomVal<Set<Int>>("activities", setOf()) val watchedActivity = PrefManager.getCustomVal<Set<Int>>("activities", setOf())
val booleanList = user.activity.map { watchedActivity.contains(it.id) } val booleanList = user.activity.map { watchedActivity.contains(it.id) }
b.profileUserStatusIndicator.setParts(user.activity.size, booleanList, user.id == Anilist.userid) b.profileUserStatusIndicator.setParts(
user.activity.size,
booleanList,
user.id == Anilist.userid
)
} }

View File

@@ -55,7 +55,11 @@ class CharacterAdapter(
).toBundle() ).toBundle()
) )
} }
itemView.setOnLongClickListener { copyToClipboard(characterList[bindingAdapterPosition].name ?: ""); true } itemView.setOnLongClickListener {
copyToClipboard(
characterList[bindingAdapterPosition].name ?: ""
); true
}
} }
} }
} }

View File

@@ -7,11 +7,9 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.buildMarkwon
import ani.dantotsu.currActivity import ani.dantotsu.currActivity
import ani.dantotsu.databinding.ItemCharacterDetailsBinding import ani.dantotsu.databinding.ItemCharacterDetailsBinding
import ani.dantotsu.others.SpoilerPlugin
import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin
class CharacterDetailsAdapter(private val character: Character, private val activity: Activity) : class CharacterDetailsAdapter(private val character: Character, private val activity: Activity) :
RecyclerView.Adapter<CharacterDetailsAdapter.GenreViewHolder>() { RecyclerView.Adapter<CharacterDetailsAdapter.GenreViewHolder>() {
@@ -24,6 +22,8 @@ class CharacterDetailsAdapter(private val character: Character, private val acti
override fun onBindViewHolder(holder: GenreViewHolder, position: Int) { override fun onBindViewHolder(holder: GenreViewHolder, position: Int) {
val binding = holder.binding val binding = holder.binding
val desc = val desc =
(if (character.id == 4004)
"![za wardo](https://media1.tenor.com/m/_z1tmCJnL2wAAAAd/za-warudo.gif) \n" else "") +
(if (character.age != "null") "${currActivity()!!.getString(R.string.age)} ${character.age}" else "") + (if (character.age != "null") "${currActivity()!!.getString(R.string.age)} ${character.age}" else "") +
(if (character.dateOfBirth.toString() != "") (if (character.dateOfBirth.toString() != "")
"${currActivity()!!.getString(R.string.birthday)} ${character.dateOfBirth.toString()}" else "") + "${currActivity()!!.getString(R.string.birthday)} ${character.dateOfBirth.toString()}" else "") +
@@ -41,8 +41,7 @@ class CharacterDetailsAdapter(private val character: Character, private val acti
} else "") + "\n" + character.description } else "") + "\n" + character.description
binding.characterDesc.isTextSelectable binding.characterDesc.isTextSelectable
val markWon = Markwon.builder(activity).usePlugin(SoftBreakAddsNewLinePlugin.create()) val markWon = buildMarkwon(activity)
.usePlugin(SpoilerPlugin()).build()
markWon.setMarkdown(binding.characterDesc, desc.replace("~!", "||").replace("!~", "||")) markWon.setMarkdown(binding.characterDesc, desc.replace("~!", "||").replace("!~", "||"))
binding.voiceActorRecycler.adapter = AuthorAdapter(character.voiceActor ?: arrayListOf()) binding.voiceActorRecycler.adapter = AuthorAdapter(character.voiceActor ?: arrayListOf())
binding.voiceActorRecycler.layoutManager = LinearLayoutManager( binding.voiceActorRecycler.layoutManager = LinearLayoutManager(

View File

@@ -147,7 +147,7 @@ fun Media?.deleteFromList(
scope.launch { scope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
this@deleteFromList?.let { media -> this@deleteFromList?.let { media ->
val _id = id ?: Anilist.query.userMediaDetails(media).userListId; val _id = id ?: Anilist.query.userMediaDetails(media).userListId
_id?.let { listId -> _id?.let { listId ->
try { try {
Anilist.mutation.deleteList(listId) Anilist.mutation.deleteList(listId)

View File

@@ -4,6 +4,7 @@ import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.view.GestureDetector import android.view.GestureDetector
@@ -12,6 +13,8 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.ImageView import android.widget.ImageView
import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -19,8 +22,10 @@ import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.text.bold import androidx.core.text.bold
import androidx.core.text.color import androidx.core.text.color
import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.marginBottom import androidx.core.view.marginBottom
import androidx.core.view.setPadding
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.view.updateMargins import androidx.core.view.updateMargins
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@@ -79,6 +84,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
var media: Media = intent.getSerialized("media") ?: mediaSingleton ?: emptyMedia() var media: Media = intent.getSerialized("media") ?: mediaSingleton ?: emptyMedia()
val id = intent.getIntExtra("mediaId", -1) val id = intent.getIntExtra("mediaId", -1)
@@ -109,6 +115,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
// Ui init // Ui init
initActivity(this) initActivity(this)
binding.mediaViewPager.updateLayoutParams<ViewGroup.MarginLayoutParams> { binding.mediaViewPager.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = navBarHeight bottomMargin = navBarHeight
} }
@@ -132,10 +139,12 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
val navBarBottomMargin = if (resources.configuration.orientation == val navBarBottomMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE Configuration.ORIENTATION_LANDSCAPE
) 0 else navBarHeight ) 0 else navBarHeight
navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> { navBar.setPadding(
rightMargin = navBarRightMargin navBar.paddingLeft,
bottomMargin = navBarBottomMargin navBar.paddingTop,
} navBar.paddingRight + navBarRightMargin,
navBar.paddingBottom + navBarBottomMargin
)
binding.mediaBanner.updateLayoutParams { height += statusBarHeight } binding.mediaBanner.updateLayoutParams { height += statusBarHeight }
binding.mediaBannerNoKen.updateLayoutParams { height += statusBarHeight } binding.mediaBannerNoKen.updateLayoutParams { height += statusBarHeight }
binding.mediaClose.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight } binding.mediaClose.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
@@ -251,10 +260,12 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
fun total() { fun total() {
val text = SpannableStringBuilder().apply { val text = SpannableStringBuilder().apply {
val white = this@MediaDetailsActivity.getThemeColor(com.google.android.material.R.attr.colorOnBackground) val white =
this@MediaDetailsActivity.getThemeColor(com.google.android.material.R.attr.colorOnBackground)
if (media.userStatus != null) { if (media.userStatus != null) {
append(if (media.anime != null) getString(R.string.watched_num) else getString(R.string.read_num)) append(if (media.anime != null) getString(R.string.watched_num) else getString(R.string.read_num))
val colorSecondary = getThemeColor(com.google.android.material.R.attr.colorSecondary) val colorSecondary =
getThemeColor(com.google.android.material.R.attr.colorSecondary)
bold { color(colorSecondary) { append("${media.userProgress}") } } bold { color(colorSecondary) { append("${media.userProgress}") } }
append( append(
if (media.anime != null) getString(R.string.episodes_out_of) else getString( if (media.anime != null) getString(R.string.episodes_out_of) else getString(

View File

@@ -100,6 +100,7 @@ class MediaDetailsViewModel : ViewModel() {
if (kitsuEpisodes.value == null) kitsuEpisodes.postValue(Kitsu.getKitsuEpisodesDetails(s)) if (kitsuEpisodes.value == null) kitsuEpisodes.postValue(Kitsu.getKitsuEpisodesDetails(s))
} }
} }
private val anifyEpisodes: MutableLiveData<Map<String, Episode>> = private val anifyEpisodes: MutableLiveData<Map<String, Episode>> =
MutableLiveData<Map<String, Episode>>(null) MutableLiveData<Map<String, Episode>>(null)

View File

@@ -18,7 +18,6 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import java.util.ArrayList
class MediaListViewActivity : AppCompatActivity() { class MediaListViewActivity : AppCompatActivity() {
private lateinit var binding: ActivityMediaListViewBinding private lateinit var binding: ActivityMediaListViewBinding
@@ -52,7 +51,8 @@ class MediaListViewActivity: AppCompatActivity() {
binding.listAppBar.setBackgroundColor(primaryColor) binding.listAppBar.setBackgroundColor(primaryColor)
binding.listTitle.setTextColor(primaryTextColor) binding.listTitle.setTextColor(primaryTextColor)
val screenWidth = resources.displayMetrics.run { widthPixels / density } val screenWidth = resources.displayMetrics.run { widthPixels / density }
val mediaList = passedMedia ?: intent.getSerialized("media") as? ArrayList<Media> ?: ArrayList() val mediaList =
passedMedia ?: intent.getSerialized("media") as? ArrayList<Media> ?: ArrayList()
if (passedMedia != null) passedMedia = null if (passedMedia != null) passedMedia = null
val view = PrefManager.getCustomVal("mediaView", 0) val view = PrefManager.getCustomVal("mediaView", 0)
var mediaView: View = when (view) { var mediaView: View = when (view) {

View File

@@ -44,7 +44,10 @@ class MediaSocialAdapter(
profileUserName.text = user.name profileUserName.text = user.name
profileInfo.apply { profileInfo.apply {
text = when (user.status) { text = when (user.status) {
"CURRENT" -> if (type == "ANIME") getAppString(R.string.watching) else getAppString(R.string.reading) "CURRENT" -> if (type == "ANIME") getAppString(R.string.watching) else getAppString(
R.string.reading
)
else -> user.status ?: "" else -> user.status ?: ""
} }
visibility = View.VISIBLE visibility = View.VISIBLE
@@ -63,10 +66,12 @@ class MediaSocialAdapter(
profileCompactProgressContainer.visibility = View.VISIBLE profileCompactProgressContainer.visibility = View.VISIBLE
profileUserAvatar.setOnClickListener { profileUserAvatar.setOnClickListener {
ContextCompat.startActivity(root.context, ContextCompat.startActivity(
root.context,
Intent(root.context, ProfileActivity::class.java) Intent(root.context, ProfileActivity::class.java)
.putExtra("userId", user.id), .putExtra("userId", user.id),
null) null
)
} }
profileUserAvatarContainer.setOnLongClickListener { profileUserAvatarContainer.setOnLongClickListener {
ImageViewDialog.newInstance( ImageViewDialog.newInstance(

View File

@@ -1,23 +1,15 @@
package ani.dantotsu.media package ani.dantotsu.media
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.view.View import android.view.View
import androidx.activity.ComponentActivity
import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.util.Pair
import androidx.core.view.ViewCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.Query import ani.dantotsu.connections.anilist.api.Query
import ani.dantotsu.databinding.ItemReviewsBinding import ani.dantotsu.databinding.ItemReviewsBinding
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.openImage import ani.dantotsu.openImage
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.profile.activity.ActivityItemBuilder import ani.dantotsu.profile.activity.ActivityItemBuilder
import ani.dantotsu.toast import ani.dantotsu.toast
@@ -85,6 +77,7 @@ class ReviewAdapter(
override fun initializeViewBinding(view: View): ItemReviewsBinding { override fun initializeViewBinding(view: View): ItemReviewsBinding {
return ItemReviewsBinding.bind(view) return ItemReviewsBinding.bind(view)
} }
private fun userVote(type: String) { private fun userVote(type: String) {
when (type) { when (type) {
"NO_VOTE" -> { "NO_VOTE" -> {

View File

@@ -20,7 +20,6 @@ import ani.dantotsu.initActivity
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.openImage import ani.dantotsu.openImage
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.profile.activity.ActivityItemBuilder import ani.dantotsu.profile.activity.ActivityItemBuilder
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
@@ -52,7 +51,8 @@ class ReviewViewActivity : AppCompatActivity() {
binding.userAvatar.loadImage(review.user?.avatar?.medium) binding.userAvatar.loadImage(review.user?.avatar?.medium)
binding.userTime.text = ActivityItemBuilder.getDateTime(review.createdAt) binding.userTime.text = ActivityItemBuilder.getDateTime(review.createdAt)
binding.userContainer.setOnClickListener { binding.userContainer.setOnClickListener {
startActivity(Intent(this, ProfileActivity::class.java) startActivity(
Intent(this, ProfileActivity::class.java)
.putExtra("userId", review.user?.id) .putExtra("userId", review.user?.id)
) )
} }
@@ -61,7 +61,8 @@ class ReviewViewActivity : AppCompatActivity() {
review.user?.avatar?.medium ?: "" review.user?.avatar?.medium ?: ""
) )
binding.userAvatar.setOnClickListener { binding.userAvatar.setOnClickListener {
startActivity(Intent(this, ProfileActivity::class.java) startActivity(
Intent(this, ProfileActivity::class.java)
.putExtra("userId", review.user?.id) .putExtra("userId", review.user?.id)
) )
} }

View File

@@ -13,10 +13,10 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.connections.anilist.AniMangaSearchResults
import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistSearch import ani.dantotsu.connections.anilist.AnilistSearch
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType import ani.dantotsu.connections.anilist.AnilistSearch.SearchType
import ani.dantotsu.connections.anilist.AniMangaSearchResults
import ani.dantotsu.connections.anilist.CharacterSearchResults import ani.dantotsu.connections.anilist.CharacterSearchResults
import ani.dantotsu.connections.anilist.StaffSearchResults import ani.dantotsu.connections.anilist.StaffSearchResults
import ani.dantotsu.connections.anilist.StudioSearchResults import ani.dantotsu.connections.anilist.StudioSearchResults
@@ -377,7 +377,10 @@ class SearchActivity : AppCompatActivity() {
} }
SearchType.CHARACTER -> { SearchType.CHARACTER -> {
characterAdaptor.notifyItemRangeRemoved(0, model.characterSearchResults.results.size) characterAdaptor.notifyItemRangeRemoved(
0,
model.characterSearchResults.results.size
)
model.characterSearchResults.results.clear() model.characterSearchResults.results.clear()
} }

View File

@@ -57,6 +57,7 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Sear
searchHistoryAdapter = SearchHistoryAdapter(type) { searchHistoryAdapter = SearchHistoryAdapter(type) {
binding.searchBarText.setText(it) binding.searchBarText.setText(it)
binding.searchBarText.setSelection(it.length)
} }
binding.searchHistoryList.layoutManager = LinearLayoutManager(binding.root.context) binding.searchHistoryList.layoutManager = LinearLayoutManager(binding.root.context)
binding.searchHistoryList.adapter = searchHistoryAdapter binding.searchHistoryList.adapter = searchHistoryAdapter

View File

@@ -346,7 +346,9 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
binding.searchGenresGrid.isChecked = false binding.searchGenresGrid.isChecked = false
binding.searchFilterTags.adapter = binding.searchFilterTags.adapter =
FilterChipAdapter(Anilist.tags?.get(activity.aniMangaResult.isAdult) ?: listOf()) { chip -> FilterChipAdapter(
Anilist.tags?.get(activity.aniMangaResult.isAdult) ?: listOf()
) { chip ->
val tag = chip.text.toString() val tag = chip.text.toString()
chip.isChecked = selectedTags.contains(tag) chip.isChecked = selectedTags.contains(tag)
chip.isCloseIconVisible = exTags.contains(tag) chip.isCloseIconVisible = exTags.contains(tag)

View File

@@ -55,7 +55,11 @@ class StudioAdapter(
).toBundle() ).toBundle()
) )
} }
itemView.setOnLongClickListener { copyToClipboard(studioList[bindingAdapterPosition].name ?: ""); true } itemView.setOnLongClickListener {
copyToClipboard(
studioList[bindingAdapterPosition].name
); true
}
} }
} }
} }

View File

@@ -1,6 +1,8 @@
package ani.dantotsu.media package ani.dantotsu.media
import android.content.Context import android.content.Context
import androidx.core.net.toFile
import androidx.core.net.toUri
import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.parsers.SubtitleType import ani.dantotsu.parsers.SubtitleType
@@ -21,6 +23,7 @@ class SubtitleDownloader {
suspend fun loadSubtitleType(url: String): SubtitleType = suspend fun loadSubtitleType(url: String): SubtitleType =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
return@withContext try { return@withContext try {
if (!url.startsWith("file")) {
// Initialize the NetworkHelper instance. Replace this line based on how you usually initialize it // Initialize the NetworkHelper instance. Replace this line based on how you usually initialize it
val networkHelper = Injekt.get<NetworkHelper>() val networkHelper = Injekt.get<NetworkHelper>()
val request = Request.Builder() val request = Request.Builder()
@@ -34,22 +37,34 @@ class SubtitleDownloader {
val responseBody = response.body.string() val responseBody = response.body.string()
val subtitleType = when { val subtitleType = getType(responseBody)
responseBody.contains("[Script Info]") -> SubtitleType.ASS
responseBody.contains("WEBVTT") -> SubtitleType.VTT
else -> SubtitleType.SRT
}
subtitleType subtitleType
} else { } else {
SubtitleType.UNKNOWN SubtitleType.UNKNOWN
} }
} else {
val uri = url.toUri()
val file = uri.toFile()
val fileBody = file.readText()
val subtitleType = getType(fileBody)
subtitleType
}
} catch (e: Exception) { } catch (e: Exception) {
Logger.log(e) Logger.log(e)
SubtitleType.UNKNOWN SubtitleType.UNKNOWN
} }
} }
private fun getType(content: String): SubtitleType {
return when {
content.contains("[Script Info]") -> SubtitleType.ASS
content.contains("WEBVTT") -> SubtitleType.VTT
content.contains("SRT") -> SubtitleType.SRT
else -> SubtitleType.UNKNOWN
}
}
//actually downloads lol //actually downloads lol
@Deprecated("handled externally") @Deprecated("handled externally")
suspend fun downloadSubtitle( suspend fun downloadSubtitle(

View File

@@ -31,6 +31,7 @@ class SupportingSearchAdapter(private val activity: SearchActivity, private val
searchHistoryAdapter = SearchHistoryAdapter(type) { searchHistoryAdapter = SearchHistoryAdapter(type) {
binding.searchBarText.setText(it) binding.searchBarText.setText(it)
binding.searchBarText.setSelection(it.length)
} }
binding.searchHistoryList.layoutManager = LinearLayoutManager(binding.root.context) binding.searchHistoryList.layoutManager = LinearLayoutManager(binding.root.context)
binding.searchHistoryList.adapter = searchHistoryAdapter binding.searchHistoryList.adapter = searchHistoryAdapter

View File

@@ -20,8 +20,8 @@ import ani.dantotsu.R
import ani.dantotsu.currActivity import ani.dantotsu.currActivity
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.databinding.DialogLayoutBinding import ani.dantotsu.databinding.DialogLayoutBinding
import ani.dantotsu.databinding.ItemMediaSourceBinding
import ani.dantotsu.databinding.ItemChipBinding import ani.dantotsu.databinding.ItemChipBinding
import ani.dantotsu.databinding.ItemMediaSourceBinding
import ani.dantotsu.displayTimer import ani.dantotsu.displayTimer
import ani.dantotsu.isOnline import ani.dantotsu.isOnline
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
@@ -61,7 +61,8 @@ class AnimeWatchAdapter(
private var _binding: ItemMediaSourceBinding? = null private var _binding: ItemMediaSourceBinding? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val bind = ItemMediaSourceBinding.inflate(LayoutInflater.from(parent.context), parent, false) val bind =
ItemMediaSourceBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(bind) return ViewHolder(bind)
} }
@@ -478,7 +479,8 @@ class AnimeWatchAdapter(
binding.sourceProgressBar.visibility = View.GONE binding.sourceProgressBar.visibility = View.GONE
val sourceFound = media.anime.episodes!!.isNotEmpty() val sourceFound = media.anime.episodes!!.isNotEmpty()
val isDownloadedSource = watchSources[media.selected!!.sourceIndex] is OfflineAnimeParser val isDownloadedSource =
watchSources[media.selected!!.sourceIndex] is OfflineAnimeParser
if (isDownloadedSource) { if (isDownloadedSource) {
binding.sourceNotFound.text = if (sourceFound) { binding.sourceNotFound.text = if (sourceFound) {
@@ -487,7 +489,8 @@ class AnimeWatchAdapter(
currActivity()!!.getString(R.string.download_not_found) currActivity()!!.getString(R.string.download_not_found)
} }
} else { } else {
binding.sourceNotFound.text = currActivity()!!.getString(R.string.source_not_found) binding.sourceNotFound.text =
currActivity()!!.getString(R.string.source_not_found)
} }
binding.sourceNotFound.isGone = sourceFound binding.sourceNotFound.isGone = sourceFound

View File

@@ -1,7 +1,6 @@
package ani.dantotsu.media.anime package ani.dantotsu.media.anime
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@@ -28,10 +27,8 @@ import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.FileUrl
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.addons.download.DownloadAddonManager import ani.dantotsu.addons.download.DownloadAddonManager
import ani.dantotsu.connections.anilist.api.MediaStreamingEpisode
import ani.dantotsu.databinding.FragmentMediaSourceBinding import ani.dantotsu.databinding.FragmentMediaSourceBinding
import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
@@ -49,7 +46,6 @@ import ani.dantotsu.media.MediaType
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.notifications.subscription.SubscriptionHelper import ani.dantotsu.notifications.subscription.SubscriptionHelper
import ani.dantotsu.notifications.subscription.SubscriptionHelper.Companion.saveSubscription import ani.dantotsu.notifications.subscription.SubscriptionHelper.Companion.saveSubscription
import ani.dantotsu.others.Anify
import ani.dantotsu.others.LanguageMapper import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.parsers.AnimeParser import ani.dantotsu.parsers.AnimeParser
import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.parsers.AnimeSources
@@ -236,13 +232,16 @@ class AnimeWatchFragment : Fragment() {
episodes.forEach { (i, episode) -> episodes.forEach { (i, episode) ->
if (media.anime?.anifyEpisodes != null) { if (media.anime?.anifyEpisodes != null) {
if (media.anime!!.anifyEpisodes!!.containsKey(i)) { if (media.anime!!.anifyEpisodes!!.containsKey(i)) {
episode.desc = media.anime!!.anifyEpisodes!![i]?.desc ?: episode.desc episode.desc =
media.anime!!.anifyEpisodes!![i]?.desc ?: episode.desc
episode.title = if (MediaNameAdapter.removeEpisodeNumberCompletely( episode.title = if (MediaNameAdapter.removeEpisodeNumberCompletely(
episode.title ?: "" episode.title ?: ""
).isBlank() ).isBlank()
) media.anime!!.anifyEpisodes!![i]?.title ?: episode.title else episode.title ) media.anime!!.anifyEpisodes!![i]?.title
?: episode.title else episode.title
?: media.anime!!.anifyEpisodes!![i]?.title ?: episode.title ?: media.anime!!.anifyEpisodes!![i]?.title ?: episode.title
episode.thumb = media.anime!!.anifyEpisodes!![i]?.thumb ?: episode.thumb episode.thumb =
media.anime!!.anifyEpisodes!![i]?.thumb ?: episode.thumb
} }
} }
@@ -255,13 +254,16 @@ class AnimeWatchFragment : Fragment() {
} }
if (media.anime?.kitsuEpisodes != null) { if (media.anime?.kitsuEpisodes != null) {
if (media.anime!!.kitsuEpisodes!!.containsKey(i)) { if (media.anime!!.kitsuEpisodes!!.containsKey(i)) {
episode.desc = media.anime!!.kitsuEpisodes!![i]?.desc ?: episode.desc episode.desc =
media.anime!!.kitsuEpisodes!![i]?.desc ?: episode.desc
episode.title = if (MediaNameAdapter.removeEpisodeNumberCompletely( episode.title = if (MediaNameAdapter.removeEpisodeNumberCompletely(
episode.title ?: "" episode.title ?: ""
).isBlank() ).isBlank()
) media.anime!!.kitsuEpisodes!![i]?.title ?: episode.title else episode.title ) media.anime!!.kitsuEpisodes!![i]?.title
?: episode.title else episode.title
?: media.anime!!.kitsuEpisodes!![i]?.title ?: episode.title ?: media.anime!!.kitsuEpisodes!![i]?.title ?: episode.title
episode.thumb = media.anime!!.kitsuEpisodes!![i]?.thumb ?: episode.thumb episode.thumb =
media.anime!!.kitsuEpisodes!![i]?.thumb ?: episode.thumb
} }
} }
} }
@@ -405,7 +407,8 @@ class AnimeWatchFragment : Fragment() {
selectedSetting = allSettings[which] selectedSetting = allSettings[which]
itemSelected = true itemSelected = true
requireActivity().runOnUiThread { requireActivity().runOnUiThread {
val fragment = AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) { val fragment =
AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) {
changeUIVisibility(true) changeUIVisibility(true)
loadEpisodes(media.selected!!.sourceIndex, true) loadEpisodes(media.selected!!.sourceIndex, true)
} }

View File

@@ -1,6 +1,5 @@
package ani.dantotsu.media.anime package ani.dantotsu.media.anime
import android.app.AlertDialog
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -13,7 +12,6 @@ import androidx.media3.common.util.UnstableApi
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.connections.updateProgress import ani.dantotsu.connections.updateProgress
import ani.dantotsu.currContext
import ani.dantotsu.databinding.ItemEpisodeCompactBinding import ani.dantotsu.databinding.ItemEpisodeCompactBinding
import ani.dantotsu.databinding.ItemEpisodeGridBinding import ani.dantotsu.databinding.ItemEpisodeGridBinding
import ani.dantotsu.databinding.ItemEpisodeListBinding import ani.dantotsu.databinding.ItemEpisodeListBinding

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,6 @@ package ani.dantotsu.media.anime
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.AlertDialog
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.ComponentName import android.content.ComponentName
import android.content.DialogInterface import android.content.DialogInterface
@@ -564,6 +563,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
snackString(R.string.no_video_selected) snackString(R.string.no_video_selected)
} }
} }
fun checkAudioTracks() { fun checkAudioTracks() {
val audioTracks = extractor.audioTracks.map { it.lang } val audioTracks = extractor.audioTracks.map { it.lang }
if (audioTracks.isNotEmpty()) { if (audioTracks.isNotEmpty()) {
@@ -574,7 +574,10 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
setTitle(R.string.download_audio_tracks) setTitle(R.string.download_audio_tracks)
multiChoiceItems(audioNamesArray, checkedItems) { multiChoiceItems(audioNamesArray, checkedItems) {
it.forEachIndexed { index, isChecked -> it.forEachIndexed { index, isChecked ->
val audioPair = Pair(extractor.audioTracks[index].url, extractor.audioTracks[index].lang) val audioPair = Pair(
extractor.audioTracks[index].url,
extractor.audioTracks[index].lang
)
if (isChecked) { if (isChecked) {
selectedAudioTracks.add(audioPair) selectedAudioTracks.add(audioPair)
} else { } else {
@@ -606,7 +609,8 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
setTitle(R.string.download_subtitle) setTitle(R.string.download_subtitle)
multiChoiceItems(subtitleNamesArray, checkedItems) { multiChoiceItems(subtitleNamesArray, checkedItems) {
it.forEachIndexed { index, isChecked -> it.forEachIndexed { index, isChecked ->
val subtitlePair = Pair(subtitles[index].file.url, subtitles[index].language) val subtitlePair =
Pair(subtitles[index].file.url, subtitles[index].language)
if (isChecked) { if (isChecked) {
selectedSubtitles.add(subtitlePair) selectedSubtitles.add(subtitlePair)
} else { } else {

View File

@@ -63,8 +63,12 @@ class TrackGroupDialogFragment(
override fun onBindViewHolder(holder: StreamViewHolder, position: Int) { override fun onBindViewHolder(holder: StreamViewHolder, position: Int) {
val binding = holder.binding val binding = holder.binding
trackGroups[position].let { trackGroup -> trackGroups[position].let { trackGroup ->
if (overrideTrackNames?.getOrNull(position - (trackGroups.size - (overrideTrackNames?.size?:0))) != null) { if (overrideTrackNames?.getOrNull(
val pair = overrideTrackNames!![position - (trackGroups.size - overrideTrackNames!!.size)] position - (trackGroups.size - (overrideTrackNames?.size ?: 0))
) != null
) {
val pair =
overrideTrackNames!![position - (trackGroups.size - overrideTrackNames!!.size)]
binding.subtitleTitle.text = binding.subtitleTitle.text =
"[${pair.second}] ${pair.first}" "[${pair.second}] ${pair.first}"
} else when (val language = trackGroup.getTrackFormat(0).language?.lowercase()) { } else when (val language = trackGroup.getTrackFormat(0).language?.lowercase()) {

View File

@@ -15,7 +15,6 @@ import ani.dantotsu.databinding.ItemCommentsBinding
import ani.dantotsu.getAppString import ani.dantotsu.getAppString
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.openImage import ani.dantotsu.openImage
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.setAnimation import ani.dantotsu.setAnimation
import ani.dantotsu.snackString import ani.dantotsu.snackString

View File

@@ -2,7 +2,6 @@ package ani.dantotsu.media.comments
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context.INPUT_METHOD_SERVICE import android.content.Context.INPUT_METHOD_SERVICE
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.Bundle import android.os.Bundle
@@ -12,7 +11,6 @@ import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.animation.doOnEnd import androidx.core.animation.doOnEnd
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
@@ -33,7 +31,6 @@ import ani.dantotsu.setBaseline
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.toast
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog import ani.dantotsu.util.customAlertDialog
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter

View File

@@ -16,6 +16,7 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream import java.io.FileOutputStream
data class ImageData( data class ImageData(
@@ -76,7 +77,7 @@ fun saveImage(
uri?.let { uri?.let {
contentResolver.openOutputStream(it)?.use { os -> contentResolver.openOutputStream(it)?.use { os ->
bitmap.compress(format, quality, os) bitmap.compress(format, quality, os)
} } ?: throw FileNotFoundException("Failed to open output stream for URI: $uri")
} }
} else { } else {
val directory = val directory =
@@ -86,12 +87,20 @@ fun saveImage(
} }
val file = File(directory, filename) val file = File(directory, filename)
// Check if the file already exists
if (file.exists()) {
println("File already exists: ${file.absolutePath}")
return
}
FileOutputStream(file).use { outputStream -> FileOutputStream(file).use { outputStream ->
bitmap.compress(format, quality, outputStream) bitmap.compress(format, quality, outputStream)
} }
} }
} catch (e: FileNotFoundException) {
println("File not found: ${e.message}")
} catch (e: Exception) { } catch (e: Exception) {
// Handle exception here
println("Exception while saving image: ${e.message}") println("Exception while saving image: ${e.message}")
} }
} }

View File

@@ -1,6 +1,5 @@
package ani.dantotsu.media.manga package ani.dantotsu.media.manga
import android.app.AlertDialog
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup

View File

@@ -7,9 +7,11 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.CheckBox import android.widget.CheckBox
import android.widget.EditText
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.NumberPicker import android.widget.NumberPicker
import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getString import androidx.core.content.ContextCompat.getString
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
@@ -22,8 +24,8 @@ import ani.dantotsu.currActivity
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.databinding.CustomDialogLayoutBinding import ani.dantotsu.databinding.CustomDialogLayoutBinding
import ani.dantotsu.databinding.DialogLayoutBinding import ani.dantotsu.databinding.DialogLayoutBinding
import ani.dantotsu.databinding.ItemMediaSourceBinding
import ani.dantotsu.databinding.ItemChipBinding import ani.dantotsu.databinding.ItemChipBinding
import ani.dantotsu.databinding.ItemMediaSourceBinding
import ani.dantotsu.isOnline import ani.dantotsu.isOnline
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
@@ -75,7 +77,8 @@ class MangaReadAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val bind = ItemMediaSourceBinding.inflate(LayoutInflater.from(parent.context), parent, false) val bind =
ItemMediaSourceBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(bind) return ViewHolder(bind)
} }
@@ -264,19 +267,22 @@ class MangaReadAdapter(
} }
// Multi download // Multi download
downloadNo.text = "0" //downloadNo.text = "0"
mediaDownloadTop.setOnClickListener { mediaDownloadTop.setOnClickListener {
// Alert dialog asking for the number of chapters to download
fragment.requireContext().customAlertDialog().apply { fragment.requireContext().customAlertDialog().apply {
setTitle("Multi Chapter Downloader") setTitle("Multi Chapter Downloader")
setMessage("Enter the number of chapters to download") setMessage("Enter the number of chapters to download")
val input = NumberPicker(currContext()) val input = View.inflate(currContext(), R.layout.dialog_layout, null)
input.minValue = 1 val editText = input.findViewById<EditText>(R.id.downloadNo)
input.maxValue = 20
input.value = 1
setCustomView(input) setCustomView(input)
setPosButton(R.string.ok) { setPosButton(R.string.ok) {
downloadNo.text = "${input.value}" val value = editText.text.toString().toIntOrNull()
if (value != null && value > 0) {
downloadNo.setText(value.toString(), TextView.BufferType.EDITABLE)
fragment.multiDownload(value)
} else {
toast("Please enter a valid number")
}
} }
setNegButton(R.string.cancel) setNegButton(R.string.cancel)
show() show()
@@ -331,12 +337,17 @@ class MangaReadAdapter(
val checkBox = CheckBox(currContext()).apply { val checkBox = CheckBox(currContext()).apply {
text = option text = option
setOnCheckedChangeListener { _, _ -> setOnCheckedChangeListener { _, _ ->
tickAllButton.setImageResource(getToggleImageResource(checkboxContainer)) tickAllButton.setImageResource(
getToggleImageResource(
checkboxContainer
)
)
} }
} }
if (media.selected!!.scanlators != null) { if (media.selected!!.scanlators != null) {
checkBox.isChecked = media.selected!!.scanlators?.contains(option) != true checkBox.isChecked =
media.selected!!.scanlators?.contains(option) != true
scanlatorSelectionListener?.onScanlatorsSelected() scanlatorSelectionListener?.onScanlatorsSelected()
} else { } else {
checkBox.isChecked = true checkBox.isChecked = true
@@ -376,8 +387,9 @@ class MangaReadAdapter(
setCustomView(root) setCustomView(root)
setPosButton("OK") { setPosButton("OK") {
if (run) fragment.onIconPressed(style, reversed) if (run) fragment.onIconPressed(style, reversed)
if (downloadNo.text != "0") { val value = downloadNo.text.toString().toIntOrNull()
fragment.multiDownload(downloadNo.text.toString().toInt()) if (value != null && value > 0) {
fragment.multiDownload(value)
} }
if (refresh) fragment.loadChapters(source, true) if (refresh) fragment.loadChapters(source, true)
} }
@@ -488,10 +500,12 @@ class MangaReadAdapter(
} }
} }
val formattedChapters = filteredChapters.map { val formattedChapters = filteredChapters.map {
MediaNameAdapter.findChapterNumber(it.value.number)?.toInt()?.toString() to it.key MediaNameAdapter.findChapterNumber(it.value.number)?.toInt()
?.toString() to it.key
} }
if (formattedChapters.any { it.first == continueNumber }) { if (formattedChapters.any { it.first == continueNumber }) {
var continueEp = media.manga.chapters!![formattedChapters.first { it.first == continueNumber }.second] var continueEp =
media.manga.chapters!![formattedChapters.first { it.first == continueNumber }.second]
binding.sourceContinue.visibility = View.VISIBLE binding.sourceContinue.visibility = View.VISIBLE
handleProgress( handleProgress(
binding.itemMediaProgressCont, binding.itemMediaProgressCont,
@@ -501,9 +515,11 @@ class MangaReadAdapter(
continueEp!!.number continueEp!!.number
) )
if ((binding.itemMediaProgress.layoutParams as LinearLayout.LayoutParams).weight > 0.8f) { if ((binding.itemMediaProgress.layoutParams as LinearLayout.LayoutParams).weight > 0.8f) {
val numberPlusOne = formattedChapters.indexOfFirst { it.first?.toIntOrNull() == continueNumber.toInt() + 1 } val numberPlusOne =
formattedChapters.indexOfFirst { it.first?.toIntOrNull() == continueNumber.toInt() + 1 }
if (numberPlusOne != -1) { if (numberPlusOne != -1) {
continueEp = media.manga.chapters!![formattedChapters[numberPlusOne].second] continueEp =
media.manga.chapters!![formattedChapters[numberPlusOne].second]
} }
} }
binding.itemMediaImage.loadImage(media.banner ?: media.cover) binding.itemMediaImage.loadImage(media.banner ?: media.cover)
@@ -530,7 +546,8 @@ class MangaReadAdapter(
binding.sourceProgressBar.visibility = View.GONE binding.sourceProgressBar.visibility = View.GONE
val sourceFound = filteredChapters.isNotEmpty() val sourceFound = filteredChapters.isNotEmpty()
val isDownloadedSource = mangaReadSources[media.selected!!.sourceIndex] is OfflineMangaParser val isDownloadedSource =
mangaReadSources[media.selected!!.sourceIndex] is OfflineMangaParser
if (isDownloadedSource) { if (isDownloadedSource) {
binding.sourceNotFound.text = if (sourceFound) { binding.sourceNotFound.text = if (sourceFound) {
@@ -539,7 +556,8 @@ class MangaReadAdapter(
currActivity()!!.getString(R.string.download_not_found) currActivity()!!.getString(R.string.download_not_found)
} }
} else { } else {
binding.sourceNotFound.text = currActivity()!!.getString(R.string.source_not_found) binding.sourceNotFound.text =
currActivity()!!.getString(R.string.source_not_found)
} }
binding.sourceNotFound.isGone = sourceFound binding.sourceNotFound.isGone = sourceFound

View File

@@ -66,6 +66,7 @@ import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@@ -232,25 +233,35 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
} }
fun multiDownload(n: Int) { fun multiDownload(n: Int) {
// Get last viewed chapter lifecycleScope.launch {
val selected = media.userProgress // Get the last viewed chapter
val selected = media.userProgress ?: 0
val chapters = media.manga?.chapters?.values?.toList() val chapters = media.manga?.chapters?.values?.toList()
// Filter by selected language // Ensure chapters are available in the extensions
val progressChapterIndex = (chapters?.indexOfFirst { if (chapters.isNullOrEmpty() || n < 1) return@launch
// Find the index of the last viewed chapter
val progressChapterIndex = (chapters.indexOfFirst {
MediaNameAdapter.findChapterNumber(it.number)?.toInt() == selected MediaNameAdapter.findChapterNumber(it.number)?.toInt() == selected
} ?: 0) + 1 } + 1).coerceAtLeast(0)
// Calculate the end value for the range of chapters to download
if (progressChapterIndex < 0 || n < 1 || chapters == null) return val endIndex = (progressChapterIndex + n).coerceAtMost(chapters.size)
// Get the list of chapters to download
// Calculate the end index
val endIndex = minOf(progressChapterIndex + n, chapters.size)
// Make sure there are enough chapters
val chaptersToDownload = chapters.subList(progressChapterIndex, endIndex) val chaptersToDownload = chapters.subList(progressChapterIndex, endIndex)
// Trigger the download for each chapter sequentially
for (chapter in chaptersToDownload) { for (chapter in chaptersToDownload) {
try {
downloadChapterSequentially(chapter)
} catch (e: Exception) {
Toast.makeText(requireContext(), "Failed to download chapter: ${chapter.title}", Toast.LENGTH_SHORT).show()
}
}
Toast.makeText(requireContext(), "All downloads completed!", Toast.LENGTH_SHORT).show()
}
}
private suspend fun downloadChapterSequentially(chapter: MangaChapter) {
withContext(Dispatchers.IO) {
onMangaChapterDownloadClick(chapter) onMangaChapterDownloadClick(chapter)
delay(2000) // A 2-second download
} }
} }
@@ -261,7 +272,8 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
val chapters = loadedChapters[media.selected!!.sourceIndex] val chapters = loadedChapters[media.selected!!.sourceIndex]
if (chapters != null) { if (chapters != null) {
headerAdapter.options = getScanlators(chapters) headerAdapter.options = getScanlators(chapters)
val filteredChapters = if (model.mangaReadSources?.get(media.selected!!.sourceIndex) is OfflineMangaParser) { val filteredChapters =
if (model.mangaReadSources?.get(media.selected!!.sourceIndex) is OfflineMangaParser) {
chapters chapters
} else { } else {
chapters.filterNot { (_, chapter) -> chapters.filterNot { (_, chapter) ->
@@ -397,7 +409,8 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
selectedSetting = allSettings[which] selectedSetting = allSettings[which]
itemSelected = true itemSelected = true
val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id) { val fragment =
MangaSourcePreferencesFragment().getInstance(selectedSetting.id) {
changeUIVisibility(true) changeUIVisibility(true)
loadChapters(media.selected!!.sourceIndex, true) loadChapters(media.selected!!.sourceIndex, true)
} }
@@ -472,7 +485,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
scanlator = chapter.scanlator ?: "Unknown", scanlator = chapter.scanlator ?: "Unknown",
imageData = images, imageData = images,
sourceMedia = media, sourceMedia = media,
retries = 2, retries = 25,
simultaneousDownloads = 2 simultaneousDownloads = 2
) )
@@ -590,7 +603,9 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
// Find latest chapter for subscription // Find latest chapter for subscription
selected.latest = selected.latest =
media.manga?.chapters?.values?.maxOfOrNull { MediaNameAdapter.findChapterNumber(it.number) ?: 0f } ?: 0f media.manga?.chapters?.values?.maxOfOrNull {
MediaNameAdapter.findChapterNumber(it.number) ?: 0f
} ?: 0f
selected.latest = selected.latest =
media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest

View File

@@ -78,7 +78,8 @@ class ChapterLoaderDialog : BottomSheetDialogFragment() {
_binding = BottomSheetSelectorBinding.inflate(inflater, container, false) _binding = BottomSheetSelectorBinding.inflate(inflater, container, false)
val window = dialog?.window val window = dialog?.window
window?.statusBarColor = Color.TRANSPARENT window?.statusBarColor = Color.TRANSPARENT
window?.navigationBarColor = requireContext().getThemeColor(com.google.android.material.R.attr.colorSurface) window?.navigationBarColor =
requireContext().getThemeColor(com.google.android.material.R.attr.colorSurface)
return binding.root return binding.root
} }

View File

@@ -2,7 +2,6 @@ package ani.dantotsu.media.manga.mangareader
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
@@ -58,8 +57,6 @@ import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsViewModel import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.media.MediaNameAdapter
import ani.dantotsu.media.MediaSingleton import ani.dantotsu.media.MediaSingleton
import ani.dantotsu.media.anime.ExoplayerView
import ani.dantotsu.media.anime.ExoplayerView.Companion
import ani.dantotsu.media.manga.MangaCache import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.media.manga.MangaChapter import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.others.ImageViewDialog import ani.dantotsu.others.ImageViewDialog
@@ -196,7 +193,8 @@ class MangaReaderActivity : AppCompatActivity() {
finish() finish()
return@addCallback return@addCallback
} }
val chapter = (MediaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!.number) val chapter =
(MediaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!.number)
?.minus(1L) ?: 0).toString() ?.minus(1L) ?: 0).toString()
if (chapter == "0.0" && PrefManager.getVal(PrefName.ChapterZeroReader) if (chapter == "0.0" && PrefManager.getVal(PrefName.ChapterZeroReader)
// Not asking individually or incognito // Not asking individually or incognito

View File

@@ -55,6 +55,7 @@ class Swipy @JvmOverloads constructor(
else else
VerticalPosition.Top VerticalPosition.Top
} }
!it.canScrollVertically(1) -> VerticalPosition.Bottom !it.canScrollVertically(1) -> VerticalPosition.Bottom
!it.canScrollVertically(-1) -> VerticalPosition.Top !it.canScrollVertically(-1) -> VerticalPosition.Top
else -> VerticalPosition.None else -> VerticalPosition.None
@@ -67,6 +68,7 @@ class Swipy @JvmOverloads constructor(
else else
HorizontalPosition.Left HorizontalPosition.Left
} }
!it.canScrollHorizontally(1) -> HorizontalPosition.Right !it.canScrollHorizontally(1) -> HorizontalPosition.Right
!it.canScrollHorizontally(-1) -> HorizontalPosition.Left !it.canScrollHorizontally(-1) -> HorizontalPosition.Left
else -> HorizontalPosition.None else -> HorizontalPosition.None
@@ -97,12 +99,14 @@ class Swipy @JvmOverloads constructor(
initialDown = if (vertical) ev.getY(0) else ev.getX(0) initialDown = if (vertical) ev.getY(0) else ev.getX(0)
isBeingDragged = false isBeingDragged = false
} }
MotionEvent.ACTION_MOVE -> { MotionEvent.ACTION_MOVE -> {
val pointerIndex = ev.findPointerIndex(activePointerId) val pointerIndex = ev.findPointerIndex(activePointerId)
if (pointerIndex >= 0) { if (pointerIndex >= 0) {
startDragging(if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex)) startDragging(if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex))
} }
} }
MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev) MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev)
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
isBeingDragged = false isBeingDragged = false
@@ -122,6 +126,7 @@ class Swipy @JvmOverloads constructor(
activePointerId = ev.getPointerId(0) activePointerId = ev.getPointerId(0)
isBeingDragged = false isBeingDragged = false
} }
MotionEvent.ACTION_MOVE -> { MotionEvent.ACTION_MOVE -> {
pointerIndex = ev.findPointerIndex(activePointerId) pointerIndex = ev.findPointerIndex(activePointerId)
if (pointerIndex >= 0) { if (pointerIndex >= 0) {
@@ -130,25 +135,33 @@ class Swipy @JvmOverloads constructor(
if (isBeingDragged) handleDrag(pos) if (isBeingDragged) handleDrag(pos)
} }
} }
MotionEvent.ACTION_POINTER_DOWN -> { MotionEvent.ACTION_POINTER_DOWN -> {
pointerIndex = ev.actionIndex pointerIndex = ev.actionIndex
if (pointerIndex >= 0) activePointerId = ev.getPointerId(pointerIndex) if (pointerIndex >= 0) activePointerId = ev.getPointerId(pointerIndex)
} }
MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev) MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev)
MotionEvent.ACTION_UP -> { MotionEvent.ACTION_UP -> {
resetSwipes() resetSwipes()
pointerIndex = ev.findPointerIndex(activePointerId) pointerIndex = ev.findPointerIndex(activePointerId)
if (pointerIndex >= 0) finishSpinner(if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex)) if (pointerIndex >= 0) finishSpinner(
if (vertical) ev.getY(pointerIndex) else ev.getX(
pointerIndex
)
)
activePointerId = INVALID_POINTER activePointerId = INVALID_POINTER
return false return false
} }
MotionEvent.ACTION_CANCEL -> return false MotionEvent.ACTION_CANCEL -> return false
} }
return true return true
} }
private fun startDragging(pos: Float) { private fun startDragging(pos: Float) {
val posDiff = if ((vertical && verticalPos == VerticalPosition.Top) || (!vertical && horizontalPos == HorizontalPosition.Left)) val posDiff =
if ((vertical && verticalPos == VerticalPosition.Top) || (!vertical && horizontalPos == HorizontalPosition.Left))
pos - initialDown pos - initialDown
else else
initialDown - pos initialDown - pos

View File

@@ -14,8 +14,6 @@ import ani.dantotsu.setAnimation
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import ani.dantotsu.util.customAlertDialog import ani.dantotsu.util.customAlertDialog
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
class NovelResponseAdapter( class NovelResponseAdapter(
val fragment: NovelReadFragment, val fragment: NovelReadFragment,
@@ -41,7 +39,8 @@ class NovelResponseAdapter(
setAnimation(fragment.requireContext(), holder.binding.root) setAnimation(fragment.requireContext(), holder.binding.root)
binding.itemMediaImage.loadImage(novel.coverUrl, 400, 0) binding.itemMediaImage.loadImage(novel.coverUrl, 400, 0)
val color =fragment.requireContext().getThemeColor(com.google.android.material.R.attr.colorOnBackground) val color = fragment.requireContext()
.getThemeColor(com.google.android.material.R.attr.colorOnBackground)
binding.itemEpisodeTitle.text = novel.name binding.itemEpisodeTitle.text = novel.name
binding.itemEpisodeFiller.text = binding.itemEpisodeFiller.text =
if (downloadedCheckCallback.downloadedCheck(novel)) { if (downloadedCheckCallback.downloadedCheck(novel)) {

View File

@@ -5,7 +5,6 @@ import ani.dantotsu.currContext
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.media.MediaNameAdapter
import ani.dantotsu.media.Selected import ani.dantotsu.media.Selected
import ani.dantotsu.media.emptyMedia
import ani.dantotsu.parsers.AnimeParser import ani.dantotsu.parsers.AnimeParser
import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.BaseParser import ani.dantotsu.parsers.BaseParser
@@ -123,7 +122,11 @@ class SubscriptionHelper {
} }
} }
private suspend fun forceLoadShowResponse(subscribeMedia: SubscribeMedia, selected: Selected, parser: BaseParser): ShowResponse? { private suspend fun forceLoadShowResponse(
subscribeMedia: SubscribeMedia,
selected: Selected,
parser: BaseParser
): ShowResponse? {
val tempMedia = Media( val tempMedia = Media(
id = subscribeMedia.id, id = subscribeMedia.id,
name = null, name = null,

View File

@@ -127,8 +127,10 @@ class SubscriptionNotificationTask : Task {
banner = media.banner banner = media.banner
) )
) )
PrefManager.setVal(PrefName.UnreadCommentNotifications, PrefManager.setVal(
PrefManager.getVal<Int>(PrefName.UnreadCommentNotifications) + 1) PrefName.UnreadCommentNotifications,
PrefManager.getVal<Int>(PrefName.UnreadCommentNotifications) + 1
)
val notification = createNotification( val notification = createNotification(
context.applicationContext, context.applicationContext,
media, media,

View File

@@ -4,7 +4,6 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import ani.dantotsu.R import ani.dantotsu.R

View File

@@ -24,6 +24,7 @@ object Anify {
) )
} ?: emptyMap() } ?: emptyMap()
} }
@Serializable @Serializable
data class AnifyElement( data class AnifyElement(
@SerialName("providerId") @SerialName("providerId")

View File

@@ -24,11 +24,11 @@ class CrashActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
ThemeManager(this).applyTheme() ThemeManager(this).applyTheme()
initActivity(this) initActivity(this)
binding = ActivityCrashBinding.inflate(layoutInflater)
window.setFlags( window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE WindowManager.LayoutParams.FLAG_SECURE
) )
binding = ActivityCrashBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> { binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = statusBarHeight topMargin = statusBarHeight

View File

@@ -26,7 +26,6 @@ object AnimeSources : WatchSources() {
) )
isInitialized = true isInitialized = true
// Update as StateFlow emits new values
fromExtensions.collect { extensions -> fromExtensions.collect { extensions ->
list = sortPinnedAnimeSources( list = sortPinnedAnimeSources(
createParsersFromExtensions(extensions), createParsersFromExtensions(extensions),

View File

@@ -226,8 +226,18 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
?: return emptyList()) ?: return emptyList())
return try { return try {
// TODO(1.6): Remove else block when dropping support for ext lib <1.6
if ((source as AnimeHttpSource).javaClass.declaredMethods.any { it.name == "getHosterList" }){
val hosters = source.getHosterList(sEpisode)
val allVideos = hosters.flatMap { hoster ->
val videos = source.getVideoList(hoster)
videos.map { it.copy(videoTitle = "${hoster.hosterName} - ${it.videoTitle}") }
}
allVideos.map { videoToVideoServer(it) }
} else {
val videos = source.getVideoList(sEpisode) val videos = source.getVideoList(sEpisode)
videos.map { videoToVideoServer(it) } videos.map { videoToVideoServer(it) }
}
} catch (e: Exception) { } catch (e: Exception) {
Logger.log("Exception occurred: ${e.message}") Logger.log("Exception occurred: ${e.message}")
emptyList() emptyList()
@@ -576,7 +586,7 @@ class VideoServerPassthrough(private val videoServer: VideoServer) : VideoExtrac
number, number,
format!!, format!!,
FileUrl(videoUrl, headersMap), FileUrl(videoUrl, headersMap),
if (aniVideo.totalContentLength == 0L) null else aniVideo.bytesDownloaded.toDouble() null
) )
} }
@@ -636,7 +646,6 @@ class VideoServerPassthrough(private val videoServer: VideoServer) : VideoExtrac
} }
private fun trackToSubtitle(track: Track): Subtitle { private fun trackToSubtitle(track: Track): Subtitle {
//use Dispatchers.IO to make a HTTP request to determine the subtitle type
var type: SubtitleType? var type: SubtitleType?
runBlocking { runBlocking {
type = findSubtitleType(track.url) type = findSubtitleType(track.url)

View File

@@ -1,4 +1,3 @@
package ani.dantotsu.parsers package ani.dantotsu.parsers
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
@@ -6,7 +5,6 @@ import ani.dantotsu.FileUrl
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.okHttpClient
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.SAnime

View File

@@ -266,8 +266,10 @@ class ExtensionTestItem(
) )
binding.searchResultText.isVisible = true binding.searchResultText.isVisible = true
if (searchResultData.size == 0) { if (searchResultData.size == 0) {
val text = context.getString(R.string.title_search_test, val text = context.getString(
context.getString(R.string.no_results_found)) R.string.title_search_test,
context.getString(R.string.no_results_found)
)
binding.searchResultText.text = text binding.searchResultText.text = text
binding.searchResultText.setCompoundDrawablesWithIntrinsicBounds( binding.searchResultText.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_circle_cancel, 0, 0, 0 R.drawable.ic_circle_cancel, 0, 0, 0
@@ -277,8 +279,10 @@ class ExtensionTestItem(
) )
return return
} }
val text = context.getString(R.string.title_search_test, val text = context.getString(
context.getString(R.string.results_found, searchResultData.size.toString())) R.string.title_search_test,
context.getString(R.string.results_found, searchResultData.size.toString())
)
binding.searchResultText.text = text + "\n${searchResultData.time}ms" binding.searchResultText.text = text + "\n${searchResultData.time}ms"
binding.searchResultText.setCompoundDrawablesWithIntrinsicBounds( binding.searchResultText.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_circle_check, 0, 0, 0 R.drawable.ic_circle_check, 0, 0, 0
@@ -298,12 +302,20 @@ class ExtensionTestItem(
binding.episodeResultText.isVisible = true binding.episodeResultText.isVisible = true
if (episodeResultData.size == 0) { if (episodeResultData.size == 0) {
val text = when (extensionType) { val text = when (extensionType) {
"anime" -> context.getString(R.string.episode_search_test, "anime" -> context.getString(
context.getString(R.string.no_results_found)) R.string.episode_search_test,
"manga" -> context.getString(R.string.chapter_search_test, context.getString(R.string.no_results_found)
context.getString(R.string.no_results_found)) )
else -> context.getString(R.string.book_search_test,
context.getString(R.string.no_results_found)) "manga" -> context.getString(
R.string.chapter_search_test,
context.getString(R.string.no_results_found)
)
else -> context.getString(
R.string.book_search_test,
context.getString(R.string.no_results_found)
)
} }
binding.episodeResultText.text = text binding.episodeResultText.text = text
binding.episodeResultText.setCompoundDrawablesWithIntrinsicBounds( binding.episodeResultText.setCompoundDrawablesWithIntrinsicBounds(
@@ -315,12 +327,20 @@ class ExtensionTestItem(
return return
} }
val text = when (extensionType) { val text = when (extensionType) {
"anime" -> context.getString(R.string.episode_search_test, "anime" -> context.getString(
context.getString(R.string.results_found, episodeResultData.size.toString())) R.string.episode_search_test,
"manga" -> context.getString(R.string.chapter_search_test, context.getString(R.string.results_found, episodeResultData.size.toString())
context.getString(R.string.results_found, episodeResultData.size.toString())) )
else -> context.getString(R.string.book_search_test,
context.getString(R.string.results_found, episodeResultData.size.toString())) "manga" -> context.getString(
R.string.chapter_search_test,
context.getString(R.string.results_found, episodeResultData.size.toString())
)
else -> context.getString(
R.string.book_search_test,
context.getString(R.string.results_found, episodeResultData.size.toString())
)
} }
binding.episodeResultText.text = text + "\n${episodeResultData.time}ms" binding.episodeResultText.text = text + "\n${episodeResultData.time}ms"
binding.episodeResultText.setCompoundDrawablesWithIntrinsicBounds( binding.episodeResultText.setCompoundDrawablesWithIntrinsicBounds(
@@ -349,12 +369,20 @@ class ExtensionTestItem(
binding.serverResultText.isVisible = true binding.serverResultText.isVisible = true
if (serverResultData.size == 0) { if (serverResultData.size == 0) {
val text = when (extensionType) { val text = when (extensionType) {
"anime" -> context.getString(R.string.video_search_test, "anime" -> context.getString(
context.getString(R.string.no_results_found)) R.string.video_search_test,
"manga" -> context.getString(R.string.image_search_test, context.getString(R.string.no_results_found)
context.getString(R.string.no_results_found)) )
else -> context.getString(R.string.book_search_test,
context.getString(R.string.no_results_found)) "manga" -> context.getString(
R.string.image_search_test,
context.getString(R.string.no_results_found)
)
else -> context.getString(
R.string.book_search_test,
context.getString(R.string.no_results_found)
)
} }
binding.serverResultText.text = text + "\n${serverResultData.time}ms" binding.serverResultText.text = text + "\n${serverResultData.time}ms"
binding.serverResultText.setCompoundDrawablesWithIntrinsicBounds( binding.serverResultText.setCompoundDrawablesWithIntrinsicBounds(
@@ -366,12 +394,20 @@ class ExtensionTestItem(
return return
} }
val text = when (extensionType) { val text = when (extensionType) {
"anime" -> context.getString(R.string.video_search_test, "anime" -> context.getString(
context.getString(R.string.results_found, serverResultData.size.toString())) R.string.video_search_test,
"manga" -> context.getString(R.string.image_search_test, context.getString(R.string.results_found, serverResultData.size.toString())
context.getString(R.string.results_found, serverResultData.size.toString())) )
else -> context.getString(R.string.book_search_test,
context.getString(R.string.results_found, serverResultData.size.toString())) "manga" -> context.getString(
R.string.image_search_test,
context.getString(R.string.results_found, serverResultData.size.toString())
)
else -> context.getString(
R.string.book_search_test,
context.getString(R.string.results_found, serverResultData.size.toString())
)
} }
binding.serverResultText.text = text binding.serverResultText.text = text
binding.serverResultText.setCompoundDrawablesWithIntrinsicBounds( binding.serverResultText.setCompoundDrawablesWithIntrinsicBounds(

View File

@@ -73,6 +73,7 @@ class ParserTestActivity : AppCompatActivity() {
} }
} }
} }
"manga" -> { "manga" -> {
ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name -> ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name ->
val extension = val extension =
@@ -89,6 +90,7 @@ class ParserTestActivity : AppCompatActivity() {
} }
} }
} }
"novel" -> { "novel" -> {
ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name -> ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name ->
val extension = val extension =

View File

@@ -120,8 +120,10 @@ class NovelExtensionManager(private val context: Context) {
* @param extension The anime extension to be installed. * @param extension The anime extension to be installed.
*/ */
fun installExtension(extension: NovelExtension.Available): Observable<InstallStep> { fun installExtension(extension: NovelExtension.Available): Observable<InstallStep> {
return installer.downloadAndInstall(api.getNovelApkUrl(extension), extension.pkgName, return installer.downloadAndInstall(
extension.name, MediaType.NOVEL) api.getNovelApkUrl(extension), extension.pkgName,
extension.name, MediaType.NOVEL
)
} }
/** /**

View File

@@ -53,7 +53,8 @@ class ChartBuilder {
scrollPos: Float? = null, scrollPos: Float? = null,
normalize: Boolean = false normalize: Boolean = false
): AAOptions { ): AAOptions {
val primaryColor = context.getThemeColor(com.google.android.material.R.attr.colorPrimary) val primaryColor =
context.getThemeColor(com.google.android.material.R.attr.colorPrimary)
var chartType = passedChartType var chartType = passedChartType
var aaChartType = passedAaChartType var aaChartType = passedAaChartType
var categories = passedCategories var categories = passedCategories
@@ -303,7 +304,8 @@ class ChartBuilder {
} }
private fun setColors(aaOptions: AAOptions, context: Context) { private fun setColors(aaOptions: AAOptions, context: Context) {
val backgroundColor = context.getThemeColor(com.google.android.material.R.attr.colorSurfaceVariant) val backgroundColor =
context.getThemeColor(com.google.android.material.R.attr.colorSurfaceVariant)
val backgroundStyle = AAStyle().color( val backgroundStyle = AAStyle().color(
AAColor.rgbaColor( AAColor.rgbaColor(
Color.red(backgroundColor), Color.red(backgroundColor),
@@ -312,7 +314,8 @@ class ChartBuilder {
1f 1f
) )
) )
val colorOnBackground = context.getThemeColor(com.google.android.material.R.attr.colorOnSurface) val colorOnBackground =
context.getThemeColor(com.google.android.material.R.attr.colorOnSurface)
val onBackgroundStyle = AAStyle().color( val onBackgroundStyle = AAStyle().color(
AAColor.rgbaColor( AAColor.rgbaColor(
Color.red(colorOnBackground), Color.red(colorOnBackground),

View File

@@ -156,17 +156,28 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
openLinkInBrowser(getString(R.string.anilist_link, user.name)) openLinkInBrowser(getString(R.string.anilist_link, user.name))
true true
} }
R.id.action_share_profile -> { R.id.action_share_profile -> {
val shareIntent = Intent(Intent.ACTION_SEND) val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.type = "text/plain" shareIntent.type = "text/plain"
shareIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.anilist_link, user.name)) shareIntent.putExtra(
startActivity(Intent.createChooser(shareIntent, "Share Profile")) Intent.EXTRA_TEXT,
getString(R.string.anilist_link, user.name)
)
startActivity(
Intent.createChooser(
shareIntent,
"Share Profile"
)
)
true true
} }
R.id.action_copy_user_id -> { R.id.action_copy_user_id -> {
copyToClipboard(user.id.toString(), true) copyToClipboard(user.id.toString(), true)
true true
} }
else -> false else -> false
} }
} }

View File

@@ -16,8 +16,8 @@ import ani.dantotsu.profile.User
import ani.dantotsu.profile.UsersDialogFragment import ani.dantotsu.profile.UsersDialogFragment
import ani.dantotsu.setAnimation import ani.dantotsu.setAnimation
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML
import ani.dantotsu.util.ActivityMarkdownCreator import ani.dantotsu.util.ActivityMarkdownCreator
import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.BindableItem
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -55,7 +55,12 @@ class ActivityItem(
.show((context as FragmentActivity).supportFragmentManager, "replies") .show((context as FragmentActivity).supportFragmentManager, "replies")
} }
binding.replyCount.text = activity.replyCount.toString() binding.replyCount.text = activity.replyCount.toString()
binding.activityReplies.setColorFilter(ContextCompat.getColor(binding.root.context, R.color.bg_opp)) binding.activityReplies.setColorFilter(
ContextCompat.getColor(
binding.root.context,
R.color.bg_opp
)
)
binding.activityLikeContainer.setOnLongClickListener { binding.activityLikeContainer.setOnLongClickListener {
UsersDialogFragment().apply { UsersDialogFragment().apply {
userList(userList) userList(userList)
@@ -84,7 +89,8 @@ class ActivityItem(
} }
} }
} }
binding.activityDelete.isVisible = activity.userId == Anilist.userid || activity.messenger?.id == Anilist.userid binding.activityDelete.isVisible =
activity.userId == Anilist.userid || activity.messenger?.id == Anilist.userid
binding.activityDelete.setOnClickListener { binding.activityDelete.setOnClickListener {
scope.launch { scope.launch {
val res = Anilist.mutation.deleteActivity(activity.id) val res = Anilist.mutation.deleteActivity(activity.id)
@@ -161,7 +167,8 @@ class ActivityItem(
"MessageActivity" -> { "MessageActivity" -> {
binding.activityBannerContainer.visibility = View.GONE binding.activityBannerContainer.visibility = View.GONE
binding.activityContent.visibility = View.VISIBLE binding.activityContent.visibility = View.VISIBLE
binding.activityPrivate.visibility = if (activity.isPrivate == true) View.VISIBLE else View.GONE binding.activityPrivate.visibility =
if (activity.isPrivate == true) View.VISIBLE else View.GONE
if (!(context as android.app.Activity).isDestroyed) { if (!(context as android.app.Activity).isDestroyed) {
val markwon = buildMarkwon(context, false) val markwon = buildMarkwon(context, false)
markwon.setMarkdown( markwon.setMarkdown(

View File

@@ -14,8 +14,8 @@ import ani.dantotsu.loadImage
import ani.dantotsu.profile.User import ani.dantotsu.profile.User
import ani.dantotsu.profile.UsersDialogFragment import ani.dantotsu.profile.UsersDialogFragment
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML
import ani.dantotsu.util.ActivityMarkdownCreator import ani.dantotsu.util.ActivityMarkdownCreator
import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.BindableItem
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope

View File

@@ -1,26 +1,21 @@
package ani.dantotsu.profile.activity package ani.dantotsu.profile.activity
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.view.updateMargins
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.databinding.ActivityFeedBinding
import ani.dantotsu.databinding.ActivityNotificationBinding import ani.dantotsu.databinding.ActivityNotificationBinding
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.profile.activity.ActivityFragment.Companion.ActivityType
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.profile.activity.ActivityFragment.Companion.ActivityType
import ani.dantotsu.profile.notification.NotificationActivity
import nl.joery.animatedbottombar.AnimatedBottomBar import nl.joery.animatedbottombar.AnimatedBottomBar
class FeedActivity : AppCompatActivity() { class FeedActivity : AppCompatActivity() {
@@ -50,9 +45,12 @@ class FeedActivity : AppCompatActivity() {
binding.notificationBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() } binding.notificationBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
val getOne = intent.getIntExtra("activityId", -1) val getOne = intent.getIntExtra("activityId", -1)
if (getOne != -1) { navBar.visibility = View.GONE } if (getOne != -1) {
navBar.visibility = View.GONE
}
binding.notificationViewPager.isUserInputEnabled = false binding.notificationViewPager.isUserInputEnabled = false
binding.notificationViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne) binding.notificationViewPager.adapter =
ViewPagerAdapter(supportFragmentManager, lifecycle, getOne)
binding.notificationViewPager.setOffscreenPageLimit(4) binding.notificationViewPager.setOffscreenPageLimit(4)
binding.notificationViewPager.setCurrentItem(selected, false) binding.notificationViewPager.setCurrentItem(selected, false)
navBar.selectTabAt(selected) navBar.selectTabAt(selected)
@@ -83,7 +81,11 @@ class FeedActivity : AppCompatActivity() {
override fun createFragment(position: Int): Fragment { override fun createFragment(position: Int): Fragment {
return when (position) { return when (position) {
0 -> ActivityFragment.newInstance(if (activityId != -1) ActivityType.ONE else ActivityType.USER, activityId = activityId) 0 -> ActivityFragment.newInstance(
if (activityId != -1) ActivityType.ONE else ActivityType.USER,
activityId = activityId
)
else -> ActivityFragment.newInstance(ActivityType.GLOBAL) else -> ActivityFragment.newInstance(ActivityType.GLOBAL)
} }
} }

View File

@@ -9,17 +9,20 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.databinding.ActivityNotificationBinding import ani.dantotsu.databinding.ActivityNotificationBinding
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.COMMENT
import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.MEDIA
import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.ONE
import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.SUBSCRIPTION
import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.USER
import ani.dantotsu.profile.notification.NotificationFragment.Companion.newInstance
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.*
import ani.dantotsu.profile.notification.NotificationFragment.Companion.newInstance
import nl.joery.animatedbottombar.AnimatedBottomBar import nl.joery.animatedbottombar.AnimatedBottomBar
class NotificationActivity : AppCompatActivity() { class NotificationActivity : AppCompatActivity() {
@@ -58,7 +61,8 @@ class NotificationActivity : AppCompatActivity() {
val getOne = intent.getIntExtra("activityId", -1) val getOne = intent.getIntExtra("activityId", -1)
if (getOne != -1) navBar.isVisible = false if (getOne != -1) navBar.isVisible = false
binding.notificationViewPager.isUserInputEnabled = false binding.notificationViewPager.isUserInputEnabled = false
binding.notificationViewPager.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne, CommentsEnabled) binding.notificationViewPager.adapter =
ViewPagerAdapter(supportFragmentManager, lifecycle, getOne, CommentsEnabled)
binding.notificationViewPager.setCurrentItem(selected, false) binding.notificationViewPager.setCurrentItem(selected, false)
navBar.selectTabAt(selected) navBar.selectTabAt(selected)
navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener { navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener {

View File

@@ -60,7 +60,8 @@ class NotificationItem(
PrefName.SubscriptionNotificationStore, PrefName.SubscriptionNotificationStore,
null null
) ?: listOf() ) ?: listOf()
val newList = list.filter { (it.time / 1000L).toInt() != notification.createdAt} val newList =
list.filter { (it.time / 1000L).toInt() != notification.createdAt }
PrefManager.setVal(PrefName.SubscriptionNotificationStore, newList) PrefManager.setVal(PrefName.SubscriptionNotificationStore, newList)
parentAdapter.remove(this@NotificationItem) parentAdapter.remove(this@NotificationItem)
} }

View File

@@ -110,7 +110,8 @@ class AddRepositoryBottomSheet : BottomSheetDialogFragment() {
binding.repositoryInput.setOnEditorActionListener { textView, action, keyEvent -> binding.repositoryInput.setOnEditorActionListener { textView, action, keyEvent ->
if (action == EditorInfo.IME_ACTION_DONE || if (action == EditorInfo.IME_ACTION_DONE ||
(keyEvent?.action == KeyEvent.ACTION_UP && keyEvent.keyCode == KeyEvent.KEYCODE_ENTER)) { (keyEvent?.action == KeyEvent.ACTION_UP && keyEvent.keyCode == KeyEvent.KEYCODE_ENTER)
) {
val url = textView.text.toString() val url = textView.text.toString()
if (url.isNotBlank()) { if (url.isNotBlank()) {
val error = isValidUrl(url) val error = isValidUrl(url)
@@ -215,6 +216,7 @@ class AddRepositoryBottomSheet : BottomSheetDialogFragment() {
Injekt.get<AnimeExtensionManager>().findAvailableExtensions() Injekt.get<AnimeExtensionManager>().findAvailableExtensions()
} }
} }
MediaType.MANGA -> { MediaType.MANGA -> {
val manga = val manga =
PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos) PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos)
@@ -224,6 +226,7 @@ class AddRepositoryBottomSheet : BottomSheetDialogFragment() {
Injekt.get<MangaExtensionManager>().findAvailableExtensions() Injekt.get<MangaExtensionManager>().findAvailableExtensions()
} }
} }
MediaType.NOVEL -> { MediaType.NOVEL -> {
val novel = val novel =
PrefManager.getVal<Set<String>>(PrefName.NovelExtensionRepos) PrefManager.getVal<Set<String>>(PrefName.NovelExtensionRepos)
@@ -247,6 +250,7 @@ class AddRepositoryBottomSheet : BottomSheetDialogFragment() {
Injekt.get<AnimeExtensionManager>().findAvailableExtensions() Injekt.get<AnimeExtensionManager>().findAvailableExtensions()
} }
} }
MediaType.MANGA -> { MediaType.MANGA -> {
val manga = val manga =
PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos) PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos)
@@ -256,6 +260,7 @@ class AddRepositoryBottomSheet : BottomSheetDialogFragment() {
Injekt.get<MangaExtensionManager>().findAvailableExtensions() Injekt.get<MangaExtensionManager>().findAvailableExtensions()
} }
} }
MediaType.NOVEL -> { MediaType.NOVEL -> {
val novel = val novel =
PrefManager.getVal<Set<String>>(PrefName.NovelExtensionRepos) PrefManager.getVal<Set<String>>(PrefName.NovelExtensionRepos)

View File

@@ -56,7 +56,8 @@ class AnilistSettingsActivity : AppCompatActivity() {
} }
val currentTitleLang = Anilist.titleLanguage val currentTitleLang = Anilist.titleLanguage
val titleFormat = UserTitleLanguage.entries.firstOrNull { it.name == currentTitleLang } ?: UserTitleLanguage.ENGLISH val titleFormat = UserTitleLanguage.entries.firstOrNull { it.name == currentTitleLang }
?: UserTitleLanguage.ENGLISH
settingsAnilistTitleLanguage.setText(titleLang[titleFormat.ordinal]) settingsAnilistTitleLanguage.setText(titleLang[titleFormat.ordinal])
settingsAnilistTitleLanguage.setAdapter( settingsAnilistTitleLanguage.setAdapter(
@@ -78,7 +79,9 @@ class AnilistSettingsActivity : AppCompatActivity() {
} }
val currentStaffNameLang = Anilist.staffNameLanguage val currentStaffNameLang = Anilist.staffNameLanguage
val staffNameFormat = UserStaffNameLanguage.entries.firstOrNull { it.name == currentStaffNameLang } ?: UserStaffNameLanguage.ROMAJI_WESTERN val staffNameFormat =
UserStaffNameLanguage.entries.firstOrNull { it.name == currentStaffNameLang }
?: UserStaffNameLanguage.ROMAJI_WESTERN
settingsAnilistStaffLanguage.setText(staffNameLang[staffNameFormat.ordinal]) settingsAnilistStaffLanguage.setText(staffNameLang[staffNameFormat.ordinal])
settingsAnilistStaffLanguage.setAdapter( settingsAnilistStaffLanguage.setAdapter(
@@ -99,7 +102,8 @@ class AnilistSettingsActivity : AppCompatActivity() {
settingsAnilistStaffLanguage.clearFocus() settingsAnilistStaffLanguage.clearFocus()
} }
val currentMergeTimeDisplay = activityMergeTimeMap.entries.firstOrNull { it.value == Anilist.activityMergeTime }?.key val currentMergeTimeDisplay =
activityMergeTimeMap.entries.firstOrNull { it.value == Anilist.activityMergeTime }?.key
?: "${Anilist.activityMergeTime} mins" ?: "${Anilist.activityMergeTime} mins"
settingsAnilistActivityMergeTime.setText(currentMergeTimeDisplay) settingsAnilistActivityMergeTime.setText(currentMergeTimeDisplay)
settingsAnilistActivityMergeTime.setAdapter( settingsAnilistActivityMergeTime.setAdapter(
@@ -117,7 +121,8 @@ class AnilistSettingsActivity : AppCompatActivity() {
} }
val currentScoreFormat = Anilist.scoreFormat val currentScoreFormat = Anilist.scoreFormat
val scoreFormat = ScoreFormat.entries.firstOrNull{ it.name == currentScoreFormat } ?: ScoreFormat.POINT_100 val scoreFormat = ScoreFormat.entries.firstOrNull { it.name == currentScoreFormat }
?: ScoreFormat.POINT_100
settingsAnilistScoreFormat.setText(scoreFormats[scoreFormat.ordinal]) settingsAnilistScoreFormat.setText(scoreFormats[scoreFormat.ordinal])
settingsAnilistScoreFormat.setAdapter( settingsAnilistScoreFormat.setAdapter(
ArrayAdapter(context, R.layout.item_dropdown, scoreFormats) ArrayAdapter(context, R.layout.item_dropdown, scoreFormats)
@@ -139,7 +144,8 @@ class AnilistSettingsActivity : AppCompatActivity() {
settingsAnilistScoreFormat.clearFocus() settingsAnilistScoreFormat.clearFocus()
} }
val currentRowOrder = rowOrderMap.entries.firstOrNull { it.value == Anilist.rowOrder }?.key ?: "Score" val currentRowOrder =
rowOrderMap.entries.firstOrNull { it.value == Anilist.rowOrder }?.key ?: "Score"
settingsAnilistRowOrder.setText(currentRowOrder) settingsAnilistRowOrder.setText(currentRowOrder)
settingsAnilistRowOrder.setAdapter( settingsAnilistRowOrder.setAdapter(
ArrayAdapter(context, R.layout.item_dropdown, rowOrderMap.keys.toList()) ArrayAdapter(context, R.layout.item_dropdown, rowOrderMap.keys.toList())
@@ -155,7 +161,8 @@ class AnilistSettingsActivity : AppCompatActivity() {
settingsAnilistRowOrder.clearFocus() settingsAnilistRowOrder.clearFocus()
} }
val containers = listOf(binding.animeCustomListsContainer, binding.mangaCustomListsContainer) val containers =
listOf(binding.animeCustomListsContainer, binding.mangaCustomListsContainer)
val customLists = listOf(Anilist.animeCustomLists, Anilist.mangaCustomLists) val customLists = listOf(Anilist.animeCustomLists, Anilist.mangaCustomLists)
val buttons = listOf(binding.addAnimeListButton, binding.addMangaListButton) val buttons = listOf(binding.addAnimeListButton, binding.addMangaListButton)
@@ -175,7 +182,8 @@ class AnilistSettingsActivity : AppCompatActivity() {
saveCustomLists() saveCustomLists()
} }
val currentTimezone = Anilist.timezone?.let { Anilist.getDisplayTimezone(it, context) } ?: context.getString(R.string.selected_no_time_zone) val currentTimezone = Anilist.timezone?.let { Anilist.getDisplayTimezone(it, context) }
?: context.getString(R.string.selected_no_time_zone)
settingsAnilistTimezone.setText(currentTimezone) settingsAnilistTimezone.setText(currentTimezone)
settingsAnilistTimezone.setAdapter( settingsAnilistTimezone.setAdapter(
ArrayAdapter(context, R.layout.item_dropdown, Anilist.timeZone) ArrayAdapter(context, R.layout.item_dropdown, Anilist.timeZone)

View File

@@ -169,8 +169,12 @@ class ExtensionsActivity : AppCompatActivity() {
customAlertDialog().apply { customAlertDialog().apply {
setTitle("Language") setTitle("Language")
singleChoiceItems(languageOptions, index) { selected -> singleChoiceItems(languageOptions, index) { selected ->
PrefManager.setVal(PrefName.LangSort, LanguageMapper.Companion.Language.entries[selected].code) PrefManager.setVal(
val currentFragment = supportFragmentManager.findFragmentByTag("f${viewPager.currentItem}") PrefName.LangSort,
LanguageMapper.Companion.Language.entries[selected].code
)
val currentFragment =
supportFragmentManager.findFragmentByTag("f${viewPager.currentItem}")
if (currentFragment is SearchQueryHandler) { if (currentFragment is SearchQueryHandler) {
currentFragment.notifyDataChanged() currentFragment.notifyDataChanged()
} }

View File

@@ -70,7 +70,7 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
if (allSettings.isNotEmpty()) { if (allSettings.isNotEmpty()) {
var selectedSetting = allSettings[0] var selectedSetting = allSettings[0]
if (allSettings.size > 1) { if (allSettings.size > 1) {
val names = allSettings.map { LanguageMapper.getLanguageName(it.lang) } val names = allSettings.map { getLanguageName(it.lang) }
.toTypedArray() .toTypedArray()
var selectedIndex = 0 var selectedIndex = 0
requireContext().customAlertDialog().apply { requireContext().customAlertDialog().apply {

View File

@@ -290,7 +290,8 @@ class PlayerSettingsActivity :
PrefManager.setVal(PrefName.UseInternalCast, isChecked) PrefManager.setVal(PrefName.UseInternalCast, isChecked)
} }
binding.playerSettingsAdditionalCodec.isChecked = PrefManager.getVal(PrefName.UseAdditionalCodec) binding.playerSettingsAdditionalCodec.isChecked =
PrefManager.getVal(PrefName.UseAdditionalCodec)
binding.playerSettingsAdditionalCodec.setOnCheckedChangeListener { _, isChecked -> binding.playerSettingsAdditionalCodec.setOnCheckedChangeListener { _, isChecked ->
PrefManager.setVal(PrefName.UseAdditionalCodec, isChecked) PrefManager.setVal(PrefName.UseAdditionalCodec, isChecked)
} }

View File

@@ -6,9 +6,9 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import ani.dantotsu.BottomSheetDialogFragment import ani.dantotsu.BottomSheetDialogFragment
import ani.dantotsu.databinding.BottomSheetProxyBinding import ani.dantotsu.databinding.BottomSheetProxyBinding
import ani.dantotsu.restartApp
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.restartApp
class ProxyDialogFragment : BottomSheetDialogFragment() { class ProxyDialogFragment : BottomSheetDialogFragment() {
private var _binding: BottomSheetProxyBinding? = null private var _binding: BottomSheetProxyBinding? = null
@@ -16,8 +16,10 @@ class ProxyDialogFragment : BottomSheetDialogFragment() {
private var proxyHost: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyHost).orEmpty() private var proxyHost: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyHost).orEmpty()
private var proxyPort: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyPort).orEmpty() private var proxyPort: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyPort).orEmpty()
private var proxyUsername: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyUsername).orEmpty() private var proxyUsername: String? =
private var proxyPassword: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyPassword).orEmpty() PrefManager.getVal<String>(PrefName.Socks5ProxyUsername).orEmpty()
private var proxyPassword: String? =
PrefManager.getVal<String>(PrefName.Socks5ProxyPassword).orEmpty()
private var authEnabled: Boolean = PrefManager.getVal<Boolean>(PrefName.ProxyAuthEnabled) private var authEnabled: Boolean = PrefManager.getVal<Boolean>(PrefName.ProxyAuthEnabled)
private val proxyEnabled: Boolean = PrefManager.getVal<Boolean>(PrefName.EnableSocks5Proxy) private val proxyEnabled: Boolean = PrefManager.getVal<Boolean>(PrefName.EnableSocks5Proxy)

View File

@@ -142,8 +142,10 @@ class SettingsAboutActivity : AppCompatActivity() {
icon = R.drawable.ic_incognito_24, icon = R.drawable.ic_incognito_24,
onClick = { onClick = {
val text = TextView(context) val text = TextView(context)
val pPLink = "https://raw.githubusercontent.com/rebelonion/Dantotsu/main/privacy_policy.md" val pPLink =
val backup = "https://gcore.jsdelivr.net/gh/rebelonion/dantotsu/privacy_policy.md" "https://raw.githubusercontent.com/rebelonion/Dantotsu/main/privacy_policy.md"
val backup =
"https://gcore.jsdelivr.net/gh/rebelonion/dantotsu/privacy_policy.md"
text.text = getString(R.string.loading) text.text = getString(R.string.loading)
val markWon = try { val markWon = try {
buildMarkwon(this@SettingsAboutActivity, false) buildMarkwon(this@SettingsAboutActivity, false)

View File

@@ -252,6 +252,7 @@ class SettingsAccountActivity : AppCompatActivity() {
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
} }
fun reload() { fun reload() {
snackString(getString(R.string.restart_app_extra)) snackString(getString(R.string.restart_app_extra))
//snackString(R.string.restart_app_extra) //snackString(R.string.restart_app_extra)

View File

@@ -11,8 +11,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.addons.AddonDownloader import ani.dantotsu.addons.AddonDownloader
import ani.dantotsu.addons.download.DownloadAddonManager import ani.dantotsu.addons.download.DownloadAddonManager
import ani.dantotsu.addons.torrent.TorrentServerService
import ani.dantotsu.addons.torrent.TorrentAddonManager import ani.dantotsu.addons.torrent.TorrentAddonManager
import ani.dantotsu.addons.torrent.TorrentServerService
import ani.dantotsu.databinding.ActivitySettingsAddonsBinding import ani.dantotsu.databinding.ActivitySettingsAddonsBinding
import ani.dantotsu.databinding.ItemSettingsBinding import ani.dantotsu.databinding.ItemSettingsBinding
import ani.dantotsu.initActivity import ani.dantotsu.initActivity

View File

@@ -19,6 +19,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.customAlertDialog
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@@ -57,23 +58,16 @@ class SettingsAnimeActivity : AppCompatActivity() {
desc = getString(R.string.purge_anime_downloads_desc), desc = getString(R.string.purge_anime_downloads_desc),
icon = R.drawable.ic_round_delete_24, icon = R.drawable.ic_round_delete_24,
onClick = { onClick = {
val dialog = AlertDialog.Builder(context, R.style.MyPopup) context.customAlertDialog().apply {
.setTitle(R.string.purge_anime_downloads) setTitle(R.string.purge_anime_downloads)
.setMessage( setMessage(R.string.purge_confirm, getString(R.string.anime))
getString( setPosButton(R.string.yes, onClick = {
R.string.purge_confirm,
getString(R.string.anime)
)
)
.setPositiveButton(R.string.yes) { dialog, _ ->
val downloadsManager = Injekt.get<DownloadsManager>() val downloadsManager = Injekt.get<DownloadsManager>()
downloadsManager.purgeDownloads(MediaType.ANIME) downloadsManager.purgeDownloads(MediaType.ANIME)
dialog.dismiss() })
}.setNegativeButton(R.string.no) { dialog, _ -> setNegButton(R.string.no)
dialog.dismiss() show()
}.create() }
dialog.window?.setDimAmount(0.8f)
dialog.show()
} }
), ),

View File

@@ -8,8 +8,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.CheckBox
import android.widget.EditText
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager
@@ -47,7 +45,6 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.UUID import java.util.UUID
class SettingsCommonActivity : AppCompatActivity() { class SettingsCommonActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingsCommonBinding private lateinit var binding: ActivitySettingsCommonBinding
private lateinit var launcher: LauncherWrapper private lateinit var launcher: LauncherWrapper
@@ -64,7 +61,8 @@ class SettingsCommonActivity : AppCompatActivity() {
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
if (uri != null) { if (uri != null) {
try { try {
val jsonString = contentResolver.openInputStream(uri)?.readBytes() val jsonString =
contentResolver.openInputStream(uri)?.readBytes()
?: throw Exception("Error reading file") ?: throw Exception("Error reading file")
val name = DocumentFile.fromSingleUri(this, uri)?.name ?: "settings" val name = DocumentFile.fromSingleUri(this, uri)?.name ?: "settings"
// .sani is encrypted, .ani is not // .sani is encrypted, .ani is not
@@ -73,9 +71,12 @@ class SettingsCommonActivity : AppCompatActivity() {
if (password != null) { if (password != null) {
val salt = jsonString.copyOfRange(0, 16) val salt = jsonString.copyOfRange(0, 16)
val encrypted = jsonString.copyOfRange(16, jsonString.size) val encrypted = jsonString.copyOfRange(16, jsonString.size)
val decryptedJson = try { val decryptedJson =
try {
PreferenceKeystore.decryptWithPassword( PreferenceKeystore.decryptWithPassword(
password, encrypted, salt password,
encrypted,
salt,
) )
} catch (e: Exception) { } catch (e: Exception) {
toast(getString(R.string.incorrect_password)) toast(getString(R.string.incorrect_password))
@@ -102,7 +103,6 @@ class SettingsCommonActivity : AppCompatActivity() {
launcher = LauncherWrapper(this, contract) launcher = LauncherWrapper(this, contract)
binding.apply { binding.apply {
settingsCommonLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> { settingsCommonLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = statusBarHeight topMargin = statusBarHeight
bottomMargin = navBarHeight bottomMargin = navBarHeight
@@ -110,7 +110,8 @@ class SettingsCommonActivity : AppCompatActivity() {
commonSettingsBack.setOnClickListener { commonSettingsBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed() onBackPressedDispatcher.onBackPressed()
} }
val exDns = listOf( val exDns =
listOf(
"None", "None",
"Cloudflare", "Cloudflare",
"Google", "Google",
@@ -124,13 +125,15 @@ class SettingsCommonActivity : AppCompatActivity() {
"Controld", "Controld",
"Njalla", "Njalla",
"Shecan", "Shecan",
"Libre" "Libre",
) )
settingsExtensionDns.setText(exDns[PrefManager.getVal(PrefName.DohProvider)]) settingsExtensionDns.setText(exDns[PrefManager.getVal(PrefName.DohProvider)])
settingsExtensionDns.setAdapter( settingsExtensionDns.setAdapter(
ArrayAdapter( ArrayAdapter(
context, R.layout.item_dropdown, exDns context,
) R.layout.item_dropdown,
exDns,
),
) )
settingsExtensionDns.setOnItemClickListener { _, _, i, _ -> settingsExtensionDns.setOnItemClickListener { _, _, i, _ ->
PrefManager.setVal(PrefName.DohProvider, i) PrefManager.setVal(PrefName.DohProvider, i)
@@ -138,7 +141,8 @@ class SettingsCommonActivity : AppCompatActivity() {
restartApp() restartApp()
} }
settingsRecyclerView.adapter = SettingsAdapter( settingsRecyclerView.adapter =
SettingsAdapter(
arrayListOf( arrayListOf(
Settings( Settings(
type = 1, type = 1,
@@ -149,21 +153,11 @@ class SettingsCommonActivity : AppCompatActivity() {
startActivity( startActivity(
Intent( Intent(
context, context,
UserInterfaceSettingsActivity::class.java UserInterfaceSettingsActivity::class.java,
) ),
) )
}, },
isActivity = true isActivity = true,
),
Settings(
type = 2,
name = getString(R.string.open_animanga_directly),
desc = getString(R.string.open_animanga_directly_info),
icon = R.drawable.ic_round_search_24,
isChecked = PrefManager.getVal(PrefName.AniMangaSearchDirect),
switch = { isChecked, _ ->
PrefManager.setVal(PrefName.AniMangaSearchDirect, isChecked)
}
), ),
Settings( Settings(
type = 1, type = 1,
@@ -176,13 +170,13 @@ class SettingsCommonActivity : AppCompatActivity() {
setTitle(getString(R.string.download_manager)) setTitle(getString(R.string.download_manager))
singleChoiceItems( singleChoiceItems(
managers, managers,
PrefManager.getVal(PrefName.DownloadManager) PrefManager.getVal(PrefName.DownloadManager),
) { count -> ) { count ->
PrefManager.setVal(PrefName.DownloadManager, count) PrefManager.setVal(PrefName.DownloadManager, count)
} }
show() show()
} }
} },
), ),
Settings( Settings(
type = 1, type = 1,
@@ -204,8 +198,10 @@ class SettingsCommonActivity : AppCompatActivity() {
PrefManager.setVal(PrefName.AppPassword, password) PrefManager.setVal(PrefName.AppPassword, password)
if (view.biometricCheckbox.isChecked) { if (view.biometricCheckbox.isChecked) {
val canBiometricPrompt = val canBiometricPrompt =
BiometricManager.from(applicationContext) BiometricManager
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS .from(applicationContext)
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) ==
BiometricManager.BIOMETRIC_SUCCESS
if (canBiometricPrompt) { if (canBiometricPrompt) {
val biometricPrompt = val biometricPrompt =
@@ -213,7 +209,7 @@ class SettingsCommonActivity : AppCompatActivity() {
val token = UUID.randomUUID().toString() val token = UUID.randomUUID().toString()
PrefManager.setVal( PrefManager.setVal(
PrefName.BiometricToken, PrefName.BiometricToken,
token token,
) )
toast(R.string.success) toast(R.string.success)
} }
@@ -221,7 +217,6 @@ class SettingsCommonActivity : AppCompatActivity() {
BiometricPromptUtils.createPromptInfo(this@SettingsCommonActivity) BiometricPromptUtils.createPromptInfo(this@SettingsCommonActivity)
biometricPrompt.authenticate(promptInfo) biometricPrompt.authenticate(promptInfo)
} }
} else { } else {
PrefManager.setVal(PrefName.BiometricToken, "") PrefManager.setVal(PrefName.BiometricToken, "")
toast(R.string.success) toast(R.string.success)
@@ -241,7 +236,7 @@ class SettingsCommonActivity : AppCompatActivity() {
view.passwordInput.requestFocus() view.passwordInput.requestFocus()
val canAuthenticate = val canAuthenticate =
BiometricManager.from(applicationContext).canAuthenticate( BiometricManager.from(applicationContext).canAuthenticate(
BiometricManager.Authenticators.BIOMETRIC_WEAK BiometricManager.Authenticators.BIOMETRIC_WEAK,
) == BiometricManager.BIOMETRIC_SUCCESS ) == BiometricManager.BIOMETRIC_SUCCESS
view.biometricCheckbox.isVisible = canAuthenticate view.biometricCheckbox.isVisible = canAuthenticate
view.biometricCheckbox.isChecked = view.biometricCheckbox.isChecked =
@@ -251,8 +246,7 @@ class SettingsCommonActivity : AppCompatActivity() {
} }
show() show()
} }
} },
), ),
Settings( Settings(
type = 1, type = 1,
@@ -261,24 +255,26 @@ class SettingsCommonActivity : AppCompatActivity() {
icon = R.drawable.backup_restore, icon = R.drawable.backup_restore,
onClick = { onClick = {
StoragePermissions.downloadsPermission(context) StoragePermissions.downloadsPermission(context)
val selectedArray = mutableListOf(false)
val filteredLocations = Location.entries.filter { it.exportable } val filteredLocations = Location.entries.filter { it.exportable }
selectedArray.addAll(List(filteredLocations.size - 1) { false }) val selectedArray = BooleanArray(filteredLocations.size) { false }
val dialog = AlertDialog.Builder(context, R.style.MyPopup) context.customAlertDialog().apply {
.setTitle(R.string.backup_restore).setMultiChoiceItems( setTitle(R.string.backup_restore)
multiChoiceItems(
filteredLocations.map { it.name }.toTypedArray(), filteredLocations.map { it.name }.toTypedArray(),
selectedArray.toBooleanArray() selectedArray,
) { _, which, isChecked -> ) { updatedSelection ->
selectedArray[which] = isChecked for (i in updatedSelection.indices) {
}.setPositiveButton(R.string.button_restore) { dialog, _ -> selectedArray[i] = updatedSelection[i]
}
}
setPosButton(R.string.button_restore) {
openDocumentLauncher.launch(arrayOf("*/*")) openDocumentLauncher.launch(arrayOf("*/*"))
dialog.dismiss() }
}.setNegativeButton(R.string.button_backup) { dialog, _ -> setNegButton(R.string.button_backup) {
if (!selectedArray.contains(true)) { if (!selectedArray.contains(true)) {
toast(R.string.no_location_selected) toast(R.string.no_location_selected)
return@setNegativeButton return@setNegButton
} }
dialog.dismiss()
val selected = val selected =
filteredLocations.filterIndexed { index, _ -> selectedArray[index] } filteredLocations.filterIndexed { index, _ -> selectedArray[index] }
if (selected.contains(Location.Protected)) { if (selected.contains(Location.Protected)) {
@@ -288,7 +284,7 @@ class SettingsCommonActivity : AppCompatActivity() {
"DantotsuSettings", "DantotsuSettings",
PrefManager.exportAllPrefs(selected), PrefManager.exportAllPrefs(selected),
context, context,
password password,
) )
} else { } else {
toast(R.string.password_cannot_be_empty) toast(R.string.password_cannot_be_empty)
@@ -299,14 +295,13 @@ class SettingsCommonActivity : AppCompatActivity() {
"DantotsuSettings", "DantotsuSettings",
PrefManager.exportAllPrefs(selected), PrefManager.exportAllPrefs(selected),
context, context,
null null,
) )
} }
}.setNeutralButton(R.string.cancel) { dialog, _ -> }
dialog.dismiss() setNeutralButton(R.string.cancel) {}
}.create() show()
dialog.window?.setDimAmount(0.8f) }
dialog.show()
}, },
), ),
Settings( Settings(
@@ -327,7 +322,9 @@ class SettingsCommonActivity : AppCompatActivity() {
PrefManager.getVal<String>(PrefName.DownloadsDir) PrefManager.getVal<String>(PrefName.DownloadsDir)
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
Injekt.get<DownloadsManager>().moveDownloadsDir( Injekt.get<DownloadsManager>().moveDownloadsDir(
context, Uri.parse(oldUri), Uri.parse(newUri) context,
Uri.parse(oldUri),
Uri.parse(newUri),
) { finished, message -> ) { finished, message ->
if (finished) { if (finished) {
toast(getString(R.string.success)) toast(getString(R.string.success))
@@ -345,7 +342,7 @@ class SettingsCommonActivity : AppCompatActivity() {
setNegButton(R.string.cancel) setNegButton(R.string.cancel)
show() show()
} }
} },
), ),
Settings( Settings(
type = 2, type = 2,
@@ -355,7 +352,7 @@ class SettingsCommonActivity : AppCompatActivity() {
isChecked = PrefManager.getVal(PrefName.ContinueMedia), isChecked = PrefManager.getVal(PrefName.ContinueMedia),
switch = { isChecked, _ -> switch = { isChecked, _ ->
PrefManager.setVal(PrefName.ContinueMedia, isChecked) PrefManager.setVal(PrefName.ContinueMedia, isChecked)
} },
), ),
Settings( Settings(
type = 2, type = 2,
@@ -366,7 +363,7 @@ class SettingsCommonActivity : AppCompatActivity() {
switch = { isChecked, _ -> switch = { isChecked, _ ->
PrefManager.setVal(PrefName.HidePrivate, isChecked) PrefManager.setVal(PrefName.HidePrivate, isChecked)
restartApp() restartApp()
} },
), ),
Settings( Settings(
type = 2, type = 2,
@@ -376,7 +373,7 @@ class SettingsCommonActivity : AppCompatActivity() {
isChecked = PrefManager.getVal(PrefName.SearchSources), isChecked = PrefManager.getVal(PrefName.SearchSources),
switch = { isChecked, _ -> switch = { isChecked, _ ->
PrefManager.setVal(PrefName.SearchSources, isChecked) PrefManager.setVal(PrefName.SearchSources, isChecked)
} },
), ),
Settings( Settings(
type = 2, type = 2,
@@ -386,7 +383,7 @@ class SettingsCommonActivity : AppCompatActivity() {
isChecked = PrefManager.getVal(PrefName.RecentlyListOnly), isChecked = PrefManager.getVal(PrefName.RecentlyListOnly),
switch = { isChecked, _ -> switch = { isChecked, _ ->
PrefManager.setVal(PrefName.RecentlyListOnly, isChecked) PrefManager.setVal(PrefName.RecentlyListOnly, isChecked)
} },
), ),
Settings( Settings(
type = 2, type = 2,
@@ -398,23 +395,27 @@ class SettingsCommonActivity : AppCompatActivity() {
PrefManager.setVal(PrefName.AdultOnly, isChecked) PrefManager.setVal(PrefName.AdultOnly, isChecked)
restartApp() restartApp()
}, },
isVisible = Anilist.adult isVisible = Anilist.adult,
),
), ),
)
) )
settingsRecyclerView.apply { settingsRecyclerView.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
setHasFixedSize(true) setHasFixedSize(true)
} }
var previousStart: View = when (PrefManager.getVal<Int>(PrefName.DefaultStartUpTab)) { var previousStart: View =
when (PrefManager.getVal<Int>(PrefName.DefaultStartUpTab)) {
0 -> uiSettingsAnime 0 -> uiSettingsAnime
1 -> uiSettingsHome 1 -> uiSettingsHome
2 -> uiSettingsManga 2 -> uiSettingsManga
else -> uiSettingsHome else -> uiSettingsHome
} }
previousStart.alpha = 1f previousStart.alpha = 1f
fun uiDefault(mode: Int, current: View) {
fun uiDefault(
mode: Int,
current: View,
) {
previousStart.alpha = 0.33f previousStart.alpha = 0.33f
previousStart = current previousStart = current
current.alpha = 1f current.alpha = 1f
@@ -433,11 +434,13 @@ class SettingsCommonActivity : AppCompatActivity() {
uiSettingsManga.setOnClickListener { uiSettingsManga.setOnClickListener {
uiDefault(2, it) uiDefault(2, it)
} }
} }
} }
private fun passwordAlertDialog(isExporting: Boolean, callback: (CharArray?) -> Unit) { private fun passwordAlertDialog(
isExporting: Boolean,
callback: (CharArray?) -> Unit,
) {
val password = CharArray(16).apply { fill('0') } val password = CharArray(16).apply { fill('0') }
// Inflate the dialog layout // Inflate the dialog layout
@@ -447,7 +450,9 @@ class SettingsCommonActivity : AppCompatActivity() {
box.setSingleLine() box.setSingleLine()
val dialog = val dialog =
AlertDialog.Builder(this, R.style.MyPopup).setTitle(getString(R.string.enter_password)) AlertDialog
.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.enter_password))
.setView(dialogView.root) .setView(dialogView.root)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel) { dialog, _ -> .setNegativeButton(R.string.cancel) { dialog, _ ->
@@ -459,7 +464,10 @@ class SettingsCommonActivity : AppCompatActivity() {
fun handleOkAction() { fun handleOkAction() {
val editText = dialogView.userAgentTextBox val editText = dialogView.userAgentTextBox
if (editText.text?.isNotBlank() == true) { if (editText.text?.isNotBlank() == true) {
editText.text?.toString()?.trim()?.toCharArray(password) editText.text
?.toString()
?.trim()
?.toCharArray(password)
dialog.dismiss() dialog.dismiss()
callback(password) callback(password)
} else { } else {
@@ -475,18 +483,20 @@ class SettingsCommonActivity : AppCompatActivity() {
} }
} }
dialogView.subtitle.visibility = View.VISIBLE dialogView.subtitle.visibility = View.VISIBLE
if (!isExporting) dialogView.subtitle.text = if (!isExporting) {
dialogView.subtitle.text =
getString(R.string.enter_password_to_decrypt_file) getString(R.string.enter_password_to_decrypt_file)
}
dialog.window?.apply {
dialog.window?.setDimAmount(0.8f) setDimAmount(0.8f)
attributes.windowAnimations = android.R.style.Animation_Dialog
}
dialog.show() dialog.show()
// Override the positive button here // Override the positive button here
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
handleOkAction() handleOkAction()
} }
} }
} }

View File

@@ -59,7 +59,8 @@ class SettingsDialogFragment : BottomSheetDialogFragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val window = dialog?.window val window = dialog?.window
window?.statusBarColor = Color.CYAN window?.statusBarColor = Color.CYAN
window?.navigationBarColor = requireContext().getThemeColor(com.google.android.material.R.attr.colorSurface) window?.navigationBarColor =
requireContext().getThemeColor(com.google.android.material.R.attr.colorSurface)
val notificationIcon = if (Anilist.unreadNotificationCount > 0) { val notificationIcon = if (Anilist.unreadNotificationCount > 0) {
R.drawable.ic_round_notifications_active_24 R.drawable.ic_round_notifications_active_24
} else { } else {

View File

@@ -26,11 +26,8 @@ import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.customAlertDialog import ani.dantotsu.util.customAlertDialog
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
class SettingsExtensionsActivity : AppCompatActivity() { class SettingsExtensionsActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingsExtensionsBinding private lateinit var binding: ActivitySettingsExtensionsBinding

View File

@@ -1,6 +1,5 @@
package ani.dantotsu.settings package ani.dantotsu.settings
import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View

View File

@@ -119,25 +119,26 @@ class SettingsNotificationActivity : AppCompatActivity() {
PrefManager.getVal<Set<String>>(PrefName.AnilistFilteredTypes) PrefManager.getVal<Set<String>>(PrefName.AnilistFilteredTypes)
.toMutableSet() .toMutableSet()
val selected = types.map { filteredTypes.contains(it) }.toBooleanArray() val selected = types.map { filteredTypes.contains(it) }.toBooleanArray()
val dialog = AlertDialog.Builder(context, R.style.MyPopup) context.customAlertDialog().apply {
.setTitle(R.string.anilist_notification_filters) setTitle(R.string.anilist_notification_filters)
.setMultiChoiceItems( multiChoiceItems(
types.map { name -> types.map { name ->
name.replace("_", " ").lowercase().replaceFirstChar { name.replace("_", " ").lowercase().replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString() if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString()
} }.toTypedArray(), } }.toTypedArray(),
selected selected
) { _, which, isChecked -> ) { updatedSelected ->
val type = types[which] types.forEachIndexed { index, type ->
if (isChecked) { if (updatedSelected[index]) {
filteredTypes.add(type) filteredTypes.add(type)
} else { } else {
filteredTypes.remove(type) filteredTypes.remove(type)
} }
}
PrefManager.setVal(PrefName.AnilistFilteredTypes, filteredTypes) PrefManager.setVal(PrefName.AnilistFilteredTypes, filteredTypes)
}.create() }
dialog.window?.setDimAmount(0.8f) show()
dialog.show() }
} }
), ),
@@ -150,27 +151,24 @@ class SettingsNotificationActivity : AppCompatActivity() {
desc = getString(R.string.anilist_notifications_checking_time_desc), desc = getString(R.string.anilist_notifications_checking_time_desc),
icon = R.drawable.ic_round_notifications_none_24, icon = R.drawable.ic_round_notifications_none_24,
onClick = { onClick = {
val selected = context.customAlertDialog().apply {
PrefManager.getVal<Int>(PrefName.AnilistNotificationInterval) setTitle(R.string.subscriptions_checking_time)
val dialog = AlertDialog.Builder(context, R.style.MyPopup) singleChoiceItems(
.setTitle(R.string.subscriptions_checking_time)
.setSingleChoiceItems(
aItems.toTypedArray(), aItems.toTypedArray(),
selected PrefManager.getVal<Int>(PrefName.AnilistNotificationInterval)
) { dialog, i -> ) { i ->
PrefManager.setVal(PrefName.AnilistNotificationInterval, i) PrefManager.setVal(PrefName.AnilistNotificationInterval, i)
it.settingsTitle.text = it.settingsTitle.text =
getString( getString(
R.string.anilist_notifications_checking_time, R.string.anilist_notifications_checking_time,
aItems[i] aItems[i]
) )
dialog.dismiss()
TaskScheduler.create( TaskScheduler.create(
context, PrefManager.getVal(PrefName.UseAlarmManager) context, PrefManager.getVal(PrefName.UseAlarmManager)
).scheduleAllTasks(context) ).scheduleAllTasks(context)
}.create() }
dialog.window?.setDimAmount(0.8f) show()
dialog.show() }
} }
), ),
Settings( Settings(
@@ -182,27 +180,24 @@ class SettingsNotificationActivity : AppCompatActivity() {
desc = getString(R.string.comment_notification_checking_time_desc), desc = getString(R.string.comment_notification_checking_time_desc),
icon = R.drawable.ic_round_notifications_none_24, icon = R.drawable.ic_round_notifications_none_24,
onClick = { onClick = {
val selected = context.customAlertDialog().apply {
PrefManager.getVal<Int>(PrefName.CommentNotificationInterval) setTitle(R.string.subscriptions_checking_time)
val dialog = AlertDialog.Builder(context, R.style.MyPopup) singleChoiceItems(
.setTitle(R.string.subscriptions_checking_time)
.setSingleChoiceItems(
cItems.toTypedArray(), cItems.toTypedArray(),
selected PrefManager.getVal<Int>(PrefName.CommentNotificationInterval)
) { dialog, i -> ) { i ->
PrefManager.setVal(PrefName.CommentNotificationInterval, i) PrefManager.setVal(PrefName.CommentNotificationInterval, i)
it.settingsTitle.text = it.settingsTitle.text =
getString( getString(
R.string.comment_notification_checking_time, R.string.comment_notification_checking_time,
cItems[i] cItems[i]
) )
dialog.dismiss()
TaskScheduler.create( TaskScheduler.create(
context, PrefManager.getVal(PrefName.UseAlarmManager) context, PrefManager.getVal(PrefName.UseAlarmManager)
).scheduleAllTasks(context) ).scheduleAllTasks(context)
}.create() }
dialog.window?.setDimAmount(0.8f) show()
dialog.show() }
} }
), ),
Settings( Settings(
@@ -229,10 +224,10 @@ class SettingsNotificationActivity : AppCompatActivity() {
isChecked = PrefManager.getVal(PrefName.UseAlarmManager), isChecked = PrefManager.getVal(PrefName.UseAlarmManager),
switch = { isChecked, view -> switch = { isChecked, view ->
if (isChecked) { if (isChecked) {
val alertDialog = AlertDialog.Builder(context, R.style.MyPopup) context.customAlertDialog().apply {
.setTitle(R.string.use_alarm_manager) setTitle(R.string.use_alarm_manager)
.setMessage(R.string.use_alarm_manager_confirm) setMessage(R.string.use_alarm_manager_confirm)
.setPositiveButton(R.string.use) { dialog, _ -> setPosButton(R.string.use) {
PrefManager.setVal(PrefName.UseAlarmManager, true) PrefManager.setVal(PrefName.UseAlarmManager, true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (!(getSystemService(Context.ALARM_SERVICE) as AlarmManager).canScheduleExactAlarms()) { if (!(getSystemService(Context.ALARM_SERVICE) as AlarmManager).canScheduleExactAlarms()) {
@@ -242,15 +237,13 @@ class SettingsNotificationActivity : AppCompatActivity() {
view.settingsButton.isChecked = true view.settingsButton.isChecked = true
} }
} }
dialog.dismiss() }
}.setNegativeButton(R.string.cancel) { dialog, _ -> setNegButton(R.string.cancel) {
view.settingsButton.isChecked = false view.settingsButton.isChecked = false
PrefManager.setVal(PrefName.UseAlarmManager, false) PrefManager.setVal(PrefName.UseAlarmManager, false)
}
dialog.dismiss() show()
}.create() }
alertDialog.window?.setDimAmount(0.8f)
alertDialog.show()
} else { } else {
PrefManager.setVal(PrefName.UseAlarmManager, false) PrefManager.setVal(PrefName.UseAlarmManager, false)
TaskScheduler.create(context, true).cancelAllTasks() TaskScheduler.create(context, true).cancelAllTasks()

View File

@@ -47,7 +47,8 @@ class SettingsThemeActivity : AppCompatActivity(), SimpleDialog.OnDialogResultLi
val mainIntent = Intent.makeRestartActivityTask( val mainIntent = Intent.makeRestartActivityTask(
packageManager.getLaunchIntentForPackage(packageName)!!.component packageManager.getLaunchIntentForPackage(packageName)!!.component
) )
val component = ComponentName(packageName, SettingsActivity::class.qualifiedName!!) val component =
ComponentName(packageName, SettingsActivity::class.qualifiedName!!)
try { try {
startActivity(Intent().setComponent(component)) startActivity(Intent().setComponent(component))
} catch (e: Exception) { } catch (e: Exception) {

View File

@@ -41,5 +41,6 @@ class SubscriptionItem(
override fun getLayout(): Int = R.layout.item_subscription override fun getLayout(): Int = R.layout.item_subscription
override fun initializeViewBinding(view: View): ItemSubscriptionBinding = ItemSubscriptionBinding.bind(view) override fun initializeViewBinding(view: View): ItemSubscriptionBinding =
ItemSubscriptionBinding.bind(view)
} }

View File

@@ -1,6 +1,5 @@
package ani.dantotsu.settings package ani.dantotsu.settings
import android.app.AlertDialog
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.view.View import android.view.View
@@ -36,7 +35,8 @@ class SubscriptionSource(
true true
} }
binding.extensionIconImageView.visibility = View.VISIBLE binding.extensionIconImageView.visibility = View.VISIBLE
val layoutParams = binding.extensionIconImageView.layoutParams as ViewGroup.MarginLayoutParams val layoutParams =
binding.extensionIconImageView.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.leftMargin = 28 layoutParams.leftMargin = 28
binding.extensionIconImageView.layoutParams = layoutParams binding.extensionIconImageView.layoutParams = layoutParams
@@ -55,7 +55,8 @@ class SubscriptionSource(
private fun updateSubscriptionCount() { private fun updateSubscriptionCount() {
binding.subscriptionCount.text = subscriptions.size.toString() binding.subscriptionCount.text = subscriptions.size.toString()
binding.subscriptionCount.visibility = if (subscriptions.isEmpty()) View.GONE else View.VISIBLE binding.subscriptionCount.visibility =
if (subscriptions.isEmpty()) View.GONE else View.VISIBLE
} }
private fun showRemoveAllSubscriptionsDialog(context: Context) { private fun showRemoveAllSubscriptionsDialog(context: Context) {
@@ -96,7 +97,9 @@ class SubscriptionSource(
val startPosition = adapter.getAdapterPosition(this) + 1 val startPosition = adapter.getAdapterPosition(this) + 1
if (isExpanded) { if (isExpanded) {
subscriptions.forEachIndexed { index, subscribeMedia -> subscriptions.forEachIndexed { index, subscribeMedia ->
adapter.add(startPosition + index, SubscriptionItem(subscribeMedia.id, subscribeMedia, adapter) { removedId -> adapter.add(
startPosition + index,
SubscriptionItem(subscribeMedia.id, subscribeMedia, adapter) { removedId ->
removeSubscription(removedId) removeSubscription(removedId)
}) })
} }
@@ -109,5 +112,6 @@ class SubscriptionSource(
override fun getLayout(): Int = R.layout.item_extension override fun getLayout(): Int = R.layout.item_extension
override fun initializeViewBinding(view: View): ItemExtensionBinding = ItemExtensionBinding.bind(view) override fun initializeViewBinding(view: View): ItemExtensionBinding =
ItemExtensionBinding.bind(view)
} }

View File

@@ -67,10 +67,13 @@ class SubscriptionsBottomDialog : BottomSheetDialogFragment() {
return when { return when {
animeExtension.installedExtensionsFlow.value.any { it.name == parserName } -> animeExtension.installedExtensionsFlow.value.any { it.name == parserName } ->
animeExtension.installedExtensionsFlow.value.find { it.name == parserName }?.icon animeExtension.installedExtensionsFlow.value.find { it.name == parserName }?.icon
mangaExtensions.installedExtensionsFlow.value.any { it.name == parserName } -> mangaExtensions.installedExtensionsFlow.value.any { it.name == parserName } ->
mangaExtensions.installedExtensionsFlow.value.find { it.name == parserName }?.icon mangaExtensions.installedExtensionsFlow.value.find { it.name == parserName }?.icon
novelExtensions.installedExtensionsFlow.value.any { it.name == parserName } -> novelExtensions.installedExtensionsFlow.value.any { it.name == parserName } ->
novelExtensions.installedExtensionsFlow.value.find { it.name == parserName }?.icon novelExtensions.installedExtensionsFlow.value.find { it.name == parserName }?.icon
else -> null else -> null
} }
} }

View File

@@ -1,6 +1,5 @@
package ani.dantotsu.settings package ani.dantotsu.settings
import android.app.AlertDialog
import android.os.Bundle import android.os.Bundle
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -43,7 +42,8 @@ class UserInterfaceSettingsActivity : AppCompatActivity() {
setTitle(getString(R.string.home_layout_show)) setTitle(getString(R.string.home_layout_show))
multiChoiceItems( multiChoiceItems(
items = views, items = views,
checkedItems = PrefManager.getVal<List<Boolean>>(PrefName.HomeLayout).toBooleanArray() checkedItems = PrefManager.getVal<List<Boolean>>(PrefName.HomeLayout)
.toBooleanArray()
) { selectedItems -> ) { selectedItems ->
for (i in selectedItems.indices) { for (i in selectedItems.indices) {
set[i] = selectedItems[i] set[i] = selectedItems[i]
@@ -69,7 +69,8 @@ class UserInterfaceSettingsActivity : AppCompatActivity() {
PrefManager.setVal(PrefName.ImmersiveMode, isChecked) PrefManager.setVal(PrefName.ImmersiveMode, isChecked)
restartApp() restartApp()
} }
binding.uiSettingsHideRedDot.isChecked = !PrefManager.getVal<Boolean>(PrefName.ShowNotificationRedDot) binding.uiSettingsHideRedDot.isChecked =
!PrefManager.getVal<Boolean>(PrefName.ShowNotificationRedDot)
binding.uiSettingsHideRedDot.setOnCheckedChangeListener { _, isChecked -> binding.uiSettingsHideRedDot.setOnCheckedChangeListener { _, isChecked ->
PrefManager.setVal(PrefName.ShowNotificationRedDot, !isChecked) PrefManager.setVal(PrefName.ShowNotificationRedDot, !isChecked)
} }

View File

@@ -2,13 +2,7 @@ package ani.dantotsu.settings.extensionprefs
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.Typeface
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.DialogPreference import androidx.preference.DialogPreference
@@ -38,10 +32,12 @@ class AnimeSourcePreferencesFragment : PreferenceFragmentCompat() {
preferenceManager.createPreferenceScreen(requireContext()) preferenceManager.createPreferenceScreen(requireContext())
} }
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
ThemeManager(requireActivity()).applyTheme() ThemeManager(requireActivity()).applyTheme()
} }
private var onCloseAction: (() -> Unit)? = null private var onCloseAction: (() -> Unit)? = null
override fun onDestroyView() { override fun onDestroyView() {
@@ -104,7 +100,8 @@ class InitialAnimeSourcePreferencesFragment(
preferenceManager.createPreferenceScreen(requireContext()) preferenceManager.createPreferenceScreen(requireContext())
} }
//set background color //set background color
val color = requireContext().getThemeColor(com.google.android.material.R.attr.backgroundColor,) val color =
requireContext().getThemeColor(com.google.android.material.R.attr.backgroundColor)
view?.setBackgroundColor(color) view?.setBackgroundColor(color)
} }

View File

@@ -203,7 +203,13 @@ enum class PrefName(val data: Pref) {
LogToFile(Pref(Location.Irrelevant, Boolean::class, false)), LogToFile(Pref(Location.Irrelevant, Boolean::class, false)),
RecentGlobalNotification(Pref(Location.Irrelevant, Int::class, 0)), RecentGlobalNotification(Pref(Location.Irrelevant, Int::class, 0)),
CommentNotificationStore(Pref(Location.Irrelevant, List::class, listOf<CommentStore>())), CommentNotificationStore(Pref(Location.Irrelevant, List::class, listOf<CommentStore>())),
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)), OC(Pref(Location.Irrelevant, Boolean::class, false)),

Some files were not shown because too many files have changed in this diff Show More