mirror of
https://github.com/rebelonion/Dantotsu.git
synced 2026-01-12 23:27:39 +00:00
Compare commits
19 Commits
v3.2.0-bet
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3f6d0ecee | ||
|
|
5124d6a2d8 | ||
|
|
e83a0fe7da | ||
|
|
61a8350043 | ||
|
|
baffbc845c | ||
|
|
afd9f6b884 | ||
|
|
7d0894cd92 | ||
|
|
dec2ed7959 | ||
|
|
e4630df3e0 | ||
|
|
6fd3515d2c | ||
|
|
6fa2f11db2 | ||
|
|
a5babea27c | ||
|
|
8a9b8cca7e | ||
|
|
7479f5f43b | ||
|
|
3ac9307329 | ||
|
|
f606bef2a5 | ||
|
|
f9f9767ecc | ||
|
|
31a67c8edb | ||
|
|
7fc69b4edd |
@@ -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!
|
||||
|
||||
<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
|
||||
By downloading, installing, or using this application, you agree to:
|
||||
- Use the application in compliance with all applicable laws
|
||||
|
||||
@@ -18,8 +18,8 @@ android {
|
||||
minSdk 21
|
||||
targetSdk 35
|
||||
versionCode((System.currentTimeMillis() / 60000).toInteger())
|
||||
versionName "3.2.0"
|
||||
versionCode 300200000
|
||||
versionName "3.2.1"
|
||||
versionCode versionName.split("\\.").collect { it.toInteger() * 100 }.join("") as Integer
|
||||
signingConfig signingConfigs.debug
|
||||
|
||||
}
|
||||
@@ -48,6 +48,10 @@ android {
|
||||
manifestPlaceholders.icon_placeholder_round = "@mipmap/ic_launcher_alpha_round"
|
||||
debuggable System.getenv("CI") == null
|
||||
isDefault true
|
||||
debuggable true
|
||||
jniDebuggable true
|
||||
minifyEnabled false
|
||||
shrinkResources false
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix ".beta"
|
||||
@@ -81,25 +85,26 @@ android {
|
||||
dependencies {
|
||||
|
||||
// FireBase
|
||||
googleImplementation platform('com.google.firebase:firebase-bom:33.0.0')
|
||||
googleImplementation 'com.google.firebase:firebase-analytics-ktx:22.0.0'
|
||||
googleImplementation 'com.google.firebase:firebase-crashlytics-ktx:19.0.0'
|
||||
googleImplementation platform('com.google.firebase:firebase-bom:33.13.0')
|
||||
googleImplementation 'com.google.firebase:firebase-analytics-ktx:22.4.0'
|
||||
googleImplementation 'com.google.firebase:firebase-crashlytics-ktx:19.4.3'
|
||||
// Core
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.browser:browser:1.8.0'
|
||||
implementation 'androidx.core:core-ktx:1.13.1'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.6.2'
|
||||
implementation 'androidx.core:core-ktx:1.16.0'
|
||||
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.legacy:legacy-support-v4:1.0.0'
|
||||
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-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
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.webkit:webkit:1.11.0'
|
||||
implementation 'androidx.webkit:webkit:1.13.0'
|
||||
implementation "com.anggrayudi:storage:1.5.5"
|
||||
implementation "androidx.biometric:biometric:1.1.0"
|
||||
|
||||
@@ -113,7 +118,7 @@ dependencies {
|
||||
implementation 'jp.wasabeef:glide-transformations:4.3.0'
|
||||
|
||||
// Exoplayer
|
||||
ext.exo_version = '1.5.0'
|
||||
ext.exo_version = '1.6.1'
|
||||
implementation "androidx.media3:media3-exoplayer:$exo_version"
|
||||
implementation "androidx.media3:media3-ui:$exo_version"
|
||||
implementation "androidx.media3:media3-exoplayer-hls:$exo_version"
|
||||
@@ -124,7 +129,7 @@ dependencies {
|
||||
implementation "androidx.media3:media3-cast:$exo_version"
|
||||
implementation "androidx.mediarouter:mediarouter:1.7.0"
|
||||
// Media3 extension
|
||||
implementation "com.github.anilbeesetti.nextlib:nextlib-media3ext:0.8.3"
|
||||
implementation "com.github.anilbeesetti.nextlib:nextlib-media3ext:0.8.4"
|
||||
|
||||
// UI
|
||||
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.alexvasilkov:gesture-views:2.8.3'
|
||||
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.AAChartModel:AAChartCore-Kotlin:7.2.3'
|
||||
|
||||
@@ -162,13 +167,13 @@ dependencies {
|
||||
implementation 'ca.gosyer:voyager-navigator:1.0.0-rc07'
|
||||
implementation 'com.squareup.logcat:logcat:0.1'
|
||||
implementation 'uy.kohesive.injekt:injekt-core:1.16.+'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.12'
|
||||
implementation 'com.squareup.okhttp3:okhttp: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.14'
|
||||
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps'
|
||||
implementation 'com.squareup.okio:okio:3.8.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp-brotli:5.0.0-alpha.12'
|
||||
implementation 'org.jsoup:jsoup:1.16.1'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.6.3'
|
||||
implementation 'com.squareup.okio:okio:3.9.1'
|
||||
implementation 'com.squareup.okhttp3:okhttp-brotli:5.0.0-alpha.14'
|
||||
implementation 'org.jsoup:jsoup:1.18.1'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.7.3'
|
||||
implementation 'com.jakewharton.rxrelay:rxrelay:1.2.0'
|
||||
implementation 'com.github.tachiyomiorg:unifile:17bec43'
|
||||
implementation 'com.github.gpanther:java-nat-sort:natural-comparator-1.1'
|
||||
|
||||
@@ -18,7 +18,9 @@ import ani.dantotsu.Mapper
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.buildMarkwon
|
||||
import ani.dantotsu.client
|
||||
import ani.dantotsu.connections.comments.CommentsAPI
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.decodeBase64ToString
|
||||
import ani.dantotsu.logError
|
||||
import ani.dantotsu.openLinkInBrowser
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
@@ -37,26 +39,88 @@ import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
object AppUpdater {
|
||||
private val fallbackStableUrl: String
|
||||
get() = "aHR0cHM6Ly9hcGkuZGFudG90c3UuYXBwL3VwZGF0ZXMvc3RhYmxl".decodeBase64ToString()
|
||||
private val fallbackBetaUrl: String
|
||||
get() = "aHR0cHM6Ly9hcGkuZGFudG90c3UuYXBwL3VwZGF0ZXMvYmV0YQ==".decodeBase64ToString()
|
||||
|
||||
@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")
|
||||
.parsed<JsonArray>().map {
|
||||
Mapper.json.decodeFromJsonElement<GithubResponse>(it)
|
||||
}
|
||||
val r = res.filter { it.prerelease }.filter { !it.tagName.contains("fdroid") }
|
||||
.maxByOrNull {
|
||||
it.timeStamp()
|
||||
} ?: throw Exception("No Pre Release Found")
|
||||
val v = r.tagName.substringAfter("v", "")
|
||||
(r.body ?: "") to v.ifEmpty { throw Exception("Weird Version : ${r.tagName}") }
|
||||
} else {
|
||||
val res = client.get("https://raw.githubusercontent.com/$repo/main/stable.md").text
|
||||
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) = if (BuildConfig.DEBUG) {
|
||||
val res = client.get("https://api.github.com/repos/$repo/releases")
|
||||
.parsed<JsonArray>().map {
|
||||
Mapper.json.decodeFromJsonElement<GithubResponse>(it)
|
||||
}
|
||||
val r = res.filter { it.prerelease }.filter { !it.tagName.contains("fdroid") }
|
||||
.maxByOrNull {
|
||||
it.timeStamp()
|
||||
} ?: throw Exception("No Pre Release Found")
|
||||
val v = r.tagName.substringAfter("v", "")
|
||||
(r.body ?: "") to v.ifEmpty { throw Exception("Weird Version : ${r.tagName}") }
|
||||
} else {
|
||||
val res =
|
||||
client.get("https://raw.githubusercontent.com/$repo/main/stable.md").text
|
||||
res to res.substringAfter("# ").substringBefore("\n")
|
||||
}
|
||||
val (md, version) = fetchUpdateInfo(repo, BuildConfig.DEBUG) ?: return@tryWithSuspend
|
||||
|
||||
Logger.log("Git Version : $version")
|
||||
val dontShow = PrefManager.getCustomVal("dont_ask_for_update_$version", false)
|
||||
@@ -69,7 +133,7 @@ object AppUpdater {
|
||||
)
|
||||
addView(
|
||||
TextView(activity).apply {
|
||||
val markWon = try { //slower phones can destroy the activity before this is done
|
||||
val markWon = try {
|
||||
buildMarkwon(activity, false)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
return@runOnUiThread
|
||||
@@ -89,17 +153,11 @@ object AppUpdater {
|
||||
setPositiveButton(currContext()!!.getString(R.string.lets_go)) {
|
||||
MainScope().launch(Dispatchers.IO) {
|
||||
try {
|
||||
val apks =
|
||||
client.get("https://api.github.com/repos/$repo/releases/tags/v$version")
|
||||
.parsed<GithubResponse>().assets?.filter {
|
||||
it.browserDownloadURL.endsWith(
|
||||
".apk"
|
||||
)
|
||||
}
|
||||
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")
|
||||
val apkUrl = fetchApkUrl(repo, version, BuildConfig.DEBUG)
|
||||
if (apkUrl != null) {
|
||||
activity.downloadUpdate(version, apkUrl)
|
||||
} else {
|
||||
openLinkInBrowser("https://github.com/repos/$repo/releases/tag/v$version")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
@@ -112,8 +170,7 @@ object AppUpdater {
|
||||
}
|
||||
show(activity.supportFragmentManager, "dialog")
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
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
|
||||
private fun Activity.downloadUpdate(version: String, url: String): Boolean {
|
||||
|
||||
private fun Activity.downloadUpdate(version: String, url: String) {
|
||||
toast(getString(R.string.downloading_update, version))
|
||||
|
||||
val downloadManager = this.getSystemService<DownloadManager>()!!
|
||||
@@ -167,7 +223,7 @@ object AppUpdater {
|
||||
logError(e)
|
||||
-1
|
||||
}
|
||||
if (id == -1L) return true
|
||||
if (id == -1L) return
|
||||
ContextCompat.registerReceiver(
|
||||
this,
|
||||
object : BroadcastReceiver() {
|
||||
@@ -188,7 +244,6 @@ object AppUpdater {
|
||||
}, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
||||
ContextCompat.RECEIVER_EXPORTED
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun openApk(context: Context, uri: Uri) {
|
||||
|
||||
@@ -113,10 +113,8 @@
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:host="*"/>
|
||||
<data android:mimeType="application/epub+zip"/>
|
||||
<data android:mimeType="application/x-mobipocket-ebook" />
|
||||
<data android:mimeType="application/vnd.amazon.ebook" />
|
||||
@@ -385,10 +383,10 @@
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="content" />
|
||||
<data android:scheme="file" />
|
||||
<data android:mimeType="*/*" />
|
||||
<data android:pathPattern=".*\\.ani" />
|
||||
<data android:pathPattern=".*\\.sani" />
|
||||
<data android:host="*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
@@ -107,27 +107,34 @@ class App : MultiDexApplication() {
|
||||
|
||||
if (PrefManager.getVal<Int>(PrefName.CommentsEnabled) == 0) {
|
||||
if (BuildConfig.FLAVOR.contains("fdroid")) {
|
||||
PrefManager.setVal(PrefName.CommentsEnabled, 2)
|
||||
PrefManager.setVal(PrefName.CommentsEnabled, 2)
|
||||
} else {
|
||||
PrefManager.setVal(PrefName.CommentsEnabled, 1)
|
||||
PrefManager.setVal(PrefName.CommentsEnabled, 1)
|
||||
}
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val scope = CoroutineScope(Dispatchers.IO)
|
||||
scope.launch {
|
||||
animeExtensionManager = Injekt.get()
|
||||
animeExtensionManager.findAvailableExtensions()
|
||||
launch {
|
||||
animeExtensionManager.findAvailableExtensions()
|
||||
}
|
||||
Logger.log("Anime Extensions: ${animeExtensionManager.installedExtensionsFlow.first()}")
|
||||
AnimeSources.init(animeExtensionManager.installedExtensionsFlow)
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
scope.launch {
|
||||
mangaExtensionManager = Injekt.get()
|
||||
mangaExtensionManager.findAvailableExtensions()
|
||||
launch {
|
||||
mangaExtensionManager.findAvailableExtensions()
|
||||
}
|
||||
Logger.log("Manga Extensions: ${mangaExtensionManager.installedExtensionsFlow.first()}")
|
||||
MangaSources.init(mangaExtensionManager.installedExtensionsFlow)
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
scope.launch {
|
||||
novelExtensionManager = Injekt.get()
|
||||
novelExtensionManager.findAvailableExtensions()
|
||||
launch {
|
||||
novelExtensionManager.findAvailableExtensions()
|
||||
}
|
||||
Logger.log("Novel Extensions: ${novelExtensionManager.installedExtensionsFlow.first()}")
|
||||
NovelSources.init(novelExtensionManager.installedExtensionsFlow)
|
||||
}
|
||||
@@ -136,9 +143,9 @@ class App : MultiDexApplication() {
|
||||
downloadAddonManager = Injekt.get()
|
||||
torrentAddonManager.init()
|
||||
downloadAddonManager.init()
|
||||
if (PrefManager.getVal<Int>(PrefName.CommentsEnabled) == 1) {
|
||||
if (PrefManager.getVal<Int>(PrefName.CommentsEnabled) == 1) {
|
||||
CommentsAPI.fetchAuthToken(this@App)
|
||||
}
|
||||
}
|
||||
|
||||
val useAlarmManager = PrefManager.getVal<Boolean>(PrefName.UseAlarmManager)
|
||||
val scheduler = TaskScheduler.create(this@App, useAlarmManager)
|
||||
|
||||
@@ -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.request.RequestListener
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
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.glide.GlideImagesPlugin
|
||||
import jp.wasabeef.glide.transformations.BlurTransformation
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import nl.joery.animatedbottombar.AnimatedBottomBar
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
@@ -157,6 +153,8 @@ import java.util.TimeZone
|
||||
import java.util.Timer
|
||||
import java.util.TimerTask
|
||||
import kotlin.collections.set
|
||||
import kotlin.io.encoding.Base64
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
import kotlin.math.log2
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
@@ -854,6 +852,7 @@ fun savePrefsToDownloads(
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@SuppressLint("StringFormatMatches")
|
||||
fun savePrefs(serialized: String, path: String, title: String, context: Context): File? {
|
||||
var file = File(path, "$title.ani")
|
||||
@@ -921,6 +920,7 @@ fun shareImage(title: String, bitmap: Bitmap, context: Context) {
|
||||
intent.putExtra(Intent.EXTRA_STREAM, contentUri)
|
||||
context.startActivity(Intent.createChooser(intent, "Share $title"))
|
||||
}
|
||||
|
||||
@SuppressLint("StringFormatMatches")
|
||||
fun saveImage(image: Bitmap, path: String, imageFileName: String): File? {
|
||||
val imageFile = File(path, "$imageFileName.png")
|
||||
@@ -1467,6 +1467,7 @@ fun buildMarkwon(
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
@@ -1495,9 +1496,9 @@ fun buildMarkwon(
|
||||
}
|
||||
|
||||
|
||||
|
||||
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)
|
||||
return matchResult?.groupValues?.getOrNull(1) ?: ""
|
||||
}
|
||||
@@ -1524,3 +1525,13 @@ fun getLanguageName(language: String): String? {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
fun String.decodeBase64ToString(): String {
|
||||
return try {
|
||||
String(Base64.decode(this), Charsets.UTF_8)
|
||||
} catch (e: Exception) {
|
||||
Logger.log(e)
|
||||
""
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,17 @@ interface DownloadAddonApiV2 {
|
||||
statCallback: (Double) -> Unit
|
||||
): 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
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.addons.AddonDownloader.Companion.hasUpdate
|
||||
import ani.dantotsu.addons.AddonInstallReceiver
|
||||
import ani.dantotsu.addons.AddonListener
|
||||
import ani.dantotsu.addons.AddonLoader
|
||||
import ani.dantotsu.addons.AddonManager
|
||||
import ani.dantotsu.addons.LoadResult
|
||||
import ani.dantotsu.addons.AddonInstallReceiver
|
||||
import ani.dantotsu.media.AddonType
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
|
||||
@@ -41,7 +41,8 @@ class TorrentServerService : Service() {
|
||||
flags: Int,
|
||||
startId: Int,
|
||||
): Int {
|
||||
extension = Injekt.get<TorrentAddonManager>().extension?.extension ?: return START_NOT_STICKY
|
||||
extension =
|
||||
Injekt.get<TorrentAddonManager>().extension?.extension ?: return START_NOT_STICKY
|
||||
intent?.let {
|
||||
if (it.action != null) {
|
||||
when (it.action) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.database.StandaloneDatabaseProvider
|
||||
import ani.dantotsu.addons.download.DownloadAddonManager
|
||||
import ani.dantotsu.addons.torrent.TorrentAddonManager
|
||||
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||
import ani.dantotsu.download.DownloadsManager
|
||||
import ani.dantotsu.media.manga.MangaCache
|
||||
import ani.dantotsu.parsers.novel.NovelExtensionManager
|
||||
|
||||
@@ -145,7 +145,10 @@ class AnilistMutations {
|
||||
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 = """
|
||||
mutation (${"$"}animeListOptions: MediaListOptionsInput, ${"$"}mangaListOptions: MediaListOptionsInput) {
|
||||
UpdateUser(animeListOptions: ${"$"}animeListOptions, mangaListOptions: ${"$"}mangaListOptions) {
|
||||
@@ -291,7 +294,8 @@ class AnilistMutations {
|
||||
isFollower
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun toggleLike(id: Int, type: String): ToggleLike? {
|
||||
@@ -302,7 +306,8 @@ class AnilistMutations {
|
||||
__typename
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun postActivity(text: String, edit: Int? = null): String {
|
||||
@@ -316,10 +321,16 @@ class AnilistMutations {
|
||||
""".trimIndent()
|
||||
val result = executeQuery<JsonObject>(query)
|
||||
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 query = """
|
||||
mutation {
|
||||
@@ -335,7 +346,8 @@ class AnilistMutations {
|
||||
""".trimIndent()
|
||||
val result = executeQuery<JsonObject>(query)
|
||||
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 {
|
||||
@@ -353,7 +365,8 @@ class AnilistMutations {
|
||||
""".trimIndent()
|
||||
val result = executeQuery<JsonObject>(query)
|
||||
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 {
|
||||
@@ -373,7 +386,8 @@ class AnilistMutations {
|
||||
""".trimIndent()
|
||||
val result = executeQuery<JsonObject>(query)
|
||||
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 {
|
||||
|
||||
@@ -359,19 +359,24 @@ class AnilistSearch : ViewModel() {
|
||||
var searched = false
|
||||
var notSet = true
|
||||
lateinit var aniMangaSearchResults: AniMangaSearchResults
|
||||
private val aniMangaResult: MutableLiveData<AniMangaSearchResults?> = MutableLiveData<AniMangaSearchResults?>(null)
|
||||
private val aniMangaResult: MutableLiveData<AniMangaSearchResults?> =
|
||||
MutableLiveData<AniMangaSearchResults?>(null)
|
||||
|
||||
lateinit var characterSearchResults: CharacterSearchResults
|
||||
private val characterResult: MutableLiveData<CharacterSearchResults?> = MutableLiveData<CharacterSearchResults?>(null)
|
||||
private val characterResult: MutableLiveData<CharacterSearchResults?> =
|
||||
MutableLiveData<CharacterSearchResults?>(null)
|
||||
|
||||
lateinit var studioSearchResults: StudioSearchResults
|
||||
private val studioResult: MutableLiveData<StudioSearchResults?> = MutableLiveData<StudioSearchResults?>(null)
|
||||
private val studioResult: MutableLiveData<StudioSearchResults?> =
|
||||
MutableLiveData<StudioSearchResults?>(null)
|
||||
|
||||
lateinit var staffSearchResults: StaffSearchResults
|
||||
private val staffResult: MutableLiveData<StaffSearchResults?> = MutableLiveData<StaffSearchResults?>(null)
|
||||
private val staffResult: MutableLiveData<StaffSearchResults?> =
|
||||
MutableLiveData<StaffSearchResults?>(null)
|
||||
|
||||
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?> {
|
||||
return when (type) {
|
||||
@@ -517,12 +522,13 @@ class AnilistSearch : ViewModel() {
|
||||
)
|
||||
)
|
||||
|
||||
private suspend fun loadNextCharacterPage(r: CharacterSearchResults) = characterResult.postValue(
|
||||
Anilist.query.searchCharacters(
|
||||
r.page + 1,
|
||||
r.search,
|
||||
private suspend fun loadNextCharacterPage(r: CharacterSearchResults) =
|
||||
characterResult.postValue(
|
||||
Anilist.query.searchCharacters(
|
||||
r.page + 1,
|
||||
r.search,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private suspend fun loadNextStudiosPage(r: StudioSearchResults) = studioResult.postValue(
|
||||
Anilist.query.searchStudios(
|
||||
|
||||
@@ -34,7 +34,8 @@ fun characterInformation(includeMediaInfo: Boolean) = """
|
||||
month
|
||||
day
|
||||
}
|
||||
${if (includeMediaInfo) """
|
||||
${
|
||||
if (includeMediaInfo) """
|
||||
media(page: 0,sort:[POPULARITY_DESC,SCORE_DESC]) {
|
||||
$standardPageInformation
|
||||
edges {
|
||||
@@ -79,7 +80,8 @@ fun characterInformation(includeMediaInfo: Boolean) = """
|
||||
}
|
||||
}
|
||||
}
|
||||
}""".prepare() else ""}
|
||||
}""".prepare() else ""
|
||||
}
|
||||
""".prepare()
|
||||
|
||||
fun studioInformation(page: Int, perPage: Int) = """
|
||||
|
||||
@@ -14,6 +14,7 @@ data class FeedResponse(
|
||||
val page: ActivityPage
|
||||
) : java.io.Serializable
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ActivityPage(
|
||||
@SerialName("activities")
|
||||
|
||||
@@ -189,7 +189,7 @@ data class MediaTitle(
|
||||
|
||||
// The currently authenticated users preferred title language. Default romaji for non-authenticated
|
||||
@SerialName("userPreferred") var userPreferred: String,
|
||||
): java.io.Serializable
|
||||
) : java.io.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class MediaType {
|
||||
@@ -239,6 +239,7 @@ data class AiringSchedule(
|
||||
// The associate media of the airing episode
|
||||
@SerialName("media") var media: Media?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MediaStreamingEpisode(
|
||||
// The title of the episode
|
||||
@@ -252,7 +253,8 @@ data class MediaStreamingEpisode(
|
||||
|
||||
// The site location of the streaming episode
|
||||
@SerialName("site") var site: String?,
|
||||
)
|
||||
) : java.io.Serializable
|
||||
|
||||
@Serializable
|
||||
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.
|
||||
|
||||
@@ -69,7 +69,7 @@ data class User(
|
||||
// The user's previously used names.
|
||||
// @SerialName("previousNames") var previousNames: List<UserPreviousName>?,
|
||||
|
||||
): java.io.Serializable
|
||||
) : java.io.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UserOptions(
|
||||
@@ -123,8 +123,10 @@ data class UserStatisticTypes(
|
||||
enum class UserTitleLanguage {
|
||||
@SerialName("ENGLISH")
|
||||
ENGLISH,
|
||||
|
||||
@SerialName("ROMAJI")
|
||||
ROMAJI,
|
||||
|
||||
@SerialName("NATIVE")
|
||||
NATIVE
|
||||
}
|
||||
@@ -133,8 +135,10 @@ enum class UserTitleLanguage {
|
||||
enum class UserStaffNameLanguage {
|
||||
@SerialName("ROMAJI_WESTERN")
|
||||
ROMAJI_WESTERN,
|
||||
|
||||
@SerialName("ROMAJI")
|
||||
ROMAJI,
|
||||
|
||||
@SerialName("NATIVE")
|
||||
NATIVE
|
||||
}
|
||||
@@ -143,12 +147,16 @@ enum class UserStaffNameLanguage {
|
||||
enum class ScoreFormat {
|
||||
@SerialName("POINT_100")
|
||||
POINT_100,
|
||||
|
||||
@SerialName("POINT_10_DECIMAL")
|
||||
POINT_10_DECIMAL,
|
||||
|
||||
@SerialName("POINT_10")
|
||||
POINT_10,
|
||||
|
||||
@SerialName("POINT_5")
|
||||
POINT_5,
|
||||
|
||||
@SerialName("POINT_3")
|
||||
POINT_3,
|
||||
}
|
||||
|
||||
@@ -372,6 +372,7 @@ object CommentsAPI {
|
||||
}
|
||||
errorMessage("Failed to login after multiple attempts")
|
||||
}
|
||||
|
||||
private fun errorMessage(reason: String) {
|
||||
if (commentsEnabled) Logger.log(reason)
|
||||
if (isOnline && commentsEnabled) snackString(reason)
|
||||
@@ -410,7 +411,7 @@ object CommentsAPI {
|
||||
return map
|
||||
}
|
||||
|
||||
private fun requestBuilder(client: OkHttpClient = Injekt.get<NetworkHelper>().client): Requests {
|
||||
fun requestBuilder(client: OkHttpClient = Injekt.get<NetworkHelper>().client): Requests {
|
||||
return Requests(
|
||||
client,
|
||||
headerBuilder()
|
||||
|
||||
@@ -47,9 +47,9 @@ class Login : AppCompatActivity() {
|
||||
view.evaluateJavascript(
|
||||
"""
|
||||
(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();
|
||||
return wreq;
|
||||
})()
|
||||
const m = []; webpackChunkdiscord_app.push([[""], {}, e => {for (let c in e.c)m.push(e.c[c])}]);
|
||||
return m.find(n => n?.exports?.default?.getToken !== void 0)?.exports?.default?.getToken();
|
||||
})()
|
||||
""".trimIndent()
|
||||
) { result ->
|
||||
login(result.trim('"'))
|
||||
|
||||
@@ -8,8 +8,8 @@ import ani.dantotsu.settings.saving.PrefName
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.OkHttpClient
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import java.util.concurrent.TimeUnit.SECONDS
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
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 buttons: MutableList<Link> = mutableListOf()
|
||||
)
|
||||
|
||||
suspend fun createPresence(data: RPCData): String {
|
||||
val json = Json {
|
||||
encodeDefaults = true
|
||||
|
||||
@@ -30,7 +30,6 @@ import ani.dantotsu.bottomBar
|
||||
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||
import ani.dantotsu.currActivity
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.download.DownloadCompat
|
||||
import ani.dantotsu.download.DownloadCompat.Companion.loadMediaCompat
|
||||
import ani.dantotsu.download.DownloadCompat.Companion.loadOfflineAnimeModelCompat
|
||||
import ani.dantotsu.download.DownloadedType
|
||||
@@ -209,7 +208,9 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||
setMessage("Are you sure you want to delete ${item.title}?")
|
||||
setPosButton(R.string.yes) {
|
||||
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()) {
|
||||
snackString("No media found") // if this happens, terrible things have happened
|
||||
}
|
||||
@@ -287,10 +288,12 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||
}
|
||||
downloadsJob = Job()
|
||||
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>()
|
||||
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 offlineAnimeModel = loadOfflineAnimeModel(download)
|
||||
if (offlineAnimeModel.title == "unknown") offlineAnimeModel.title = title
|
||||
|
||||
@@ -232,12 +232,18 @@ class MangaDownloaderService : Service() {
|
||||
image.page,
|
||||
image.source
|
||||
)
|
||||
if (bitmap == null) {
|
||||
snackString("${task.chapter} - Retrying to download page ${index.ofLength(3)}, attempt ${retryCount + 1}.")
|
||||
}
|
||||
retryCount++
|
||||
}
|
||||
|
||||
if (bitmap != null) {
|
||||
saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap)
|
||||
if (bitmap == null) {
|
||||
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++
|
||||
|
||||
builder.setProgress(task.imageData.size, farthest, false)
|
||||
|
||||
@@ -177,6 +177,7 @@ object Helper {
|
||||
downloadManager
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("exoplayer download manager is no longer used")
|
||||
@OptIn(UnstableApi::class)
|
||||
fun getSimpleCache(context: Context): SimpleCache {
|
||||
@@ -189,6 +190,7 @@ object Helper {
|
||||
simpleCache!!
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@Deprecated("exoplayer download manager is no longer used")
|
||||
private fun getDownloadDirectory(context: Context): File {
|
||||
@@ -200,12 +202,16 @@ object Helper {
|
||||
}
|
||||
return downloadDirectory!!
|
||||
}
|
||||
|
||||
@Deprecated("exoplayer download manager is no longer used")
|
||||
private var download: DownloadManager? = null
|
||||
|
||||
@Deprecated("exoplayer download manager is no longer used")
|
||||
private const val DOWNLOAD_CONTENT_DIRECTORY = "Anime_Downloads"
|
||||
|
||||
@Deprecated("exoplayer download manager is no longer used")
|
||||
private var simpleCache: SimpleCache? = null
|
||||
|
||||
@Deprecated("exoplayer download manager is no longer used")
|
||||
private var downloadDirectory: File? = null
|
||||
}
|
||||
@@ -22,9 +22,9 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.Refresh
|
||||
import ani.dantotsu.bottomBar
|
||||
import ani.dantotsu.connections.anilist.AniMangaSearchResults
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.AnilistAnimeViewModel
|
||||
import ani.dantotsu.connections.anilist.AniMangaSearchResults
|
||||
import ani.dantotsu.connections.anilist.getUserId
|
||||
import ani.dantotsu.databinding.FragmentAnimeBinding
|
||||
import ani.dantotsu.media.MediaAdaptor
|
||||
@@ -277,8 +277,9 @@ class AnimeFragment : Fragment() {
|
||||
running = true
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
Anilist.userid = PrefManager.getNullableVal<String>(PrefName.AnilistUserId, null)
|
||||
?.toIntOrNull()
|
||||
Anilist.userid =
|
||||
PrefManager.getNullableVal<String>(PrefName.AnilistUserId, null)
|
||||
?.toIntOrNull()
|
||||
if (Anilist.userid == null) {
|
||||
getUserId(requireContext()) {
|
||||
load()
|
||||
|
||||
@@ -20,7 +20,6 @@ import androidx.viewpager2.widget.ViewPager2
|
||||
import ani.dantotsu.MediaPageTransformer
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType.Companion.toAnilistString
|
||||
import ani.dantotsu.databinding.ItemAnimePageBinding
|
||||
import ani.dantotsu.databinding.LayoutTrendingBinding
|
||||
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
|
||||
recyclerView.adapter = adaptor
|
||||
recyclerView.layoutManager =
|
||||
|
||||
@@ -469,7 +469,9 @@ class HomeFragment : Fragment() {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
// 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) {
|
||||
withContext(Dispatchers.Main) {
|
||||
getUserId(requireContext()) {
|
||||
|
||||
@@ -102,7 +102,7 @@ class LoginFragment : Fragment() {
|
||||
requireActivity().customAlertDialog().apply {
|
||||
setTitle("Enter Password")
|
||||
setCustomView(dialogView.root)
|
||||
setPosButton(R.string.ok){
|
||||
setPosButton(R.string.ok) {
|
||||
val editText = dialogView.userAgentTextBox
|
||||
if (editText.text?.isNotBlank() == true) {
|
||||
editText.text?.toString()?.trim()?.toCharArray(password)
|
||||
|
||||
@@ -20,9 +20,9 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.Refresh
|
||||
import ani.dantotsu.bottomBar
|
||||
import ani.dantotsu.connections.anilist.AniMangaSearchResults
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.AnilistMangaViewModel
|
||||
import ani.dantotsu.connections.anilist.AniMangaSearchResults
|
||||
import ani.dantotsu.connections.anilist.getUserId
|
||||
import ani.dantotsu.databinding.FragmentMangaBinding
|
||||
import ani.dantotsu.media.MediaAdaptor
|
||||
@@ -169,7 +169,10 @@ class MangaFragment : Fragment() {
|
||||
}
|
||||
model.getPopularManga().observe(viewLifecycleOwner) {
|
||||
if (it != null) {
|
||||
mangaPageAdapter.updateTrendingManga(MediaAdaptor(0, it, requireActivity()), it)
|
||||
mangaPageAdapter.updateTrendingManga(
|
||||
MediaAdaptor(0, it, requireActivity()),
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
model.getPopularManhwa().observe(viewLifecycleOwner) {
|
||||
@@ -262,8 +265,9 @@ class MangaFragment : Fragment() {
|
||||
running = true
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
Anilist.userid = PrefManager.getNullableVal<String>(PrefName.AnilistUserId, null)
|
||||
?.toIntOrNull()
|
||||
Anilist.userid =
|
||||
PrefManager.getNullableVal<String>(PrefName.AnilistUserId, null)
|
||||
?.toIntOrNull()
|
||||
if (Anilist.userid == null) {
|
||||
getUserId(requireContext()) {
|
||||
load()
|
||||
|
||||
@@ -266,10 +266,10 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
|
||||
adaptor: MediaAdaptor,
|
||||
recyclerView: RecyclerView,
|
||||
progress: View,
|
||||
title: View ,
|
||||
more: View ,
|
||||
title: View,
|
||||
more: View,
|
||||
string: String,
|
||||
media : MutableList<Media>
|
||||
media: MutableList<Media>
|
||||
) {
|
||||
progress.visibility = View.GONE
|
||||
recyclerView.adapter = adaptor
|
||||
|
||||
@@ -38,7 +38,7 @@ class CircleView(context: Context, attrs: AttributeSet?) : View(context, attrs)
|
||||
|
||||
fun setColor(int: Int) {
|
||||
paint.color = if (int < booleanList.size && booleanList[int]) {
|
||||
Color.GRAY
|
||||
Color.GRAY
|
||||
} else {
|
||||
if (isUser) secondColor else primaryColor
|
||||
}
|
||||
@@ -58,7 +58,7 @@ class CircleView(context: Context, attrs: AttributeSet?) : View(context, attrs)
|
||||
} else {
|
||||
val effectiveAngle = totalAngle / parts
|
||||
for (i in 0 until parts) {
|
||||
val startAngle = i * (effectiveAngle + gapAngle) -90f
|
||||
val startAngle = i * (effectiveAngle + gapAngle) - 90f
|
||||
path.reset()
|
||||
path.addArc(
|
||||
centerX - radius,
|
||||
@@ -74,7 +74,7 @@ class CircleView(context: Context, attrs: AttributeSet?) : View(context, attrs)
|
||||
|
||||
}
|
||||
|
||||
fun setParts(parts: Int, list : List<Boolean> = mutableListOf(), isUser: Boolean) {
|
||||
fun setParts(parts: Int, list: List<Boolean> = mutableListOf(), isUser: Boolean) {
|
||||
this.parts = parts
|
||||
this.booleanList = list
|
||||
this.isUser = isUser
|
||||
|
||||
@@ -46,8 +46,8 @@ class StatusActivity : AppCompatActivity(), StoriesCallback {
|
||||
val key = "activities"
|
||||
val watchedActivity = PrefManager.getCustomVal<Set<Int>>(key, setOf())
|
||||
if (activity.getOrNull(position) != null) {
|
||||
val startFrom = findFirstNonMatch(watchedActivity, activity[position].activity )
|
||||
val startIndex = if ( startFrom > 0) startFrom else 0
|
||||
val startFrom = findFirstNonMatch(watchedActivity, activity[position].activity)
|
||||
val startIndex = if (startFrom > 0) startFrom else 0
|
||||
binding.stories.setStoriesList(
|
||||
activityList = activity[position].activity,
|
||||
startIndex = startIndex + 1
|
||||
@@ -58,6 +58,7 @@ class StatusActivity : AppCompatActivity(), StoriesCallback {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun findFirstNonMatch(watchedActivity: Set<Int>, activity: List<Activity>): Int {
|
||||
for (activityItem in activity) {
|
||||
if (activityItem.id !in watchedActivity) {
|
||||
@@ -86,13 +87,14 @@ class StatusActivity : AppCompatActivity(), StoriesCallback {
|
||||
binding.stories.pause()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStoriesEnd() {
|
||||
position += 1
|
||||
if (position < activity.size) {
|
||||
val key = "activities"
|
||||
val watchedActivity = PrefManager.getCustomVal<Set<Int>>(key, setOf())
|
||||
val startFrom = findFirstNonMatch(watchedActivity, activity[position].activity )
|
||||
val startIndex= if ( startFrom > 0) startFrom else 0
|
||||
val startFrom = findFirstNonMatch(watchedActivity, activity[position].activity)
|
||||
val startIndex = if (startFrom > 0) startFrom else 0
|
||||
binding.stories.startAnimation(slideOutLeft)
|
||||
binding.stories.setStoriesList(activity[position].activity, startIndex + 1)
|
||||
binding.stories.startAnimation(slideInRight)
|
||||
@@ -106,15 +108,16 @@ class StatusActivity : AppCompatActivity(), StoriesCallback {
|
||||
if (position >= 0 && activity[position].activity.isNotEmpty()) {
|
||||
val key = "activities"
|
||||
val watchedActivity = PrefManager.getCustomVal<Set<Int>>(key, setOf())
|
||||
val startFrom = findFirstNonMatch(watchedActivity, activity[position].activity )
|
||||
val startIndex = if ( startFrom > 0) startFrom else 0
|
||||
val startFrom = findFirstNonMatch(watchedActivity, activity[position].activity)
|
||||
val startIndex = if (startFrom > 0) startFrom else 0
|
||||
binding.stories.startAnimation(slideOutRight)
|
||||
binding.stories.setStoriesList(activity[position].activity,startIndex + 1)
|
||||
binding.stories.setStoriesList(activity[position].activity, startIndex + 1)
|
||||
binding.stories.startAnimation(slideInLeft)
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
var user: ArrayList<User> = arrayListOf()
|
||||
}
|
||||
|
||||
@@ -262,8 +262,6 @@ class Stories @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private fun rightPanelTouch() {
|
||||
Logger.log("rightPanelTouch: $storyIndex")
|
||||
if (storyIndex == activityList.size) {
|
||||
@@ -366,8 +364,8 @@ class Stories @JvmOverloads constructor(
|
||||
if (
|
||||
story.status?.contains("completed") == false &&
|
||||
!story.status.contains("plans") &&
|
||||
!story.status.contains("repeating")&&
|
||||
!story.status.contains("paused")&&
|
||||
!story.status.contains("repeating") &&
|
||||
!story.status.contains("paused") &&
|
||||
!story.status.contains("dropped")
|
||||
) {
|
||||
"of ${story.media?.title?.userPreferred}"
|
||||
@@ -469,6 +467,7 @@ class Stories @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var startClickTime = 0L
|
||||
private var startX = 0f
|
||||
private var startY = 0f
|
||||
@@ -478,7 +477,8 @@ class Stories @JvmOverloads constructor(
|
||||
onTouchView(view, event)
|
||||
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 screenWidth = view.width
|
||||
val leftHalf = screenWidth / 2
|
||||
@@ -492,6 +492,7 @@ class Stories @JvmOverloads constructor(
|
||||
pause()
|
||||
isLongPress = false
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val deltaX = event.x - startX
|
||||
val deltaY = event.y - startY
|
||||
@@ -499,6 +500,7 @@ class Stories @JvmOverloads constructor(
|
||||
isLongPress = true
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
val clickDuration = Calendar.getInstance().timeInMillis - startClickTime
|
||||
if (isText) {
|
||||
|
||||
@@ -46,7 +46,7 @@ class UserStatusAdapter(private val user: ArrayList<User>) :
|
||||
.putExtra("type", "activity"),
|
||||
null
|
||||
)
|
||||
}else{
|
||||
} else {
|
||||
ContextCompat.startActivity(
|
||||
itemView.context,
|
||||
Intent(
|
||||
@@ -76,10 +76,15 @@ class UserStatusAdapter(private val user: ArrayList<User>) :
|
||||
setAnimation(b.root.context, b.root)
|
||||
val user = user[position]
|
||||
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 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
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,11 @@ class CharacterAdapter(
|
||||
).toBundle()
|
||||
)
|
||||
}
|
||||
itemView.setOnLongClickListener { copyToClipboard(characterList[bindingAdapterPosition].name ?: ""); true }
|
||||
itemView.setOnLongClickListener {
|
||||
copyToClipboard(
|
||||
characterList[bindingAdapterPosition].name ?: ""
|
||||
); true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,9 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.buildMarkwon
|
||||
import ani.dantotsu.currActivity
|
||||
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) :
|
||||
RecyclerView.Adapter<CharacterDetailsAdapter.GenreViewHolder>() {
|
||||
@@ -24,7 +22,9 @@ class CharacterDetailsAdapter(private val character: Character, private val acti
|
||||
override fun onBindViewHolder(holder: GenreViewHolder, position: Int) {
|
||||
val binding = holder.binding
|
||||
val desc =
|
||||
(if (character.age != "null") "${currActivity()!!.getString(R.string.age)} ${character.age}" else "") +
|
||||
(if (character.id == 4004)
|
||||
" \n" else "") +
|
||||
(if (character.age != "null") "${currActivity()!!.getString(R.string.age)} ${character.age}" else "") +
|
||||
(if (character.dateOfBirth.toString() != "")
|
||||
"${currActivity()!!.getString(R.string.birthday)} ${character.dateOfBirth.toString()}" else "") +
|
||||
(if (character.gender != "null")
|
||||
@@ -41,8 +41,7 @@ class CharacterDetailsAdapter(private val character: Character, private val acti
|
||||
} else "") + "\n" + character.description
|
||||
|
||||
binding.characterDesc.isTextSelectable
|
||||
val markWon = Markwon.builder(activity).usePlugin(SoftBreakAddsNewLinePlugin.create())
|
||||
.usePlugin(SpoilerPlugin()).build()
|
||||
val markWon = buildMarkwon(activity)
|
||||
markWon.setMarkdown(binding.characterDesc, desc.replace("~!", "||").replace("!~", "||"))
|
||||
binding.voiceActorRecycler.adapter = AuthorAdapter(character.voiceActor ?: arrayListOf())
|
||||
binding.voiceActorRecycler.layoutManager = LinearLayoutManager(
|
||||
|
||||
@@ -9,7 +9,7 @@ import android.view.animation.Animation
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ani.dantotsu.databinding.ItemSearchHeaderBinding
|
||||
|
||||
abstract class HeaderInterface: RecyclerView.Adapter<HeaderInterface.SearchHeaderViewHolder>() {
|
||||
abstract class HeaderInterface : RecyclerView.Adapter<HeaderInterface.SearchHeaderViewHolder>() {
|
||||
private val itemViewType = 6969
|
||||
var search: Runnable? = null
|
||||
var requestFocus: Runnable? = null
|
||||
|
||||
@@ -147,7 +147,7 @@ fun Media?.deleteFromList(
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
this@deleteFromList?.let { media ->
|
||||
val _id = id ?: Anilist.query.userMediaDetails(media).userListId;
|
||||
val _id = id ?: Anilist.query.userMediaDetails(media).userListId
|
||||
_id?.let { listId ->
|
||||
try {
|
||||
Anilist.mutation.deleteList(listId)
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.GestureDetector
|
||||
@@ -12,6 +13,8 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.widget.ImageView
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -19,8 +22,10 @@ import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.bold
|
||||
import androidx.core.text.color
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.marginBottom
|
||||
import androidx.core.view.setPadding
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updateMargins
|
||||
import androidx.fragment.app.Fragment
|
||||
@@ -79,6 +84,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
var media: Media = intent.getSerialized("media") ?: mediaSingleton ?: emptyMedia()
|
||||
val id = intent.getIntExtra("mediaId", -1)
|
||||
@@ -109,6 +115,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||
// Ui init
|
||||
|
||||
initActivity(this)
|
||||
|
||||
binding.mediaViewPager.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = navBarHeight
|
||||
}
|
||||
@@ -132,10 +139,12 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||
val navBarBottomMargin = if (resources.configuration.orientation ==
|
||||
Configuration.ORIENTATION_LANDSCAPE
|
||||
) 0 else navBarHeight
|
||||
navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
rightMargin = navBarRightMargin
|
||||
bottomMargin = navBarBottomMargin
|
||||
}
|
||||
navBar.setPadding(
|
||||
navBar.paddingLeft,
|
||||
navBar.paddingTop,
|
||||
navBar.paddingRight + navBarRightMargin,
|
||||
navBar.paddingBottom + navBarBottomMargin
|
||||
)
|
||||
binding.mediaBanner.updateLayoutParams { height += statusBarHeight }
|
||||
binding.mediaBannerNoKen.updateLayoutParams { height += statusBarHeight }
|
||||
binding.mediaClose.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
|
||||
@@ -251,10 +260,12 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
|
||||
fun total() {
|
||||
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) {
|
||||
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}") } }
|
||||
append(
|
||||
if (media.anime != null) getString(R.string.episodes_out_of) else getString(
|
||||
|
||||
@@ -100,6 +100,7 @@ class MediaDetailsViewModel : ViewModel() {
|
||||
if (kitsuEpisodes.value == null) kitsuEpisodes.postValue(Kitsu.getKitsuEpisodesDetails(s))
|
||||
}
|
||||
}
|
||||
|
||||
private val anifyEpisodes: MutableLiveData<Map<String, Episode>> =
|
||||
MutableLiveData<Map<String, Episode>>(null)
|
||||
|
||||
|
||||
@@ -18,9 +18,8 @@ import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import java.util.ArrayList
|
||||
|
||||
class MediaListViewActivity: AppCompatActivity() {
|
||||
class MediaListViewActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityMediaListViewBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -52,7 +51,8 @@ class MediaListViewActivity: AppCompatActivity() {
|
||||
binding.listAppBar.setBackgroundColor(primaryColor)
|
||||
binding.listTitle.setTextColor(primaryTextColor)
|
||||
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
|
||||
val view = PrefManager.getCustomVal("mediaView", 0)
|
||||
var mediaView: View = when (view) {
|
||||
|
||||
@@ -44,7 +44,10 @@ class MediaSocialAdapter(
|
||||
profileUserName.text = user.name
|
||||
profileInfo.apply {
|
||||
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 ?: ""
|
||||
}
|
||||
visibility = View.VISIBLE
|
||||
@@ -63,10 +66,12 @@ class MediaSocialAdapter(
|
||||
profileCompactProgressContainer.visibility = View.VISIBLE
|
||||
|
||||
profileUserAvatar.setOnClickListener {
|
||||
ContextCompat.startActivity(root.context,
|
||||
ContextCompat.startActivity(
|
||||
root.context,
|
||||
Intent(root.context, ProfileActivity::class.java)
|
||||
.putExtra("userId", user.id),
|
||||
null)
|
||||
null
|
||||
)
|
||||
}
|
||||
profileUserAvatarContainer.setOnLongClickListener {
|
||||
ImageViewDialog.newInstance(
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
package ani.dantotsu.media
|
||||
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
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.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.api.Query
|
||||
import ani.dantotsu.databinding.ItemReviewsBinding
|
||||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.openImage
|
||||
import ani.dantotsu.others.ImageViewDialog
|
||||
import ani.dantotsu.profile.ProfileActivity
|
||||
import ani.dantotsu.profile.activity.ActivityItemBuilder
|
||||
import ani.dantotsu.toast
|
||||
@@ -40,7 +32,7 @@ class ReviewAdapter(
|
||||
binding.reviewUserAvatar.loadImage(review.user?.avatar?.medium)
|
||||
binding.reviewText.text = review.summary
|
||||
binding.reviewPostTime.text = ActivityItemBuilder.getDateTime(review.createdAt)
|
||||
val text = "[${review.score/ 10.0f}]"
|
||||
val text = "[${review.score / 10.0f}]"
|
||||
binding.reviewTag.text = text
|
||||
binding.root.setOnClickListener {
|
||||
ContextCompat.startActivity(
|
||||
@@ -85,6 +77,7 @@ class ReviewAdapter(
|
||||
override fun initializeViewBinding(view: View): ItemReviewsBinding {
|
||||
return ItemReviewsBinding.bind(view)
|
||||
}
|
||||
|
||||
private fun userVote(type: String) {
|
||||
when (type) {
|
||||
"NO_VOTE" -> {
|
||||
|
||||
@@ -20,7 +20,6 @@ import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.openImage
|
||||
import ani.dantotsu.others.ImageViewDialog
|
||||
import ani.dantotsu.profile.ProfileActivity
|
||||
import ani.dantotsu.profile.activity.ActivityItemBuilder
|
||||
import ani.dantotsu.statusBarHeight
|
||||
@@ -52,8 +51,9 @@ class ReviewViewActivity : AppCompatActivity() {
|
||||
binding.userAvatar.loadImage(review.user?.avatar?.medium)
|
||||
binding.userTime.text = ActivityItemBuilder.getDateTime(review.createdAt)
|
||||
binding.userContainer.setOnClickListener {
|
||||
startActivity(Intent(this, ProfileActivity::class.java)
|
||||
.putExtra("userId", review.user?.id)
|
||||
startActivity(
|
||||
Intent(this, ProfileActivity::class.java)
|
||||
.putExtra("userId", review.user?.id)
|
||||
)
|
||||
}
|
||||
binding.userAvatar.openImage(
|
||||
@@ -61,8 +61,9 @@ class ReviewViewActivity : AppCompatActivity() {
|
||||
review.user?.avatar?.medium ?: ""
|
||||
)
|
||||
binding.userAvatar.setOnClickListener {
|
||||
startActivity(Intent(this, ProfileActivity::class.java)
|
||||
.putExtra("userId", review.user?.id)
|
||||
startActivity(
|
||||
Intent(this, ProfileActivity::class.java)
|
||||
.putExtra("userId", review.user?.id)
|
||||
)
|
||||
}
|
||||
binding.profileUserBio.settings.loadWithOverviewMode = true
|
||||
|
||||
@@ -13,10 +13,10 @@ import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.ConcatAdapter
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ani.dantotsu.connections.anilist.AniMangaSearchResults
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.AnilistSearch
|
||||
import ani.dantotsu.connections.anilist.AnilistSearch.SearchType
|
||||
import ani.dantotsu.connections.anilist.AniMangaSearchResults
|
||||
import ani.dantotsu.connections.anilist.CharacterSearchResults
|
||||
import ani.dantotsu.connections.anilist.StaffSearchResults
|
||||
import ani.dantotsu.connections.anilist.StudioSearchResults
|
||||
@@ -377,7 +377,10 @@ class SearchActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
SearchType.CHARACTER -> {
|
||||
characterAdaptor.notifyItemRangeRemoved(0, model.characterSearchResults.results.size)
|
||||
characterAdaptor.notifyItemRangeRemoved(
|
||||
0,
|
||||
model.characterSearchResults.results.size
|
||||
)
|
||||
model.characterSearchResults.results.clear()
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Sear
|
||||
|
||||
searchHistoryAdapter = SearchHistoryAdapter(type) {
|
||||
binding.searchBarText.setText(it)
|
||||
binding.searchBarText.setSelection(it.length)
|
||||
}
|
||||
binding.searchHistoryList.layoutManager = LinearLayoutManager(binding.root.context)
|
||||
binding.searchHistoryList.adapter = searchHistoryAdapter
|
||||
|
||||
@@ -346,7 +346,9 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
|
||||
binding.searchGenresGrid.isChecked = false
|
||||
|
||||
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()
|
||||
chip.isChecked = selectedTags.contains(tag)
|
||||
chip.isCloseIconVisible = exTags.contains(tag)
|
||||
|
||||
@@ -55,7 +55,11 @@ class StudioAdapter(
|
||||
).toBundle()
|
||||
)
|
||||
}
|
||||
itemView.setOnLongClickListener { copyToClipboard(studioList[bindingAdapterPosition].name ?: ""); true }
|
||||
itemView.setOnLongClickListener {
|
||||
copyToClipboard(
|
||||
studioList[bindingAdapterPosition].name
|
||||
); true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package ani.dantotsu.media
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import ani.dantotsu.download.DownloadedType
|
||||
import ani.dantotsu.download.DownloadsManager
|
||||
import ani.dantotsu.parsers.SubtitleType
|
||||
@@ -21,28 +23,32 @@ class SubtitleDownloader {
|
||||
suspend fun loadSubtitleType(url: String): SubtitleType =
|
||||
withContext(Dispatchers.IO) {
|
||||
return@withContext try {
|
||||
// Initialize the NetworkHelper instance. Replace this line based on how you usually initialize it
|
||||
val networkHelper = Injekt.get<NetworkHelper>()
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.build()
|
||||
if (!url.startsWith("file")) {
|
||||
// Initialize the NetworkHelper instance. Replace this line based on how you usually initialize it
|
||||
val networkHelper = Injekt.get<NetworkHelper>()
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.build()
|
||||
|
||||
val response = networkHelper.client.newCall(request).execute()
|
||||
val response = networkHelper.client.newCall(request).execute()
|
||||
|
||||
// Check if response is successful
|
||||
if (response.isSuccessful) {
|
||||
val responseBody = response.body.string()
|
||||
// Check if response is successful
|
||||
if (response.isSuccessful) {
|
||||
val responseBody = response.body.string()
|
||||
|
||||
|
||||
val subtitleType = when {
|
||||
responseBody.contains("[Script Info]") -> SubtitleType.ASS
|
||||
responseBody.contains("WEBVTT") -> SubtitleType.VTT
|
||||
else -> SubtitleType.SRT
|
||||
val subtitleType = getType(responseBody)
|
||||
|
||||
subtitleType
|
||||
} else {
|
||||
SubtitleType.UNKNOWN
|
||||
}
|
||||
|
||||
subtitleType
|
||||
} else {
|
||||
SubtitleType.UNKNOWN
|
||||
val uri = url.toUri()
|
||||
val file = uri.toFile()
|
||||
val fileBody = file.readText()
|
||||
val subtitleType = getType(fileBody)
|
||||
subtitleType
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Logger.log(e)
|
||||
@@ -50,6 +56,15 @@ class SubtitleDownloader {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@Deprecated("handled externally")
|
||||
suspend fun downloadSubtitle(
|
||||
|
||||
@@ -31,6 +31,7 @@ class SupportingSearchAdapter(private val activity: SearchActivity, private val
|
||||
|
||||
searchHistoryAdapter = SearchHistoryAdapter(type) {
|
||||
binding.searchBarText.setText(it)
|
||||
binding.searchBarText.setSelection(it.length)
|
||||
}
|
||||
binding.searchHistoryList.layoutManager = LinearLayoutManager(binding.root.context)
|
||||
binding.searchHistoryList.adapter = searchHistoryAdapter
|
||||
|
||||
@@ -20,8 +20,8 @@ import ani.dantotsu.R
|
||||
import ani.dantotsu.currActivity
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.databinding.DialogLayoutBinding
|
||||
import ani.dantotsu.databinding.ItemMediaSourceBinding
|
||||
import ani.dantotsu.databinding.ItemChipBinding
|
||||
import ani.dantotsu.databinding.ItemMediaSourceBinding
|
||||
import ani.dantotsu.displayTimer
|
||||
import ani.dantotsu.isOnline
|
||||
import ani.dantotsu.loadImage
|
||||
@@ -61,7 +61,8 @@ class AnimeWatchAdapter(
|
||||
private var _binding: ItemMediaSourceBinding? = null
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -285,7 +286,7 @@ class AnimeWatchAdapter(
|
||||
fragment.requireContext().customAlertDialog().apply {
|
||||
setTitle(" Delete Progress for all episodes of ${media.nameRomaji}")
|
||||
setMessage("This will delete all the locally stored progress for all episodes")
|
||||
setPosButton(R.string.ok){
|
||||
setPosButton(R.string.ok) {
|
||||
val prefix = "${media.id}_"
|
||||
val regex = Regex("^${prefix}\\d+$")
|
||||
|
||||
@@ -300,7 +301,7 @@ class AnimeWatchAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
resetProgressDef.text = getString(currContext()!!,R.string.clear_stored_episode)
|
||||
resetProgressDef.text = getString(currContext()!!, R.string.clear_stored_episode)
|
||||
|
||||
// Hidden
|
||||
mangaScanlatorContainer.visibility = View.GONE
|
||||
@@ -327,7 +328,7 @@ class AnimeWatchAdapter(
|
||||
fragment.requireContext().customAlertDialog().apply {
|
||||
setTitle(" Delete Progress for all episodes of ${media.nameRomaji}")
|
||||
setMessage("This will delete all the locally stored progress for all episodes")
|
||||
setPosButton(R.string.ok){
|
||||
setPosButton(R.string.ok) {
|
||||
val prefix = "${media.id}_"
|
||||
val regex = Regex("^${prefix}\\d+$")
|
||||
|
||||
@@ -478,7 +479,8 @@ class AnimeWatchAdapter(
|
||||
binding.sourceProgressBar.visibility = View.GONE
|
||||
|
||||
val sourceFound = media.anime.episodes!!.isNotEmpty()
|
||||
val isDownloadedSource = watchSources[media.selected!!.sourceIndex] is OfflineAnimeParser
|
||||
val isDownloadedSource =
|
||||
watchSources[media.selected!!.sourceIndex] is OfflineAnimeParser
|
||||
|
||||
if (isDownloadedSource) {
|
||||
binding.sourceNotFound.text = if (sourceFound) {
|
||||
@@ -487,7 +489,8 @@ class AnimeWatchAdapter(
|
||||
currActivity()!!.getString(R.string.download_not_found)
|
||||
}
|
||||
} 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
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package ani.dantotsu.media.anime
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -28,10 +27,8 @@ import androidx.recyclerview.widget.ConcatAdapter
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import ani.dantotsu.FileUrl
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.addons.download.DownloadAddonManager
|
||||
import ani.dantotsu.connections.anilist.api.MediaStreamingEpisode
|
||||
import ani.dantotsu.databinding.FragmentMediaSourceBinding
|
||||
import ani.dantotsu.download.DownloadedType
|
||||
import ani.dantotsu.download.DownloadsManager
|
||||
@@ -49,7 +46,6 @@ import ani.dantotsu.media.MediaType
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.notifications.subscription.SubscriptionHelper
|
||||
import ani.dantotsu.notifications.subscription.SubscriptionHelper.Companion.saveSubscription
|
||||
import ani.dantotsu.others.Anify
|
||||
import ani.dantotsu.others.LanguageMapper
|
||||
import ani.dantotsu.parsers.AnimeParser
|
||||
import ani.dantotsu.parsers.AnimeSources
|
||||
@@ -236,13 +232,16 @@ class AnimeWatchFragment : Fragment() {
|
||||
episodes.forEach { (i, episode) ->
|
||||
if (media.anime?.anifyEpisodes != null) {
|
||||
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 ?: ""
|
||||
).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
|
||||
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!!.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 ?: ""
|
||||
).isBlank()
|
||||
) media.anime!!.kitsuEpisodes!![i]?.title ?: episode.title else episode.title
|
||||
?: media.anime!!.kitsuEpisodes!![i]?.title ?: episode.title
|
||||
episode.thumb = media.anime!!.kitsuEpisodes!![i]?.thumb ?: episode.thumb
|
||||
) media.anime!!.kitsuEpisodes!![i]?.title
|
||||
?: episode.title else episode.title
|
||||
?: media.anime!!.kitsuEpisodes!![i]?.title ?: episode.title
|
||||
episode.thumb =
|
||||
media.anime!!.kitsuEpisodes!![i]?.thumb ?: episode.thumb
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,29 +402,30 @@ class AnimeWatchFragment : Fragment() {
|
||||
requireContext()
|
||||
.customAlertDialog()
|
||||
.apply {
|
||||
setTitle("Select a Source")
|
||||
singleChoiceItems(names) { which ->
|
||||
selectedSetting = allSettings[which]
|
||||
itemSelected = true
|
||||
requireActivity().runOnUiThread {
|
||||
val fragment = AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) {
|
||||
changeUIVisibility(true)
|
||||
loadEpisodes(media.selected!!.sourceIndex, true)
|
||||
setTitle("Select a Source")
|
||||
singleChoiceItems(names) { which ->
|
||||
selectedSetting = allSettings[which]
|
||||
itemSelected = true
|
||||
requireActivity().runOnUiThread {
|
||||
val fragment =
|
||||
AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) {
|
||||
changeUIVisibility(true)
|
||||
loadEpisodes(media.selected!!.sourceIndex, true)
|
||||
}
|
||||
parentFragmentManager.beginTransaction()
|
||||
.setCustomAnimations(R.anim.slide_up, R.anim.slide_down)
|
||||
.replace(R.id.fragmentExtensionsContainer, fragment)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
parentFragmentManager.beginTransaction()
|
||||
.setCustomAnimations(R.anim.slide_up, R.anim.slide_down)
|
||||
.replace(R.id.fragmentExtensionsContainer, fragment)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
onDismiss {
|
||||
if (!itemSelected) {
|
||||
changeUIVisibility(true)
|
||||
onDismiss {
|
||||
if (!itemSelected) {
|
||||
changeUIVisibility(true)
|
||||
}
|
||||
}
|
||||
show()
|
||||
}
|
||||
show()
|
||||
}
|
||||
} else {
|
||||
// If there's only one setting, proceed with the fragment transaction
|
||||
requireActivity().runOnUiThread {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package ani.dantotsu.media.anime
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -13,7 +12,6 @@ import androidx.media3.common.util.UnstableApi
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.updateProgress
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.databinding.ItemEpisodeCompactBinding
|
||||
import ani.dantotsu.databinding.ItemEpisodeGridBinding
|
||||
import ani.dantotsu.databinding.ItemEpisodeListBinding
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@ package ani.dantotsu.media.anime
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ComponentName
|
||||
import android.content.DialogInterface
|
||||
@@ -446,7 +445,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
||||
var subtitleToDownload: Subtitle? = null
|
||||
requireActivity().customAlertDialog().apply {
|
||||
setTitle(R.string.download_subtitle)
|
||||
singleChoiceItems(subtitleNames.toTypedArray()) {which ->
|
||||
singleChoiceItems(subtitleNames.toTypedArray()) { which ->
|
||||
subtitleToDownload = subtitles[which]
|
||||
}
|
||||
setPosButton(R.string.download) {
|
||||
@@ -483,7 +482,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
||||
)
|
||||
} else {
|
||||
val downloadAddonManager: DownloadAddonManager = Injekt.get()
|
||||
if (!downloadAddonManager.isAvailable()){
|
||||
if (!downloadAddonManager.isAvailable()) {
|
||||
val context = currContext() ?: requireContext()
|
||||
context.customAlertDialog().apply {
|
||||
setTitle(R.string.download_addon_not_installed)
|
||||
@@ -564,17 +563,21 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
||||
snackString(R.string.no_video_selected)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkAudioTracks() {
|
||||
val audioTracks = extractor.audioTracks.map { it.lang }
|
||||
if (audioTracks.isNotEmpty()) {
|
||||
val audioNamesArray = audioTracks.toTypedArray()
|
||||
val checkedItems = BooleanArray(audioNamesArray.size) { false }
|
||||
|
||||
currContext.customAlertDialog().apply{ // ToTest
|
||||
currContext.customAlertDialog().apply { // ToTest
|
||||
setTitle(R.string.download_audio_tracks)
|
||||
multiChoiceItems(audioNamesArray, checkedItems) {
|
||||
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) {
|
||||
selectedAudioTracks.add(audioPair)
|
||||
} else {
|
||||
@@ -606,7 +609,8 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
|
||||
setTitle(R.string.download_subtitle)
|
||||
multiChoiceItems(subtitleNamesArray, checkedItems) {
|
||||
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) {
|
||||
selectedSubtitles.add(subtitlePair)
|
||||
} else {
|
||||
|
||||
@@ -63,8 +63,12 @@ class TrackGroupDialogFragment(
|
||||
override fun onBindViewHolder(holder: StreamViewHolder, position: Int) {
|
||||
val binding = holder.binding
|
||||
trackGroups[position].let { trackGroup ->
|
||||
if (overrideTrackNames?.getOrNull(position - (trackGroups.size - (overrideTrackNames?.size?:0))) != null) {
|
||||
val pair = overrideTrackNames!![position - (trackGroups.size - overrideTrackNames!!.size)]
|
||||
if (overrideTrackNames?.getOrNull(
|
||||
position - (trackGroups.size - (overrideTrackNames?.size ?: 0))
|
||||
) != null
|
||||
) {
|
||||
val pair =
|
||||
overrideTrackNames!![position - (trackGroups.size - overrideTrackNames!!.size)]
|
||||
binding.subtitleTitle.text =
|
||||
"[${pair.second}] ${pair.first}"
|
||||
} else when (val language = trackGroup.getTrackFormat(0).language?.lowercase()) {
|
||||
|
||||
@@ -15,7 +15,6 @@ import ani.dantotsu.databinding.ItemCommentsBinding
|
||||
import ani.dantotsu.getAppString
|
||||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.openImage
|
||||
import ani.dantotsu.others.ImageViewDialog
|
||||
import ani.dantotsu.profile.ProfileActivity
|
||||
import ani.dantotsu.setAnimation
|
||||
import ani.dantotsu.snackString
|
||||
|
||||
@@ -2,7 +2,6 @@ package ani.dantotsu.media.comments
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context.INPUT_METHOD_SERVICE
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
@@ -12,7 +11,6 @@ import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.animation.doOnEnd
|
||||
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.PrefName
|
||||
import ani.dantotsu.snackString
|
||||
import ani.dantotsu.toast
|
||||
import ani.dantotsu.util.Logger
|
||||
import ani.dantotsu.util.customAlertDialog
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
@@ -587,19 +584,19 @@ class CommentsFragment : Fragment() {
|
||||
private fun showCommentRulesDialog() {
|
||||
activity.customAlertDialog().apply {
|
||||
setTitle("Commenting Rules")
|
||||
.setMessage(
|
||||
"🚨 BREAK ANY RULE = YOU'RE GONE\n\n" +
|
||||
"1. NO RACISM, DISCRIMINATION, OR HATE SPEECH\n" +
|
||||
"2. NO SPAMMING OR SELF-PROMOTION\n" +
|
||||
"3. ABSOLUTELY NO NSFW CONTENT\n" +
|
||||
"4. ENGLISH ONLY – NO EXCEPTIONS\n" +
|
||||
"5. NO IMPERSONATION, HARASSMENT, OR ABUSE\n" +
|
||||
"6. NO ILLEGAL CONTENT OR EXTREME DISRESPECT TOWARDS ANY FANDOM\n" +
|
||||
"7. DO NOT REQUEST OR SHARE REPOSITORIES/EXTENSIONS\n" +
|
||||
"8. SPOILERS ALLOWED ONLY WITH SPOILER TAGS AND A WARNING\n" +
|
||||
"9. NO SEXUALIZING OR INAPPROPRIATE COMMENTS ABOUT MINOR CHARACTERS\n" +
|
||||
"10. IF IT'S WRONG, DON'T POST IT!\n\n"
|
||||
)
|
||||
.setMessage(
|
||||
"🚨 BREAK ANY RULE = YOU'RE GONE\n\n" +
|
||||
"1. NO RACISM, DISCRIMINATION, OR HATE SPEECH\n" +
|
||||
"2. NO SPAMMING OR SELF-PROMOTION\n" +
|
||||
"3. ABSOLUTELY NO NSFW CONTENT\n" +
|
||||
"4. ENGLISH ONLY – NO EXCEPTIONS\n" +
|
||||
"5. NO IMPERSONATION, HARASSMENT, OR ABUSE\n" +
|
||||
"6. NO ILLEGAL CONTENT OR EXTREME DISRESPECT TOWARDS ANY FANDOM\n" +
|
||||
"7. DO NOT REQUEST OR SHARE REPOSITORIES/EXTENSIONS\n" +
|
||||
"8. SPOILERS ALLOWED ONLY WITH SPOILER TAGS AND A WARNING\n" +
|
||||
"9. NO SEXUALIZING OR INAPPROPRIATE COMMENTS ABOUT MINOR CHARACTERS\n" +
|
||||
"10. IF IT'S WRONG, DON'T POST IT!\n\n"
|
||||
)
|
||||
setPosButton("I Understand") {
|
||||
PrefManager.setVal(PrefName.FirstComment, false)
|
||||
processComment()
|
||||
|
||||
@@ -16,6 +16,7 @@ import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.FileOutputStream
|
||||
|
||||
data class ImageData(
|
||||
@@ -76,7 +77,7 @@ fun saveImage(
|
||||
uri?.let {
|
||||
contentResolver.openOutputStream(it)?.use { os ->
|
||||
bitmap.compress(format, quality, os)
|
||||
}
|
||||
} ?: throw FileNotFoundException("Failed to open output stream for URI: $uri")
|
||||
}
|
||||
} else {
|
||||
val directory =
|
||||
@@ -86,12 +87,20 @@ fun saveImage(
|
||||
}
|
||||
|
||||
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 ->
|
||||
bitmap.compress(format, quality, outputStream)
|
||||
}
|
||||
}
|
||||
} catch (e: FileNotFoundException) {
|
||||
println("File not found: ${e.message}")
|
||||
} catch (e: Exception) {
|
||||
// Handle exception here
|
||||
println("Exception while saving image: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package ani.dantotsu.media.manga
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
@@ -7,9 +7,11 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.CheckBox
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.NumberPicker
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.ContextCompat.getString
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
@@ -22,8 +24,8 @@ import ani.dantotsu.currActivity
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.databinding.CustomDialogLayoutBinding
|
||||
import ani.dantotsu.databinding.DialogLayoutBinding
|
||||
import ani.dantotsu.databinding.ItemMediaSourceBinding
|
||||
import ani.dantotsu.databinding.ItemChipBinding
|
||||
import ani.dantotsu.databinding.ItemMediaSourceBinding
|
||||
import ani.dantotsu.isOnline
|
||||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.media.Media
|
||||
@@ -75,7 +77,8 @@ class MangaReadAdapter(
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -99,11 +102,11 @@ class MangaReadAdapter(
|
||||
}
|
||||
val offline = !isOnline(binding.root.context) || PrefManager.getVal(PrefName.OfflineMode)
|
||||
//for removing saved progress
|
||||
binding.sourceTitle.setOnLongClickListener{
|
||||
binding.sourceTitle.setOnLongClickListener {
|
||||
fragment.requireContext().customAlertDialog().apply {
|
||||
setTitle(" Delete Progress for all chapters of ${media.nameRomaji}")
|
||||
setMessage("This will delete all the locally stored progress for chapters")
|
||||
setPosButton(R.string.ok){
|
||||
setPosButton(R.string.ok) {
|
||||
clearCustomValsForMedia("${media.id}", "_Chapter")
|
||||
clearCustomValsForMedia("${media.id}", "_Vol")
|
||||
snackString("Deleted the progress of Chapters for ${media.nameRomaji}")
|
||||
@@ -264,19 +267,22 @@ class MangaReadAdapter(
|
||||
}
|
||||
|
||||
// Multi download
|
||||
downloadNo.text = "0"
|
||||
//downloadNo.text = "0"
|
||||
mediaDownloadTop.setOnClickListener {
|
||||
// Alert dialog asking for the number of chapters to download
|
||||
fragment.requireContext().customAlertDialog().apply {
|
||||
setTitle("Multi Chapter Downloader")
|
||||
setMessage("Enter the number of chapters to download")
|
||||
val input = NumberPicker(currContext())
|
||||
input.minValue = 1
|
||||
input.maxValue = 20
|
||||
input.value = 1
|
||||
val input = View.inflate(currContext(), R.layout.dialog_layout, null)
|
||||
val editText = input.findViewById<EditText>(R.id.downloadNo)
|
||||
setCustomView(input)
|
||||
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)
|
||||
show()
|
||||
@@ -286,7 +292,7 @@ class MangaReadAdapter(
|
||||
fragment.requireContext().customAlertDialog().apply {
|
||||
setTitle(" Delete Progress for all chapters of ${media.nameRomaji}")
|
||||
setMessage("This will delete all the locally stored progress for chapters")
|
||||
setPosButton(R.string.ok){
|
||||
setPosButton(R.string.ok) {
|
||||
// Usage
|
||||
clearCustomValsForMedia("${media.id}", "_Chapter")
|
||||
clearCustomValsForMedia("${media.id}", "_Vol")
|
||||
@@ -297,7 +303,7 @@ class MangaReadAdapter(
|
||||
show()
|
||||
}
|
||||
}
|
||||
resetProgressDef.text = getString(currContext()!!,R.string.clear_stored_chapter)
|
||||
resetProgressDef.text = getString(currContext()!!, R.string.clear_stored_chapter)
|
||||
|
||||
// Scanlator
|
||||
mangaScanlatorContainer.isVisible = options.count() > 1
|
||||
@@ -331,12 +337,17 @@ class MangaReadAdapter(
|
||||
val checkBox = CheckBox(currContext()).apply {
|
||||
text = option
|
||||
setOnCheckedChangeListener { _, _ ->
|
||||
tickAllButton.setImageResource(getToggleImageResource(checkboxContainer))
|
||||
tickAllButton.setImageResource(
|
||||
getToggleImageResource(
|
||||
checkboxContainer
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (media.selected!!.scanlators != null) {
|
||||
checkBox.isChecked = media.selected!!.scanlators?.contains(option) != true
|
||||
checkBox.isChecked =
|
||||
media.selected!!.scanlators?.contains(option) != true
|
||||
scanlatorSelectionListener?.onScanlatorsSelected()
|
||||
} else {
|
||||
checkBox.isChecked = true
|
||||
@@ -376,8 +387,9 @@ class MangaReadAdapter(
|
||||
setCustomView(root)
|
||||
setPosButton("OK") {
|
||||
if (run) fragment.onIconPressed(style, reversed)
|
||||
if (downloadNo.text != "0") {
|
||||
fragment.multiDownload(downloadNo.text.toString().toInt())
|
||||
val value = downloadNo.text.toString().toIntOrNull()
|
||||
if (value != null && value > 0) {
|
||||
fragment.multiDownload(value)
|
||||
}
|
||||
if (refresh) fragment.loadChapters(source, true)
|
||||
}
|
||||
@@ -488,10 +500,12 @@ class MangaReadAdapter(
|
||||
}
|
||||
}
|
||||
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 }) {
|
||||
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
|
||||
handleProgress(
|
||||
binding.itemMediaProgressCont,
|
||||
@@ -501,9 +515,11 @@ class MangaReadAdapter(
|
||||
continueEp!!.number
|
||||
)
|
||||
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) {
|
||||
continueEp = media.manga.chapters!![formattedChapters[numberPlusOne].second]
|
||||
continueEp =
|
||||
media.manga.chapters!![formattedChapters[numberPlusOne].second]
|
||||
}
|
||||
}
|
||||
binding.itemMediaImage.loadImage(media.banner ?: media.cover)
|
||||
@@ -530,7 +546,8 @@ class MangaReadAdapter(
|
||||
binding.sourceProgressBar.visibility = View.GONE
|
||||
|
||||
val sourceFound = filteredChapters.isNotEmpty()
|
||||
val isDownloadedSource = mangaReadSources[media.selected!!.sourceIndex] is OfflineMangaParser
|
||||
val isDownloadedSource =
|
||||
mangaReadSources[media.selected!!.sourceIndex] is OfflineMangaParser
|
||||
|
||||
if (isDownloadedSource) {
|
||||
binding.sourceNotFound.text = if (sourceFound) {
|
||||
@@ -539,7 +556,8 @@ class MangaReadAdapter(
|
||||
currActivity()!!.getString(R.string.download_not_found)
|
||||
}
|
||||
} 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
|
||||
|
||||
@@ -66,6 +66,7 @@ import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import uy.kohesive.injekt.Injekt
|
||||
@@ -232,25 +233,35 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||
}
|
||||
|
||||
fun multiDownload(n: Int) {
|
||||
// Get last viewed chapter
|
||||
val selected = media.userProgress
|
||||
val chapters = media.manga?.chapters?.values?.toList()
|
||||
// Filter by selected language
|
||||
val progressChapterIndex = (chapters?.indexOfFirst {
|
||||
MediaNameAdapter.findChapterNumber(it.number)?.toInt() == selected
|
||||
} ?: 0) + 1
|
||||
|
||||
if (progressChapterIndex < 0 || n < 1 || chapters == null) return
|
||||
|
||||
// Calculate the end index
|
||||
val endIndex = minOf(progressChapterIndex + n, chapters.size)
|
||||
|
||||
// Make sure there are enough chapters
|
||||
val chaptersToDownload = chapters.subList(progressChapterIndex, endIndex)
|
||||
|
||||
|
||||
for (chapter in chaptersToDownload) {
|
||||
lifecycleScope.launch {
|
||||
// Get the last viewed chapter
|
||||
val selected = media.userProgress ?: 0
|
||||
val chapters = media.manga?.chapters?.values?.toList()
|
||||
// Ensure chapters are available in the extensions
|
||||
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
|
||||
} + 1).coerceAtLeast(0)
|
||||
// Calculate the end value for the range of chapters to download
|
||||
val endIndex = (progressChapterIndex + n).coerceAtMost(chapters.size)
|
||||
// Get the list of chapters to download
|
||||
val chaptersToDownload = chapters.subList(progressChapterIndex, endIndex)
|
||||
// Trigger the download for each chapter sequentially
|
||||
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)
|
||||
delay(2000) // A 2-second download
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,13 +272,14 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||
val chapters = loadedChapters[media.selected!!.sourceIndex]
|
||||
if (chapters != null) {
|
||||
headerAdapter.options = getScanlators(chapters)
|
||||
val filteredChapters = if (model.mangaReadSources?.get(media.selected!!.sourceIndex) is OfflineMangaParser) {
|
||||
chapters
|
||||
} else {
|
||||
chapters.filterNot { (_, chapter) ->
|
||||
chapter.scanlator in headerAdapter.hiddenScanlators
|
||||
val filteredChapters =
|
||||
if (model.mangaReadSources?.get(media.selected!!.sourceIndex) is OfflineMangaParser) {
|
||||
chapters
|
||||
} else {
|
||||
chapters.filterNot { (_, chapter) ->
|
||||
chapter.scanlator in headerAdapter.hiddenScanlators
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
media.manga?.chapters = filteredChapters.toMutableMap()
|
||||
|
||||
@@ -397,17 +409,18 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||
selectedSetting = allSettings[which]
|
||||
itemSelected = true
|
||||
|
||||
val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id) {
|
||||
changeUIVisibility(true)
|
||||
loadChapters(media.selected!!.sourceIndex, true)
|
||||
}
|
||||
val fragment =
|
||||
MangaSourcePreferencesFragment().getInstance(selectedSetting.id) {
|
||||
changeUIVisibility(true)
|
||||
loadChapters(media.selected!!.sourceIndex, true)
|
||||
}
|
||||
parentFragmentManager.beginTransaction()
|
||||
.setCustomAnimations(R.anim.slide_up, R.anim.slide_down)
|
||||
.replace(R.id.fragmentExtensionsContainer, fragment)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
onDismiss{
|
||||
onDismiss {
|
||||
if (!itemSelected) {
|
||||
changeUIVisibility(true)
|
||||
}
|
||||
@@ -472,7 +485,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||
scanlator = chapter.scanlator ?: "Unknown",
|
||||
imageData = images,
|
||||
sourceMedia = media,
|
||||
retries = 2,
|
||||
retries = 25,
|
||||
simultaneousDownloads = 2
|
||||
)
|
||||
|
||||
@@ -590,7 +603,9 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||
|
||||
// Find latest chapter for subscription
|
||||
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 =
|
||||
media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest
|
||||
|
||||
|
||||
@@ -78,7 +78,8 @@ class ChapterLoaderDialog : BottomSheetDialogFragment() {
|
||||
_binding = BottomSheetSelectorBinding.inflate(inflater, container, false)
|
||||
val window = dialog?.window
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package ani.dantotsu.media.manga.mangareader
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
@@ -58,8 +57,6 @@ import ani.dantotsu.media.Media
|
||||
import ani.dantotsu.media.MediaDetailsViewModel
|
||||
import ani.dantotsu.media.MediaNameAdapter
|
||||
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.MangaChapter
|
||||
import ani.dantotsu.others.ImageViewDialog
|
||||
@@ -196,8 +193,9 @@ class MangaReaderActivity : AppCompatActivity() {
|
||||
finish()
|
||||
return@addCallback
|
||||
}
|
||||
val chapter = (MediaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!.number)
|
||||
?.minus(1L) ?: 0).toString()
|
||||
val chapter =
|
||||
(MediaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!.number)
|
||||
?.minus(1L) ?: 0).toString()
|
||||
if (chapter == "0.0" && PrefManager.getVal(PrefName.ChapterZeroReader)
|
||||
// Not asking individually or incognito
|
||||
&& !showProgressDialog && !PrefManager.getVal<Boolean>(PrefName.Incognito)
|
||||
|
||||
@@ -55,6 +55,7 @@ class Swipy @JvmOverloads constructor(
|
||||
else
|
||||
VerticalPosition.Top
|
||||
}
|
||||
|
||||
!it.canScrollVertically(1) -> VerticalPosition.Bottom
|
||||
!it.canScrollVertically(-1) -> VerticalPosition.Top
|
||||
else -> VerticalPosition.None
|
||||
@@ -67,6 +68,7 @@ class Swipy @JvmOverloads constructor(
|
||||
else
|
||||
HorizontalPosition.Left
|
||||
}
|
||||
|
||||
!it.canScrollHorizontally(1) -> HorizontalPosition.Right
|
||||
!it.canScrollHorizontally(-1) -> HorizontalPosition.Left
|
||||
else -> HorizontalPosition.None
|
||||
@@ -97,12 +99,14 @@ class Swipy @JvmOverloads constructor(
|
||||
initialDown = if (vertical) ev.getY(0) else ev.getX(0)
|
||||
isBeingDragged = false
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val pointerIndex = ev.findPointerIndex(activePointerId)
|
||||
if (pointerIndex >= 0) {
|
||||
startDragging(if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex))
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev)
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
isBeingDragged = false
|
||||
@@ -122,6 +126,7 @@ class Swipy @JvmOverloads constructor(
|
||||
activePointerId = ev.getPointerId(0)
|
||||
isBeingDragged = false
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
pointerIndex = ev.findPointerIndex(activePointerId)
|
||||
if (pointerIndex >= 0) {
|
||||
@@ -130,28 +135,36 @@ class Swipy @JvmOverloads constructor(
|
||||
if (isBeingDragged) handleDrag(pos)
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_POINTER_DOWN -> {
|
||||
pointerIndex = ev.actionIndex
|
||||
if (pointerIndex >= 0) activePointerId = ev.getPointerId(pointerIndex)
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev)
|
||||
MotionEvent.ACTION_UP -> {
|
||||
resetSwipes()
|
||||
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
|
||||
return false
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_CANCEL -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun startDragging(pos: Float) {
|
||||
val posDiff = if ((vertical && verticalPos == VerticalPosition.Top) || (!vertical && horizontalPos == HorizontalPosition.Left))
|
||||
pos - initialDown
|
||||
else
|
||||
initialDown - pos
|
||||
val posDiff =
|
||||
if ((vertical && verticalPos == VerticalPosition.Top) || (!vertical && horizontalPos == HorizontalPosition.Left))
|
||||
pos - initialDown
|
||||
else
|
||||
initialDown - pos
|
||||
if (posDiff > touchSlop && !isBeingDragged) {
|
||||
initialMotion = initialDown + touchSlop
|
||||
isBeingDragged = true
|
||||
@@ -185,7 +198,7 @@ class Swipy @JvmOverloads constructor(
|
||||
leftBeingSwiped.invoke(0f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun finishSpinner(overscrollDistance: Float) {
|
||||
if (vertical) {
|
||||
val totalDragDistance = Resources.getSystem().displayMetrics.heightPixels / dragDivider
|
||||
|
||||
@@ -14,8 +14,6 @@ import ani.dantotsu.setAnimation
|
||||
import ani.dantotsu.snackString
|
||||
import ani.dantotsu.util.Logger
|
||||
import ani.dantotsu.util.customAlertDialog
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
|
||||
class NovelResponseAdapter(
|
||||
val fragment: NovelReadFragment,
|
||||
@@ -41,7 +39,8 @@ class NovelResponseAdapter(
|
||||
setAnimation(fragment.requireContext(), holder.binding.root)
|
||||
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.itemEpisodeFiller.text =
|
||||
if (downloadedCheckCallback.downloadedCheck(novel)) {
|
||||
|
||||
@@ -50,10 +50,10 @@ class ListViewModel : ViewModel() {
|
||||
search,
|
||||
ignoreCase = true
|
||||
) == true || media.synonyms.any { it.contains(search, ignoreCase = true) } ||
|
||||
media.nameRomaji.contains(
|
||||
search,
|
||||
ignoreCase = true
|
||||
)
|
||||
media.nameRomaji.contains(
|
||||
search,
|
||||
ignoreCase = true
|
||||
)
|
||||
} as ArrayList<Media>
|
||||
}.toMutableMap()
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@ class AlarmManagerScheduler(private val context: Context) : TaskScheduler {
|
||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
|
||||
val intent = when {
|
||||
taskType == TaskType.COMMENT_NOTIFICATION && PrefManager.getVal<Int>(PrefName.CommentsEnabled) == 1 ->
|
||||
taskType == TaskType.COMMENT_NOTIFICATION && PrefManager.getVal<Int>(PrefName.CommentsEnabled) == 1 ->
|
||||
Intent(context, CommentNotificationReceiver::class.java)
|
||||
|
||||
taskType == TaskType.ANILIST_NOTIFICATION ->
|
||||
taskType == TaskType.ANILIST_NOTIFICATION ->
|
||||
Intent(context, AnilistNotificationReceiver::class.java)
|
||||
|
||||
taskType == TaskType.SUBSCRIPTION_NOTIFICATION ->
|
||||
taskType == TaskType.SUBSCRIPTION_NOTIFICATION ->
|
||||
Intent(context, SubscriptionNotificationReceiver::class.java)
|
||||
|
||||
else -> return
|
||||
@@ -63,13 +63,13 @@ class AlarmManagerScheduler(private val context: Context) : TaskScheduler {
|
||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
|
||||
val intent = when {
|
||||
taskType == TaskType.COMMENT_NOTIFICATION && PrefManager.getVal<Int>(PrefName.CommentsEnabled) == 1 ->
|
||||
taskType == TaskType.COMMENT_NOTIFICATION && PrefManager.getVal<Int>(PrefName.CommentsEnabled) == 1 ->
|
||||
Intent(context, CommentNotificationReceiver::class.java)
|
||||
|
||||
taskType == TaskType.ANILIST_NOTIFICATION ->
|
||||
taskType == TaskType.ANILIST_NOTIFICATION ->
|
||||
Intent(context, AnilistNotificationReceiver::class.java)
|
||||
|
||||
taskType == TaskType.SUBSCRIPTION_NOTIFICATION ->
|
||||
taskType == TaskType.SUBSCRIPTION_NOTIFICATION ->
|
||||
Intent(context, SubscriptionNotificationReceiver::class.java)
|
||||
|
||||
else -> return
|
||||
|
||||
@@ -5,7 +5,6 @@ import ani.dantotsu.currContext
|
||||
import ani.dantotsu.media.Media
|
||||
import ani.dantotsu.media.MediaNameAdapter
|
||||
import ani.dantotsu.media.Selected
|
||||
import ani.dantotsu.media.emptyMedia
|
||||
import ani.dantotsu.parsers.AnimeParser
|
||||
import ani.dantotsu.parsers.AnimeSources
|
||||
import ani.dantotsu.parsers.BaseParser
|
||||
@@ -63,11 +62,11 @@ class SubscriptionHelper {
|
||||
val show = parser.loadSavedShowResponse(subscribeMedia.id)
|
||||
?: forceLoadShowResponse(subscribeMedia, selected, parser)
|
||||
?: throw Exception(
|
||||
currContext()?.getString(
|
||||
R.string.failed_to_load_data,
|
||||
subscribeMedia.id
|
||||
currContext()?.getString(
|
||||
R.string.failed_to_load_data,
|
||||
subscribeMedia.id
|
||||
)
|
||||
)
|
||||
)
|
||||
show.sAnime?.let {
|
||||
parser.getLatestEpisode(
|
||||
show.link, show.extra,
|
||||
@@ -103,11 +102,11 @@ class SubscriptionHelper {
|
||||
val show = parser.loadSavedShowResponse(subscribeMedia.id)
|
||||
?: forceLoadShowResponse(subscribeMedia, selected, parser)
|
||||
?: throw Exception(
|
||||
currContext()?.getString(
|
||||
R.string.failed_to_load_data,
|
||||
subscribeMedia.id
|
||||
currContext()?.getString(
|
||||
R.string.failed_to_load_data,
|
||||
subscribeMedia.id
|
||||
)
|
||||
)
|
||||
)
|
||||
show.sManga?.let {
|
||||
parser.getLatestChapter(
|
||||
show.link, show.extra,
|
||||
@@ -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(
|
||||
id = subscribeMedia.id,
|
||||
name = null,
|
||||
|
||||
@@ -127,8 +127,10 @@ class SubscriptionNotificationTask : Task {
|
||||
banner = media.banner
|
||||
)
|
||||
)
|
||||
PrefManager.setVal(PrefName.UnreadCommentNotifications,
|
||||
PrefManager.getVal<Int>(PrefName.UnreadCommentNotifications) + 1)
|
||||
PrefManager.setVal(
|
||||
PrefName.UnreadCommentNotifications,
|
||||
PrefManager.getVal<Int>(PrefName.UnreadCommentNotifications) + 1
|
||||
)
|
||||
val notification = createNotification(
|
||||
context.applicationContext,
|
||||
media,
|
||||
@@ -240,7 +242,7 @@ class SubscriptionNotificationTask : Task {
|
||||
if (newStore.size >= 100) {
|
||||
newStore.remove(newStore.minByOrNull { it.time })
|
||||
}
|
||||
if (newStore.any { it.title == notification.title && it.content == notification.content}) {
|
||||
if (newStore.any { it.title == notification.title && it.content == notification.content }) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.Fragment
|
||||
import ani.dantotsu.R
|
||||
@@ -37,7 +36,7 @@ class OfflineFragment : Fragment() {
|
||||
PrefManager.setVal(PrefName.OfflineMode, false)
|
||||
startMainActivity(requireActivity())
|
||||
} else {
|
||||
if (isOnline(requireContext()) ) {
|
||||
if (isOnline(requireContext())) {
|
||||
startMainActivity(requireActivity())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
|
||||
object Anify {
|
||||
suspend fun fetchAndParseMetadata(id :Int): Map<String, Episode> {
|
||||
suspend fun fetchAndParseMetadata(id: Int): Map<String, Episode> {
|
||||
val response = client.get("https://anify.eltik.cc/content-metadata/$id")
|
||||
.parsed<JsonArray>().map {
|
||||
Mapper.json.decodeFromJsonElement<AnifyElement>(it)
|
||||
@@ -24,15 +24,16 @@ object Anify {
|
||||
)
|
||||
} ?: emptyMap()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class AnifyElement (
|
||||
data class AnifyElement(
|
||||
@SerialName("providerId")
|
||||
val providerID: String? = null,
|
||||
val data: List<Datum>? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Datum (
|
||||
data class Datum(
|
||||
val id: String? = null,
|
||||
val description: String? = null,
|
||||
val hasDub: Boolean? = null,
|
||||
|
||||
@@ -24,11 +24,11 @@ class CrashActivity : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
ThemeManager(this).applyTheme()
|
||||
initActivity(this)
|
||||
binding = ActivityCrashBinding.inflate(layoutInflater)
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE
|
||||
)
|
||||
binding = ActivityCrashBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = statusBarHeight
|
||||
|
||||
@@ -13,132 +13,132 @@ import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
|
||||
class Xubtitle
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
) : AppCompatTextView(context, attrs, defStyleAttr) {
|
||||
private var outlineThickness: Float = 0f
|
||||
private var effectColor: Int = currentTextColor
|
||||
private var currentEffect: Effect = Effect.NONE
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
) : AppCompatTextView(context, attrs, defStyleAttr) {
|
||||
private var outlineThickness: Float = 0f
|
||||
private var effectColor: Int = currentTextColor
|
||||
private var currentEffect: Effect = Effect.NONE
|
||||
|
||||
private val shadowPaint = Paint().apply { isAntiAlias = true }
|
||||
private val outlinePaint = Paint().apply { isAntiAlias = true }
|
||||
private var shineShader: Shader? = null
|
||||
private val shadowPaint = Paint().apply { isAntiAlias = true }
|
||||
private val outlinePaint = Paint().apply { isAntiAlias = true }
|
||||
private var shineShader: Shader? = null
|
||||
|
||||
enum class Effect {
|
||||
NONE,
|
||||
OUTLINE,
|
||||
SHINE,
|
||||
DROP_SHADOW,
|
||||
}
|
||||
enum class Effect {
|
||||
NONE,
|
||||
OUTLINE,
|
||||
SHINE,
|
||||
DROP_SHADOW,
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
val text = text.toString()
|
||||
val textPaint =
|
||||
TextPaint(paint).apply {
|
||||
color = currentTextColor
|
||||
}
|
||||
val staticLayout =
|
||||
StaticLayout.Builder
|
||||
.obtain(text, 0, text.length, textPaint, width)
|
||||
.setAlignment(Layout.Alignment.ALIGN_CENTER)
|
||||
.setLineSpacing(0f, 1f)
|
||||
.build()
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
val text = text.toString()
|
||||
val textPaint =
|
||||
TextPaint(paint).apply {
|
||||
color = currentTextColor
|
||||
}
|
||||
val staticLayout =
|
||||
StaticLayout.Builder
|
||||
.obtain(text, 0, text.length, textPaint, width)
|
||||
.setAlignment(Layout.Alignment.ALIGN_CENTER)
|
||||
.setLineSpacing(0f, 1f)
|
||||
.build()
|
||||
|
||||
when (currentEffect) {
|
||||
Effect.OUTLINE -> {
|
||||
textPaint.style = Paint.Style.STROKE
|
||||
textPaint.strokeWidth = outlineThickness
|
||||
textPaint.color = effectColor
|
||||
when (currentEffect) {
|
||||
Effect.OUTLINE -> {
|
||||
textPaint.style = Paint.Style.STROKE
|
||||
textPaint.strokeWidth = outlineThickness
|
||||
textPaint.color = effectColor
|
||||
|
||||
staticLayout.draw(canvas)
|
||||
staticLayout.draw(canvas)
|
||||
|
||||
textPaint.style = Paint.Style.FILL
|
||||
textPaint.color = currentTextColor
|
||||
staticLayout.draw(canvas)
|
||||
}
|
||||
textPaint.style = Paint.Style.FILL
|
||||
textPaint.color = currentTextColor
|
||||
staticLayout.draw(canvas)
|
||||
}
|
||||
|
||||
Effect.DROP_SHADOW -> {
|
||||
setLayerType(LAYER_TYPE_SOFTWARE, null)
|
||||
textPaint.setShadowLayer(outlineThickness, 4f, 4f, effectColor)
|
||||
Effect.DROP_SHADOW -> {
|
||||
setLayerType(LAYER_TYPE_SOFTWARE, null)
|
||||
textPaint.setShadowLayer(outlineThickness, 4f, 4f, effectColor)
|
||||
|
||||
staticLayout.draw(canvas)
|
||||
staticLayout.draw(canvas)
|
||||
|
||||
textPaint.clearShadowLayer()
|
||||
}
|
||||
textPaint.clearShadowLayer()
|
||||
}
|
||||
|
||||
Effect.SHINE -> {
|
||||
val shadowShader =
|
||||
LinearGradient(
|
||||
0f,
|
||||
0f,
|
||||
width.toFloat(),
|
||||
height.toFloat(),
|
||||
intArrayOf(Color.WHITE, effectColor, Color.BLACK),
|
||||
null,
|
||||
Shader.TileMode.CLAMP,
|
||||
)
|
||||
|
||||
val shadowPaint =
|
||||
Paint().apply {
|
||||
isAntiAlias = true
|
||||
style = Paint.Style.FILL
|
||||
textSize = textPaint.textSize
|
||||
typeface = textPaint.typeface
|
||||
shader = shadowShader
|
||||
}
|
||||
|
||||
canvas.drawText(
|
||||
text,
|
||||
x + 4f, // Shadow offset
|
||||
y + 4f,
|
||||
shadowPaint,
|
||||
Effect.SHINE -> {
|
||||
val shadowShader =
|
||||
LinearGradient(
|
||||
0f,
|
||||
0f,
|
||||
width.toFloat(),
|
||||
height.toFloat(),
|
||||
intArrayOf(Color.WHITE, effectColor, Color.BLACK),
|
||||
null,
|
||||
Shader.TileMode.CLAMP,
|
||||
)
|
||||
|
||||
val shader =
|
||||
LinearGradient(
|
||||
0f,
|
||||
0f,
|
||||
width.toFloat(),
|
||||
height.toFloat(),
|
||||
intArrayOf(effectColor, Color.WHITE, Color.WHITE),
|
||||
null,
|
||||
Shader.TileMode.CLAMP,
|
||||
)
|
||||
textPaint.shader = shader
|
||||
staticLayout.draw(canvas)
|
||||
textPaint.shader = null
|
||||
}
|
||||
val shadowPaint =
|
||||
Paint().apply {
|
||||
isAntiAlias = true
|
||||
style = Paint.Style.FILL
|
||||
textSize = textPaint.textSize
|
||||
typeface = textPaint.typeface
|
||||
shader = shadowShader
|
||||
}
|
||||
|
||||
Effect.NONE -> {
|
||||
staticLayout.draw(canvas)
|
||||
}
|
||||
canvas.drawText(
|
||||
text,
|
||||
x + 4f, // Shadow offset
|
||||
y + 4f,
|
||||
shadowPaint,
|
||||
)
|
||||
|
||||
val shader =
|
||||
LinearGradient(
|
||||
0f,
|
||||
0f,
|
||||
width.toFloat(),
|
||||
height.toFloat(),
|
||||
intArrayOf(effectColor, Color.WHITE, Color.WHITE),
|
||||
null,
|
||||
Shader.TileMode.CLAMP,
|
||||
)
|
||||
textPaint.shader = shader
|
||||
staticLayout.draw(canvas)
|
||||
textPaint.shader = null
|
||||
}
|
||||
|
||||
Effect.NONE -> {
|
||||
staticLayout.draw(canvas)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun applyOutline(
|
||||
color: Int,
|
||||
outlineThickness: Float,
|
||||
) {
|
||||
this.effectColor = color
|
||||
this.outlineThickness = outlineThickness
|
||||
currentEffect = Effect.OUTLINE
|
||||
}
|
||||
fun applyOutline(
|
||||
color: Int,
|
||||
outlineThickness: Float,
|
||||
) {
|
||||
this.effectColor = color
|
||||
this.outlineThickness = outlineThickness
|
||||
currentEffect = Effect.OUTLINE
|
||||
}
|
||||
|
||||
// Too hard for me to figure it out
|
||||
fun applyShineEffect(color: Int) {
|
||||
this.effectColor = color
|
||||
currentEffect = Effect.SHINE
|
||||
}
|
||||
// Too hard for me to figure it out
|
||||
fun applyShineEffect(color: Int) {
|
||||
this.effectColor = color
|
||||
currentEffect = Effect.SHINE
|
||||
}
|
||||
|
||||
fun applyDropShadow(
|
||||
color: Int,
|
||||
outlineThickness: Float,
|
||||
) {
|
||||
this.effectColor = color
|
||||
this.outlineThickness = outlineThickness
|
||||
currentEffect = Effect.DROP_SHADOW
|
||||
}
|
||||
}
|
||||
fun applyDropShadow(
|
||||
color: Int,
|
||||
outlineThickness: Float,
|
||||
) {
|
||||
this.effectColor = color
|
||||
this.outlineThickness = outlineThickness
|
||||
currentEffect = Effect.DROP_SHADOW
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ object AnimeSources : WatchSources() {
|
||||
)
|
||||
isInitialized = true
|
||||
|
||||
// Update as StateFlow emits new values
|
||||
fromExtensions.collect { extensions ->
|
||||
list = sortPinnedAnimeSources(
|
||||
createParsersFromExtensions(extensions),
|
||||
|
||||
@@ -226,8 +226,18 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
|
||||
?: return emptyList())
|
||||
|
||||
return try {
|
||||
val videos = source.getVideoList(sEpisode)
|
||||
videos.map { videoToVideoServer(it) }
|
||||
// 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)
|
||||
videos.map { videoToVideoServer(it) }
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Logger.log("Exception occurred: ${e.message}")
|
||||
emptyList()
|
||||
@@ -576,7 +586,7 @@ class VideoServerPassthrough(private val videoServer: VideoServer) : VideoExtrac
|
||||
number,
|
||||
format!!,
|
||||
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 {
|
||||
//use Dispatchers.IO to make a HTTP request to determine the subtitle type
|
||||
var type: SubtitleType?
|
||||
runBlocking {
|
||||
type = findSubtitleType(track.url)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
package ani.dantotsu.parsers
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
@@ -6,7 +5,6 @@ import ani.dantotsu.FileUrl
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.media.Media
|
||||
import ani.dantotsu.okHttpClient
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.util.Logger
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
|
||||
@@ -168,7 +168,7 @@ class ExtensionTestItem(
|
||||
return
|
||||
}
|
||||
val serverResultStart = System.currentTimeMillis()
|
||||
val serverResult = extension.loadImages("", chapterResult.first().sChapter)
|
||||
val serverResult = extension.loadImages("", chapterResult.first().sChapter)
|
||||
serverResultData.time = (System.currentTimeMillis() - serverResultStart).toInt()
|
||||
serverResultData.size = serverResult.size
|
||||
withContext(Dispatchers.Main) {
|
||||
@@ -266,8 +266,10 @@ class ExtensionTestItem(
|
||||
)
|
||||
binding.searchResultText.isVisible = true
|
||||
if (searchResultData.size == 0) {
|
||||
val text = context.getString(R.string.title_search_test,
|
||||
context.getString(R.string.no_results_found))
|
||||
val text = context.getString(
|
||||
R.string.title_search_test,
|
||||
context.getString(R.string.no_results_found)
|
||||
)
|
||||
binding.searchResultText.text = text
|
||||
binding.searchResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_circle_cancel, 0, 0, 0
|
||||
@@ -277,8 +279,10 @@ class ExtensionTestItem(
|
||||
)
|
||||
return
|
||||
}
|
||||
val text = context.getString(R.string.title_search_test,
|
||||
context.getString(R.string.results_found, searchResultData.size.toString()))
|
||||
val text = context.getString(
|
||||
R.string.title_search_test,
|
||||
context.getString(R.string.results_found, searchResultData.size.toString())
|
||||
)
|
||||
binding.searchResultText.text = text + "\n${searchResultData.time}ms"
|
||||
binding.searchResultText.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_circle_check, 0, 0, 0
|
||||
@@ -297,13 +301,21 @@ class ExtensionTestItem(
|
||||
)
|
||||
binding.episodeResultText.isVisible = true
|
||||
if (episodeResultData.size == 0) {
|
||||
val text = when(extensionType) {
|
||||
"anime" -> context.getString(R.string.episode_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))
|
||||
val text = when (extensionType) {
|
||||
"anime" -> context.getString(
|
||||
R.string.episode_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.setCompoundDrawablesWithIntrinsicBounds(
|
||||
@@ -314,13 +326,21 @@ class ExtensionTestItem(
|
||||
)
|
||||
return
|
||||
}
|
||||
val text = when(extensionType) {
|
||||
"anime" -> context.getString(R.string.episode_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()))
|
||||
val text = when (extensionType) {
|
||||
"anime" -> context.getString(
|
||||
R.string.episode_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.setCompoundDrawablesWithIntrinsicBounds(
|
||||
@@ -348,13 +368,21 @@ class ExtensionTestItem(
|
||||
)
|
||||
binding.serverResultText.isVisible = true
|
||||
if (serverResultData.size == 0) {
|
||||
val text = when(extensionType) {
|
||||
"anime" -> context.getString(R.string.video_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))
|
||||
val text = when (extensionType) {
|
||||
"anime" -> context.getString(
|
||||
R.string.video_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.setCompoundDrawablesWithIntrinsicBounds(
|
||||
@@ -365,13 +393,21 @@ class ExtensionTestItem(
|
||||
)
|
||||
return
|
||||
}
|
||||
val text = when(extensionType) {
|
||||
"anime" -> context.getString(R.string.video_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()))
|
||||
val text = when (extensionType) {
|
||||
"anime" -> context.getString(
|
||||
R.string.video_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.setCompoundDrawablesWithIntrinsicBounds(
|
||||
|
||||
@@ -110,7 +110,7 @@ class ExtensionTestSettingsBottomDialog : BottomSheetDialogFragment() {
|
||||
}
|
||||
|
||||
private fun setupAdapter() {
|
||||
val namesAndUrls: Map<String,Drawable?> = when (extensionType) {
|
||||
val namesAndUrls: Map<String, Drawable?> = when (extensionType) {
|
||||
"anime" -> animeExtension.installedExtensionsFlow.value.associate { it.name to it.icon }
|
||||
"manga" -> mangaExtensions.installedExtensionsFlow.value.associate { it.name to it.icon }
|
||||
"novel" -> novelExtensions.installedExtensionsFlow.value.associate { it.name to it.icon }
|
||||
|
||||
@@ -33,7 +33,7 @@ class OfflineMangaParser : MangaParser() {
|
||||
directory.listFiles().forEach {
|
||||
val scanlator = downloadManager.mangaDownloadedTypes.find { items ->
|
||||
items.titleName == mangaLink &&
|
||||
items.chapterName == it.name
|
||||
items.chapterName == it.name
|
||||
}?.scanlator ?: "Unknown"
|
||||
if (it.isDirectory) {
|
||||
val chapter = MangaChapter(
|
||||
|
||||
@@ -73,6 +73,7 @@ class ParserTestActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"manga" -> {
|
||||
ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name ->
|
||||
val extension =
|
||||
@@ -89,6 +90,7 @@ class ParserTestActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"novel" -> {
|
||||
ExtensionTestSettingsBottomDialog.extensionsToTest.forEach { name ->
|
||||
val extension =
|
||||
|
||||
@@ -120,8 +120,10 @@ class NovelExtensionManager(private val context: Context) {
|
||||
* @param extension The anime extension to be installed.
|
||||
*/
|
||||
fun installExtension(extension: NovelExtension.Available): Observable<InstallStep> {
|
||||
return installer.downloadAndInstall(api.getNovelApkUrl(extension), extension.pkgName,
|
||||
extension.name, MediaType.NOVEL)
|
||||
return installer.downloadAndInstall(
|
||||
api.getNovelApkUrl(extension), extension.pkgName,
|
||||
extension.name, MediaType.NOVEL
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,7 +53,8 @@ class ChartBuilder {
|
||||
scrollPos: Float? = null,
|
||||
normalize: Boolean = false
|
||||
): 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 aaChartType = passedAaChartType
|
||||
var categories = passedCategories
|
||||
@@ -303,7 +304,8 @@ class ChartBuilder {
|
||||
}
|
||||
|
||||
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(
|
||||
AAColor.rgbaColor(
|
||||
Color.red(backgroundColor),
|
||||
@@ -312,7 +314,8 @@ class ChartBuilder {
|
||||
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(
|
||||
AAColor.rgbaColor(
|
||||
Color.red(colorOnBackground),
|
||||
|
||||
@@ -36,10 +36,10 @@ class FollowerItem(
|
||||
}
|
||||
|
||||
override fun getLayout(): Int {
|
||||
return if(grid) R.layout.item_follower_grid else R.layout.item_follower
|
||||
return if (grid) R.layout.item_follower_grid else R.layout.item_follower
|
||||
}
|
||||
|
||||
override fun initializeViewBinding(view: View): ViewBinding {
|
||||
return if(grid) ItemFollowerGridBinding.bind(view) else ItemFollowerBinding.bind(view)
|
||||
return if (grid) ItemFollowerGridBinding.bind(view) else ItemFollowerBinding.bind(view)
|
||||
}
|
||||
}
|
||||
@@ -156,17 +156,28 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
|
||||
openLinkInBrowser(getString(R.string.anilist_link, user.name))
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_share_profile -> {
|
||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
||||
shareIntent.type = "text/plain"
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.anilist_link, user.name))
|
||||
startActivity(Intent.createChooser(shareIntent, "Share Profile"))
|
||||
shareIntent.putExtra(
|
||||
Intent.EXTRA_TEXT,
|
||||
getString(R.string.anilist_link, user.name)
|
||||
)
|
||||
startActivity(
|
||||
Intent.createChooser(
|
||||
shareIntent,
|
||||
"Share Profile"
|
||||
)
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_copy_user_id -> {
|
||||
copyToClipboard(user.id.toString(), true)
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,11 +35,11 @@ class UsersAdapter(private val user: MutableList<User>, private val grid: Boolea
|
||||
parent,
|
||||
false
|
||||
) else
|
||||
ItemFollowerBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
ItemFollowerBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ import ani.dantotsu.profile.User
|
||||
import ani.dantotsu.profile.UsersDialogFragment
|
||||
import ani.dantotsu.setAnimation
|
||||
import ani.dantotsu.snackString
|
||||
import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML
|
||||
import ani.dantotsu.util.ActivityMarkdownCreator
|
||||
import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import com.xwray.groupie.viewbinding.BindableItem
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -52,14 +52,19 @@ class ActivityItem(
|
||||
}
|
||||
binding.activityRepliesContainer.setOnClickListener {
|
||||
RepliesBottomDialog.newInstance(activity.id)
|
||||
.show((context as FragmentActivity).supportFragmentManager, "replies")
|
||||
.show((context as FragmentActivity).supportFragmentManager, "replies")
|
||||
}
|
||||
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 {
|
||||
UsersDialogFragment().apply {
|
||||
userList(userList)
|
||||
show((context as FragmentActivity).supportFragmentManager, "dialog")
|
||||
show((context as FragmentActivity).supportFragmentManager, "dialog")
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -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 {
|
||||
scope.launch {
|
||||
val res = Anilist.mutation.deleteActivity(activity.id)
|
||||
@@ -161,7 +167,8 @@ class ActivityItem(
|
||||
"MessageActivity" -> {
|
||||
binding.activityBannerContainer.visibility = View.GONE
|
||||
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) {
|
||||
val markwon = buildMarkwon(context, false)
|
||||
markwon.setMarkdown(
|
||||
|
||||
@@ -14,8 +14,8 @@ import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.profile.User
|
||||
import ani.dantotsu.profile.UsersDialogFragment
|
||||
import ani.dantotsu.snackString
|
||||
import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML
|
||||
import ani.dantotsu.util.ActivityMarkdownCreator
|
||||
import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import com.xwray.groupie.viewbinding.BindableItem
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -26,7 +26,7 @@ import kotlinx.coroutines.withContext
|
||||
|
||||
class ActivityReplyItem(
|
||||
private val reply: ActivityReply,
|
||||
private val parentId : Int,
|
||||
private val parentId: Int,
|
||||
private val fragActivity: FragmentActivity,
|
||||
private val parentAdapter: GroupieAdapter,
|
||||
private val clickCallback: (Int, type: String) -> Unit,
|
||||
@@ -46,7 +46,7 @@ class ActivityReplyItem(
|
||||
binding.activityLike.setColorFilter(if (reply.isLiked) likeColor else notLikeColor)
|
||||
val markwon = buildMarkwon(context)
|
||||
markwon.setMarkdown(binding.activityContent, getBasicAniHTML(reply.text))
|
||||
|
||||
|
||||
val userList = arrayListOf<User>()
|
||||
reply.likes?.forEach { i ->
|
||||
userList.add(User(i.id, i.name.toString(), i.avatar?.medium, i.bannerImage))
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
package ani.dantotsu.profile.activity
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updateMargins
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.databinding.ActivityFeedBinding
|
||||
import ani.dantotsu.databinding.ActivityNotificationBinding
|
||||
import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.profile.activity.ActivityFragment.Companion.ActivityType
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.profile.activity.ActivityFragment.Companion.ActivityType
|
||||
import ani.dantotsu.profile.notification.NotificationActivity
|
||||
import nl.joery.animatedbottombar.AnimatedBottomBar
|
||||
|
||||
class FeedActivity : AppCompatActivity() {
|
||||
@@ -50,9 +45,12 @@ class FeedActivity : AppCompatActivity() {
|
||||
|
||||
binding.notificationBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
|
||||
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.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, getOne)
|
||||
binding.notificationViewPager.adapter =
|
||||
ViewPagerAdapter(supportFragmentManager, lifecycle, getOne)
|
||||
binding.notificationViewPager.setOffscreenPageLimit(4)
|
||||
binding.notificationViewPager.setCurrentItem(selected, false)
|
||||
navBar.selectTabAt(selected)
|
||||
@@ -83,7 +81,11 @@ class FeedActivity : AppCompatActivity() {
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,20 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
import ani.dantotsu.databinding.ActivityNotificationBinding
|
||||
import ani.dantotsu.initActivity
|
||||
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.themes.ThemeManager
|
||||
import ani.dantotsu.profile.notification.NotificationFragment.Companion.NotificationType.*
|
||||
import ani.dantotsu.profile.notification.NotificationFragment.Companion.newInstance
|
||||
import nl.joery.animatedbottombar.AnimatedBottomBar
|
||||
|
||||
class NotificationActivity : AppCompatActivity() {
|
||||
@@ -58,7 +61,8 @@ class NotificationActivity : AppCompatActivity() {
|
||||
val getOne = intent.getIntExtra("activityId", -1)
|
||||
if (getOne != -1) navBar.isVisible = 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)
|
||||
navBar.selectTabAt(selected)
|
||||
navBar.setOnTabSelectListener(object : AnimatedBottomBar.OnTabSelectListener {
|
||||
|
||||
@@ -60,7 +60,8 @@ class NotificationItem(
|
||||
PrefName.SubscriptionNotificationStore,
|
||||
null
|
||||
) ?: 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)
|
||||
parentAdapter.remove(this@NotificationItem)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class RepoItem(
|
||||
val url: String,
|
||||
private val mediaType: MediaType,
|
||||
val onRemove: (String, MediaType) -> Unit
|
||||
) :BindableItem<ItemRepoBinding>() {
|
||||
) : BindableItem<ItemRepoBinding>() {
|
||||
override fun getLayout() = R.layout.item_repo
|
||||
|
||||
override fun bind(viewBinding: ItemRepoBinding, position: Int) {
|
||||
@@ -88,7 +88,7 @@ class AddRepositoryBottomSheet : BottomSheetDialogFragment() {
|
||||
)
|
||||
adapter.addAll(repositories.map { RepoItem(it, mediaType, ::onRepositoryRemoved) })
|
||||
|
||||
binding.repositoryInput.hint = when(mediaType) {
|
||||
binding.repositoryInput.hint = when (mediaType) {
|
||||
MediaType.ANIME -> getString(R.string.anime_add_repository)
|
||||
MediaType.MANGA -> getString(R.string.manga_add_repository)
|
||||
MediaType.NOVEL -> getString(R.string.novel_add_repository)
|
||||
@@ -110,7 +110,8 @@ class AddRepositoryBottomSheet : BottomSheetDialogFragment() {
|
||||
|
||||
binding.repositoryInput.setOnEditorActionListener { textView, action, keyEvent ->
|
||||
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()
|
||||
if (url.isNotBlank()) {
|
||||
val error = isValidUrl(url)
|
||||
@@ -215,6 +216,7 @@ class AddRepositoryBottomSheet : BottomSheetDialogFragment() {
|
||||
Injekt.get<AnimeExtensionManager>().findAvailableExtensions()
|
||||
}
|
||||
}
|
||||
|
||||
MediaType.MANGA -> {
|
||||
val manga =
|
||||
PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos)
|
||||
@@ -224,6 +226,7 @@ class AddRepositoryBottomSheet : BottomSheetDialogFragment() {
|
||||
Injekt.get<MangaExtensionManager>().findAvailableExtensions()
|
||||
}
|
||||
}
|
||||
|
||||
MediaType.NOVEL -> {
|
||||
val novel =
|
||||
PrefManager.getVal<Set<String>>(PrefName.NovelExtensionRepos)
|
||||
@@ -247,6 +250,7 @@ class AddRepositoryBottomSheet : BottomSheetDialogFragment() {
|
||||
Injekt.get<AnimeExtensionManager>().findAvailableExtensions()
|
||||
}
|
||||
}
|
||||
|
||||
MediaType.MANGA -> {
|
||||
val manga =
|
||||
PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos)
|
||||
@@ -256,6 +260,7 @@ class AddRepositoryBottomSheet : BottomSheetDialogFragment() {
|
||||
Injekt.get<MangaExtensionManager>().findAvailableExtensions()
|
||||
}
|
||||
}
|
||||
|
||||
MediaType.NOVEL -> {
|
||||
val novel =
|
||||
PrefManager.getVal<Set<String>>(PrefName.NovelExtensionRepos)
|
||||
|
||||
@@ -56,7 +56,8 @@ class AnilistSettingsActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
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.setAdapter(
|
||||
@@ -78,7 +79,9 @@ class AnilistSettingsActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
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.setAdapter(
|
||||
@@ -99,8 +102,9 @@ class AnilistSettingsActivity : AppCompatActivity() {
|
||||
settingsAnilistStaffLanguage.clearFocus()
|
||||
}
|
||||
|
||||
val currentMergeTimeDisplay = activityMergeTimeMap.entries.firstOrNull { it.value == Anilist.activityMergeTime }?.key
|
||||
?: "${Anilist.activityMergeTime} mins"
|
||||
val currentMergeTimeDisplay =
|
||||
activityMergeTimeMap.entries.firstOrNull { it.value == Anilist.activityMergeTime }?.key
|
||||
?: "${Anilist.activityMergeTime} mins"
|
||||
settingsAnilistActivityMergeTime.setText(currentMergeTimeDisplay)
|
||||
settingsAnilistActivityMergeTime.setAdapter(
|
||||
ArrayAdapter(context, R.layout.item_dropdown, activityMergeTimeMap.keys.toList())
|
||||
@@ -117,7 +121,8 @@ class AnilistSettingsActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
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.setAdapter(
|
||||
ArrayAdapter(context, R.layout.item_dropdown, scoreFormats)
|
||||
@@ -139,7 +144,8 @@ class AnilistSettingsActivity : AppCompatActivity() {
|
||||
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.setAdapter(
|
||||
ArrayAdapter(context, R.layout.item_dropdown, rowOrderMap.keys.toList())
|
||||
@@ -155,7 +161,8 @@ class AnilistSettingsActivity : AppCompatActivity() {
|
||||
settingsAnilistRowOrder.clearFocus()
|
||||
}
|
||||
|
||||
val containers = listOf(binding.animeCustomListsContainer, binding.mangaCustomListsContainer)
|
||||
val containers =
|
||||
listOf(binding.animeCustomListsContainer, binding.mangaCustomListsContainer)
|
||||
val customLists = listOf(Anilist.animeCustomLists, Anilist.mangaCustomLists)
|
||||
val buttons = listOf(binding.addAnimeListButton, binding.addMangaListButton)
|
||||
|
||||
@@ -175,7 +182,8 @@ class AnilistSettingsActivity : AppCompatActivity() {
|
||||
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.setAdapter(
|
||||
ArrayAdapter(context, R.layout.item_dropdown, Anilist.timeZone)
|
||||
|
||||
@@ -169,8 +169,12 @@ class ExtensionsActivity : AppCompatActivity() {
|
||||
customAlertDialog().apply {
|
||||
setTitle("Language")
|
||||
singleChoiceItems(languageOptions, index) { selected ->
|
||||
PrefManager.setVal(PrefName.LangSort, LanguageMapper.Companion.Language.entries[selected].code)
|
||||
val currentFragment = supportFragmentManager.findFragmentByTag("f${viewPager.currentItem}")
|
||||
PrefManager.setVal(
|
||||
PrefName.LangSort,
|
||||
LanguageMapper.Companion.Language.entries[selected].code
|
||||
)
|
||||
val currentFragment =
|
||||
supportFragmentManager.findFragmentByTag("f${viewPager.currentItem}")
|
||||
if (currentFragment is SearchQueryHandler) {
|
||||
currentFragment.notifyDataChanged()
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
|
||||
if (allSettings.isNotEmpty()) {
|
||||
var selectedSetting = allSettings[0]
|
||||
if (allSettings.size > 1) {
|
||||
val names = allSettings.map { LanguageMapper.getLanguageName(it.lang) }
|
||||
val names = allSettings.map { getLanguageName(it.lang) }
|
||||
.toTypedArray()
|
||||
var selectedIndex = 0
|
||||
requireContext().customAlertDialog().apply {
|
||||
|
||||
@@ -290,7 +290,8 @@ class PlayerSettingsActivity :
|
||||
PrefManager.setVal(PrefName.UseInternalCast, isChecked)
|
||||
}
|
||||
|
||||
binding.playerSettingsAdditionalCodec.isChecked = PrefManager.getVal(PrefName.UseAdditionalCodec)
|
||||
binding.playerSettingsAdditionalCodec.isChecked =
|
||||
PrefManager.getVal(PrefName.UseAdditionalCodec)
|
||||
binding.playerSettingsAdditionalCodec.setOnCheckedChangeListener { _, isChecked ->
|
||||
PrefManager.setVal(PrefName.UseAdditionalCodec, isChecked)
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import ani.dantotsu.BottomSheetDialogFragment
|
||||
import ani.dantotsu.databinding.BottomSheetProxyBinding
|
||||
import ani.dantotsu.restartApp
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
import ani.dantotsu.restartApp
|
||||
|
||||
class ProxyDialogFragment : BottomSheetDialogFragment() {
|
||||
private var _binding: BottomSheetProxyBinding? = null
|
||||
@@ -16,8 +16,10 @@ class ProxyDialogFragment : BottomSheetDialogFragment() {
|
||||
|
||||
private var proxyHost: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyHost).orEmpty()
|
||||
private var proxyPort: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyPort).orEmpty()
|
||||
private var proxyUsername: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyUsername).orEmpty()
|
||||
private var proxyPassword: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyPassword).orEmpty()
|
||||
private var proxyUsername: String? =
|
||||
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 val proxyEnabled: Boolean = PrefManager.getVal<Boolean>(PrefName.EnableSocks5Proxy)
|
||||
|
||||
|
||||
@@ -142,8 +142,10 @@ class SettingsAboutActivity : AppCompatActivity() {
|
||||
icon = R.drawable.ic_incognito_24,
|
||||
onClick = {
|
||||
val text = TextView(context)
|
||||
val pPLink = "https://raw.githubusercontent.com/rebelonion/Dantotsu/main/privacy_policy.md"
|
||||
val backup = "https://gcore.jsdelivr.net/gh/rebelonion/dantotsu/privacy_policy.md"
|
||||
val pPLink =
|
||||
"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)
|
||||
val markWon = try {
|
||||
buildMarkwon(this@SettingsAboutActivity, false)
|
||||
@@ -177,7 +179,7 @@ class SettingsAboutActivity : AppCompatActivity() {
|
||||
}
|
||||
),
|
||||
|
||||
)
|
||||
)
|
||||
)
|
||||
binding.settingsRecyclerView.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
|
||||
@@ -211,16 +211,16 @@ class SettingsAccountActivity : AppCompatActivity() {
|
||||
binding.settingsRecyclerView.adapter = SettingsAdapter(
|
||||
arrayListOf(
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.enable_rpc),
|
||||
desc = getString(R.string.enable_rpc_desc),
|
||||
icon = R.drawable.interests_24,
|
||||
isChecked = PrefManager.getVal(PrefName.rpcEnabled),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.rpcEnabled, isChecked)
|
||||
},
|
||||
isVisible = Discord.token != null
|
||||
),
|
||||
type = 2,
|
||||
name = getString(R.string.enable_rpc),
|
||||
desc = getString(R.string.enable_rpc_desc),
|
||||
icon = R.drawable.interests_24,
|
||||
isChecked = PrefManager.getVal(PrefName.rpcEnabled),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.rpcEnabled, isChecked)
|
||||
},
|
||||
isVisible = Discord.token != null
|
||||
),
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.anilist_settings),
|
||||
@@ -235,30 +235,31 @@ class SettingsAccountActivity : AppCompatActivity() {
|
||||
isActivity = true
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.comments_button),
|
||||
desc = getString(R.string.comments_button_desc),
|
||||
icon = R.drawable.ic_round_comment_24,
|
||||
isChecked = PrefManager.getVal<Int>(PrefName.CommentsEnabled) == 1,
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.CommentsEnabled, if (isChecked) 1 else 2 )
|
||||
reload()
|
||||
},
|
||||
isVisible = Anilist.token != null
|
||||
),
|
||||
type = 2,
|
||||
name = getString(R.string.comments_button),
|
||||
desc = getString(R.string.comments_button_desc),
|
||||
icon = R.drawable.ic_round_comment_24,
|
||||
isChecked = PrefManager.getVal<Int>(PrefName.CommentsEnabled) == 1,
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.CommentsEnabled, if (isChecked) 1 else 2)
|
||||
reload()
|
||||
},
|
||||
isVisible = Anilist.token != null
|
||||
),
|
||||
)
|
||||
)
|
||||
binding.settingsRecyclerView.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
|
||||
}
|
||||
|
||||
fun reload() {
|
||||
snackString(getString(R.string.restart_app_extra))
|
||||
//snackString(R.string.restart_app_extra)
|
||||
//?.setDuration(Snackbar.LENGTH_LONG)
|
||||
//?.setAction(R.string.do_it) {
|
||||
//startMainActivity(this@SettingsAccountActivity)
|
||||
//} Disabled for now. Doesn't update the ADDRESS even after this
|
||||
snackString(getString(R.string.restart_app_extra))
|
||||
//snackString(R.string.restart_app_extra)
|
||||
//?.setDuration(Snackbar.LENGTH_LONG)
|
||||
//?.setAction(R.string.do_it) {
|
||||
//startMainActivity(this@SettingsAccountActivity)
|
||||
//} Disabled for now. Doesn't update the ADDRESS even after this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.addons.AddonDownloader
|
||||
import ani.dantotsu.addons.download.DownloadAddonManager
|
||||
import ani.dantotsu.addons.torrent.TorrentServerService
|
||||
import ani.dantotsu.addons.torrent.TorrentAddonManager
|
||||
import ani.dantotsu.addons.torrent.TorrentServerService
|
||||
import ani.dantotsu.databinding.ActivitySettingsAddonsBinding
|
||||
import ani.dantotsu.databinding.ItemSettingsBinding
|
||||
import ani.dantotsu.initActivity
|
||||
|
||||
@@ -19,6 +19,7 @@ import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.util.customAlertDialog
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@@ -57,23 +58,16 @@ class SettingsAnimeActivity : AppCompatActivity() {
|
||||
desc = getString(R.string.purge_anime_downloads_desc),
|
||||
icon = R.drawable.ic_round_delete_24,
|
||||
onClick = {
|
||||
val dialog = AlertDialog.Builder(context, R.style.MyPopup)
|
||||
.setTitle(R.string.purge_anime_downloads)
|
||||
.setMessage(
|
||||
getString(
|
||||
R.string.purge_confirm,
|
||||
getString(R.string.anime)
|
||||
)
|
||||
)
|
||||
.setPositiveButton(R.string.yes) { dialog, _ ->
|
||||
context.customAlertDialog().apply {
|
||||
setTitle(R.string.purge_anime_downloads)
|
||||
setMessage(R.string.purge_confirm, getString(R.string.anime))
|
||||
setPosButton(R.string.yes, onClick = {
|
||||
val downloadsManager = Injekt.get<DownloadsManager>()
|
||||
downloadsManager.purgeDownloads(MediaType.ANIME)
|
||||
dialog.dismiss()
|
||||
}.setNegativeButton(R.string.no) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}.create()
|
||||
dialog.window?.setDimAmount(0.8f)
|
||||
dialog.show()
|
||||
})
|
||||
setNegButton(R.string.no)
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
),
|
||||
@@ -143,4 +137,4 @@ class SettingsAnimeActivity : AppCompatActivity() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.CheckBox
|
||||
import android.widget.EditText
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.biometric.BiometricManager
|
||||
@@ -47,7 +45,6 @@ import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.UUID
|
||||
|
||||
|
||||
class SettingsCommonActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivitySettingsCommonBinding
|
||||
private lateinit var launcher: LauncherWrapper
|
||||
@@ -64,23 +61,27 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
|
||||
if (uri != null) {
|
||||
try {
|
||||
val jsonString = contentResolver.openInputStream(uri)?.readBytes()
|
||||
?: throw Exception("Error reading file")
|
||||
val jsonString =
|
||||
contentResolver.openInputStream(uri)?.readBytes()
|
||||
?: throw Exception("Error reading file")
|
||||
val name = DocumentFile.fromSingleUri(this, uri)?.name ?: "settings"
|
||||
//.sani is encrypted, .ani is not
|
||||
// .sani is encrypted, .ani is not
|
||||
if (name.endsWith(".sani")) {
|
||||
passwordAlertDialog(false) { password ->
|
||||
if (password != null) {
|
||||
val salt = jsonString.copyOfRange(0, 16)
|
||||
val encrypted = jsonString.copyOfRange(16, jsonString.size)
|
||||
val decryptedJson = try {
|
||||
PreferenceKeystore.decryptWithPassword(
|
||||
password, encrypted, salt
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
toast(getString(R.string.incorrect_password))
|
||||
return@passwordAlertDialog
|
||||
}
|
||||
val decryptedJson =
|
||||
try {
|
||||
PreferenceKeystore.decryptWithPassword(
|
||||
password,
|
||||
encrypted,
|
||||
salt,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
toast(getString(R.string.incorrect_password))
|
||||
return@passwordAlertDialog
|
||||
}
|
||||
if (PreferencePackager.unpack(decryptedJson)) restartApp()
|
||||
} else {
|
||||
toast(getString(R.string.password_cannot_be_empty))
|
||||
@@ -102,7 +103,6 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||
launcher = LauncherWrapper(this, contract)
|
||||
|
||||
binding.apply {
|
||||
|
||||
settingsCommonLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = statusBarHeight
|
||||
bottomMargin = navBarHeight
|
||||
@@ -110,27 +110,30 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||
commonSettingsBack.setOnClickListener {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
val exDns = listOf(
|
||||
"None",
|
||||
"Cloudflare",
|
||||
"Google",
|
||||
"AdGuard",
|
||||
"Quad9",
|
||||
"AliDNS",
|
||||
"DNSPod",
|
||||
"360",
|
||||
"Quad101",
|
||||
"Mullvad",
|
||||
"Controld",
|
||||
"Njalla",
|
||||
"Shecan",
|
||||
"Libre"
|
||||
)
|
||||
val exDns =
|
||||
listOf(
|
||||
"None",
|
||||
"Cloudflare",
|
||||
"Google",
|
||||
"AdGuard",
|
||||
"Quad9",
|
||||
"AliDNS",
|
||||
"DNSPod",
|
||||
"360",
|
||||
"Quad101",
|
||||
"Mullvad",
|
||||
"Controld",
|
||||
"Njalla",
|
||||
"Shecan",
|
||||
"Libre",
|
||||
)
|
||||
settingsExtensionDns.setText(exDns[PrefManager.getVal(PrefName.DohProvider)])
|
||||
settingsExtensionDns.setAdapter(
|
||||
ArrayAdapter(
|
||||
context, R.layout.item_dropdown, exDns
|
||||
)
|
||||
context,
|
||||
R.layout.item_dropdown,
|
||||
exDns,
|
||||
),
|
||||
)
|
||||
settingsExtensionDns.setOnItemClickListener { _, _, i, _ ->
|
||||
PrefManager.setVal(PrefName.DohProvider, i)
|
||||
@@ -138,283 +141,281 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||
restartApp()
|
||||
}
|
||||
|
||||
settingsRecyclerView.adapter = SettingsAdapter(
|
||||
arrayListOf(
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.ui_settings),
|
||||
desc = getString(R.string.ui_settings_desc),
|
||||
icon = R.drawable.ic_round_auto_awesome_24,
|
||||
onClick = {
|
||||
startActivity(
|
||||
Intent(
|
||||
context,
|
||||
UserInterfaceSettingsActivity::class.java
|
||||
settingsRecyclerView.adapter =
|
||||
SettingsAdapter(
|
||||
arrayListOf(
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.ui_settings),
|
||||
desc = getString(R.string.ui_settings_desc),
|
||||
icon = R.drawable.ic_round_auto_awesome_24,
|
||||
onClick = {
|
||||
startActivity(
|
||||
Intent(
|
||||
context,
|
||||
UserInterfaceSettingsActivity::class.java,
|
||||
),
|
||||
)
|
||||
)
|
||||
},
|
||||
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(
|
||||
type = 1,
|
||||
name = getString(R.string.download_manager_select),
|
||||
desc = getString(R.string.download_manager_select_desc),
|
||||
icon = R.drawable.ic_download_24,
|
||||
onClick = {
|
||||
val managers = arrayOf("Default", "1DM", "ADM")
|
||||
customAlertDialog().apply {
|
||||
setTitle(getString(R.string.download_manager))
|
||||
singleChoiceItems(
|
||||
managers,
|
||||
PrefManager.getVal(PrefName.DownloadManager)
|
||||
) { count ->
|
||||
PrefManager.setVal(PrefName.DownloadManager, count)
|
||||
}
|
||||
show()
|
||||
}
|
||||
}
|
||||
),
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.app_lock),
|
||||
desc = getString(R.string.app_lock_desc),
|
||||
icon = R.drawable.ic_round_lock_open_24,
|
||||
onClick = {
|
||||
customAlertDialog().apply {
|
||||
val view = DialogSetPasswordBinding.inflate(layoutInflater)
|
||||
setTitle(R.string.app_lock)
|
||||
setCustomView(view.root)
|
||||
setPosButton(R.string.ok) {
|
||||
if (view.forgotPasswordCheckbox.isChecked) {
|
||||
PrefManager.setVal(PrefName.OverridePassword, true)
|
||||
},
|
||||
isActivity = true,
|
||||
),
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.download_manager_select),
|
||||
desc = getString(R.string.download_manager_select_desc),
|
||||
icon = R.drawable.ic_download_24,
|
||||
onClick = {
|
||||
val managers = arrayOf("Default", "1DM", "ADM")
|
||||
customAlertDialog().apply {
|
||||
setTitle(getString(R.string.download_manager))
|
||||
singleChoiceItems(
|
||||
managers,
|
||||
PrefManager.getVal(PrefName.DownloadManager),
|
||||
) { count ->
|
||||
PrefManager.setVal(PrefName.DownloadManager, count)
|
||||
}
|
||||
val password = view.passwordInput.text.toString()
|
||||
val confirmPassword = view.confirmPasswordInput.text.toString()
|
||||
if (password == confirmPassword && password.isNotEmpty()) {
|
||||
PrefManager.setVal(PrefName.AppPassword, password)
|
||||
if (view.biometricCheckbox.isChecked) {
|
||||
val canBiometricPrompt =
|
||||
BiometricManager.from(applicationContext)
|
||||
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
|
||||
|
||||
if (canBiometricPrompt) {
|
||||
val biometricPrompt =
|
||||
BiometricPromptUtils.createBiometricPrompt(this@SettingsCommonActivity) { _ ->
|
||||
val token = UUID.randomUUID().toString()
|
||||
PrefManager.setVal(
|
||||
PrefName.BiometricToken,
|
||||
token
|
||||
)
|
||||
toast(R.string.success)
|
||||
}
|
||||
val promptInfo =
|
||||
BiometricPromptUtils.createPromptInfo(this@SettingsCommonActivity)
|
||||
biometricPrompt.authenticate(promptInfo)
|
||||
}
|
||||
|
||||
} else {
|
||||
PrefManager.setVal(PrefName.BiometricToken, "")
|
||||
toast(R.string.success)
|
||||
show()
|
||||
}
|
||||
},
|
||||
),
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.app_lock),
|
||||
desc = getString(R.string.app_lock_desc),
|
||||
icon = R.drawable.ic_round_lock_open_24,
|
||||
onClick = {
|
||||
customAlertDialog().apply {
|
||||
val view = DialogSetPasswordBinding.inflate(layoutInflater)
|
||||
setTitle(R.string.app_lock)
|
||||
setCustomView(view.root)
|
||||
setPosButton(R.string.ok) {
|
||||
if (view.forgotPasswordCheckbox.isChecked) {
|
||||
PrefManager.setVal(PrefName.OverridePassword, true)
|
||||
}
|
||||
} else {
|
||||
toast(R.string.password_mismatch)
|
||||
}
|
||||
}
|
||||
setNegButton(R.string.cancel)
|
||||
setNeutralButton(R.string.remove){
|
||||
PrefManager.setVal(PrefName.AppPassword, "")
|
||||
PrefManager.setVal(PrefName.BiometricToken, "")
|
||||
PrefManager.setVal(PrefName.OverridePassword, false)
|
||||
toast(R.string.success)
|
||||
}
|
||||
setOnShowListener {
|
||||
view.passwordInput.requestFocus()
|
||||
val canAuthenticate =
|
||||
BiometricManager.from(applicationContext).canAuthenticate(
|
||||
BiometricManager.Authenticators.BIOMETRIC_WEAK
|
||||
) == BiometricManager.BIOMETRIC_SUCCESS
|
||||
view.biometricCheckbox.isVisible = canAuthenticate
|
||||
view.biometricCheckbox.isChecked =
|
||||
PrefManager.getVal(PrefName.BiometricToken, "").isNotEmpty()
|
||||
view.forgotPasswordCheckbox.isChecked =
|
||||
PrefManager.getVal(PrefName.OverridePassword)
|
||||
}
|
||||
show()
|
||||
}
|
||||
}
|
||||
val password = view.passwordInput.text.toString()
|
||||
val confirmPassword = view.confirmPasswordInput.text.toString()
|
||||
if (password == confirmPassword && password.isNotEmpty()) {
|
||||
PrefManager.setVal(PrefName.AppPassword, password)
|
||||
if (view.biometricCheckbox.isChecked) {
|
||||
val canBiometricPrompt =
|
||||
BiometricManager
|
||||
.from(applicationContext)
|
||||
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) ==
|
||||
BiometricManager.BIOMETRIC_SUCCESS
|
||||
|
||||
),
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.backup_restore),
|
||||
desc = getString(R.string.backup_restore_desc),
|
||||
icon = R.drawable.backup_restore,
|
||||
onClick = {
|
||||
StoragePermissions.downloadsPermission(context)
|
||||
val selectedArray = mutableListOf(false)
|
||||
val filteredLocations = Location.entries.filter { it.exportable }
|
||||
selectedArray.addAll(List(filteredLocations.size - 1) { false })
|
||||
val dialog = AlertDialog.Builder(context, R.style.MyPopup)
|
||||
.setTitle(R.string.backup_restore).setMultiChoiceItems(
|
||||
filteredLocations.map { it.name }.toTypedArray(),
|
||||
selectedArray.toBooleanArray()
|
||||
) { _, which, isChecked ->
|
||||
selectedArray[which] = isChecked
|
||||
}.setPositiveButton(R.string.button_restore) { dialog, _ ->
|
||||
openDocumentLauncher.launch(arrayOf("*/*"))
|
||||
dialog.dismiss()
|
||||
}.setNegativeButton(R.string.button_backup) { dialog, _ ->
|
||||
if (!selectedArray.contains(true)) {
|
||||
toast(R.string.no_location_selected)
|
||||
return@setNegativeButton
|
||||
}
|
||||
dialog.dismiss()
|
||||
val selected =
|
||||
filteredLocations.filterIndexed { index, _ -> selectedArray[index] }
|
||||
if (selected.contains(Location.Protected)) {
|
||||
passwordAlertDialog(true) { password ->
|
||||
if (password != null) {
|
||||
savePrefsToDownloads(
|
||||
"DantotsuSettings",
|
||||
PrefManager.exportAllPrefs(selected),
|
||||
context,
|
||||
password
|
||||
)
|
||||
if (canBiometricPrompt) {
|
||||
val biometricPrompt =
|
||||
BiometricPromptUtils.createBiometricPrompt(this@SettingsCommonActivity) { _ ->
|
||||
val token = UUID.randomUUID().toString()
|
||||
PrefManager.setVal(
|
||||
PrefName.BiometricToken,
|
||||
token,
|
||||
)
|
||||
toast(R.string.success)
|
||||
}
|
||||
val promptInfo =
|
||||
BiometricPromptUtils.createPromptInfo(this@SettingsCommonActivity)
|
||||
biometricPrompt.authenticate(promptInfo)
|
||||
}
|
||||
} else {
|
||||
toast(R.string.password_cannot_be_empty)
|
||||
PrefManager.setVal(PrefName.BiometricToken, "")
|
||||
toast(R.string.success)
|
||||
}
|
||||
} else {
|
||||
toast(R.string.password_mismatch)
|
||||
}
|
||||
} else {
|
||||
savePrefsToDownloads(
|
||||
"DantotsuSettings",
|
||||
PrefManager.exportAllPrefs(selected),
|
||||
context,
|
||||
null
|
||||
)
|
||||
}
|
||||
}.setNeutralButton(R.string.cancel) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}.create()
|
||||
dialog.window?.setDimAmount(0.8f)
|
||||
dialog.show()
|
||||
},
|
||||
),
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.change_download_location),
|
||||
desc = getString(R.string.change_download_location_desc),
|
||||
icon = R.drawable.ic_round_source_24,
|
||||
onClick = {
|
||||
context.customAlertDialog().apply{
|
||||
setTitle(R.string.change_download_location)
|
||||
setMessage(R.string.download_location_msg)
|
||||
setPosButton(R.string.ok){
|
||||
val oldUri = PrefManager.getVal<String>(PrefName.DownloadsDir)
|
||||
launcher.registerForCallback { success ->
|
||||
if (success) {
|
||||
toast(getString(R.string.please_wait))
|
||||
val newUri =
|
||||
PrefManager.getVal<String>(PrefName.DownloadsDir)
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
Injekt.get<DownloadsManager>().moveDownloadsDir(
|
||||
context, Uri.parse(oldUri), Uri.parse(newUri)
|
||||
) { finished, message ->
|
||||
if (finished) {
|
||||
toast(getString(R.string.success))
|
||||
} else {
|
||||
toast(message)
|
||||
}
|
||||
setNegButton(R.string.cancel)
|
||||
setNeutralButton(R.string.remove) {
|
||||
PrefManager.setVal(PrefName.AppPassword, "")
|
||||
PrefManager.setVal(PrefName.BiometricToken, "")
|
||||
PrefManager.setVal(PrefName.OverridePassword, false)
|
||||
toast(R.string.success)
|
||||
}
|
||||
setOnShowListener {
|
||||
view.passwordInput.requestFocus()
|
||||
val canAuthenticate =
|
||||
BiometricManager.from(applicationContext).canAuthenticate(
|
||||
BiometricManager.Authenticators.BIOMETRIC_WEAK,
|
||||
) == BiometricManager.BIOMETRIC_SUCCESS
|
||||
view.biometricCheckbox.isVisible = canAuthenticate
|
||||
view.biometricCheckbox.isChecked =
|
||||
PrefManager.getVal(PrefName.BiometricToken, "").isNotEmpty()
|
||||
view.forgotPasswordCheckbox.isChecked =
|
||||
PrefManager.getVal(PrefName.OverridePassword)
|
||||
}
|
||||
show()
|
||||
}
|
||||
},
|
||||
),
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.backup_restore),
|
||||
desc = getString(R.string.backup_restore_desc),
|
||||
icon = R.drawable.backup_restore,
|
||||
onClick = {
|
||||
StoragePermissions.downloadsPermission(context)
|
||||
val filteredLocations = Location.entries.filter { it.exportable }
|
||||
val selectedArray = BooleanArray(filteredLocations.size) { false }
|
||||
context.customAlertDialog().apply {
|
||||
setTitle(R.string.backup_restore)
|
||||
multiChoiceItems(
|
||||
filteredLocations.map { it.name }.toTypedArray(),
|
||||
selectedArray,
|
||||
) { updatedSelection ->
|
||||
for (i in updatedSelection.indices) {
|
||||
selectedArray[i] = updatedSelection[i]
|
||||
}
|
||||
}
|
||||
setPosButton(R.string.button_restore) {
|
||||
openDocumentLauncher.launch(arrayOf("*/*"))
|
||||
}
|
||||
setNegButton(R.string.button_backup) {
|
||||
if (!selectedArray.contains(true)) {
|
||||
toast(R.string.no_location_selected)
|
||||
return@setNegButton
|
||||
}
|
||||
val selected =
|
||||
filteredLocations.filterIndexed { index, _ -> selectedArray[index] }
|
||||
if (selected.contains(Location.Protected)) {
|
||||
passwordAlertDialog(true) { password ->
|
||||
if (password != null) {
|
||||
savePrefsToDownloads(
|
||||
"DantotsuSettings",
|
||||
PrefManager.exportAllPrefs(selected),
|
||||
context,
|
||||
password,
|
||||
)
|
||||
} else {
|
||||
toast(R.string.password_cannot_be_empty)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
toast(getString(R.string.error))
|
||||
savePrefsToDownloads(
|
||||
"DantotsuSettings",
|
||||
PrefManager.exportAllPrefs(selected),
|
||||
context,
|
||||
null,
|
||||
)
|
||||
}
|
||||
}
|
||||
launcher.launch()
|
||||
setNeutralButton(R.string.cancel) {}
|
||||
show()
|
||||
}
|
||||
setNegButton(R.string.cancel)
|
||||
show()
|
||||
}
|
||||
}
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.always_continue_content),
|
||||
desc = getString(R.string.always_continue_content_desc),
|
||||
icon = R.drawable.ic_round_delete_24,
|
||||
isChecked = PrefManager.getVal(PrefName.ContinueMedia),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.ContinueMedia, isChecked)
|
||||
}
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.hide_private),
|
||||
desc = getString(R.string.hide_private_desc),
|
||||
icon = R.drawable.ic_round_remove_red_eye_24,
|
||||
isChecked = PrefManager.getVal(PrefName.HidePrivate),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.HidePrivate, isChecked)
|
||||
restartApp()
|
||||
}
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.search_source_list),
|
||||
desc = getString(R.string.search_source_list_desc),
|
||||
icon = R.drawable.ic_round_search_sources_24,
|
||||
isChecked = PrefManager.getVal(PrefName.SearchSources),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.SearchSources, isChecked)
|
||||
}
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.recentlyListOnly),
|
||||
desc = getString(R.string.recentlyListOnly_desc),
|
||||
icon = R.drawable.ic_round_new_releases_24,
|
||||
isChecked = PrefManager.getVal(PrefName.RecentlyListOnly),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.RecentlyListOnly, isChecked)
|
||||
}
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.adult_only_content),
|
||||
desc = getString(R.string.adult_only_content_desc),
|
||||
icon = R.drawable.ic_round_nsfw_24,
|
||||
isChecked = PrefManager.getVal(PrefName.AdultOnly),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.AdultOnly, isChecked)
|
||||
restartApp()
|
||||
},
|
||||
isVisible = Anilist.adult
|
||||
|
||||
},
|
||||
),
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.change_download_location),
|
||||
desc = getString(R.string.change_download_location_desc),
|
||||
icon = R.drawable.ic_round_source_24,
|
||||
onClick = {
|
||||
context.customAlertDialog().apply {
|
||||
setTitle(R.string.change_download_location)
|
||||
setMessage(R.string.download_location_msg)
|
||||
setPosButton(R.string.ok) {
|
||||
val oldUri = PrefManager.getVal<String>(PrefName.DownloadsDir)
|
||||
launcher.registerForCallback { success ->
|
||||
if (success) {
|
||||
toast(getString(R.string.please_wait))
|
||||
val newUri =
|
||||
PrefManager.getVal<String>(PrefName.DownloadsDir)
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
Injekt.get<DownloadsManager>().moveDownloadsDir(
|
||||
context,
|
||||
Uri.parse(oldUri),
|
||||
Uri.parse(newUri),
|
||||
) { finished, message ->
|
||||
if (finished) {
|
||||
toast(getString(R.string.success))
|
||||
} else {
|
||||
toast(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
toast(getString(R.string.error))
|
||||
}
|
||||
}
|
||||
launcher.launch()
|
||||
}
|
||||
setNegButton(R.string.cancel)
|
||||
show()
|
||||
}
|
||||
},
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.always_continue_content),
|
||||
desc = getString(R.string.always_continue_content_desc),
|
||||
icon = R.drawable.ic_round_delete_24,
|
||||
isChecked = PrefManager.getVal(PrefName.ContinueMedia),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.ContinueMedia, isChecked)
|
||||
},
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.hide_private),
|
||||
desc = getString(R.string.hide_private_desc),
|
||||
icon = R.drawable.ic_round_remove_red_eye_24,
|
||||
isChecked = PrefManager.getVal(PrefName.HidePrivate),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.HidePrivate, isChecked)
|
||||
restartApp()
|
||||
},
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.search_source_list),
|
||||
desc = getString(R.string.search_source_list_desc),
|
||||
icon = R.drawable.ic_round_search_sources_24,
|
||||
isChecked = PrefManager.getVal(PrefName.SearchSources),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.SearchSources, isChecked)
|
||||
},
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.recentlyListOnly),
|
||||
desc = getString(R.string.recentlyListOnly_desc),
|
||||
icon = R.drawable.ic_round_new_releases_24,
|
||||
isChecked = PrefManager.getVal(PrefName.RecentlyListOnly),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.RecentlyListOnly, isChecked)
|
||||
},
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.adult_only_content),
|
||||
desc = getString(R.string.adult_only_content_desc),
|
||||
icon = R.drawable.ic_round_nsfw_24,
|
||||
isChecked = PrefManager.getVal(PrefName.AdultOnly),
|
||||
switch = { isChecked, _ ->
|
||||
PrefManager.setVal(PrefName.AdultOnly, isChecked)
|
||||
restartApp()
|
||||
},
|
||||
isVisible = Anilist.adult,
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
settingsRecyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
setHasFixedSize(true)
|
||||
}
|
||||
var previousStart: View = when (PrefManager.getVal<Int>(PrefName.DefaultStartUpTab)) {
|
||||
0 -> uiSettingsAnime
|
||||
1 -> uiSettingsHome
|
||||
2 -> uiSettingsManga
|
||||
else -> uiSettingsHome
|
||||
}
|
||||
var previousStart: View =
|
||||
when (PrefManager.getVal<Int>(PrefName.DefaultStartUpTab)) {
|
||||
0 -> uiSettingsAnime
|
||||
1 -> uiSettingsHome
|
||||
2 -> uiSettingsManga
|
||||
else -> uiSettingsHome
|
||||
}
|
||||
previousStart.alpha = 1f
|
||||
fun uiDefault(mode: Int, current: View) {
|
||||
|
||||
fun uiDefault(
|
||||
mode: Int,
|
||||
current: View,
|
||||
) {
|
||||
previousStart.alpha = 0.33f
|
||||
previousStart = current
|
||||
current.alpha = 1f
|
||||
@@ -433,11 +434,13 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||
uiSettingsManga.setOnClickListener {
|
||||
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') }
|
||||
|
||||
// Inflate the dialog layout
|
||||
@@ -447,7 +450,9 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||
box.setSingleLine()
|
||||
|
||||
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)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.cancel) { dialog, _ ->
|
||||
@@ -459,7 +464,10 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||
fun handleOkAction() {
|
||||
val editText = dialogView.userAgentTextBox
|
||||
if (editText.text?.isNotBlank() == true) {
|
||||
editText.text?.toString()?.trim()?.toCharArray(password)
|
||||
editText.text
|
||||
?.toString()
|
||||
?.trim()
|
||||
?.toCharArray(password)
|
||||
dialog.dismiss()
|
||||
callback(password)
|
||||
} else {
|
||||
@@ -475,18 +483,20 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
dialogView.subtitle.visibility = View.VISIBLE
|
||||
if (!isExporting) dialogView.subtitle.text =
|
||||
getString(R.string.enter_password_to_decrypt_file)
|
||||
if (!isExporting) {
|
||||
dialogView.subtitle.text =
|
||||
getString(R.string.enter_password_to_decrypt_file)
|
||||
}
|
||||
|
||||
|
||||
dialog.window?.setDimAmount(0.8f)
|
||||
dialog.window?.apply {
|
||||
setDimAmount(0.8f)
|
||||
attributes.windowAnimations = android.R.style.Animation_Dialog
|
||||
}
|
||||
dialog.show()
|
||||
|
||||
// Override the positive button here
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
handleOkAction()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user