This commit is contained in:
Finnley Somdahl 2023-12-01 01:22:15 -06:00
parent 1df528c0dc
commit afa960c808
171 changed files with 3458 additions and 1915 deletions

View file

@ -1,31 +1,19 @@
package ani.dantotsu
import android.R
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Resources
import android.os.Bundle
import android.util.Log
import android.util.LongSparseArray
import android.util.TypedValue
import androidx.annotation.ColorInt
import androidx.multidex.MultiDex
import androidx.multidex.MultiDexApplication
import ani.dantotsu.aniyomi.anime.custom.AppModule
import ani.dantotsu.aniyomi.anime.custom.PreferenceModule
import ani.dantotsu.download.Download
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.others.DisabledReports
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.MangaSources
import ani.dantotsu.parsers.NovelSources
import ani.dantotsu.parsers.novel.NovelExtensionManager
import com.google.android.material.color.DynamicColors
import com.google.android.material.color.HarmonizedColorAttributes
import com.google.android.material.color.HarmonizedColors
import com.google.android.material.color.HarmonizedColorsOptions
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import eu.kanade.tachiyomi.data.notification.Notifications
@ -41,8 +29,6 @@ import logcat.LogcatLogger
import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
import java.lang.reflect.Field
@SuppressLint("StaticFieldLeak")
@ -65,7 +51,7 @@ class App : MultiDexApplication() {
super.onCreate()
val sharedPreferences = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
val useMaterialYou = sharedPreferences.getBoolean("use_material_you", false)
if(useMaterialYou) {
if (useMaterialYou) {
DynamicColors.applyToActivitiesIfAvailable(this)
//TODO: HarmonizedColors
}
@ -136,7 +122,7 @@ class App : MultiDexApplication() {
companion object {
private var instance: App? = null
var context : Context? = null
var context: Context? = null
fun currentContext(): Context? {
return instance?.mFTActivityLifecycleCallbacks?.currentActivity ?: context
}

View file

@ -132,7 +132,8 @@ fun <T> loadData(fileName: String, context: Context? = null, toast: Boolean = tr
fun initActivity(a: Activity) {
val window = a.window
WindowCompat.setDecorFitsSystemWindows(window, false)
val uiSettings = loadData<UserInterfaceSettings>("ui_settings", toast = false) ?: UserInterfaceSettings().apply {
val uiSettings = loadData<UserInterfaceSettings>("ui_settings", toast = false)
?: UserInterfaceSettings().apply {
saveData("ui_settings", this)
}
uiSettings.darkMode.apply {
@ -146,7 +147,8 @@ fun initActivity(a: Activity) {
}
if (uiSettings.immersiveMode) {
if (navBarHeight == 0) {
ViewCompat.getRootWindowInsets(window.decorView.findViewById(android.R.id.content))?.apply {
ViewCompat.getRootWindowInsets(window.decorView.findViewById(android.R.id.content))
?.apply {
navBarHeight = this.getInsets(WindowInsetsCompat.Type.systemBars()).bottom
}
}
@ -160,7 +162,8 @@ fun initActivity(a: Activity) {
}
} else
if (statusBarHeight == 0) {
val windowInsets = ViewCompat.getRootWindowInsets(window.decorView.findViewById(android.R.id.content))
val windowInsets =
ViewCompat.getRootWindowInsets(window.decorView.findViewById(android.R.id.content))
if (windowInsets != null) {
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
statusBarHeight = insets.top
@ -205,7 +208,8 @@ open class BottomSheetDialogFragment : BottomSheetDialogFragment() {
}
fun isOnline(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return tryWith {
val cap = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
return@tryWith if (cap != null) {
@ -239,7 +243,8 @@ fun startMainActivity(activity: Activity, bundle: Bundle? = null) {
}
class DatePickerFragment(activity: Activity, var date: FuzzyDate = FuzzyDate().getToday()) : DialogFragment(),
class DatePickerFragment(activity: Activity, var date: FuzzyDate = FuzzyDate().getToday()) :
DialogFragment(),
DatePickerDialog.OnDateSetListener {
var dialog: DatePickerDialog
@ -264,9 +269,20 @@ class DatePickerFragment(activity: Activity, var date: FuzzyDate = FuzzyDate().g
}
}
class InputFilterMinMax(private val min: Double, private val max: Double, private val status: AutoCompleteTextView? = null) :
class InputFilterMinMax(
private val min: Double,
private val max: Double,
private val status: AutoCompleteTextView? = null
) :
InputFilter {
override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
override fun filter(
source: CharSequence,
start: Int,
end: Int,
dest: Spanned,
dstart: Int,
dend: Int
): CharSequence? {
try {
val input = (dest.toString() + source.toString()).toDouble()
if (isInRange(min, max, input)) return null
@ -289,11 +305,20 @@ class InputFilterMinMax(private val min: Double, private val max: Double, privat
}
class ZoomOutPageTransformer(private val uiSettings: UserInterfaceSettings) : ViewPager2.PageTransformer {
class ZoomOutPageTransformer(private val uiSettings: UserInterfaceSettings) :
ViewPager2.PageTransformer {
override fun transformPage(view: View, position: Float) {
if (position == 0.0f && uiSettings.layoutAnimations) {
setAnimation(view.context, view, uiSettings, 300, floatArrayOf(1.3f, 1f, 1.3f, 1f), 0.5f to 0f)
ObjectAnimator.ofFloat(view, "alpha", 0f, 1.0f).setDuration((200 * uiSettings.animationSpeed).toLong()).start()
setAnimation(
view.context,
view,
uiSettings,
300,
floatArrayOf(1.3f, 1f, 1.3f, 1f),
0.5f to 0f
)
ObjectAnimator.ofFloat(view, "alpha", 0f, 1.0f)
.setDuration((200 * uiSettings.animationSpeed).toLong()).start()
}
}
}
@ -328,7 +353,11 @@ class FadingEdgeRecyclerView : RecyclerView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
override fun isPaddingOffsetRequired(): Boolean {
return !clipToPadding
@ -414,7 +443,7 @@ fun MutableList<ShowResponse>.sortByTitle(string: String) {
}
fun String.findBetween(a: String, b: String): String? {
val string = substringAfter(a, "").substringBefore(b,"")
val string = substringAfter(a, "").substringBefore(b, "")
return string.ifEmpty { null }
}
@ -423,8 +452,7 @@ fun ImageView.loadImage(url: String?, size: Int = 0) {
val localFile = File(url)
if (localFile.exists()) {
loadLocalImage(localFile, size)
}
else {
} else {
loadImage(FileUrl(url), size)
}
}
@ -434,7 +462,8 @@ fun ImageView.loadImage(file: FileUrl?, size: Int = 0) {
if (file?.url?.isNotEmpty() == true) {
tryWith {
val glideUrl = GlideUrl(file.url) { file.headers }
Glide.with(this.context).load(glideUrl).transition(withCrossFade()).override(size).into(this)
Glide.with(this.context).load(glideUrl).transition(withCrossFade()).override(size)
.into(this)
}
}
}
@ -442,7 +471,8 @@ fun ImageView.loadImage(file: FileUrl?, size: Int = 0) {
fun ImageView.loadLocalImage(file: File?, size: Int = 0) {
if (file?.exists() == true) {
tryWith {
Glide.with(this.context).load(file).transition(withCrossFade()).override(size).into(this)
Glide.with(this.context).load(file).transition(withCrossFade()).override(size)
.into(this)
}
}
}
@ -500,7 +530,12 @@ abstract class GesturesListener : GestureDetector.SimpleOnGestureListener() {
return super.onDoubleTap(e)
}
override fun onScroll(e1: MotionEvent?, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
override fun onScroll(
e1: MotionEvent?,
e2: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
onScrollYClick(distanceY)
onScrollXClick(distanceX)
return super.onScroll(e1, e2, distanceX, distanceY)
@ -642,9 +677,15 @@ fun countDown(media: Media, view: ViewGroup) {
val v = ItemCountDownBinding.inflate(LayoutInflater.from(view.context), view, false)
view.addView(v.root, 0)
v.mediaCountdownText.text =
currActivity()?.getString(R.string.episode_release_countdown, media.anime.nextAiringEpisode!! + 1)
currActivity()?.getString(
R.string.episode_release_countdown,
media.anime.nextAiringEpisode!! + 1
)
object : CountDownTimer((media.anime.nextAiringEpisodeTime!! + 10000) * 1000 - System.currentTimeMillis(), 1000) {
object : CountDownTimer(
(media.anime.nextAiringEpisodeTime!! + 10000) * 1000 - System.currentTimeMillis(),
1000
) {
override fun onTick(millisUntilFinished: Long) {
val a = millisUntilFinished / 1000
v.mediaCountdown.text = currActivity()?.getString(
@ -735,7 +776,8 @@ fun toast(string: String?) {
if (string != null) {
logger(string)
MainScope().launch {
Toast.makeText(currActivity()?.application ?: return@launch, string, Toast.LENGTH_SHORT).show()
Toast.makeText(currActivity()?.application ?: return@launch, string, Toast.LENGTH_SHORT)
.show()
}
}
}
@ -744,7 +786,11 @@ fun snackString(s: String?, activity: Activity? = null, clipboard: String? = nul
if (s != null) {
(activity ?: currActivity())?.apply {
runOnUiThread {
val snackBar = Snackbar.make(window.decorView.findViewById(android.R.id.content), s, Snackbar.LENGTH_SHORT)
val snackBar = Snackbar.make(
window.decorView.findViewById(android.R.id.content),
s,
Snackbar.LENGTH_SHORT
)
snackBar.view.apply {
updateLayoutParams<FrameLayout.LayoutParams> {
gravity = (Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM)
@ -769,7 +815,8 @@ fun snackString(s: String?, activity: Activity? = null, clipboard: String? = nul
}
}
open class NoPaddingArrayAdapter<T>(context: Context, layoutId: Int, items: List<T>) : ArrayAdapter<T>(context, layoutId, items) {
open class NoPaddingArrayAdapter<T>(context: Context, layoutId: Int, items: List<T>) :
ArrayAdapter<T>(context, layoutId, items) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = super.getView(position, convertView, parent)
view.setPadding(0, view.paddingTop, view.paddingRight, view.paddingBottom)
@ -790,12 +837,17 @@ class SpinnerNoSwipe : androidx.appcompat.widget.AppCompatSpinner {
setup()
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
setup()
}
private fun setup() {
mGestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
mGestureDetector =
GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapUp(e: MotionEvent): Boolean {
return performClick()
}
@ -843,7 +895,11 @@ fun getCurrentBrightnessValue(context: Context): Float {
}
fun getCur(): Float {
return Settings.System.getInt(context.contentResolver, Settings.System.SCREEN_BRIGHTNESS, 127).toFloat()
return Settings.System.getInt(
context.contentResolver,
Settings.System.SCREEN_BRIGHTNESS,
127
).toFloat()
}
return brightnessConverter(getCur() / getMax(), true)

View file

@ -1,15 +1,9 @@
package ani.dantotsu
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.graphics.Color
import android.graphics.drawable.Animatable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.GradientDrawable
import android.net.Uri
import android.os.Build
@ -17,35 +11,26 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnticipateInterpolator
import android.widget.FrameLayout
import android.widget.TextView
import androidx.activity.addCallback
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.animation.doOnEnd
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.doOnAttach
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider.NewInstanceFactory.Companion.instance
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.adapter.FragmentStateAdapter
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistHomeViewModel
import ani.dantotsu.databinding.ActivityMainBinding
import ani.dantotsu.databinding.ItemNavbarBinding
import ani.dantotsu.databinding.SplashScreenBinding
import ani.dantotsu.download.manga.MangaDownloaderService
import ani.dantotsu.download.manga.OfflineMangaFragment
import ani.dantotsu.home.AnimeFragment
import ani.dantotsu.home.HomeFragment
import ani.dantotsu.home.LoginFragment
@ -53,34 +38,18 @@ import ani.dantotsu.home.MangaFragment
import ani.dantotsu.home.NoInternet
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.others.CustomBottomDialog
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.MangaSources
import ani.dantotsu.settings.SettingsActivity
import ani.dantotsu.others.LangSet
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import ani.dantotsu.parsers.NovelInterface
import com.google.firebase.crashlytics.FirebaseCrashlytics
import dalvik.system.PathClassLoader
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import nl.joery.animatedbottombar.AnimatedBottomBar
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.Serializable
import java.nio.channels.FileChannel
class MainActivity : AppCompatActivity() {
@ -91,7 +60,6 @@ class MainActivity : AppCompatActivity() {
private var uiSettings = UserInterfaceSettings()
override fun onCreate(savedInstanceState: Bundle?) {
ThemeManager(this).applyTheme()
LangSet.setLocale(this)
@ -276,10 +244,6 @@ class MainActivity : AppCompatActivity() {
}
override fun onResume() {
super.onResume()
}
//ViewPager
private class ViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :

View file

@ -9,22 +9,23 @@ import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.ResponseParser
import com.lagradost.nicehttp.addGenericDns
import eu.kanade.tachiyomi.network.NetworkHelper
import kotlinx.coroutines.*
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import okhttp3.Cache
import okhttp3.OkHttpClient
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
import java.io.PrintWriter
import java.io.Serializable
import java.io.StringWriter
import java.util.concurrent.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
@ -114,7 +115,11 @@ fun <T> tryWith(post: Boolean = false, snackbar: Boolean = true, call: () -> T):
}
}
suspend fun <T> tryWithSuspend(post: Boolean = false, snackbar: Boolean = true, call: suspend () -> T): T? {
suspend fun <T> tryWithSuspend(
post: Boolean = false,
snackbar: Boolean = true,
call: suspend () -> T
): T? {
return try {
call.invoke()
} catch (e: Throwable) {
@ -194,17 +199,18 @@ fun OkHttpClient.Builder.addAdGuardDns() = (
@Suppress("BlockingMethodInNonBlockingContext")
suspend fun webViewInterface(webViewDialog: WebViewBottomDialog): Map<String, String>? {
var map : Map<String,String>? = null
var map: Map<String, String>? = null
val latch = CountDownLatch(1)
webViewDialog.callback = {
map = it
latch.countDown()
}
val fragmentManager = (currContext() as FragmentActivity?)?.supportFragmentManager ?: return null
val fragmentManager =
(currContext() as FragmentActivity?)?.supportFragmentManager ?: return null
webViewDialog.show(fragmentManager, "web-view")
delay(0)
latch.await(2,TimeUnit.MINUTES)
latch.await(2, TimeUnit.MINUTES)
return map
}
@ -217,5 +223,5 @@ suspend fun webViewInterface(type: String, url: FileUrl): Map<String, String>? {
}
suspend fun webViewInterface(type: String, url: String): Map<String, String>? {
return webViewInterface(type,FileUrl(url))
return webViewInterface(type, FileUrl(url))
}

View file

@ -4,18 +4,20 @@ package ani.dantotsu.aniyomi.anime.custom
import android.app.Application
import android.content.Context
import androidx.core.content.ContextCompat
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.media.manga.MangaCache
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import tachiyomi.core.preference.PreferenceStore
import ani.dantotsu.parsers.novel.NovelExtensionManager
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.source.anime.AndroidAnimeSourceManager
import eu.kanade.tachiyomi.source.manga.AndroidMangaSourceManager
import kotlinx.serialization.json.Json
import tachiyomi.core.preference.PreferenceStore
import tachiyomi.domain.source.anime.service.AnimeSourceManager
import tachiyomi.domain.source.manga.service.MangaSourceManager
import uy.kohesive.injekt.api.InjektModule
@ -23,8 +25,6 @@ import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addSingleton
import uy.kohesive.injekt.api.addSingletonFactory
import uy.kohesive.injekt.api.get
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.parsers.novel.NovelExtensionManager
class AppModule(val app: Application) : InjektModule {
override fun InjektRegistrar.registerInjectables() {

View file

@ -16,7 +16,7 @@ fun updateProgress(media: Media, number: String) {
if (Anilist.userid != null) {
CoroutineScope(Dispatchers.IO).launch {
val a = number.toFloatOrNull()?.roundToInt()
if ((a?:0) > (media.userProgress?:0)) {
if ((a ?: 0) > (media.userProgress ?: 0)) {
Anilist.mutation.editList(
media.id,
a,

View file

@ -10,7 +10,7 @@ import ani.dantotsu.currContext
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.tryWithSuspend
import java.io.File
import java.util.*
import java.util.Calendar
object Anilist {
val query: AnilistQueries = AnilistQueries()
@ -29,7 +29,12 @@ object Anilist {
var tags: Map<Boolean, List<String>>? = null
val sortBy = listOf(
"SCORE_DESC","POPULARITY_DESC","TRENDING_DESC","TITLE_ENGLISH","TITLE_ENGLISH_DESC","SCORE"
"SCORE_DESC",
"POPULARITY_DESC",
"TRENDING_DESC",
"TITLE_ENGLISH",
"TITLE_ENGLISH_DESC",
"SCORE"
)
val seasons = listOf(
@ -132,7 +137,12 @@ object Anilist {
if (token != null || force) {
if (token != null && useToken) headers["Authorization"] = "Bearer $token"
val json = client.post("https://graphql.anilist.co/", headers, data = data, cacheTime = cache ?: 10)
val json = client.post(
"https://graphql.anilist.co/",
headers,
data = data,
cacheTime = cache ?: 10
)
if (!json.text.startsWith("{")) throw Exception(currContext()?.getString(R.string.anilist_down))
if (show) println("Response : ${json.text}")
json.parsed()

View file

@ -20,7 +20,7 @@ class AnilistMutations {
repeat: Int? = null,
notes: String? = null,
status: String? = null,
private:Boolean? = null,
private: Boolean? = null,
startedAt: FuzzyDate? = null,
completedAt: FuzzyDate? = null,
customList: List<String>? = null
@ -41,7 +41,7 @@ class AnilistMutations {
${if (repeat != null) ""","repeat":$repeat""" else ""}
${if (notes != null) ""","notes":"${notes.replace("\n", "\\n")}"""" else ""}
${if (status != null) ""","status":"$status"""" else ""}
${if (customList !=null) ""","customLists":[${customList.joinToString { "\"$it\"" }}]""" else ""}
${if (customList != null) ""","customLists":[${customList.joinToString { "\"$it\"" }}]""" else ""}
}""".replace("\n", "").replace(""" """, "")
println(variables)
executeQuery<JsonObject>(query, variables, show = true)

View file

@ -2,13 +2,13 @@ package ani.dantotsu.connections.anilist
import android.app.Activity
import ani.dantotsu.R
import ani.dantotsu.checkGenreTime
import ani.dantotsu.checkId
import ani.dantotsu.connections.anilist.Anilist.authorRoles
import ani.dantotsu.connections.anilist.Anilist.executeQuery
import ani.dantotsu.connections.anilist.api.FuzzyDate
import ani.dantotsu.connections.anilist.api.Page
import ani.dantotsu.connections.anilist.api.Query
import ani.dantotsu.checkGenreTime
import ani.dantotsu.checkId
import ani.dantotsu.currContext
import ani.dantotsu.loadData
import ani.dantotsu.logError
@ -113,9 +113,13 @@ class AnilistQueries {
name = i.node?.name?.userPreferred,
image = i.node?.image?.medium,
banner = media.banner ?: media.cover,
role = when (i.role.toString()){
"MAIN" -> currContext()?.getString(R.string.main_role) ?: "MAIN"
"SUPPORTING" -> currContext()?.getString(R.string.supporting_role) ?: "SUPPORTING"
role = when (i.role.toString()) {
"MAIN" -> currContext()?.getString(R.string.main_role)
?: "MAIN"
"SUPPORTING" -> currContext()?.getString(R.string.supporting_role)
?: "SUPPORTING"
else -> i.role.toString()
}
)
@ -129,11 +133,16 @@ class AnilistQueries {
val m = Media(mediaEdge)
media.relations?.add(m)
if (m.relation == "SEQUEL") {
media.sequel = if ((media.sequel?.popularity ?: 0) < (m.popularity ?: 0)) m else media.sequel
media.sequel =
if ((media.sequel?.popularity ?: 0) < (m.popularity
?: 0)
) m else media.sequel
} else if (m.relation == "PREQUEL") {
media.prequel =
if ((media.prequel?.popularity ?: 0) < (m.popularity ?: 0)) m else media.prequel
if ((media.prequel?.popularity ?: 0) < (m.popularity
?: 0)
) m else media.prequel
}
}
media.relations?.sortByDescending { it.popularity }
@ -199,17 +208,19 @@ class AnilistQueries {
)
}
media.anime.nextAiringEpisodeTime = fetchedMedia.nextAiringEpisode?.airingAt?.toLong()
media.anime.nextAiringEpisodeTime =
fetchedMedia.nextAiringEpisode?.airingAt?.toLong()
fetchedMedia.externalLinks?.forEach { i ->
when (i.site.lowercase()) {
"youtube" -> media.anime.youtube = i.url
"crunchyroll" -> media.crunchySlug = i.url?.split("/")?.getOrNull(3)
"crunchyroll" -> media.crunchySlug =
i.url?.split("/")?.getOrNull(3)
"vrv" -> media.vrvId = i.url?.split("/")?.getOrNull(4)
}
}
}
else if (media.manga != null) {
} else if (media.manga != null) {
fetchedMedia.staff?.edges?.find { authorRoles.contains(it.role?.trim()) }?.node?.let {
media.manga.author = Author(
it.id.toString(),
@ -241,10 +252,10 @@ class AnilistQueries {
return media
}
suspend fun continueMedia(type: String,planned:Boolean=false): ArrayList<Media> {
suspend fun continueMedia(type: String, planned: Boolean = false): ArrayList<Media> {
val returnArray = arrayListOf<Media>()
val map = mutableMapOf<Int, Media>()
val statuses = if(!planned) arrayOf("CURRENT", "REPEATING") else arrayOf("PLANNING")
val statuses = if (!planned) arrayOf("CURRENT", "REPEATING") else arrayOf("PLANNING")
suspend fun repeat(status: String) {
val response =
executeQuery<Query.MediaListCollection>(""" { MediaListCollection(userId: ${Anilist.userid}, type: $type, status: $status , sort: UPDATED_TIME ) { lists { entries { progress private score(format:POINT_100) status media { id idMal type isAdult status chapters episodes nextAiringEpisode {episode} meanScore isFavourite format bannerImage coverImage{large} title { english romaji userPreferred } } } } } } """)
@ -275,21 +286,21 @@ class AnilistQueries {
var hasNextPage = true
var page = 0
suspend fun getNextPage(page:Int): List<Media> {
suspend fun getNextPage(page: Int): List<Media> {
val response =
executeQuery<Query.User>("""{User(id:${Anilist.userid}){id favourites{${if (anime) "anime" else "manga"}(page:$page){pageInfo{hasNextPage}edges{favouriteOrder node{id idMal isAdult mediaListEntry{ progress private score(format:POINT_100) status } chapters isFavourite format episodes nextAiringEpisode{episode}meanScore isFavourite format startDate{year month day} title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}}}}""")
val favourites = response?.data?.user?.favourites
val apiMediaList = if (anime) favourites?.anime else favourites?.manga
hasNextPage = apiMediaList?.pageInfo?.hasNextPage ?: false
return apiMediaList?.edges?.mapNotNull {
it.node?.let { i->
it.node?.let { i ->
Media(i).apply { isFav = true }
}
} ?: return listOf()
}
val responseArray = arrayListOf<Media>()
while(hasNextPage){
while (hasNextPage) {
page++
responseArray.addAll(getNextPage(page))
}
@ -361,7 +372,11 @@ class AnilistQueries {
return default
}
suspend fun getMediaLists(anime: Boolean, userId: Int, sortOrder: String? = null): MutableMap<String, ArrayList<Media>> {
suspend fun getMediaLists(
anime: Boolean,
userId: Int,
sortOrder: String? = null
): MutableMap<String, ArrayList<Media>> {
val response =
executeQuery<Query.MediaListCollection>("""{ MediaListCollection(userId: $userId, type: ${if (anime) "ANIME" else "MANGA"}) { lists { name isCustomList entries { status progress private score(format:POINT_100) updatedAt media { id idMal isAdult type status chapters episodes nextAiringEpisode {episode} bannerImage meanScore isFavourite format coverImage{large} startDate{year month day} title {english romaji userPreferred } } } } user { id mediaListOptions { rowOrder animeList { sectionOrder } mangaList { sectionOrder } } } } }""")
val sorted = mutableMapOf<String, ArrayList<Media>>()
@ -388,7 +403,7 @@ class AnilistQueries {
if (unsorted.containsKey(it)) sorted[it] = unsorted[it]!!
}
unsorted.forEach {
if(!sorted.containsKey(it.key)) sorted[it.key] = it.value
if (!sorted.containsKey(it.key)) sorted[it.key] = it.value
}
sorted["Favourites"] = favMedia(anime)
@ -399,7 +414,14 @@ class AnilistQueries {
val sort = sortOrder ?: options?.rowOrder
for (i in sorted.keys) {
when (sort) {
"score" -> sorted[i]?.sortWith { b, a -> compareValuesBy(a, b, { it.userScore }, { it.meanScore }) }
"score" -> sorted[i]?.sortWith { b, a ->
compareValuesBy(
a,
b,
{ it.userScore },
{ it.meanScore })
}
"title" -> sorted[i]?.sortWith(compareBy { it.userPreferredName })
"updatedAt" -> sorted[i]?.sortWith(compareByDescending { it.userUpdatedAt })
"release" -> sorted[i]?.sortWith(compareByDescending { it.startDate })
@ -559,18 +581,36 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
${if (seasonYear != null) ""","seasonYear":"$seasonYear"""" else ""}
${if (season != null) ""","season":"$season"""" else ""}
${if (search != null) ""","search":"$search"""" else ""}
${if (sort!=null) ""","sort":"$sort"""" else ""}
${if (sort != null) ""","sort":"$sort"""" else ""}
${if (format != null) ""","format":"${format.replace(" ", "_")}"""" else ""}
${if (genres?.isNotEmpty() == true) ""","genres":[${genres.joinToString { "\"$it\"" }}]""" else ""}
${
if (excludedGenres?.isNotEmpty() == true)
""","excludedGenres":[${excludedGenres.joinToString { "\"${it.replace("Not ", "")}\"" }}]"""
""","excludedGenres":[${
excludedGenres.joinToString {
"\"${
it.replace(
"Not ",
""
)
}\""
}
}]"""
else ""
}
${if (tags?.isNotEmpty() == true) ""","tags":[${tags.joinToString { "\"$it\"" }}]""" else ""}
${
if (excludedTags?.isNotEmpty() == true)
""","excludedTags":[${excludedTags.joinToString { "\"${it.replace("Not ", "")}\"" }}]"""
""","excludedTags":[${
excludedTags.joinToString {
"\"${
it.replace(
"Not ",
""
)
}\""
}
}]"""
else ""
}
}""".replace("\n", " ").replace(""" """, "")
@ -622,7 +662,7 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
greater: Long = 0,
lesser: Long = System.currentTimeMillis() / 1000 - 10000
): MutableList<Media>? {
suspend fun execute(page:Int = 1):Page?{
suspend fun execute(page: Int = 1): Page? {
val query = """{
Page(page:$page,perPage:50) {
pageInfo {
@ -668,7 +708,7 @@ Page(page:$page,perPage:50) {
}""".replace("\n", " ").replace(""" """, "")
return executeQuery<Query.Page>(query, force = true)?.data?.page
}
if(smaller) {
if (smaller) {
val response = execute()?.airingSchedules ?: return null
val idArr = mutableListOf<Int>()
val listOnly = loadData("recently_list_only") ?: false
@ -682,11 +722,11 @@ Page(page:$page,perPage:50) {
else null
}
}.toMutableList()
}else{
} else {
var i = 1
val list = mutableListOf<Media>()
var res : Page? = null
suspend fun next(){
var res: Page? = null
suspend fun next() {
res = execute(i)
list.addAll(res?.airingSchedules?.mapNotNull { j ->
j.media?.let {
@ -694,10 +734,10 @@ Page(page:$page,perPage:50) {
Media(it).apply { relation = "${j.episode},${j.airingAt}" }
} else null
}
}?: listOf())
} ?: listOf())
}
next()
while (res?.pageInfo?.hasNextPage == true){
while (res?.pageInfo?.hasNextPage == true) {
next()
i++
}
@ -822,7 +862,8 @@ Page(page:$page,perPage:50) {
var page = 0
while (hasNextPage) {
page++
hasNextPage = executeQuery<Query.Studio>(query(page), force = true)?.data?.studio?.media?.let {
hasNextPage =
executeQuery<Query.Studio>(query(page), force = true)?.data?.studio?.media?.let {
it.edges?.forEach { i ->
i.node?.apply {
val status = status.toString()
@ -896,7 +937,10 @@ Page(page:$page,perPage:50) {
while (hasNextPage) {
page++
hasNextPage = executeQuery<Query.Author>(query(page), force = true)?.data?.author?.staffMedia?.let {
hasNextPage = executeQuery<Query.Author>(
query(page),
force = true
)?.data?.author?.staffMedia?.let {
it.edges?.forEach { i ->
i.node?.apply {
val status = status.toString()

View file

@ -7,8 +7,8 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import ani.dantotsu.R
import ani.dantotsu.connections.discord.Discord
import ani.dantotsu.loadData
import ani.dantotsu.connections.mal.MAL
import ani.dantotsu.loadData
import ani.dantotsu.media.Media
import ani.dantotsu.others.AppUpdater
import ani.dantotsu.snackString
@ -45,39 +45,57 @@ suspend fun getUserId(context: Context, block: () -> Unit) {
}
} else true
if(anilist) block.invoke()
if (anilist) block.invoke()
}
class AnilistHomeViewModel : ViewModel() {
private val listImages: MutableLiveData<ArrayList<String?>> = MutableLiveData<ArrayList<String?>>(arrayListOf())
private val listImages: MutableLiveData<ArrayList<String?>> =
MutableLiveData<ArrayList<String?>>(arrayListOf())
fun getListImages(): LiveData<ArrayList<String?>> = listImages
suspend fun setListImages() = listImages.postValue(Anilist.query.getBannerImages())
private val animeContinue: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
private val animeContinue: MutableLiveData<ArrayList<Media>> =
MutableLiveData<ArrayList<Media>>(null)
fun getAnimeContinue(): LiveData<ArrayList<Media>> = animeContinue
suspend fun setAnimeContinue() = animeContinue.postValue(Anilist.query.continueMedia("ANIME"))
private val animeFav: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
private val animeFav: MutableLiveData<ArrayList<Media>> =
MutableLiveData<ArrayList<Media>>(null)
fun getAnimeFav(): LiveData<ArrayList<Media>> = animeFav
suspend fun setAnimeFav() = animeFav.postValue(Anilist.query.favMedia(true))
private val animePlanned: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
fun getAnimePlanned(): LiveData<ArrayList<Media>> = animePlanned
suspend fun setAnimePlanned() = animePlanned.postValue(Anilist.query.continueMedia("ANIME", true))
private val animePlanned: MutableLiveData<ArrayList<Media>> =
MutableLiveData<ArrayList<Media>>(null)
fun getAnimePlanned(): LiveData<ArrayList<Media>> = animePlanned
suspend fun setAnimePlanned() =
animePlanned.postValue(Anilist.query.continueMedia("ANIME", true))
private val mangaContinue: MutableLiveData<ArrayList<Media>> =
MutableLiveData<ArrayList<Media>>(null)
private val mangaContinue: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
fun getMangaContinue(): LiveData<ArrayList<Media>> = mangaContinue
suspend fun setMangaContinue() = mangaContinue.postValue(Anilist.query.continueMedia("MANGA"))
private val mangaFav: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
private val mangaFav: MutableLiveData<ArrayList<Media>> =
MutableLiveData<ArrayList<Media>>(null)
fun getMangaFav(): LiveData<ArrayList<Media>> = mangaFav
suspend fun setMangaFav() = mangaFav.postValue(Anilist.query.favMedia(false))
private val mangaPlanned: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
fun getMangaPlanned(): LiveData<ArrayList<Media>> = mangaPlanned
suspend fun setMangaPlanned() = mangaPlanned.postValue(Anilist.query.continueMedia("MANGA", true))
private val mangaPlanned: MutableLiveData<ArrayList<Media>> =
MutableLiveData<ArrayList<Media>>(null)
fun getMangaPlanned(): LiveData<ArrayList<Media>> = mangaPlanned
suspend fun setMangaPlanned() =
mangaPlanned.postValue(Anilist.query.continueMedia("MANGA", true))
private val recommendation: MutableLiveData<ArrayList<Media>> =
MutableLiveData<ArrayList<Media>>(null)
private val recommendation: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
fun getRecommendation(): LiveData<ArrayList<Media>> = recommendation
suspend fun setRecommendation() = recommendation.postValue(Anilist.query.recommendations())
@ -100,7 +118,9 @@ class AnilistAnimeViewModel : ViewModel() {
var notSet = true
lateinit var searchResults: SearchResults
private val type = "ANIME"
private val trending: MutableLiveData<MutableList<Media>> = MutableLiveData<MutableList<Media>>(null)
private val trending: MutableLiveData<MutableList<Media>> =
MutableLiveData<MutableList<Media>>(null)
fun getTrending(): LiveData<MutableList<Media>> = trending
suspend fun loadTrending(i: Int) {
val (season, year) = Anilist.currentSeasons[i]
@ -116,7 +136,9 @@ class AnilistAnimeViewModel : ViewModel() {
)
}
private val updated: MutableLiveData<MutableList<Media>> = MutableLiveData<MutableList<Media>>(null)
private val updated: MutableLiveData<MutableList<Media>> =
MutableLiveData<MutableList<Media>>(null)
fun getUpdated(): LiveData<MutableList<Media>> = updated
suspend fun loadUpdated() = updated.postValue(Anilist.query.recentlyUpdated())
@ -164,15 +186,33 @@ class AnilistMangaViewModel : ViewModel() {
var notSet = true
lateinit var searchResults: SearchResults
private val type = "MANGA"
private val trending: MutableLiveData<MutableList<Media>> = MutableLiveData<MutableList<Media>>(null)
private val trending: MutableLiveData<MutableList<Media>> =
MutableLiveData<MutableList<Media>>(null)
fun getTrending(): LiveData<MutableList<Media>> = trending
suspend fun loadTrending() =
trending.postValue(Anilist.query.search(type, perPage = 10, sort = Anilist.sortBy[2], hd = true)?.results)
trending.postValue(
Anilist.query.search(
type,
perPage = 10,
sort = Anilist.sortBy[2],
hd = true
)?.results
)
private val updated: MutableLiveData<MutableList<Media>> =
MutableLiveData<MutableList<Media>>(null)
private val updated: MutableLiveData<MutableList<Media>> = MutableLiveData<MutableList<Media>>(null)
fun getTrendingNovel(): LiveData<MutableList<Media>> = updated
suspend fun loadTrendingNovel() =
updated.postValue(Anilist.query.search(type, perPage = 10, sort = Anilist.sortBy[2], format = "NOVEL")?.results)
updated.postValue(
Anilist.query.search(
type,
perPage = 10,
sort = Anilist.sortBy[2],
format = "NOVEL"
)?.results
)
private val mangaPopular = MutableLiveData<SearchResults?>(null)
fun getPopular(): LiveData<SearchResults?> = mangaPopular

View file

@ -6,19 +6,20 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import ani.dantotsu.logError
import ani.dantotsu.logger
import ani.dantotsu.others.LangSet
import ani.dantotsu.startMainActivity
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
class Login : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
val data: Uri? = intent?.data
logger(data.toString())
try {
Anilist.token = Regex("""(?<=access_token=).+(?=&token_type)""").find(data.toString())!!.value
Anilist.token =
Regex("""(?<=access_token=).+(?=&token_type)""").find(data.toString())!!.value
val filename = "anilistToken"
this.openFileOutput(filename, Context.MODE_PRIVATE).use {
it.write(Anilist.token!!.toByteArray())

View file

@ -27,7 +27,15 @@ data class SearchResults(
val list = mutableListOf<SearchChip>()
sort?.let {
val c = currContext()!!
list.add(SearchChip("SORT", c.getString(R.string.filter_sort, c.resources.getStringArray(R.array.sort_by)[Anilist.sortBy.indexOf(it)])))
list.add(
SearchChip(
"SORT",
c.getString(
R.string.filter_sort,
c.resources.getStringArray(R.array.sort_by)[Anilist.sortBy.indexOf(it)]
)
)
)
}
format?.let {
list.add(SearchChip("FORMAT", currContext()!!.getString(R.string.filter_format, it)))
@ -42,13 +50,23 @@ data class SearchResults(
list.add(SearchChip("GENRE", it))
}
excludedGenres?.forEach {
list.add(SearchChip("EXCLUDED_GENRE", currContext()!!.getString(R.string.filter_exclude, it)))
list.add(
SearchChip(
"EXCLUDED_GENRE",
currContext()!!.getString(R.string.filter_exclude, it)
)
)
}
tags?.forEach {
list.add(SearchChip("TAG", it))
}
excludedTags?.forEach {
list.add(SearchChip("EXCLUDED_TAG", currContext()!!.getString(R.string.filter_exclude, it)))
list.add(
SearchChip(
"EXCLUDED_TAG",
currContext()!!.getString(R.string.filter_exclude, it)
)
)
}
return list
}

View file

@ -5,15 +5,15 @@ import android.net.Uri
import android.os.Bundle
import androidx.core.os.bundleOf
import ani.dantotsu.loadMedia
import ani.dantotsu.others.LangSet
import ani.dantotsu.startMainActivity
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
class UrlMedia : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
var id: Int? = intent?.extras?.getInt("media", 0) ?: 0
var isMAL = false
var continueMedia = true
@ -23,6 +23,9 @@ ThemeManager(this).applyTheme()
isMAL = data?.host != "anilist.co"
id = data?.pathSegments?.getOrNull(1)?.toIntOrNull()
} else loadMedia = id
startMainActivity(this, bundleOf("mediaId" to id, "mal" to isMAL, "continue" to continueMedia))
startMainActivity(
this,
bundleOf("mediaId" to id, "mal" to isMAL, "continue" to continueMedia)
)
}
}

View file

@ -3,23 +3,24 @@ package ani.dantotsu.connections.anilist.api
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
class Query{
class Query {
@Serializable
data class Viewer(
@SerialName("data")
val data : Data?
){
val data: Data?
) {
@Serializable
data class Data(
@SerialName("Viewer")
val user: ani.dantotsu.connections.anilist.api.User?
)
}
@Serializable
data class Media(
@SerialName("data")
val data : Data?
){
val data: Data?
) {
@Serializable
data class Data(
@SerialName("Media")
@ -30,12 +31,12 @@ class Query{
@Serializable
data class Page(
@SerialName("data")
val data : Data?
){
val data: Data?
) {
@Serializable
data class Data(
@SerialName("Page")
val page : ani.dantotsu.connections.anilist.api.Page?
val page: ani.dantotsu.connections.anilist.api.Page?
)
}
// data class AiringSchedule(
@ -49,8 +50,8 @@ class Query{
@Serializable
data class Character(
@SerialName("data")
val data : Data?
){
val data: Data?
) {
@Serializable
data class Data(
@ -63,7 +64,7 @@ class Query{
data class Studio(
@SerialName("data")
val data: Data?
){
) {
@Serializable
data class Data(
@SerialName("Studio")
@ -76,7 +77,7 @@ class Query{
data class Author(
@SerialName("data")
val data: Data?
){
) {
@Serializable
data class Data(
@SerialName("Staff")
@ -95,8 +96,8 @@ class Query{
@Serializable
data class MediaListCollection(
@SerialName("data")
val data : Data?
){
val data: Data?
) {
@Serializable
data class Data(
@SerialName("MediaListCollection")
@ -108,7 +109,7 @@ class Query{
data class GenreCollection(
@SerialName("data")
val data: Data
){
) {
@Serializable
data class Data(
@SerialName("GenreCollection")
@ -120,7 +121,7 @@ class Query{
data class MediaTagCollection(
@SerialName("data")
val data: Data
){
) {
@Serializable
data class Data(
@SerialName("MediaTagCollection")
@ -132,7 +133,7 @@ class Query{
data class User(
@SerialName("data")
val data: Data
){
) {
@Serializable
data class Data(
@SerialName("User")

View file

@ -3,7 +3,7 @@ package ani.dantotsu.connections.anilist.api
import kotlinx.serialization.SerialName
import java.io.Serializable
import java.text.DateFormatSymbols
import java.util.*
import java.util.Calendar
@kotlinx.serialization.Serializable
data class FuzzyDate(
@ -16,9 +16,11 @@ data class FuzzyDate(
fun isEmpty(): Boolean {
return year == null && month == null && day == null
}
override fun toString(): String {
return if ( isEmpty() ) "??" else toStringOrEmpty()
return if (isEmpty()) "??" else toStringOrEmpty()
}
fun toStringOrEmpty(): String {
return listOfNotNull(
day?.toString(),
@ -29,16 +31,21 @@ data class FuzzyDate(
fun getToday(): FuzzyDate {
val cal = Calendar.getInstance()
return FuzzyDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH))
return FuzzyDate(
cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH) + 1,
cal.get(Calendar.DAY_OF_MONTH)
)
}
fun toVariableString(): String {
return listOfNotNull(
year?.let {"year:$it"},
month?.let {"month:$it"},
day?.let {"day:$it"}
year?.let { "year:$it" },
month?.let { "month:$it" },
day?.let { "day:$it" }
).joinToString(",", "{", "}")
}
fun toMALString(): String {
val padding = '0'
val values = listOf(
@ -46,7 +53,7 @@ data class FuzzyDate(
month?.toString()?.padStart(2, padding),
day?.toString()?.padStart(2, padding)
)
return values.takeWhile {it is String}.joinToString("-")
return values.takeWhile { it is String }.joinToString("-")
}
// fun toInt(): Int {

View file

@ -292,7 +292,7 @@ data class MediaList(
@SerialName("hiddenFromStatusLists") var hiddenFromStatusLists: Boolean?,
// Map of booleans for which custom lists the entry are in
@SerialName("customLists") var customLists: Map<String,Boolean>?,
@SerialName("customLists") var customLists: Map<String, Boolean>?,
// Map of advanced scores with name keys
// @SerialName("advancedScores") var advancedScores: Json?,
@ -355,7 +355,7 @@ data class MediaTrailer(
@Serializable
data class MediaTagCollection(
@SerialName("tags") var tags : List<MediaTag>?
@SerialName("tags") var tags: List<MediaTag>?
)
@Serializable

View file

@ -2,6 +2,7 @@ package ani.dantotsu.connections.anilist.api
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Recommendation(
// The id of the recommendation
@ -22,6 +23,7 @@ data class Recommendation(
// The user that first created the recommendation
@SerialName("user") var user: User?,
)
@Serializable
data class RecommendationConnection(
//@SerialName("edges") var edges: List<RecommendationEdge>?,

View file

@ -80,8 +80,8 @@ data class Staff(
)
@Serializable
data class StaffName (
var userPreferred:String?
data class StaffName(
var userPreferred: String?
)
@Serializable
@ -96,6 +96,6 @@ data class StaffConnection(
@Serializable
data class StaffEdge(
var role:String?,
var role: String?,
var node: Staff?
)

View file

@ -5,14 +5,11 @@ import android.content.Intent
import android.widget.TextView
import androidx.core.content.edit
import ani.dantotsu.R
import ani.dantotsu.connections.discord.serializers.User
import ani.dantotsu.others.CustomBottomDialog
import ani.dantotsu.toast
import ani.dantotsu.tryWith
import ani.dantotsu.tryWithSuspend
import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin
import kotlinx.coroutines.Dispatchers
import java.io.File
object Discord {
@ -60,7 +57,7 @@ object Discord {
}
}
private var rpc : RPC? = null
private var rpc: RPC? = null
fun warning(context: Context) = CustomBottomDialog().apply {
@ -88,7 +85,8 @@ object Discord {
}
const val application_Id = "1163925779692912771"
const val small_Image: String = "mp:attachments/1167176318266380288/1176997397797277856/logo-best_of_both.png"
const val small_Image: String =
"mp:attachments/1167176318266380288/1176997397797277856/logo-best_of_both.png"
/*fun defaultRPC(): RPC? {
return token?.let {
RPC(it, Dispatchers.IO).apply {

View file

@ -16,14 +16,11 @@ import android.os.IBinder
import android.os.PowerManager
import android.provider.MediaStore
import android.util.Log
import android.widget.Button
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import ani.dantotsu.MainActivity
import ani.dantotsu.R
import ani.dantotsu.connections.discord.serializers.Activity
import ani.dantotsu.connections.discord.serializers.Presence
import ani.dantotsu.connections.discord.serializers.User
import ani.dantotsu.isOnline
@ -39,18 +36,16 @@ import okhttp3.WebSocket
import okhttp3.WebSocketListener
import java.io.File
import java.io.OutputStreamWriter
import java.text.SimpleDateFormat
import java.util.Calendar
class DiscordService : Service() {
private var heartbeat : Int = 0
private var sequence : Int? = null
private var sessionId : String = ""
private var heartbeat: Int = 0
private var sequence: Int? = null
private var sessionId: String = ""
private var resume = false
private lateinit var logFile : File
private lateinit var logFile: File
private lateinit var webSocket: WebSocket
private lateinit var heartbeatThread : Thread
private lateinit var client : OkHttpClient
private lateinit var heartbeatThread: Thread
private lateinit var client: OkHttpClient
private lateinit var wakeLock: PowerManager.WakeLock
var presenceStore = ""
val json = Json {
@ -66,7 +61,10 @@ class DiscordService : Service() {
log("Service onCreate()")
val powerManager = baseContext.getSystemService(Context.POWER_SERVICE) as PowerManager
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "discordRPC:backgroundPresence")
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"discordRPC:backgroundPresence"
)
wakeLock.acquire()
log("WakeLock Acquired")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@ -104,13 +102,13 @@ class DiscordService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
log("Service onStartCommand()")
if(intent != null) {
if (intent != null) {
if (intent.hasExtra("presence")) {
log("Service onStartCommand() setPresence")
var lPresence = intent.getStringExtra("presence")
if (this::webSocket.isInitialized) webSocket.send(lPresence!!)
presenceStore = lPresence!!
}else{
} else {
log("Service onStartCommand() no presence")
DiscordServiceRunningSingleton.running = false
client.dispatcher.executorService.shutdown()
@ -126,7 +124,7 @@ class DiscordService : Service() {
override fun onDestroy() {
log("Service Destroyed")
if (DiscordServiceRunningSingleton.running){
if (DiscordServiceRunningSingleton.running) {
log("Accidental Service Destruction, restarting service")
val intent = Intent(baseContext, DiscordService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@ -135,17 +133,19 @@ class DiscordService : Service() {
baseContext.startService(intent)
}
} else {
if(this::webSocket.isInitialized)
setPresence(json.encodeToString(
if (this::webSocket.isInitialized)
setPresence(
json.encodeToString(
Presence.Response(
3,
Presence(status = "offline")
)
))
)
)
wakeLock.release()
}
SERVICE_RUNNING = false
if(this::webSocket.isInitialized) webSocket.close(1000, "Closed by user")
if (this::webSocket.isInitialized) webSocket.close(1000, "Closed by user")
super.onDestroy()
//saveLogToFile()
}
@ -184,7 +184,7 @@ class DiscordService : Service() {
log("WebSocket: Received op code ${json.get("op")}")
when (json.get("op").asInt) {
0 -> {
if(json.has("s")) {
if (json.has("s")) {
log("WebSocket: Sequence ${json.get("s")} Received")
sequence = json.get("s").asInt
}
@ -193,9 +193,10 @@ class DiscordService : Service() {
log(text)
sessionId = json.get("d").asJsonObject.get("session_id").asString
log("WebSocket: SessionID ${json.get("d").asJsonObject.get("session_id")} Received")
if(presenceStore.isNotEmpty()) setPresence(presenceStore)
if (presenceStore.isNotEmpty()) setPresence(presenceStore)
sendBroadcast(Intent("ServiceToConnectButton"))
}
1 -> {
log("WebSocket: Received Heartbeat request, sending heartbeat")
heartbeatThread.interrupt()
@ -203,33 +204,38 @@ class DiscordService : Service() {
heartbeatThread = Thread(HeartbeatRunnable())
heartbeatThread.start()
}
7 -> {
resume = true
log("WebSocket: Requested to Restart, restarting")
webSocket.close(1000, "Requested to Restart by the server")
client = OkHttpClient()
client.newWebSocket(
Request.Builder().url("wss://gateway.discord.gg/?v=10&encoding=json").build(),
Request.Builder().url("wss://gateway.discord.gg/?v=10&encoding=json")
.build(),
DiscordWebSocketListener()
)
client.dispatcher.executorService.shutdown()
}
9 -> {
log("WebSocket: Invalid Session, restarting")
webSocket.close(1000, "Invalid Session")
Thread.sleep(5000)
client = OkHttpClient()
client.newWebSocket(
Request.Builder().url("wss://gateway.discord.gg/?v=10&encoding=json").build(),
Request.Builder().url("wss://gateway.discord.gg/?v=10&encoding=json")
.build(),
DiscordWebSocketListener()
)
client.dispatcher.executorService.shutdown()
}
10 -> {
heartbeat = json.get("d").asJsonObject.get("heartbeat_interval").asInt
heartbeatThread = Thread(HeartbeatRunnable())
heartbeatThread.start()
if(resume) {
if (resume) {
log("WebSocket: Resuming because server requested")
resume()
resume = false
@ -238,6 +244,7 @@ class DiscordService : Service() {
log("WebSocket: Identified")
}
}
11 -> {
log("WebSocket: Heartbeat ACKed")
heartbeatThread = Thread(HeartbeatRunnable())
@ -245,29 +252,31 @@ class DiscordService : Service() {
}
}
}
fun identify(webSocket: WebSocket, context: Context) {
val properties = JsonObject()
properties.addProperty("os","linux")
properties.addProperty("browser","unknown")
properties.addProperty("device","unknown")
properties.addProperty("os", "linux")
properties.addProperty("browser", "unknown")
properties.addProperty("device", "unknown")
val d = JsonObject()
d.addProperty("token", getToken(context))
d.addProperty("intents", 0)
d.add("properties", properties)
val payload = JsonObject()
payload.addProperty("op",2)
payload.addProperty("op", 2)
payload.add("d", d)
webSocket.send(payload.toString())
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
super.onFailure(webSocket, t, response)
if(!isOnline(baseContext)) {
if (!isOnline(baseContext)) {
log("WebSocket: Error, onFailure() reason: No Internet")
errorNotification("Could not set the presence", "No Internet")
return
} else{
} else {
retryAttempts++
if(retryAttempts >= maxRetryAttempts) {
if (retryAttempts >= maxRetryAttempts) {
log("WebSocket: Error, onFailure() reason: Max Retry Attempts")
errorNotification("Could not set the presence", "Max Retry Attempts")
return
@ -281,19 +290,23 @@ class DiscordService : Service() {
DiscordWebSocketListener()
)
client.dispatcher.executorService.shutdown()
if(::heartbeatThread.isInitialized && !heartbeatThread.isInterrupted) { heartbeatThread.interrupt() }
if (::heartbeatThread.isInitialized && !heartbeatThread.isInterrupted) {
heartbeatThread.interrupt()
}
}
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
super.onClosing(webSocket, code, reason)
Log.d("WebSocket", "onClosing() $code $reason")
if(::heartbeatThread.isInitialized && !heartbeatThread.isInterrupted) { heartbeatThread.interrupt() }
if (::heartbeatThread.isInitialized && !heartbeatThread.isInterrupted) {
heartbeatThread.interrupt()
}
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
super.onClosed(webSocket, code, reason)
Log.d("WebSocket", "onClosed() $code $reason")
if(code >= 4000) {
if (code >= 4000) {
log("WebSocket: Error, code: $code reason: $reason")
client = OkHttpClient()
client.newWebSocket(
@ -312,22 +325,23 @@ class DiscordService : Service() {
Context.MODE_PRIVATE
)
val token = sharedPref.getString(Discord.TOKEN, null)
if(token == null) {
if (token == null) {
log("WebSocket: Token not found")
errorNotification("Could not set the presence", "token not found")
return ""
}
else{
} else {
return token
}
}
fun heartbeatSend( webSocket: WebSocket, seq: Int? ) {
fun heartbeatSend(webSocket: WebSocket, seq: Int?) {
val json = JsonObject()
json.addProperty("op",1)
json.addProperty("op", 1)
json.addProperty("d", seq)
webSocket.send(json.toString())
}
private fun errorNotification(title : String, text: String) {
private fun errorNotification(title: String, text: String) {
val intent = Intent(this@DiscordService, MainActivity::class.java).apply {
action = Intent.ACTION_MAIN
addCategory(Intent.CATEGORY_LAUNCHER)
@ -401,7 +415,8 @@ class DiscordService : Service() {
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
} else {
val directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val directory =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val file = File(directory, fileName)
// Make sure the Downloads directory exists
@ -410,7 +425,8 @@ class DiscordService : Service() {
}
// Use FileProvider to get the URI for the file
val authority = "${baseContext.packageName}.provider" // Adjust with your app's package name
val authority =
"${baseContext.packageName}.provider" // Adjust with your app's package name
Uri.fromFile(file)
}
@ -445,7 +461,8 @@ class DiscordService : Service() {
Thread.sleep(heartbeat.toLong())
heartbeatSend(webSocket, sequence)
log("WebSocket: Heartbeat Sent")
} catch (e:InterruptedException) {}
} catch (e: InterruptedException) {
}
}
}

View file

@ -8,15 +8,12 @@ import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import ani.dantotsu.R
import ani.dantotsu.connections.discord.Discord.saveToken
import ani.dantotsu.logger
import ani.dantotsu.others.LangSet
import ani.dantotsu.startMainActivity
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import ani.dantotsu.snackString
class Login : AppCompatActivity() {
@ -40,17 +37,22 @@ class Login : AppCompatActivity() {
}
WebView.setWebContentsDebuggingEnabled(true)
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
): Boolean {
// Check if the URL is the one expected after a successful login
if (request?.url.toString() != "https://discord.com/login") {
// Delay the script execution to ensure the page is fully loaded
view?.postDelayed({
view.evaluateJavascript("""
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;
})()
""".trimIndent()) { result ->
""".trimIndent()
) { result ->
login(result.trim('"'))
}
}, 2000)
@ -67,7 +69,7 @@ class Login : AppCompatActivity() {
}
private fun login(token: String) {
if (token.isEmpty() || token == "null"){
if (token.isEmpty() || token == "null") {
Toast.makeText(this, "Failed to retrieve token", Toast.LENGTH_SHORT).show()
finish()
return

View file

@ -1,30 +1,10 @@
package ani.dantotsu.connections.discord
import android.widget.Toast
import ani.dantotsu.connections.discord.serializers.*
import ani.dantotsu.currContext
import ani.dantotsu.logger
import ani.dantotsu.snackString
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import ani.dantotsu.connections.discord.serializers.Activity
import ani.dantotsu.connections.discord.serializers.Presence
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import java.util.concurrent.TimeUnit.*
import kotlin.coroutines.CoroutineContext
import ani.dantotsu.client as app
@ -36,6 +16,7 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) {
allowStructuredMapKeys = true
ignoreUnknownKeys = true
}
enum class Type {
PLAYING, STREAMING, LISTENING, WATCHING, COMPETING
}
@ -56,14 +37,17 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) {
val stopTimestamp: Long? = null,
val buttons: MutableList<Link> = mutableListOf()
)
@Serializable
data class KizzyApi(val id: String)
val api = "https://kizzy-api.vercel.app/image?url="
private suspend fun String.discordUrl(): String? {
if (startsWith("mp:")) return this
val json = app.get("$api$this").parsedSafe<KizzyApi>()
return json?.id
}
suspend fun createPresence(data: RPCData): String {
val json = Json {
encodeDefaults = true

View file

@ -2,8 +2,9 @@ package ani.dantotsu.connections.discord.serializers
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Activity (
data class Activity(
@SerialName("application_id")
val applicationId: String? = null,
val name: String? = null,

View file

@ -12,13 +12,13 @@ data class Identity(
) {
@Serializable
data class Response (
data class Response(
val op: Long,
val d: Identity
)
@Serializable
data class Properties (
data class Properties(
@SerialName("\$os")
val os: String,

View file

@ -3,14 +3,14 @@ package ani.dantotsu.connections.discord.serializers
import kotlinx.serialization.Serializable
@Serializable
data class Presence (
data class Presence(
val activities: List<Activity> = listOf(),
val afk: Boolean = true,
val since: Long? = null,
val status: String? = null
){
) {
@Serializable
data class Response (
data class Response(
val op: Long,
val d: Presence
)

View file

@ -1,13 +1,12 @@
package ani.dantotsu.connections.discord.serializers
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
@Serializable
data class User (
data class User(
val verified: Boolean? = null,
val username: String,
@ -71,7 +70,7 @@ data class User (
)
}
fun userAvatar():String{
fun userAvatar(): String {
return "https://cdn.discordapp.com/avatars/$id/$avatar.png"
}
}

View file

@ -4,11 +4,17 @@ import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import ani.dantotsu.*
import ani.dantotsu.R
import ani.dantotsu.client
import ani.dantotsu.connections.mal.MAL.clientId
import ani.dantotsu.connections.mal.MAL.saveResponse
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.loadData
import ani.dantotsu.logError
import ani.dantotsu.others.LangSet
import ani.dantotsu.snackString
import ani.dantotsu.startMainActivity
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.tryWithSuspend
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -16,7 +22,7 @@ class Login : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
try {
val data: Uri = intent?.data
?: throw Exception(getString(R.string.mal_login_uri_not_found))
@ -46,9 +52,8 @@ ThemeManager(this).applyTheme()
}
}
}
}
catch (e:Exception){
logError(e,snackbar = false)
} catch (e: Exception) {
logError(e, snackbar = false)
startMainActivity(this)
}
}

View file

@ -6,7 +6,13 @@ import android.net.Uri
import android.util.Base64
import androidx.browser.customtabs.CustomTabsIntent
import androidx.fragment.app.FragmentActivity
import ani.dantotsu.*
import ani.dantotsu.R
import ani.dantotsu.client
import ani.dantotsu.currContext
import ani.dantotsu.loadData
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.saveData
import ani.dantotsu.tryWithSuspend
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.io.File
@ -94,6 +100,6 @@ object MAL {
@SerialName("expires_in") var expiresIn: Long,
@SerialName("access_token") val accessToken: String,
@SerialName("refresh_token") val refreshToken: String,
): java.io.Serializable
) : java.io.Serializable
}

View file

@ -1,7 +1,7 @@
package ani.dantotsu.connections.mal
import ani.dantotsu.connections.anilist.api.FuzzyDate
import ani.dantotsu.client
import ani.dantotsu.connections.anilist.api.FuzzyDate
import ani.dantotsu.tryWithSuspend
import kotlinx.serialization.Serializable
@ -43,18 +43,18 @@ class MALQueries {
start: FuzzyDate? = null,
end: FuzzyDate? = null
) {
if(idMAL==null) return
if (idMAL == null) return
val data = mutableMapOf("status" to convertStatus(isAnime, status))
if (progress != null)
data[if (isAnime) "num_watched_episodes" else "num_chapters_read"] = progress.toString()
data[if (isAnime) "is_rewatching" else "is_rereading"] = (status == "REPEATING").toString()
if (score != null)
data["score"] = score.div(10).toString()
if(rewatch!=null)
data[if(isAnime) "num_times_rewatched" else "num_times_reread"] = rewatch.toString()
if(start!=null)
if (rewatch != null)
data[if (isAnime) "num_times_rewatched" else "num_times_reread"] = rewatch.toString()
if (start != null)
data["start_date"] = start.toMALString()
if(end!=null)
if (end != null)
data["finish_date"] = end.toMALString()
tryWithSuspend {
client.put(
@ -65,8 +65,8 @@ class MALQueries {
}
}
suspend fun deleteList(isAnime: Boolean, idMAL: Int?){
if(idMAL==null) return
suspend fun deleteList(isAnime: Boolean, idMAL: Int?) {
if (idMAL == null) return
tryWithSuspend {
client.delete(
"$apiUrl/${if (isAnime) "anime" else "manga"}/$idMAL/my_list_status",

View file

@ -4,15 +4,15 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import ani.dantotsu.R
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import ani.dantotsu.themes.ThemeManager
class DownloadContainerActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
setContentView(R.layout.activity_container)
val fragmentClassName = intent.getStringExtra("FRAGMENT_CLASS_NAME")

View file

@ -10,7 +10,8 @@ import java.io.File
import java.io.Serializable
class DownloadsManager(private val context: Context) {
private val prefs: SharedPreferences = context.getSharedPreferences("downloads_pref", Context.MODE_PRIVATE)
private val prefs: SharedPreferences =
context.getSharedPreferences("downloads_pref", Context.MODE_PRIVATE)
private val gson = Gson()
private val downloadsList = loadDownloads().toMutableList()
@ -52,12 +53,21 @@ class DownloadsManager(private val context: Context) {
}
private fun removeDirectory(download: Download) {
val directory = if (download.type == Download.Type.MANGA){
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Manga/${download.title}/${download.chapter}")
val directory = if (download.type == Download.Type.MANGA) {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Manga/${download.title}/${download.chapter}"
)
} else if (download.type == Download.Type.ANIME) {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Anime/${download.title}/${download.chapter}")
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Anime/${download.title}/${download.chapter}"
)
} else {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Novel/${download.title}/${download.chapter}")
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Novel/${download.title}/${download.chapter}"
)
}
// Check if the directory exists and delete it recursively
@ -74,14 +84,26 @@ class DownloadsManager(private val context: Context) {
}
fun exportDownloads(download: Download) { //copies to the downloads folder available to the user
val directory = if (download.type == Download.Type.MANGA){
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Manga/${download.title}/${download.chapter}")
val directory = if (download.type == Download.Type.MANGA) {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Manga/${download.title}/${download.chapter}"
)
} else if (download.type == Download.Type.ANIME) {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Anime/${download.title}/${download.chapter}")
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Anime/${download.title}/${download.chapter}"
)
} else {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Novel/${download.title}/${download.chapter}")
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Novel/${download.title}/${download.chapter}"
)
}
val destination = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/${download.title}/${download.chapter}")
val destination = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/${download.title}/${download.chapter}"
)
if (directory.exists()) {
val copied = directory.copyRecursively(destination, true)
if (copied) {
@ -94,8 +116,8 @@ class DownloadsManager(private val context: Context) {
}
}
fun purgeDownloads(type: Download.Type){
val directory = if (type == Download.Type.MANGA){
fun purgeDownloads(type: Download.Type) {
val directory = if (type == Download.Type.MANGA) {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Manga")
} else if (type == Download.Type.ANIME) {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Anime")

View file

@ -9,7 +9,6 @@ import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.IBinder
@ -17,24 +16,12 @@ import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import ani.dantotsu.R
import ani.dantotsu.download.Download
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.media.Media
import ani.dantotsu.media.manga.ImageData
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
import java.io.FileOutputStream
import com.google.gson.Gson
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_DOWNLOADER_PROGRESS
import java.net.HttpURLConnection
import java.net.URL
import androidx.core.content.ContextCompat
import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_FAILED
import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_FINISHED
import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_PROGRESS
@ -44,15 +31,27 @@ import ani.dantotsu.snackString
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.gson.GsonBuilder
import com.google.gson.InstanceCreator
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_DOWNLOADER_PROGRESS
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SChapterImpl
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
import java.io.FileOutputStream
import java.net.HttpURLConnection
import java.net.URL
import java.util.Queue
import java.util.concurrent.ConcurrentLinkedQueue
@ -82,11 +81,20 @@ class MangaDownloaderService : Service() {
setProgress(0, 0, false)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(NOTIFICATION_ID, builder.build(), ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
}else{
startForeground(
NOTIFICATION_ID,
builder.build(),
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
)
} else {
startForeground(NOTIFICATION_ID, builder.build())
}
ContextCompat.registerReceiver(this, cancelReceiver, IntentFilter(ACTION_CANCEL_DOWNLOAD), ContextCompat.RECEIVER_EXPORTED)
ContextCompat.registerReceiver(
this,
cancelReceiver,
IntentFilter(ACTION_CANCEL_DOWNLOAD),
ContextCompat.RECEIVER_EXPORTED
)
}
override fun onDestroy() {
@ -296,7 +304,8 @@ class MangaDownloaderService : Service() {
}
private suspend fun downloadImage(url: String, directory: File, name: String): String? = withContext(Dispatchers.IO) {
private suspend fun downloadImage(url: String, directory: File, name: String): String? =
withContext(Dispatchers.IO) {
var connection: HttpURLConnection? = null
println("Downloading url $url")
try {
@ -316,7 +325,11 @@ class MangaDownloaderService : Service() {
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(this@MangaDownloaderService, "Exception while saving ${name}: ${e.message}", Toast.LENGTH_LONG).show()
Toast.makeText(
this@MangaDownloaderService,
"Exception while saving ${name}: ${e.message}",
Toast.LENGTH_LONG
).show()
}
null
} finally {
@ -385,6 +398,7 @@ object MangaServiceDataSingleton {
var imageData: List<ImageData> = listOf()
var sourceMedia: Media? = null
var downloadQueue: Queue<MangaDownloaderService.DownloadTask> = ConcurrentLinkedQueue()
@Volatile
var isServiceRunning: Boolean = false
}

View file

@ -11,8 +11,13 @@ import androidx.cardview.widget.CardView
import ani.dantotsu.R
class OfflineMangaAdapter(private val context: Context, private val items: List<OfflineMangaModel>) : BaseAdapter() {
private val inflater: LayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
class OfflineMangaAdapter(
private val context: Context,
private val items: List<OfflineMangaModel>
) : BaseAdapter() {
private val inflater: LayoutInflater =
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
override fun getCount(): Int {
return items.size
}

View file

@ -15,21 +15,14 @@ import android.view.animation.OvershootInterpolator
import android.widget.GridView
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
import androidx.core.view.updatePaddingRelative
import androidx.fragment.app.Fragment
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import ani.dantotsu.R
import ani.dantotsu.Refresh
import ani.dantotsu.currContext
import ani.dantotsu.databinding.FragmentMangaBinding
import ani.dantotsu.download.Download
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.manga.MangaNameAdapter
import ani.dantotsu.navBarHeight
import ani.dantotsu.px
import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.settings.SettingsDialogFragment
import ani.dantotsu.snackString
@ -38,23 +31,27 @@ import com.google.android.material.card.MaterialCardView
import com.google.android.material.imageview.ShapeableImageView
import com.google.android.material.textfield.TextInputLayout
import com.google.firebase.crashlytics.FirebaseCrashlytics
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
import com.google.gson.GsonBuilder
import com.google.gson.InstanceCreator
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SChapterImpl
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
import kotlin.math.max
import kotlin.math.min
class OfflineMangaFragment: Fragment() {
class OfflineMangaFragment : Fragment() {
private val downloadManager = Injekt.get<DownloadsManager>()
private var downloads: List<OfflineMangaModel> = listOf()
private lateinit var gridView: GridView
private lateinit var adapter: OfflineMangaAdapter
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_manga_offline, container, false)
val textInputLayout = view.findViewById<TextInputLayout>(R.id.offlineMangaSearchBar)
@ -67,12 +64,16 @@ class OfflineMangaFragment: Fragment() {
requireContext().theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true)
val color = typedValue.data
val animeUserAvatar= view.findViewById<ShapeableImageView>(R.id.offlineMangaUserAvatar)
val animeUserAvatar = view.findViewById<ShapeableImageView>(R.id.offlineMangaUserAvatar)
animeUserAvatar.setSafeOnClickListener {
SettingsDialogFragment(SettingsDialogFragment.Companion.PageType.HOME).show((it.context as AppCompatActivity).supportFragmentManager, "dialog")
SettingsDialogFragment(SettingsDialogFragment.Companion.PageType.HOME).show(
(it.context as AppCompatActivity).supportFragmentManager,
"dialog"
)
}
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.getBoolean("colorOverflow", false) ?: false
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getBoolean("colorOverflow", false) ?: false
if (!colorOverflow) {
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
@ -85,7 +86,8 @@ class OfflineMangaFragment: Fragment() {
gridView.setOnItemClickListener { parent, view, position, id ->
// Get the OfflineMangaModel that was clicked
val item = adapter.getItem(position) as OfflineMangaModel
val media = downloadManager.mangaDownloads.filter { it.title == item.title }.firstOrNull()
val media =
downloadManager.mangaDownloads.filter { it.title == item.title }.firstOrNull()
media?.let {
startActivity(
Intent(requireContext(), MediaDetailsActivity::class.java)
@ -139,9 +141,7 @@ class OfflineMangaFragment: Fragment() {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onResume() {
super.onResume()
getDownloads()
@ -162,6 +162,7 @@ class OfflineMangaFragment: Fragment() {
super.onStop()
downloads = listOf()
}
private fun getDownloads() {
val titles = downloadManager.mangaDownloads.map { it.title }.distinct()
val newDownloads = mutableListOf<OfflineMangaModel>()
@ -189,8 +190,7 @@ class OfflineMangaFragment: Fragment() {
val media = File(directory, "media.json")
val mediaJson = media.readText()
return gson.fromJson(mediaJson, Media::class.java)
}
catch (e: Exception){
} catch (e: Exception) {
logger("Error loading media.json: ${e.message}")
logger(e.printStackTrace())
FirebaseCrashlytics.getInstance().recordException(e)
@ -198,7 +198,7 @@ class OfflineMangaFragment: Fragment() {
}
}
private fun loadOfflineMangaModel(download: Download): OfflineMangaModel{
private fun loadOfflineMangaModel(download: Download): OfflineMangaModel {
val directory = File(
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Manga/${download.title}"
@ -214,14 +214,13 @@ class OfflineMangaFragment: Fragment() {
} else {
null
}
val title = mediaModel.nameMAL?:"unknown"
val title = mediaModel.nameMAL ?: "unknown"
val score = if (mediaModel.userScore != 0) mediaModel.userScore.toString() else
if (mediaModel.meanScore == null) "?" else mediaModel.meanScore.toString()
val isOngoing = false
val isUserScored = mediaModel.userScore != 0
return OfflineMangaModel(title, score, isOngoing, isUserScored, coverUri)
}
catch (e: Exception){
} catch (e: Exception) {
logger("Error loading media.json: ${e.message}")
logger(e.printStackTrace())
FirebaseCrashlytics.getInstance().recordException(e)

View file

@ -2,5 +2,10 @@ package ani.dantotsu.download.manga
import android.net.Uri
data class OfflineMangaModel(val title: String, val score: String, val isOngoing: Boolean, val isUserScored: Boolean, val image: Uri?) {
}
data class OfflineMangaModel(
val title: String,
val score: String,
val isOngoing: Boolean,
val isUserScored: Boolean,
val image: Uri?
)

View file

@ -8,7 +8,6 @@ import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
import android.graphics.Bitmap
import android.os.Build
import android.os.Environment
import android.os.IBinder
@ -32,13 +31,10 @@ import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SChapterImpl
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@ -48,7 +44,6 @@ import okio.buffer
import okio.sink
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
@ -77,7 +72,8 @@ class NovelDownloaderService : Service() {
override fun onCreate() {
super.onCreate()
notificationManager = NotificationManagerCompat.from(this)
builder = NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply {
builder =
NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply {
setContentTitle("Novel Download Progress")
setSmallIcon(R.drawable.ic_round_download_24)
priority = NotificationCompat.PRIORITY_DEFAULT
@ -85,11 +81,20 @@ class NovelDownloaderService : Service() {
setProgress(0, 0, false)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(NOTIFICATION_ID, builder.build(), ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
}else{
startForeground(
NOTIFICATION_ID,
builder.build(),
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
)
} else {
startForeground(NOTIFICATION_ID, builder.build())
}
ContextCompat.registerReceiver(this, cancelReceiver, IntentFilter(ACTION_CANCEL_DOWNLOAD), ContextCompat.RECEIVER_EXPORTED)
ContextCompat.registerReceiver(
this,
cancelReceiver,
IntentFilter(ACTION_CANCEL_DOWNLOAD),
ContextCompat.RECEIVER_EXPORTED
)
}
override fun onDestroy() {
@ -112,7 +117,7 @@ class NovelDownloaderService : Service() {
}
}
}
return Service.START_NOT_STICKY
return START_NOT_STICKY
}
private fun processQueue() {
@ -272,7 +277,10 @@ class NovelDownloaderService : Service() {
val progress = (downloadedBytes * 100 / totalBytes).toInt()
builder.setProgress(100, progress, false)
if (notifi) {
notificationManager.notify(NOTIFICATION_ID, builder.build())
notificationManager.notify(
NOTIFICATION_ID,
builder.build()
)
}
}
lastNotificationUpdate = downloadedBytes
@ -310,6 +318,7 @@ class NovelDownloaderService : Service() {
snackString("${task.title} - ${task.chapter} Download finished")
}
}
private fun saveMediaInfo(task: DownloadTask) {
GlobalScope.launch(Dispatchers.IO) {
val directory = File(
@ -339,8 +348,10 @@ class NovelDownloaderService : Service() {
}
private suspend fun downloadImage(url: String, directory: File, name: String): String? = withContext(
Dispatchers.IO) {
private suspend fun downloadImage(url: String, directory: File, name: String): String? =
withContext(
Dispatchers.IO
) {
var connection: HttpURLConnection? = null
println("Downloading url $url")
try {
@ -360,7 +371,11 @@ class NovelDownloaderService : Service() {
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(this@NovelDownloaderService, "Exception while saving ${name}: ${e.message}", Toast.LENGTH_LONG).show()
Toast.makeText(
this@NovelDownloaderService,
"Exception while saving ${name}: ${e.message}",
Toast.LENGTH_LONG
).show()
}
null
} finally {
@ -429,6 +444,7 @@ class NovelDownloaderService : Service() {
object NovelServiceDataSingleton {
var sourceMedia: Media? = null
var downloadQueue: Queue<NovelDownloaderService.DownloadTask> = ConcurrentLinkedQueue()
@Volatile
var isServiceRunning: Boolean = false
}

View file

@ -38,9 +38,10 @@ import java.util.concurrent.*
object Helper {
@SuppressLint("UnsafeOptInUsageError")
fun downloadVideo(context : Context, video: Video, subtitle: Subtitle?){
fun downloadVideo(context: Context, video: Video, subtitle: Subtitle?) {
val dataSourceFactory = DataSource.Factory {
val dataSource: HttpDataSource = OkHttpDataSource.Factory(okHttpClient).createDataSource()
val dataSource: HttpDataSource =
OkHttpDataSource.Factory(okHttpClient).createDataSource()
defaultHeaders.forEach {
dataSource.setRequestProperty(it.key, it.value)
}
@ -79,12 +80,13 @@ object Helper {
DefaultRenderersFactory(context),
dataSourceFactory
)
downloadHelper.prepare(object : DownloadHelper.Callback{
downloadHelper.prepare(object : DownloadHelper.Callback {
override fun onPrepared(helper: DownloadHelper) {
TrackSelectionDialogBuilder(context,"Select thingy",helper.getTracks(0).groups
TrackSelectionDialogBuilder(
context, "Select thingy", helper.getTracks(0).groups
) { _, overrides ->
val params = TrackSelectionParameters.Builder(context)
overrides.forEach{
overrides.forEach {
params.addOverride(it.value)
}
helper.addTrackSelection(0, params.build())
@ -124,7 +126,8 @@ object Helper {
//val dataSource: HttpDataSource = OkHttpDataSource.Factory(okHttpClient).createDataSource()
val networkHelper = Injekt.get<NetworkHelper>()
val okHttpClient = networkHelper.client
val dataSource: HttpDataSource = OkHttpDataSource.Factory(okHttpClient).createDataSource()
val dataSource: HttpDataSource =
OkHttpDataSource.Factory(okHttpClient).createDataSource()
defaultHeaders.forEach {
dataSource.setRequestProperty(it.key, it.value)
}
@ -137,7 +140,8 @@ object Helper {
dataSourceFactory,
Executor(Runnable::run)
).apply {
requirements = Requirements(Requirements.NETWORK or Requirements.DEVICE_STORAGE_NOT_LOW)
requirements =
Requirements(Requirements.NETWORK or Requirements.DEVICE_STORAGE_NOT_LOW)
maxParallelDownloads = 3
}
}

View file

@ -21,7 +21,10 @@ class MyDownloadService : DownloadService(1, 1, "download_service", R.string.dow
override fun getScheduler(): Scheduler = PlatformScheduler(this, JOB_ID)
override fun getForegroundNotification(downloads: MutableList<Download>, notMetRequirements: Int): Notification =
override fun getForegroundNotification(
downloads: MutableList<Download>,
notMetRequirements: Int
): Notification =
DownloadNotificationHelper(this, "download_service").buildProgressNotification(
this,
R.drawable.mono,

View file

@ -48,7 +48,8 @@ class AnimeFragment : Fragment() {
private var _binding: FragmentAnimeBinding? = null
private val binding get() = _binding!!
private var uiSettings: UserInterfaceSettings = loadData("ui_settings") ?: UserInterfaceSettings()
private var uiSettings: UserInterfaceSettings =
loadData("ui_settings") ?: UserInterfaceSettings()
val model: AnilistAnimeViewModel by activityViewModels()
@ -224,7 +225,8 @@ class AnimeFragment : Fragment() {
}
}
}
binding.animePageScrollTop.translationY = -(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
binding.animePageScrollTop.translationY =
-(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
}
}

View file

@ -2,7 +2,6 @@ package ani.dantotsu.home
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Handler
import android.os.Looper
import android.util.TypedValue
@ -18,7 +17,6 @@ import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.media.GenreActivity
import ani.dantotsu.MediaPageTransformer
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
@ -27,6 +25,7 @@ import ani.dantotsu.databinding.ItemAnimePageBinding
import ani.dantotsu.loadData
import ani.dantotsu.loadImage
import ani.dantotsu.media.CalendarActivity
import ani.dantotsu.media.GenreActivity
import ani.dantotsu.media.MediaAdaptor
import ani.dantotsu.media.SearchActivity
import ani.dantotsu.px
@ -45,10 +44,12 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
private var trendHandler: Handler? = null
private lateinit var trendRun: Runnable
var trendingViewPager: ViewPager2? = null
private var uiSettings: UserInterfaceSettings = loadData("ui_settings") ?: UserInterfaceSettings()
private var uiSettings: UserInterfaceSettings =
loadData("ui_settings") ?: UserInterfaceSettings()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnimePageViewHolder {
val binding = ItemAnimePageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding =
ItemAnimePageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return AnimePageViewHolder(binding)
}
@ -60,14 +61,16 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
val currentColor = textInputLayout.boxBackgroundColor
val semiTransparentColor = (currentColor and 0x00FFFFFF) or 0xA8000000.toInt()
textInputLayout.boxBackgroundColor = semiTransparentColor
val materialCardView = holder.itemView.findViewById<MaterialCardView>(R.id.animeUserAvatarContainer)
val materialCardView =
holder.itemView.findViewById<MaterialCardView>(R.id.animeUserAvatarContainer)
materialCardView.setCardBackgroundColor(semiTransparentColor)
val typedValue = TypedValue()
currContext()?.theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true)
val color = typedValue.data
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.getBoolean("colorOverflow", false) ?: false
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getBoolean("colorOverflow", false) ?: false
if (!colorOverflow) {
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
@ -95,7 +98,10 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
}
binding.animeUserAvatar.setSafeOnClickListener {
SettingsDialogFragment(SettingsDialogFragment.Companion.PageType.ANIME).show((it.context as AppCompatActivity).supportFragmentManager, "dialog")
SettingsDialogFragment(SettingsDialogFragment.Companion.PageType.ANIME).show(
(it.context as AppCompatActivity).supportFragmentManager,
"dialog"
)
}
listOf(
@ -125,7 +131,8 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
)
}
binding.animeIncludeList.visibility = if(Anilist.userid!=null) View.VISIBLE else View.GONE
binding.animeIncludeList.visibility =
if (Anilist.userid != null) View.VISIBLE else View.GONE
binding.animeIncludeList.setOnCheckedChangeListener { _, isChecked ->
onIncludeListClick.invoke(isChecked)
}
@ -133,9 +140,9 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
ready.postValue(true)
}
lateinit var onSeasonClick : ((Int)->Unit)
lateinit var onSeasonLongClick : ((Int)->Boolean)
lateinit var onIncludeListClick : ((Boolean)->Unit)
lateinit var onSeasonClick: ((Int) -> Unit)
lateinit var onSeasonLongClick: ((Int) -> Boolean)
lateinit var onIncludeListClick: ((Boolean) -> Unit)
override fun getItemCount(): Int = 1
@ -152,7 +159,8 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
trendHandler = Handler(Looper.getMainLooper())
trendRun = Runnable {
binding.animeTrendingViewPager.currentItem = binding.animeTrendingViewPager.currentItem + 1
binding.animeTrendingViewPager.currentItem =
binding.animeTrendingViewPager.currentItem + 1
}
binding.animeTrendingViewPager.registerOnPageChangeCallback(
object : ViewPager2.OnPageChangeCallback() {
@ -164,22 +172,30 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
}
)
binding.animeTrendingViewPager.layoutAnimation = LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.animeTrendingViewPager.layoutAnimation =
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.animeTitleContainer.startAnimation(setSlideUp(uiSettings))
binding.animeListContainer.layoutAnimation = LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.animeSeasonsCont.layoutAnimation = LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.animeListContainer.layoutAnimation =
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.animeSeasonsCont.layoutAnimation =
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
}
fun updateRecent(adaptor: MediaAdaptor) {
binding.animeUpdatedProgressBar.visibility = View.GONE
binding.animeUpdatedRecyclerView.adapter = adaptor
binding.animeUpdatedRecyclerView.layoutManager =
LinearLayoutManager(binding.animeUpdatedRecyclerView.context, LinearLayoutManager.HORIZONTAL, false)
LinearLayoutManager(
binding.animeUpdatedRecyclerView.context,
LinearLayoutManager.HORIZONTAL,
false
)
binding.animeUpdatedRecyclerView.visibility = View.VISIBLE
binding.animeRecently.visibility = View.VISIBLE
binding.animeRecently.startAnimation(setSlideUp(uiSettings))
binding.animeUpdatedRecyclerView.layoutAnimation = LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.animeUpdatedRecyclerView.layoutAnimation =
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.animePopular.visibility = View.VISIBLE
binding.animePopular.startAnimation(setSlideUp(uiSettings))
}
@ -191,5 +207,6 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
}
}
inner class AnimePageViewHolder(val binding: ItemAnimePageBinding) : RecyclerView.ViewHolder(binding.root)
inner class AnimePageViewHolder(val binding: ItemAnimePageBinding) :
RecyclerView.ViewHolder(binding.root)
}

View file

@ -31,13 +31,13 @@ import ani.dantotsu.loadData
import ani.dantotsu.loadImage
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaAdaptor
import ani.dantotsu.settings.SettingsDialogFragment
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.media.user.ListActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.setSlideIn
import ani.dantotsu.setSlideUp
import ani.dantotsu.settings.SettingsDialogFragment
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.snackString
import ani.dantotsu.statusBarHeight
import kotlinx.coroutines.Dispatchers
@ -52,7 +52,11 @@ class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
@ -96,10 +100,12 @@ class HomeFragment : Fragment() {
binding.homeUserAvatarContainer.startAnimation(setSlideUp(uiSettings))
binding.homeUserDataContainer.visibility = View.VISIBLE
binding.homeUserDataContainer.layoutAnimation = LayoutAnimationController(setSlideUp(uiSettings), 0.25f)
binding.homeUserDataContainer.layoutAnimation =
LayoutAnimationController(setSlideUp(uiSettings), 0.25f)
binding.homeAnimeList.visibility = View.VISIBLE
binding.homeMangaList.visibility = View.VISIBLE
binding.homeListContainer.layoutAnimation = LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.homeListContainer.layoutAnimation =
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
}
else {
snackString(currContext()?.getString(R.string.please_reload))
@ -107,7 +113,10 @@ class HomeFragment : Fragment() {
}
binding.homeUserAvatarContainer.setSafeOnClickListener {
SettingsDialogFragment(SettingsDialogFragment.Companion.PageType.HOME).show(parentFragmentManager, "dialog")
SettingsDialogFragment(SettingsDialogFragment.Companion.PageType.HOME).show(
parentFragmentManager,
"dialog"
)
}
binding.homeContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
@ -123,11 +132,13 @@ class HomeFragment : Fragment() {
if (!binding.homeScroll.canScrollVertically(1)) {
reached = true
bottomBar.animate().translationZ(0f).setDuration(duration).start()
ObjectAnimator.ofFloat(bottomBar, "elevation", 4f, 0f).setDuration(duration).start()
ObjectAnimator.ofFloat(bottomBar, "elevation", 4f, 0f).setDuration(duration)
.start()
} else {
if (reached) {
bottomBar.animate().translationZ(12f).setDuration(duration).start()
ObjectAnimator.ofFloat(bottomBar, "elevation", 0f, 4f).setDuration(duration).start()
ObjectAnimator.ofFloat(bottomBar, "elevation", 0f, 4f).setDuration(duration)
.start()
}
}
}
@ -138,7 +149,13 @@ class HomeFragment : Fragment() {
if (displayCutout != null) {
if (displayCutout.boundingRects.size > 0) {
height =
max(statusBarHeight, min(displayCutout.boundingRects[0].width(), displayCutout.boundingRects[0].height()))
max(
statusBarHeight,
min(
displayCutout.boundingRects[0].width(),
displayCutout.boundingRects[0].height()
)
)
}
}
}
@ -189,7 +206,8 @@ class HomeFragment : Fragment() {
false
)
recyclerView.visibility = View.VISIBLE
recyclerView.layoutAnimation = LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
recyclerView.layoutAnimation =
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
} else {
empty.visibility = View.VISIBLE
@ -313,7 +331,8 @@ class HomeFragment : Fragment() {
live.observe(viewLifecycleOwner) {
if (it) {
scope.launch {
uiSettings = loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
uiSettings =
loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
withContext(Dispatchers.IO) {
//Get userData First
getUserId(requireContext()) {

View file

@ -15,7 +15,11 @@ class LoginFragment : Fragment() {
private var _binding: FragmentLoginBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentLoginBinding.inflate(layoutInflater, container, false)
return binding.root
}

View file

@ -44,11 +44,16 @@ class MangaFragment : Fragment() {
private var _binding: FragmentMangaBinding? = null
private val binding get() = _binding!!
private var uiSettings: UserInterfaceSettings = loadData("ui_settings") ?: UserInterfaceSettings()
private var uiSettings: UserInterfaceSettings =
loadData("ui_settings") ?: UserInterfaceSettings()
val model: AnilistMangaViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentMangaBinding.inflate(inflater, container, false)
return binding.root
}
@ -100,7 +105,8 @@ class MangaFragment : Fragment() {
}
val popularAdaptor = MediaAdaptor(1, model.searchResults.results, requireActivity())
val progressAdaptor = ProgressAdapter(searched = model.searched)
binding.mangaPageRecyclerView.adapter = ConcatAdapter(mangaPageAdapter, popularAdaptor, progressAdaptor)
binding.mangaPageRecyclerView.adapter =
ConcatAdapter(mangaPageAdapter, popularAdaptor, progressAdaptor)
val layout = LinearLayoutManager(requireContext())
binding.mangaPageRecyclerView.layoutManager = layout
@ -177,7 +183,8 @@ class MangaFragment : Fragment() {
}
}
}
binding.mangaPageScrollTop.translationY = -(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
binding.mangaPageScrollTop.translationY =
-(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
}
}

View file

@ -17,7 +17,6 @@ import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.media.GenreActivity
import ani.dantotsu.MediaPageTransformer
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
@ -25,6 +24,7 @@ import ani.dantotsu.currContext
import ani.dantotsu.databinding.ItemMangaPageBinding
import ani.dantotsu.loadData
import ani.dantotsu.loadImage
import ani.dantotsu.media.GenreActivity
import ani.dantotsu.media.MediaAdaptor
import ani.dantotsu.media.SearchActivity
import ani.dantotsu.px
@ -43,10 +43,12 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
private var trendHandler: Handler? = null
private lateinit var trendRun: Runnable
var trendingViewPager: ViewPager2? = null
private var uiSettings: UserInterfaceSettings = loadData("ui_settings") ?: UserInterfaceSettings()
private var uiSettings: UserInterfaceSettings =
loadData("ui_settings") ?: UserInterfaceSettings()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MangaPageViewHolder {
val binding = ItemMangaPageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding =
ItemMangaPageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MangaPageViewHolder(binding)
}
@ -58,14 +60,16 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
val currentColor = textInputLayout.boxBackgroundColor
val semiTransparentColor = (currentColor and 0x00FFFFFF) or 0xA8000000.toInt()
textInputLayout.boxBackgroundColor = semiTransparentColor
val materialCardView = holder.itemView.findViewById<MaterialCardView>(R.id.mangaUserAvatarContainer)
val materialCardView =
holder.itemView.findViewById<MaterialCardView>(R.id.mangaUserAvatarContainer)
materialCardView.setCardBackgroundColor(semiTransparentColor)
val typedValue = TypedValue()
currContext()?.theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true)
val color = typedValue.data
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.getBoolean("colorOverflow", false) ?: false
val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getBoolean("colorOverflow", false) ?: false
if (!colorOverflow) {
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
@ -89,7 +93,10 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
}
binding.mangaUserAvatar.setSafeOnClickListener {
SettingsDialogFragment(SettingsDialogFragment.Companion.PageType.MANGA).show((it.context as AppCompatActivity).supportFragmentManager, "dialog")
SettingsDialogFragment(SettingsDialogFragment.Companion.PageType.MANGA).show(
(it.context as AppCompatActivity).supportFragmentManager,
"dialog"
)
}
binding.mangaSearchBar.setEndIconOnClickListener {
@ -117,7 +124,8 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
)
}
binding.mangaIncludeList.visibility = if(Anilist.userid!=null) View.VISIBLE else View.GONE
binding.mangaIncludeList.visibility =
if (Anilist.userid != null) View.VISIBLE else View.GONE
binding.mangaIncludeList.setOnCheckedChangeListener { _, isChecked ->
onIncludeListClick.invoke(isChecked)
}
@ -126,7 +134,7 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
ready.postValue(true)
}
lateinit var onIncludeListClick : ((Boolean)->Unit)
lateinit var onIncludeListClick: ((Boolean) -> Unit)
override fun getItemCount(): Int = 1
@ -142,7 +150,8 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
binding.mangaTrendingViewPager.setPageTransformer(MediaPageTransformer())
trendHandler = Handler(Looper.getMainLooper())
trendRun = Runnable {
binding.mangaTrendingViewPager.currentItem = binding.mangaTrendingViewPager.currentItem + 1
binding.mangaTrendingViewPager.currentItem =
binding.mangaTrendingViewPager.currentItem + 1
}
binding.mangaTrendingViewPager.registerOnPageChangeCallback(
object : ViewPager2.OnPageChangeCallback() {
@ -154,21 +163,28 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
}
)
binding.mangaTrendingViewPager.layoutAnimation = LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.mangaTrendingViewPager.layoutAnimation =
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.mangaTitleContainer.startAnimation(setSlideUp(uiSettings))
binding.mangaListContainer.layoutAnimation = LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.mangaListContainer.layoutAnimation =
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
}
fun updateNovel(adaptor: MediaAdaptor) {
binding.mangaNovelProgressBar.visibility = View.GONE
binding.mangaNovelRecyclerView.adapter = adaptor
binding.mangaNovelRecyclerView.layoutManager =
LinearLayoutManager(binding.mangaNovelRecyclerView.context, LinearLayoutManager.HORIZONTAL, false)
LinearLayoutManager(
binding.mangaNovelRecyclerView.context,
LinearLayoutManager.HORIZONTAL,
false
)
binding.mangaNovelRecyclerView.visibility = View.VISIBLE
binding.mangaNovel.visibility = View.VISIBLE
binding.mangaNovel.startAnimation(setSlideUp(uiSettings))
binding.mangaNovelRecyclerView.layoutAnimation = LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.mangaNovelRecyclerView.layoutAnimation =
LayoutAnimationController(setSlideIn(uiSettings), 0.25f)
binding.mangaPopular.visibility = View.VISIBLE
binding.mangaPopular.startAnimation(setSlideUp(uiSettings))
}
@ -180,5 +196,6 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
}
}
inner class MangaPageViewHolder(val binding: ItemMangaPageBinding) : RecyclerView.ViewHolder(binding.root)
inner class MangaPageViewHolder(val binding: ItemMangaPageBinding) :
RecyclerView.ViewHolder(binding.root)
}

View file

@ -16,20 +16,16 @@ import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import ani.dantotsu.R
import ani.dantotsu.ZoomOutPageTransformer
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.databinding.ActivityNoInternetBinding
import ani.dantotsu.download.manga.OfflineMangaFragment
import ani.dantotsu.initActivity
import ani.dantotsu.isOnline
import ani.dantotsu.loadData
import ani.dantotsu.navBarHeight
import ani.dantotsu.offline.OfflineFragment
import ani.dantotsu.others.LangSet
import ani.dantotsu.selectedOption
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.startMainActivity
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import nl.joery.animatedbottombar.AnimatedBottomBar
class NoInternet : AppCompatActivity() {
@ -39,7 +35,7 @@ class NoInternet : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
binding = ActivityNoInternetBinding.inflate(layoutInflater)
setContentView(binding.root)

View file

@ -12,11 +12,17 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.GridLayoutManager
import ani.dantotsu.*
import ani.dantotsu.EmptyAdapter
import ani.dantotsu.R
import ani.dantotsu.Refresh
import ani.dantotsu.databinding.ActivityAuthorBinding
import ani.dantotsu.others.getSerialized
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.others.LangSet
import ani.dantotsu.others.getSerialized
import ani.dantotsu.px
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -31,7 +37,7 @@ class AuthorActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
binding = ActivityAuthorBinding.inflate(layoutInflater)
setContentView(binding.root)

View file

@ -16,9 +16,9 @@ import ani.dantotsu.Refresh
import ani.dantotsu.databinding.ActivityListBinding
import ani.dantotsu.loadData
import ani.dantotsu.media.user.ListViewPagerAdapter
import ani.dantotsu.others.LangSet
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.Dispatchers
@ -35,7 +35,7 @@ class CalendarActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
binding = ActivityListBinding.inflate(layoutInflater)
@ -43,7 +43,11 @@ ThemeManager(this).applyTheme()
theme.resolveAttribute(com.google.android.material.R.attr.colorSurface, typedValue, true)
val primaryColor = typedValue.data
val typedValue2 = TypedValue()
theme.resolveAttribute(com.google.android.material.R.attr.colorOnBackground, typedValue2, true)
theme.resolveAttribute(
com.google.android.material.R.attr.colorOnBackground,
typedValue2,
true
)
val titleTextColor = typedValue2.data
val typedValue3 = TypedValue()
theme.resolveAttribute(com.google.android.material.R.attr.colorPrimary, typedValue3, true)
@ -65,10 +69,13 @@ ThemeManager(this).applyTheme()
ContextCompat.getColor(this, R.color.nav_bg_inv)
binding.root.fitsSystemWindows = true
}else{
} else {
binding.root.fitsSystemWindows = false
requestWindowFeature(Window.FEATURE_NO_TITLE)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
}
setContentView(binding.root)
@ -79,14 +86,15 @@ ThemeManager(this).applyTheme()
override fun onTabSelected(tab: TabLayout.Tab?) {
this@CalendarActivity.selectedTabIdx = tab?.position ?: 1
}
override fun onTabUnselected(tab: TabLayout.Tab?) { }
override fun onTabReselected(tab: TabLayout.Tab?) { }
override fun onTabUnselected(tab: TabLayout.Tab?) {}
override fun onTabReselected(tab: TabLayout.Tab?) {}
})
model.getCalendar().observe(this) {
if (it != null) {
binding.listProgressBar.visibility = View.GONE
binding.listViewPager.adapter = ListViewPagerAdapter(it.size, true,this)
binding.listViewPager.adapter = ListViewPagerAdapter(it.size, true, this)
val keys = it.keys.toList()
val values = it.values.toList()
val savedTab = this.selectedTabIdx

View file

@ -21,11 +21,13 @@ class CharacterAdapter(
private val characterList: ArrayList<Character>
) : RecyclerView.Adapter<CharacterAdapter.CharacterViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CharacterViewHolder {
val binding = ItemCharacterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding =
ItemCharacterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return CharacterViewHolder(binding)
}
private val uiSettings = loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
private val uiSettings =
loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: CharacterViewHolder, position: Int) {
@ -38,16 +40,23 @@ class CharacterAdapter(
}
override fun getItemCount(): Int = characterList.size
inner class CharacterViewHolder(val binding: ItemCharacterBinding) : RecyclerView.ViewHolder(binding.root) {
inner class CharacterViewHolder(val binding: ItemCharacterBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
val char = characterList[bindingAdapterPosition]
ContextCompat.startActivity(
itemView.context,
Intent(itemView.context, CharacterDetailsActivity::class.java).putExtra("character", char as Serializable),
Intent(
itemView.context,
CharacterDetailsActivity::class.java
).putExtra("character", char as Serializable),
ActivityOptionsCompat.makeSceneTransitionAnimation(
itemView.context as Activity,
Pair.create(binding.itemCompactImage, ViewCompat.getTransitionName(binding.itemCompactImage)!!),
Pair.create(
binding.itemCompactImage,
ViewCompat.getTransitionName(binding.itemCompactImage)!!
),
).toBundle()
)
}

View file

@ -13,13 +13,20 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.GridLayoutManager
import ani.dantotsu.*
import ani.dantotsu.R
import ani.dantotsu.Refresh
import ani.dantotsu.databinding.ActivityCharacterBinding
import ani.dantotsu.initActivity
import ani.dantotsu.loadData
import ani.dantotsu.loadImage
import ani.dantotsu.navBarHeight
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.others.getSerialized
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import ani.dantotsu.others.getSerialized
import ani.dantotsu.px
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import com.google.android.material.appbar.AppBarLayout
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -36,15 +43,17 @@ class CharacterDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChang
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
binding = ActivityCharacterBinding.inflate(layoutInflater)
setContentView(binding.root)
initActivity(this)
screenWidth = resources.displayMetrics.run { widthPixels / density }
if (uiSettings.immersiveMode) this.window.statusBarColor = ContextCompat.getColor(this, R.color.status)
if (uiSettings.immersiveMode) this.window.statusBarColor =
ContextCompat.getColor(this, R.color.status)
val banner = if (uiSettings.bannerAnimations) binding.characterBanner else binding.characterBannerNoKen
val banner =
if (uiSettings.bannerAnimations) binding.characterBanner else binding.characterBannerNoKen
banner.updateLayoutParams { height += statusBarHeight }
binding.characterClose.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin += statusBarHeight }
@ -61,7 +70,13 @@ ThemeManager(this).applyTheme()
binding.characterTitle.text = character.name
banner.loadImage(character.banner)
binding.characterCoverImage.loadImage(character.image)
binding.characterCoverImage.setOnLongClickListener { ImageViewDialog.newInstance(this, character.name, character.image) }
binding.characterCoverImage.setOnLongClickListener {
ImageViewDialog.newInstance(
this,
character.name,
character.image
)
}
model.getCharacter().observe(this) {
if (it != null && !loaded) {
@ -73,7 +88,8 @@ ThemeManager(this).applyTheme()
val roles = character.roles
if (roles != null) {
val mediaAdaptor = MediaAdaptor(0, roles, this, matchParent = true)
val concatAdaptor = ConcatAdapter(CharacterDetailsAdapter(character, this), mediaAdaptor)
val concatAdaptor =
ConcatAdapter(CharacterDetailsAdapter(character, this), mediaAdaptor)
val gridSize = (screenWidth / 124f).toInt()
val gridLayoutManager = GridLayoutManager(this, gridSize)
@ -118,16 +134,19 @@ ThemeManager(this).applyTheme()
binding.characterCover.scaleY = 1f * cap
binding.characterCover.cardElevation = 32f * cap
binding.characterCover.visibility = if (binding.characterCover.scaleX == 0f) View.GONE else View.VISIBLE
binding.characterCover.visibility =
if (binding.characterCover.scaleX == 0f) View.GONE else View.VISIBLE
if (percentage >= percent && !isCollapsed) {
isCollapsed = true
if (uiSettings.immersiveMode) this.window.statusBarColor = ContextCompat.getColor(this, R.color.nav_bg)
if (uiSettings.immersiveMode) this.window.statusBarColor =
ContextCompat.getColor(this, R.color.nav_bg)
binding.characterAppBar.setBackgroundResource(R.color.nav_bg)
}
if (percentage <= percent && isCollapsed) {
isCollapsed = false
if (uiSettings.immersiveMode) this.window.statusBarColor = ContextCompat.getColor(this, R.color.status)
if (uiSettings.immersiveMode) this.window.statusBarColor =
ContextCompat.getColor(this, R.color.status)
binding.characterAppBar.setBackgroundResource(R.color.bg)
}
}

View file

@ -15,7 +15,8 @@ import io.noties.markwon.SoftBreakAddsNewLinePlugin
class CharacterDetailsAdapter(private val character: Character, private val activity: Activity) :
RecyclerView.Adapter<CharacterDetailsAdapter.GenreViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenreViewHolder {
val binding = ItemCharacterDetailsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding =
ItemCharacterDetailsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return GenreViewHolder(binding)
}
@ -25,18 +26,20 @@ class CharacterDetailsAdapter(private val character: Character, private val acti
val desc =
(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") currActivity()!!.getString(R.string.gender) + " " + when(character.gender){
(if (character.gender != "null") currActivity()!!.getString(R.string.gender) + " " + when (character.gender) {
"Male" -> currActivity()!!.getString(R.string.male)
"Female" -> currActivity()!!.getString(R.string.female)
else -> character.gender
} else "") + "\n" + character.description
binding.characterDesc.isTextSelectable
val markWon = Markwon.builder(activity).usePlugin(SoftBreakAddsNewLinePlugin.create()).usePlugin(SpoilerPlugin()).build()
val markWon = Markwon.builder(activity).usePlugin(SoftBreakAddsNewLinePlugin.create())
.usePlugin(SpoilerPlugin()).build()
markWon.setMarkdown(binding.characterDesc, desc)
}
override fun getItemCount(): Int = 1
inner class GenreViewHolder(val binding: ItemCharacterDetailsBinding) : RecyclerView.ViewHolder(binding.root)
inner class GenreViewHolder(val binding: ItemCharacterDetailsBinding) :
RecyclerView.ViewHolder(binding.root)
}

View file

@ -14,9 +14,9 @@ import ani.dantotsu.databinding.ActivityGenreBinding
import ani.dantotsu.initActivity
import ani.dantotsu.loadData
import ani.dantotsu.navBarHeight
import ani.dantotsu.others.LangSet
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
@ -28,7 +28,7 @@ class GenreActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
binding = ActivityGenreBinding.inflate(layoutInflater)
setContentView(binding.root)
initActivity(this)
@ -50,7 +50,8 @@ ThemeManager(this).applyTheme()
model.doneListener?.invoke()
}
binding.mediaInfoGenresRecyclerView.adapter = adapter
binding.mediaInfoGenresRecyclerView.layoutManager = GridLayoutManager(this, (screenWidth / 156f).toInt())
binding.mediaInfoGenresRecyclerView.layoutManager =
GridLayoutManager(this, (screenWidth / 156f).toInt())
lifecycleScope.launch(Dispatchers.IO) {
model.loadGenres(Anilist.genres ?: loadData("genres_list") ?: arrayListOf()) {

View file

@ -37,7 +37,8 @@ class GenreAdapter(
}
override fun getItemCount(): Int = genres.size
inner class GenreViewHolder(val binding: ItemGenreBinding) : RecyclerView.ViewHolder(binding.root) {
inner class GenreViewHolder(val binding: ItemGenreBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
ContextCompat.startActivity(

View file

@ -41,7 +41,7 @@ data class Media(
var userUpdatedAt: Long? = null,
var userStartedAt: FuzzyDate = FuzzyDate(),
var userCompletedAt: FuzzyDate = FuzzyDate(),
var inCustomListsOf: MutableMap<String, Boolean>?= null,
var inCustomListsOf: MutableMap<String, Boolean>? = null,
var userFavOrder: Int? = null,
val status: String? = null,
@ -70,7 +70,7 @@ data class Media(
var shareLink: String? = null,
var selected: Selected? = null,
var idKitsu: String?=null,
var idKitsu: String? = null,
var cameFromContinue: Boolean = false
) : Serializable {

View file

@ -41,13 +41,35 @@ class MediaAdaptor(
private val viewPager: ViewPager2? = null,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val uiSettings = loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
private val uiSettings =
loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (type) {
0 -> MediaViewHolder(ItemMediaCompactBinding.inflate(LayoutInflater.from(parent.context), parent, false))
1 -> MediaLargeViewHolder(ItemMediaLargeBinding.inflate(LayoutInflater.from(parent.context), parent, false))
2 -> MediaPageViewHolder(ItemMediaPageBinding.inflate(LayoutInflater.from(parent.context), parent, false))
0 -> MediaViewHolder(
ItemMediaCompactBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
1 -> MediaLargeViewHolder(
ItemMediaLargeBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
2 -> MediaPageViewHolder(
ItemMediaPageBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
3 -> MediaPageSmallViewHolder(
ItemMediaPageSmallBinding.inflate(
LayoutInflater.from(parent.context),
@ -55,6 +77,7 @@ class MediaAdaptor(
false
)
)
else -> throw IllegalArgumentException()
}
@ -69,10 +92,12 @@ class MediaAdaptor(
val media = mediaList?.getOrNull(position)
if (media != null) {
b.itemCompactImage.loadImage(media.cover)
b.itemCompactOngoing.visibility = if (media.status == currActivity()!!.getString(R.string.status_releasing)) View.VISIBLE else View.GONE
b.itemCompactOngoing.visibility =
if (media.status == currActivity()!!.getString(R.string.status_releasing)) View.VISIBLE else View.GONE
b.itemCompactTitle.text = media.userPreferredName
b.itemCompactScore.text =
((if (media.userScore == 0) (media.meanScore ?: 0) else media.userScore) / 10.0).toString()
((if (media.userScore == 0) (media.meanScore
?: 0) else media.userScore) / 10.0).toString()
b.itemCompactScoreBG.background = ContextCompat.getDrawable(
b.root.context,
(if (media.userScore != 0) R.drawable.item_user_score else R.drawable.item_score)
@ -104,6 +129,7 @@ class MediaAdaptor(
}
}
}
1 -> {
val b = (holder as MediaLargeViewHolder).binding
setAnimation(activity, b.root, uiSettings)
@ -111,22 +137,29 @@ class MediaAdaptor(
if (media != null) {
b.itemCompactImage.loadImage(media.cover)
b.itemCompactBanner.loadImage(media.banner ?: media.cover, 400)
b.itemCompactOngoing.visibility = if (media.status == currActivity()!!.getString(R.string.status_releasing)) View.VISIBLE else View.GONE
b.itemCompactOngoing.visibility =
if (media.status == currActivity()!!.getString(R.string.status_releasing)) View.VISIBLE else View.GONE
b.itemCompactTitle.text = media.userPreferredName
b.itemCompactScore.text =
((if (media.userScore == 0) (media.meanScore ?: 0) else media.userScore) / 10.0).toString()
((if (media.userScore == 0) (media.meanScore
?: 0) else media.userScore) / 10.0).toString()
b.itemCompactScoreBG.background = ContextCompat.getDrawable(
b.root.context,
(if (media.userScore != 0) R.drawable.item_user_score else R.drawable.item_score)
)
if (media.anime != null) {
b.itemTotal.text = " " + if ((media.anime.totalEpisodes ?: 0) != 1) currActivity()!!.getString(R.string.episode_plural)
b.itemTotal.text = " " + if ((media.anime.totalEpisodes
?: 0) != 1
) currActivity()!!.getString(R.string.episode_plural)
else currActivity()!!.getString(R.string.episode_singular)
b.itemCompactTotal.text =
if (media.anime.nextAiringEpisode != null) (media.anime.nextAiringEpisode.toString() + " / " + (media.anime.totalEpisodes
?: "??").toString()) else (media.anime.totalEpisodes ?: "??").toString()
?: "??").toString()) else (media.anime.totalEpisodes
?: "??").toString()
} else if (media.manga != null) {
b.itemTotal.text = " " + if ((media.manga.totalChapters ?: 0) != 1) currActivity()!!.getString(R.string.chapter_plural)
b.itemTotal.text = " " + if ((media.manga.totalChapters
?: 0) != 1
) currActivity()!!.getString(R.string.chapter_plural)
else currActivity()!!.getString(R.string.chapter_singular)
b.itemCompactTotal.text = "${media.manga.totalChapters ?: "??"}"
}
@ -137,6 +170,7 @@ class MediaAdaptor(
}
}
}
2 -> {
val b = (holder as MediaPageViewHolder).binding
val media = mediaList?.get(position)
@ -149,7 +183,8 @@ class MediaAdaptor(
AccelerateDecelerateInterpolator()
)
)
val banner = if (uiSettings.bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
val banner =
if (uiSettings.bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
val context = b.itemCompactBanner.context
if (!(context as Activity).isDestroyed)
Glide.with(context as Context)
@ -157,22 +192,29 @@ class MediaAdaptor(
.diskCacheStrategy(DiskCacheStrategy.ALL).override(400)
.apply(RequestOptions.bitmapTransform(BlurTransformation(2, 3)))
.into(banner)
b.itemCompactOngoing.visibility = if (media.status == currActivity()!!.getString(R.string.status_releasing)) View.VISIBLE else View.GONE
b.itemCompactOngoing.visibility =
if (media.status == currActivity()!!.getString(R.string.status_releasing)) View.VISIBLE else View.GONE
b.itemCompactTitle.text = media.userPreferredName
b.itemCompactScore.text =
((if (media.userScore == 0) (media.meanScore ?: 0) else media.userScore) / 10.0).toString()
((if (media.userScore == 0) (media.meanScore
?: 0) else media.userScore) / 10.0).toString()
b.itemCompactScoreBG.background = ContextCompat.getDrawable(
b.root.context,
(if (media.userScore != 0) R.drawable.item_user_score else R.drawable.item_score)
)
if (media.anime != null) {
b.itemTotal.text = " " + if ((media.anime.totalEpisodes ?: 0) != 1) currActivity()!!.getString(R.string.episode_plural)
b.itemTotal.text = " " + if ((media.anime.totalEpisodes
?: 0) != 1
) currActivity()!!.getString(R.string.episode_plural)
else currActivity()!!.getString(R.string.episode_singular)
b.itemCompactTotal.text =
if (media.anime.nextAiringEpisode != null) (media.anime.nextAiringEpisode.toString() + " / " + (media.anime.totalEpisodes
?: "??").toString()) else (media.anime.totalEpisodes ?: "??").toString()
?: "??").toString()) else (media.anime.totalEpisodes
?: "??").toString()
} else if (media.manga != null) {
b.itemTotal.text =" " + if ((media.manga.totalChapters ?: 0) != 1) currActivity()!!.getString(R.string.chapter_plural)
b.itemTotal.text = " " + if ((media.manga.totalChapters
?: 0) != 1
) currActivity()!!.getString(R.string.chapter_plural)
else currActivity()!!.getString(R.string.chapter_singular)
b.itemCompactTotal.text = "${media.manga.totalChapters ?: "??"}"
}
@ -184,6 +226,7 @@ class MediaAdaptor(
}
}
}
3 -> {
val b = (holder as MediaPageSmallViewHolder).binding
val media = mediaList?.get(position)
@ -196,7 +239,8 @@ class MediaAdaptor(
AccelerateDecelerateInterpolator()
)
)
val banner = if (uiSettings.bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
val banner =
if (uiSettings.bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen
val context = b.itemCompactBanner.context
if (!(context as Activity).isDestroyed)
Glide.with(context as Context)
@ -204,10 +248,12 @@ class MediaAdaptor(
.diskCacheStrategy(DiskCacheStrategy.ALL).override(400)
.apply(RequestOptions.bitmapTransform(BlurTransformation(2, 3)))
.into(banner)
b.itemCompactOngoing.visibility = if (media.status == currActivity()!!.getString(R.string.status_releasing)) View.VISIBLE else View.GONE
b.itemCompactOngoing.visibility =
if (media.status == currActivity()!!.getString(R.string.status_releasing)) View.VISIBLE else View.GONE
b.itemCompactTitle.text = media.userPreferredName
b.itemCompactScore.text =
((if (media.userScore == 0) (media.meanScore ?: 0) else media.userScore) / 10.0).toString()
((if (media.userScore == 0) (media.meanScore
?: 0) else media.userScore) / 10.0).toString()
b.itemCompactScoreBG.background = ContextCompat.getDrawable(
b.root.context,
(if (media.userScore != 0) R.drawable.item_user_score else R.drawable.item_score)
@ -222,13 +268,18 @@ class MediaAdaptor(
}
b.itemCompactStatus.text = media.status ?: ""
if (media.anime != null) {
b.itemTotal.text = " " + if ((media.anime.totalEpisodes ?: 0) != 1) currActivity()!!.getString(R.string.episode_plural)
b.itemTotal.text = " " + if ((media.anime.totalEpisodes
?: 0) != 1
) currActivity()!!.getString(R.string.episode_plural)
else currActivity()!!.getString(R.string.episode_singular)
b.itemCompactTotal.text =
if (media.anime.nextAiringEpisode != null) (media.anime.nextAiringEpisode.toString() + " / " + (media.anime.totalEpisodes
?: "??").toString()) else (media.anime.totalEpisodes ?: "??").toString()
?: "??").toString()) else (media.anime.totalEpisodes
?: "??").toString()
} else if (media.manga != null) {
b.itemTotal.text = " " + if ((media.manga.totalChapters ?: 0) != 1) currActivity()!!.getString(R.string.chapter_plural)
b.itemTotal.text = " " + if ((media.manga.totalChapters
?: 0) != 1
) currActivity()!!.getString(R.string.chapter_plural)
else currActivity()!!.getString(R.string.chapter_singular)
b.itemCompactTotal.text = "${media.manga.totalChapters ?: "??"}"
}
@ -249,35 +300,64 @@ class MediaAdaptor(
return type
}
inner class MediaViewHolder(val binding: ItemMediaCompactBinding) : RecyclerView.ViewHolder(binding.root) {
inner class MediaViewHolder(val binding: ItemMediaCompactBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
if (matchParent) itemView.updateLayoutParams { width = -1 }
itemView.setSafeOnClickListener { clicked(bindingAdapterPosition, resizeBitmap(getBitmapFromImageView(binding.itemCompactImage), 100)) }
itemView.setSafeOnClickListener {
clicked(
bindingAdapterPosition,
resizeBitmap(getBitmapFromImageView(binding.itemCompactImage), 100)
)
}
itemView.setOnLongClickListener { longClicked(bindingAdapterPosition) }
}
}
inner class MediaLargeViewHolder(val binding: ItemMediaLargeBinding) : RecyclerView.ViewHolder(binding.root) {
inner class MediaLargeViewHolder(val binding: ItemMediaLargeBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setSafeOnClickListener { clicked(bindingAdapterPosition, resizeBitmap(getBitmapFromImageView(binding.itemCompactImage), 100)) }
itemView.setSafeOnClickListener {
clicked(
bindingAdapterPosition,
resizeBitmap(getBitmapFromImageView(binding.itemCompactImage), 100)
)
}
itemView.setOnLongClickListener { longClicked(bindingAdapterPosition) }
}
}
@SuppressLint("ClickableViewAccessibility")
inner class MediaPageViewHolder(val binding: ItemMediaPageBinding) : RecyclerView.ViewHolder(binding.root) {
inner class MediaPageViewHolder(val binding: ItemMediaPageBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.itemCompactImage.setSafeOnClickListener { clicked(bindingAdapterPosition, resizeBitmap(getBitmapFromImageView(binding.itemCompactImage), 100)) }
binding.itemCompactImage.setSafeOnClickListener {
clicked(
bindingAdapterPosition,
resizeBitmap(getBitmapFromImageView(binding.itemCompactImage), 100)
)
}
itemView.setOnTouchListener { _, _ -> true }
binding.itemCompactImage.setOnLongClickListener { longClicked(bindingAdapterPosition) }
}
}
@SuppressLint("ClickableViewAccessibility")
inner class MediaPageSmallViewHolder(val binding: ItemMediaPageSmallBinding) : RecyclerView.ViewHolder(binding.root) {
inner class MediaPageSmallViewHolder(val binding: ItemMediaPageSmallBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.itemCompactImage.setSafeOnClickListener { clicked(bindingAdapterPosition, resizeBitmap(getBitmapFromImageView(binding.itemCompactImage), 100)) }
binding.itemCompactTitleContainer.setSafeOnClickListener { clicked(bindingAdapterPosition, resizeBitmap(getBitmapFromImageView(binding.itemCompactImage), 100)) }
binding.itemCompactImage.setSafeOnClickListener {
clicked(
bindingAdapterPosition,
resizeBitmap(getBitmapFromImageView(binding.itemCompactImage), 100)
)
}
binding.itemCompactTitleContainer.setSafeOnClickListener {
clicked(
bindingAdapterPosition,
resizeBitmap(getBitmapFromImageView(binding.itemCompactImage), 100)
)
}
itemView.setOnTouchListener { _, _ -> true }
binding.itemCompactImage.setOnLongClickListener { longClicked(bindingAdapterPosition) }
}
@ -301,7 +381,8 @@ class MediaAdaptor(
if ((mediaList?.size ?: 0) > position && position != -1) {
val media = mediaList?.get(position) ?: return false
if (activity.supportFragmentManager.findFragmentByTag("list") == null) {
MediaListDialogSmallFragment.newInstance(media).show(activity.supportFragmentManager, "list")
MediaListDialogSmallFragment.newInstance(media)
.show(activity.supportFragmentManager, "list")
return true
}
}
@ -317,9 +398,11 @@ class MediaAdaptor(
}
// Create a bitmap with the same dimensions as the drawable
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth,
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888)
Bitmap.Config.ARGB_8888
)
// Draw the drawable onto the bitmap
val canvas = Canvas(bitmap)

View file

@ -5,7 +5,6 @@ import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.text.SpannableStringBuilder
import android.util.Log
import android.util.TypedValue
import android.view.GestureDetector
import android.view.MotionEvent
@ -32,24 +31,24 @@ import ani.dantotsu.R
import ani.dantotsu.Refresh
import ani.dantotsu.ZoomOutPageTransformer
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.media.anime.AnimeWatchFragment
import ani.dantotsu.copyToClipboard
import ani.dantotsu.databinding.ActivityMediaBinding
import ani.dantotsu.initActivity
import ani.dantotsu.loadData
import ani.dantotsu.loadImage
import ani.dantotsu.media.anime.AnimeWatchFragment
import ani.dantotsu.media.manga.MangaReadFragment
import ani.dantotsu.navBarHeight
import ani.dantotsu.media.novel.NovelReadFragment
import ani.dantotsu.navBarHeight
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.others.LangSet
import ani.dantotsu.others.getSerialized
import ani.dantotsu.saveData
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.snackString
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import com.flaviofaria.kenburnsview.RandomTransitionGenerator
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.navigation.NavigationBarView

View file

@ -1,28 +1,29 @@
package ani.dantotsu.media
import android.app.Activity
import android.content.Context
import android.content.SharedPreferences
import android.os.Environment
import android.os.Handler
import android.os.Looper
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import ani.dantotsu.FileUrl
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.media.anime.Episode
import ani.dantotsu.media.anime.SelectorDialogFragment
import ani.dantotsu.currContext
import ani.dantotsu.loadData
import ani.dantotsu.logger
import ani.dantotsu.media.anime.Episode
import ani.dantotsu.media.anime.SelectorDialogFragment
import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.others.AniSkip
import ani.dantotsu.others.Jikan
import ani.dantotsu.others.Kitsu
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.Book
import ani.dantotsu.parsers.MangaImage
import ani.dantotsu.parsers.MangaReadSources
import ani.dantotsu.parsers.MangaSources
import ani.dantotsu.parsers.NovelSources
import ani.dantotsu.parsers.ShowResponse
import ani.dantotsu.parsers.VideoExtractor
@ -30,25 +31,12 @@ import ani.dantotsu.parsers.WatchSources
import ani.dantotsu.saveData
import ani.dantotsu.snackString
import ani.dantotsu.tryWithSuspend
import ani.dantotsu.currContext
import ani.dantotsu.R
import ani.dantotsu.download.Download
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.AniyomiAdapter
import ani.dantotsu.parsers.DynamicMangaParser
import ani.dantotsu.parsers.HAnimeSources
import ani.dantotsu.parsers.HMangaSources
import ani.dantotsu.parsers.MangaSources
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
class MediaDetailsViewModel : ViewModel() {
val scrolledToTop = MutableLiveData(true)
@ -62,8 +50,8 @@ class MediaDetailsViewModel : ViewModel() {
val sharedPreferences = Injekt.get<SharedPreferences>()
val data = loadData<Selected>("${media.id}-select") ?: Selected().let {
it.sourceIndex = if (media.isAdult) 0 else when (media.anime != null) {
true ->sharedPreferences.getInt("settings_def_anime_source_s_r", 0)
else ->sharedPreferences.getInt(("settings_def_manga_source_s_r"), 0)
true -> sharedPreferences.getInt("settings_def_anime_source_s_r", 0)
else -> sharedPreferences.getInt(("settings_def_manga_source_s_r"), 0)
}
it.preferDub = loadData("settings_prefer_dub") ?: false
saveSelected(media.id, it)
@ -81,7 +69,9 @@ class MediaDetailsViewModel : ViewModel() {
fun loadSelectedStringLocation(sourceName: String): Int {
//find the location of the source in the list
var location = watchSources?.list?.indexOfFirst { it.name == sourceName } ?: 0
if (location == -1) {location = 0}
if (location == -1) {
location = 0
}
return location
}
@ -106,7 +96,9 @@ class MediaDetailsViewModel : ViewModel() {
//Anime
private val kitsuEpisodes: MutableLiveData<Map<String, Episode>> = MutableLiveData<Map<String, Episode>>(null)
private val kitsuEpisodes: MutableLiveData<Map<String, Episode>> =
MutableLiveData<Map<String, Episode>>(null)
fun getKitsuEpisodes(): LiveData<Map<String, Episode>> = kitsuEpisodes
suspend fun loadKitsuEpisodes(s: Media) {
tryWithSuspend {
@ -114,7 +106,9 @@ class MediaDetailsViewModel : ViewModel() {
}
}
private val fillerEpisodes: MutableLiveData<Map<String, Episode>> = MutableLiveData<Map<String, Episode>>(null)
private val fillerEpisodes: MutableLiveData<Map<String, Episode>> =
MutableLiveData<Map<String, Episode>>(null)
fun getFillerEpisodes(): LiveData<Map<String, Episode>> = fillerEpisodes
suspend fun loadFillerEpisodes(s: Media) {
tryWithSuspend {
@ -145,7 +139,8 @@ class MediaDetailsViewModel : ViewModel() {
suspend fun overrideEpisodes(i: Int, source: ShowResponse, id: Int) {
watchSources?.saveResponse(i, id, source)
epsLoaded[i] = watchSources?.loadEpisodes(i, source.link, source.extra, source.sAnime) ?: return
epsLoaded[i] =
watchSources?.loadEpisodes(i, source.link, source.extra, source.sAnime) ?: return
episodes.postValue(epsLoaded)
}
@ -184,7 +179,12 @@ class MediaDetailsViewModel : ViewModel() {
val timeStamps = MutableLiveData<List<AniSkip.Stamp>?>()
private val timeStampsMap: MutableMap<Int, List<AniSkip.Stamp>?> = mutableMapOf()
suspend fun loadTimeStamps(malId: Int?, episodeNum: Int?, duration: Long, useProxyForTimeStamps: Boolean) {
suspend fun loadTimeStamps(
malId: Int?,
episodeNum: Int?,
duration: Long,
useProxyForTimeStamps: Boolean
) {
malId ?: return
episodeNum ?: return
if (timeStampsMap.containsKey(episodeNum))
@ -194,7 +194,11 @@ class MediaDetailsViewModel : ViewModel() {
timeStamps.postValue(result)
}
suspend fun loadEpisodeSingleVideo(ep: Episode, selected: Selected, post: Boolean = true): Boolean {
suspend fun loadEpisodeSingleVideo(
ep: Episode,
selected: Selected,
post: Boolean = true
): Boolean {
if (ep.extractors.isNullOrEmpty()) {
val server = selected.server ?: return false
@ -204,8 +208,10 @@ class MediaDetailsViewModel : ViewModel() {
selected.sourceIndex = selected.sourceIndex
if (!post && !it.allowsPreloading) null
else ep.sEpisode?.let { it1 ->
it.loadSingleVideoServer(server, link, ep.extra,
it1, post)
it.loadSingleVideoServer(
server, link, ep.extra,
it1, post
)
}
} ?: return false)
ep.allStreams = false
@ -228,7 +234,13 @@ class MediaDetailsViewModel : ViewModel() {
}
val epChanged = MutableLiveData(true)
fun onEpisodeClick(media: Media, i: String, manager: FragmentManager, launch: Boolean = true, prevEp: String? = null) {
fun onEpisodeClick(
media: Media,
i: String,
manager: FragmentManager,
launch: Boolean = true,
prevEp: String? = null
) {
Handler(Looper.getMainLooper()).post {
if (manager.findFragmentByTag("dialog") == null && !manager.isDestroyed) {
if (media.anime?.episodes?.get(i) != null) {
@ -238,7 +250,8 @@ class MediaDetailsViewModel : ViewModel() {
return@post
}
media.selected = this.loadSelected(media)
val selector = SelectorDialogFragment.newInstance(media.selected!!.server, launch, prevEp)
val selector =
SelectorDialogFragment.newInstance(media.selected!!.server, launch, prevEp)
selector.show(manager, "dialog")
}
}
@ -248,13 +261,17 @@ class MediaDetailsViewModel : ViewModel() {
//Manga
var mangaReadSources: MangaReadSources? = null
private val mangaChapters = MutableLiveData<MutableMap<Int, MutableMap<String, MangaChapter>>>(null)
private val mangaChapters =
MutableLiveData<MutableMap<Int, MutableMap<String, MangaChapter>>>(null)
private val mangaLoaded = mutableMapOf<Int, MutableMap<String, MangaChapter>>()
fun getMangaChapters(): LiveData<MutableMap<Int, MutableMap<String, MangaChapter>>> = mangaChapters
fun getMangaChapters(): LiveData<MutableMap<Int, MutableMap<String, MangaChapter>>> =
mangaChapters
suspend fun loadMangaChapters(media: Media, i: Int, invalidate: Boolean = false) {
logger("Loading Manga Chapters : $mangaLoaded")
if (!mangaLoaded.containsKey(i) || invalidate) tryWithSuspend {
mangaLoaded[i] = mangaReadSources?.loadChaptersFromMedia(i, media) ?: return@tryWithSuspend
mangaLoaded[i] =
mangaReadSources?.loadChaptersFromMedia(i, media) ?: return@tryWithSuspend
}
mangaChapters.postValue(mangaLoaded)
}
@ -269,11 +286,17 @@ class MediaDetailsViewModel : ViewModel() {
private val mangaChapter = MutableLiveData<MangaChapter?>(null)
fun getMangaChapter(): LiveData<MangaChapter?> = mangaChapter
suspend fun loadMangaChapterImages(chapter: MangaChapter, selected: Selected, series: String, post: Boolean = true): Boolean {
suspend fun loadMangaChapterImages(
chapter: MangaChapter,
selected: Selected,
series: String,
post: Boolean = true
): Boolean {
return tryWithSuspend(true) {
chapter.addImages(
mangaReadSources?.get(selected.sourceIndex)?.loadImages(chapter.link, chapter.sChapter) ?: return@tryWithSuspend false
mangaReadSources?.get(selected.sourceIndex)
?.loadImages(chapter.link, chapter.sChapter) ?: return@tryWithSuspend false
)
if (post) mangaChapter.postValue(chapter)
true
@ -281,7 +304,8 @@ class MediaDetailsViewModel : ViewModel() {
}
fun loadTransformation(mangaImage: MangaImage, source: Int): BitmapTransformation? {
return if (mangaImage.useTransformation) mangaReadSources?.get(source)?.getTransformation() else null
return if (mangaImage.useTransformation) mangaReadSources?.get(source)
?.getTransformation() else null
}
val novelSources = NovelSources
@ -296,7 +320,7 @@ class MediaDetailsViewModel : ViewModel() {
}
suspend fun autoSearchNovels(media: Media) {
val source = novelSources[media.selected?.sourceIndex?:0]
val source = novelSources[media.selected?.sourceIndex ?: 0]
tryWithSuspend(post = true) {
if (source != null) {
novelResponses.postValue(source.sortedSearch(media))
@ -307,7 +331,9 @@ class MediaDetailsViewModel : ViewModel() {
val book: MutableLiveData<Book> = MutableLiveData(null)
suspend fun loadBook(novel: ShowResponse, i: Int) {
tryWithSuspend {
book.postValue(novelSources[i]?.loadBook(novel.link, novel.extra) ?: return@tryWithSuspend)
book.postValue(
novelSources[i]?.loadBook(novel.link, novel.extra) ?: return@tryWithSuspend
)
}
}

View file

@ -43,7 +43,11 @@ class MediaInfoFragment : Fragment() {
private var type = "ANIME"
private val genreModel: GenresViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentMediaInfoBinding.inflate(inflater, container, false)
return binding.root
}
@ -59,8 +63,8 @@ class MediaInfoFragment : Fragment() {
binding.mediaInfoContainer.visibility = if (loaded) View.VISIBLE else View.GONE
binding.mediaInfoContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin += 128f.px + navBarHeight }
model.scrolledToTop.observe(viewLifecycleOwner){
if(it) binding.mediaInfoScroll.scrollTo(0,0)
model.scrolledToTop.observe(viewLifecycleOwner) {
if (it) binding.mediaInfoScroll.scrollTo(0, 0)
}
model.getMedia().observe(viewLifecycleOwner) { media ->
@ -68,30 +72,32 @@ class MediaInfoFragment : Fragment() {
loaded = true
binding.mediaInfoProgressBar.visibility = View.GONE
binding.mediaInfoContainer.visibility = View.VISIBLE
binding.mediaInfoName.text = "\t\t\t" + (media.name?:media.nameRomaji)
binding.mediaInfoName.text = "\t\t\t" + (media.name ?: media.nameRomaji)
binding.mediaInfoName.setOnLongClickListener {
copyToClipboard(media.name?:media.nameRomaji)
copyToClipboard(media.name ?: media.nameRomaji)
true
}
if (media.name != null) binding.mediaInfoNameRomajiContainer.visibility = View.VISIBLE
if (media.name != null) binding.mediaInfoNameRomajiContainer.visibility =
View.VISIBLE
binding.mediaInfoNameRomaji.text = "\t\t\t" + media.nameRomaji
binding.mediaInfoNameRomaji.setOnLongClickListener {
copyToClipboard(media.nameRomaji)
true
}
binding.mediaInfoMeanScore.text = if (media.meanScore != null) (media.meanScore / 10.0).toString() else "??"
binding.mediaInfoMeanScore.text =
if (media.meanScore != null) (media.meanScore / 10.0).toString() else "??"
binding.mediaInfoStatus.text = media.status
binding.mediaInfoFormat.text = media.format
binding.mediaInfoSource.text = media.source
binding.mediaInfoStart.text = media.startDate?.toString() ?: "??"
binding.mediaInfoEnd.text =media.endDate?.toString() ?: "??"
binding.mediaInfoEnd.text = media.endDate?.toString() ?: "??"
if (media.anime != null) {
binding.mediaInfoDuration.text =
if (media.anime.episodeDuration != null) media.anime.episodeDuration.toString() else "??"
binding.mediaInfoDurationContainer.visibility = View.VISIBLE
binding.mediaInfoSeasonContainer.visibility = View.VISIBLE
binding.mediaInfoSeason.text =
(media.anime.season ?: "??")+ " " + (media.anime.seasonYear ?: "??")
(media.anime.season ?: "??") + " " + (media.anime.seasonYear ?: "??")
if (media.anime.mainStudio != null) {
binding.mediaInfoStudioContainer.visibility = View.VISIBLE
binding.mediaInfoStudio.text = media.anime.mainStudio!!.name
@ -246,7 +252,12 @@ class MediaInfoFragment : Fragment() {
val end = a.indexOf('"', first).let { if (it != -1) it else return a }
val name = a.subSequence(first, end).toString()
return "${a.subSequence(0, first)}" +
"[$name](https://www.youtube.com/results?search_query=${URLEncoder.encode(name, "utf-8")})" +
"[$name](https://www.youtube.com/results?search_query=${
URLEncoder.encode(
name,
"utf-8"
)
})" +
"${a.subSequence(end, a.length)}"
}
@ -270,7 +281,11 @@ class MediaInfoFragment : Fragment() {
}
if (media.anime.op.isNotEmpty()) {
val bind = ItemTitleTextBinding.inflate(LayoutInflater.from(context), parent, false)
val bind = ItemTitleTextBinding.inflate(
LayoutInflater.from(context),
parent,
false
)
bind.itemTitle.setText(R.string.opening)
makeText(bind.itemText, media.anime.op)
parent.addView(bind.root)
@ -278,7 +293,11 @@ class MediaInfoFragment : Fragment() {
if (media.anime.ed.isNotEmpty()) {
val bind = ItemTitleTextBinding.inflate(LayoutInflater.from(context), parent, false)
val bind = ItemTitleTextBinding.inflate(
LayoutInflater.from(context),
parent,
false
)
bind.itemTitle.setText(R.string.ending)
makeText(bind.itemText, media.anime.ed)
parent.addView(bind.root)
@ -458,7 +477,8 @@ class MediaInfoFragment : Fragment() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val cornerTop = ObjectAnimator.ofFloat(binding.root, "radius", 0f, 32f).setDuration(200)
val cornerNotTop = ObjectAnimator.ofFloat(binding.root, "radius", 32f, 0f).setDuration(200)
val cornerNotTop =
ObjectAnimator.ofFloat(binding.root, "radius", 32f, 0f).setDuration(200)
var cornered = true
cornerTop.start()
binding.mediaInfoScroll.setOnScrollChangeListener { v, _, _, _, _ ->

View file

@ -14,8 +14,8 @@ import androidx.lifecycle.lifecycleScope
import ani.dantotsu.*
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.api.FuzzyDate
import ani.dantotsu.databinding.BottomSheetMediaListBinding
import ani.dantotsu.connections.mal.MAL
import ani.dantotsu.databinding.BottomSheetMediaListBinding
import com.google.android.material.switchmaterial.SwitchMaterial
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -27,7 +27,11 @@ class MediaListDialogFragment : BottomSheetDialogFragment() {
private var _binding: BottomSheetMediaListBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetMediaListBinding.inflate(inflater, container, false)
return binding.root
}
@ -46,8 +50,12 @@ class MediaListDialogFragment : BottomSheetDialogFragment() {
binding.mediaListLayout.visibility = View.VISIBLE
val statuses: Array<String> = resources.getStringArray(R.array.status)
val statusStrings = if (media?.manga==null) resources.getStringArray(R.array.status_anime) else resources.getStringArray(R.array.status_manga)
val userStatus = if(media!!.userStatus != null) statusStrings[statuses.indexOf(media!!.userStatus)] else statusStrings[0]
val statusStrings =
if (media?.manga == null) resources.getStringArray(R.array.status_anime) else resources.getStringArray(
R.array.status_manga
)
val userStatus =
if (media!!.userStatus != null) statusStrings[statuses.indexOf(media!!.userStatus)] else statusStrings[0]
binding.mediaListStatus.setText(userStatus)
binding.mediaListStatus.setAdapter(
@ -160,7 +168,9 @@ class MediaListDialogFragment : BottomSheetDialogFragment() {
val init =
if (binding.mediaListProgress.text.toString() != "") binding.mediaListProgress.text.toString()
.toInt() else 0
if (init < (total ?: 5000)) binding.mediaListProgress.setText((init + 1).toString())
if (init < (total
?: 5000)
) binding.mediaListProgress.setText((init + 1).toString())
if (init + 1 == (total ?: 5000)) {
binding.mediaListStatus.setText(statusStrings[2], false)
onComplete()
@ -201,11 +211,15 @@ class MediaListDialogFragment : BottomSheetDialogFragment() {
scope.launch {
withContext(Dispatchers.IO) {
if (media != null) {
val progress = _binding?.mediaListProgress?.text.toString().toIntOrNull()
val progress =
_binding?.mediaListProgress?.text.toString().toIntOrNull()
val score =
(_binding?.mediaListScore?.text.toString().toDoubleOrNull()?.times(10))?.toInt()
val status = statuses[statusStrings.indexOf(_binding?.mediaListStatus?.text.toString())]
val rewatch = _binding?.mediaListRewatch?.text?.toString()?.toIntOrNull()
(_binding?.mediaListScore?.text.toString().toDoubleOrNull()
?.times(10))?.toInt()
val status =
statuses[statusStrings.indexOf(_binding?.mediaListStatus?.text.toString())]
val rewatch =
_binding?.mediaListRewatch?.text?.toString()?.toIntOrNull()
val notes = _binding?.mediaListNotes?.text?.toString()
val startD = start.date
val endD = end.date
@ -245,7 +259,7 @@ class MediaListDialogFragment : BottomSheetDialogFragment() {
scope.launch {
withContext(Dispatchers.IO) {
Anilist.mutation.deleteList(id)
MAL.query.deleteList(media?.anime!=null,media?.idMAL)
MAL.query.deleteList(media?.anime != null, media?.idMAL)
}
Refresh.all()
snackString(getString(R.string.deleted_from_list))

View file

@ -12,11 +12,9 @@ import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import ani.dantotsu.*
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.databinding.BottomSheetMediaListSmallBinding
import ani.dantotsu.connections.mal.MAL
import ani.dantotsu.databinding.BottomSheetMediaListSmallBinding
import ani.dantotsu.others.getSerialized
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -46,7 +44,11 @@ class MediaListDialogSmallFragment : BottomSheetDialogFragment() {
private var _binding: BottomSheetMediaListSmallBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetMediaListSmallBinding.inflate(inflater, container, false)
return binding.root
}
@ -60,8 +62,12 @@ class MediaListDialogSmallFragment : BottomSheetDialogFragment() {
binding.mediaListProgressBar.visibility = View.GONE
binding.mediaListLayout.visibility = View.VISIBLE
val statuses: Array<String> = resources.getStringArray(R.array.status)
val statusStrings = if (media.manga==null) resources.getStringArray(R.array.status_anime) else resources.getStringArray(R.array.status_manga)
val userStatus = if(media.userStatus != null) statusStrings[statuses.indexOf(media.userStatus)] else statusStrings[0]
val statusStrings =
if (media.manga == null) resources.getStringArray(R.array.status_anime) else resources.getStringArray(
R.array.status_manga
)
val userStatus =
if (media.userStatus != null) statusStrings[statuses.indexOf(media.userStatus)] else statusStrings[0]
binding.mediaListStatus.setText(userStatus)
binding.mediaListStatus.setAdapter(
@ -130,10 +136,26 @@ class MediaListDialogSmallFragment : BottomSheetDialogFragment() {
withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) {
val progress = _binding?.mediaListProgress?.text.toString().toIntOrNull()
val score = (_binding?.mediaListScore?.text.toString().toDoubleOrNull()?.times(10))?.toInt()
val status = statuses[statusStrings.indexOf(_binding?.mediaListStatus?.text.toString())]
Anilist.mutation.editList(media.id, progress, score, null, null, status, media.isListPrivate)
MAL.query.editList(media.idMAL, media.anime != null, progress, score, status)
val score = (_binding?.mediaListScore?.text.toString().toDoubleOrNull()
?.times(10))?.toInt()
val status =
statuses[statusStrings.indexOf(_binding?.mediaListStatus?.text.toString())]
Anilist.mutation.editList(
media.id,
progress,
score,
null,
null,
status,
media.isListPrivate
)
MAL.query.editList(
media.idMAL,
media.anime != null,
progress,
score,
status
)
}
}
Refresh.all()

View file

@ -5,7 +5,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import ani.dantotsu.connections.anilist.Anilist
import java.text.DateFormat
import java.util.*
import java.util.Date
class OtherDetailsViewModel : ViewModel() {
private val character: MutableLiveData<Character> = MutableLiveData(null)
@ -19,26 +19,28 @@ class OtherDetailsViewModel : ViewModel() {
suspend fun loadStudio(m: Studio) {
if (studio.value == null) studio.postValue(Anilist.query.getStudioDetails(m))
}
private val author: MutableLiveData<Author> = MutableLiveData(null)
fun getAuthor(): LiveData<Author> = author
suspend fun loadAuthor(m: Author) {
if (author.value == null) author.postValue(Anilist.query.getAuthorDetails(m))
}
private val calendar: MutableLiveData<Map<String,MutableList<Media>>> = MutableLiveData(null)
fun getCalendar(): LiveData<Map<String,MutableList<Media>>> = calendar
private val calendar: MutableLiveData<Map<String, MutableList<Media>>> = MutableLiveData(null)
fun getCalendar(): LiveData<Map<String, MutableList<Media>>> = calendar
suspend fun loadCalendar() {
val curr = System.currentTimeMillis()/1000
val res = Anilist.query.recentlyUpdated(false,curr-86400,curr+(86400*6))
val curr = System.currentTimeMillis() / 1000
val res = Anilist.query.recentlyUpdated(false, curr - 86400, curr + (86400 * 6))
val df = DateFormat.getDateInstance(DateFormat.FULL)
val map = mutableMapOf<String,MutableList<Media>>()
val idMap = mutableMapOf<String,MutableList<Int>>()
val map = mutableMapOf<String, MutableList<Media>>()
val idMap = mutableMapOf<String, MutableList<Int>>()
res?.forEach {
val v = it.relation?.split(",")?.map { i-> i.toLong() }!!
val dateInfo = df.format(Date(v[1]*1000))
val v = it.relation?.split(",")?.map { i -> i.toLong() }!!
val dateInfo = df.format(Date(v[1] * 1000))
val list = map.getOrPut(dateInfo) { mutableListOf() }
val idList = idMap.getOrPut(dateInfo) { mutableListOf() }
it.relation = "Episode ${v[0]}"
if(!idList.contains(it.id)) {
if (!idList.contains(it.id)) {
idList.add(it.id)
list.add(it)
}

View file

@ -22,7 +22,8 @@ class ProgressAdapter(private val horizontal: Boolean = true, searched: Boolean)
var bar: ProgressBar? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProgressViewHolder {
val binding = ItemProgressbarBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding =
ItemProgressbarBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ProgressViewHolder(binding)
}
@ -33,7 +34,12 @@ class ProgressAdapter(private val horizontal: Boolean = true, searched: Boolean)
val doubleClickDetector = GestureDetector(progressBar.context, object : GesturesListener() {
override fun onDoubleClick(event: MotionEvent) {
snackString(currContext()?.getString(R.string.cant_wait))
ObjectAnimator.ofFloat(progressBar, "translationX", progressBar.translationX, progressBar.translationX + 100f)
ObjectAnimator.ofFloat(
progressBar,
"translationX",
progressBar.translationX,
progressBar.translationX + 100f
)
.setDuration(300).start()
}
@ -51,7 +57,8 @@ class ProgressAdapter(private val horizontal: Boolean = true, searched: Boolean)
}
override fun getItemCount(): Int = 1
inner class ProgressViewHolder(val binding: ItemProgressbarBinding) : RecyclerView.ViewHolder(binding.root) {
inner class ProgressViewHolder(val binding: ItemProgressbarBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.updateLayoutParams { if (horizontal) width = -1 else height = -1 }
}

View file

@ -16,8 +16,8 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistSearch
import ani.dantotsu.connections.anilist.SearchResults
import ani.dantotsu.databinding.ActivitySearchBinding
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import ani.dantotsu.themes.ThemeManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.*
@ -35,12 +35,12 @@ class SearchActivity : AppCompatActivity() {
private lateinit var concatAdapter: ConcatAdapter
lateinit var result: SearchResults
lateinit var updateChips: (()->Unit)
lateinit var updateChips: (() -> Unit)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
binding = ActivitySearchBinding.inflate(layoutInflater)
setContentView(binding.root)
initActivity(this)
@ -149,7 +149,7 @@ ThemeManager(this).applyTheme()
} else
headerAdaptor.requestFocus?.run()
if(intent.getBooleanExtra("search",false)) search()
if (intent.getBooleanExtra("search", false)) search()
}
}
}

View file

@ -20,14 +20,16 @@ import ani.dantotsu.saveData
import com.google.android.material.checkbox.MaterialCheckBox.*
class SearchAdapter(private val activity: SearchActivity) : RecyclerView.Adapter<SearchAdapter.SearchHeaderViewHolder>() {
class SearchAdapter(private val activity: SearchActivity) :
RecyclerView.Adapter<SearchAdapter.SearchHeaderViewHolder>() {
private val itemViewType = 6969
var search: Runnable? = null
var requestFocus: Runnable? = null
private var textWatcher: TextWatcher? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHeaderViewHolder {
val binding = ItemSearchHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding =
ItemSearchHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SearchHeaderViewHolder(binding)
}
@ -36,13 +38,15 @@ class SearchAdapter(private val activity: SearchActivity) : RecyclerView.Adapter
val binding = holder.binding
val imm: InputMethodManager = activity.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
val imm: InputMethodManager =
activity.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
when (activity.style) {
0 -> {
binding.searchResultGrid.alpha = 1f
binding.searchResultList.alpha = 0.33f
}
1 -> {
binding.searchResultList.alpha = 1f
binding.searchResultGrid.alpha = 0.33f
@ -62,7 +66,8 @@ class SearchAdapter(private val activity: SearchActivity) : RecyclerView.Adapter
binding.searchChipRecycler.adapter = SearchChipAdapter(activity).also {
activity.updateChips = { it.update() }
}
binding.searchChipRecycler.layoutManager = LinearLayoutManager(binding.root.context, HORIZONTAL, false)
binding.searchChipRecycler.layoutManager =
LinearLayoutManager(binding.root.context, HORIZONTAL, false)
binding.searchFilter.setOnClickListener {
SearchFilterBottomDialog.newInstance().show(activity.supportFragmentManager, "dialog")
@ -70,7 +75,8 @@ class SearchAdapter(private val activity: SearchActivity) : RecyclerView.Adapter
fun searchTitle() {
activity.result.apply {
search = if (binding.searchBarText.text.toString() != "") binding.searchBarText.text.toString() else null
search =
if (binding.searchBarText.text.toString() != "") binding.searchBarText.text.toString() else null
onList = listOnly
isAdult = adult
}
@ -96,6 +102,7 @@ class SearchAdapter(private val activity: SearchActivity) : RecyclerView.Adapter
imm.hideSoftInputFromWindow(binding.searchBarText.windowToken, 0)
true
}
else -> false
}
}
@ -127,7 +134,7 @@ class SearchAdapter(private val activity: SearchActivity) : RecyclerView.Adapter
binding.searchList.apply {
if (Anilist.userid != null) {
visibility = View.VISIBLE
checkedState = when(listOnly){
checkedState = when (listOnly) {
null -> STATE_UNCHECKED
true -> STATE_CHECKED
false -> STATE_INDETERMINATE
@ -158,20 +165,24 @@ class SearchAdapter(private val activity: SearchActivity) : RecyclerView.Adapter
override fun getItemCount(): Int = 1
inner class SearchHeaderViewHolder(val binding: ItemSearchHeaderBinding) : RecyclerView.ViewHolder(binding.root)
inner class SearchHeaderViewHolder(val binding: ItemSearchHeaderBinding) :
RecyclerView.ViewHolder(binding.root)
override fun getItemViewType(position: Int): Int {
return itemViewType
}
class SearchChipAdapter(val activity: SearchActivity) : RecyclerView.Adapter<SearchChipAdapter.SearchChipViewHolder>() {
class SearchChipAdapter(val activity: SearchActivity) :
RecyclerView.Adapter<SearchChipAdapter.SearchChipViewHolder>() {
private var chips = activity.result.toChipList()
inner class SearchChipViewHolder(val binding: ItemChipBinding) : RecyclerView.ViewHolder(binding.root)
inner class SearchChipViewHolder(val binding: ItemChipBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchChipViewHolder {
val binding = ItemChipBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding =
ItemChipBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SearchChipViewHolder(binding)
}

View file

@ -18,13 +18,17 @@ import ani.dantotsu.databinding.BottomSheetSearchFilterBinding
import ani.dantotsu.databinding.ItemChipBinding
import com.google.android.material.chip.Chip
class SearchFilterBottomDialog() : BottomSheetDialogFragment() {
class SearchFilterBottomDialog : BottomSheetDialogFragment() {
private var _binding: BottomSheetSearchFilterBinding? = null
private val binding get() = _binding!!
private lateinit var activity: SearchActivity
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetSearchFilterBinding.inflate(inflater, container, false)
return binding.root
}
@ -129,7 +133,8 @@ class SearchFilterBottomDialog() : BottomSheetDialogFragment() {
}
binding.searchGenresGrid.isChecked = false
binding.searchFilterTags.adapter = FilterChipAdapter(Anilist.tags?.get(activity.result.isAdult) ?: listOf()) { chip ->
binding.searchFilterTags.adapter =
FilterChipAdapter(Anilist.tags?.get(activity.result.isAdult) ?: listOf()) { chip ->
val tag = chip.text.toString()
chip.isChecked = selectedTags.contains(tag)
chip.isCloseIconVisible = exTags.contains(tag)
@ -158,10 +163,12 @@ class SearchFilterBottomDialog() : BottomSheetDialogFragment() {
class FilterChipAdapter(val list: List<String>, private val perform: ((Chip) -> Unit)) :
RecyclerView.Adapter<FilterChipAdapter.SearchChipViewHolder>() {
inner class SearchChipViewHolder(val binding: ItemChipBinding) : RecyclerView.ViewHolder(binding.root)
inner class SearchChipViewHolder(val binding: ItemChipBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchChipViewHolder {
val binding = ItemChipBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding =
ItemChipBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SearchChipViewHolder(binding)
}

View file

@ -17,7 +17,8 @@ abstract class SourceAdapter(
private val scope: CoroutineScope
) : RecyclerView.Adapter<SourceAdapter.SourceViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SourceViewHolder {
val binding = ItemCharacterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding =
ItemCharacterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SourceViewHolder(binding)
}
@ -34,7 +35,8 @@ abstract class SourceAdapter(
abstract suspend fun onItemClick(source: ShowResponse)
inner class SourceViewHolder(val binding: ItemCharacterBinding) : RecyclerView.ViewHolder(binding.root) {
inner class SourceViewHolder(val binding: ItemCharacterBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
dialogFragment.dismiss()

View file

@ -13,8 +13,8 @@ import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import ani.dantotsu.BottomSheetDialogFragment
import ani.dantotsu.media.anime.AnimeSourceAdapter
import ani.dantotsu.databinding.BottomSheetSourceSearchBinding
import ani.dantotsu.media.anime.AnimeSourceAdapter
import ani.dantotsu.media.manga.MangaSourceAdapter
import ani.dantotsu.navBarHeight
import ani.dantotsu.parsers.AnimeSources
@ -38,7 +38,11 @@ class SourceSearchDialogFragment : BottomSheetDialogFragment() {
var id: Int? = null
var media: Media? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetSourceSearchBinding.inflate(inflater, container, false)
return binding.root
}
@ -47,7 +51,8 @@ class SourceSearchDialogFragment : BottomSheetDialogFragment() {
binding.mediaListContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin += navBarHeight }
val scope = requireActivity().lifecycleScope
val imm = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val imm =
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
model.getMedia().observe(viewLifecycleOwner) {
media = it
if (media != null) {
@ -65,6 +70,7 @@ class SourceSearchDialogFragment : BottomSheetDialogFragment() {
anime = false
(if (media!!.isAdult) HMangaSources else MangaSources)[i!!]
}
fun search() {
binding.searchBarText.clearFocus()
imm.hideSoftInputFromWindow(binding.searchBarText.windowToken, 0)
@ -86,6 +92,7 @@ class SourceSearchDialogFragment : BottomSheetDialogFragment() {
search()
true
}
else -> false
}
}
@ -101,7 +108,11 @@ class SourceSearchDialogFragment : BottomSheetDialogFragment() {
else MangaSourceAdapter(j, model, i!!, media!!.id, this, scope)
binding.searchRecyclerView.layoutManager = GridLayoutManager(
requireActivity(),
clamp(requireActivity().resources.displayMetrics.widthPixels / 124f.px, 1, 4)
clamp(
requireActivity().resources.displayMetrics.widthPixels / 124f.px,
1,
4
)
)
}
}

View file

@ -12,11 +12,17 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.GridLayoutManager
import ani.dantotsu.*
import ani.dantotsu.EmptyAdapter
import ani.dantotsu.R
import ani.dantotsu.Refresh
import ani.dantotsu.databinding.ActivityStudioBinding
import ani.dantotsu.others.getSerialized
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.initActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.others.LangSet
import ani.dantotsu.others.getSerialized
import ani.dantotsu.px
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -31,7 +37,7 @@ class StudioActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
binding = ActivityStudioBinding.inflate(layoutInflater)
setContentView(binding.root)

View file

@ -1,18 +1,13 @@
package ani.dantotsu.media
import android.content.Context
import android.os.Environment
import ani.dantotsu.parsers.SubtitleType
import eu.kanade.tachiyomi.network.NetworkHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.Request
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.FileInputStream
class SubtitleDownloader {
@ -30,7 +25,7 @@ class SubtitleDownloader {
// Check if response is successful
if (response.isSuccessful) {
val responseBody = response.body?.string()
val responseBody = response.body.string()
val subtitleType = when {

View file

@ -5,8 +5,10 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.databinding.ItemTitleBinding
class TitleAdapter(private val text: String) : RecyclerView.Adapter<TitleAdapter.TitleViewHolder>() {
inner class TitleViewHolder(val binding: ItemTitleBinding) : RecyclerView.ViewHolder(binding.root)
class TitleAdapter(private val text: String) :
RecyclerView.Adapter<TitleAdapter.TitleViewHolder>() {
inner class TitleViewHolder(val binding: ItemTitleBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TitleViewHolder {
val binding = ItemTitleBinding.inflate(LayoutInflater.from(parent.context), parent, false)

View file

@ -15,7 +15,7 @@ data class Anime(
var ed: ArrayList<String> = arrayListOf(),
var mainStudio: Studio? = null,
var author: Author?=null,
var author: Author? = null,
var youtube: String? = null,
var nextAiringEpisode: Int? = null,

View file

@ -1,23 +1,17 @@
package ani.dantotsu.media.anime
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.*
import ani.dantotsu.databinding.ItemAnimeWatchBinding
import ani.dantotsu.databinding.ItemChipBinding
@ -27,21 +21,11 @@ import ani.dantotsu.media.SourceSearchDialogFragment
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.DynamicAnimeParser
import ani.dantotsu.parsers.WatchSources
import ani.dantotsu.settings.ExtensionsActivity
import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment
import ani.dantotsu.subcriptions.Notifications.Companion.openSettings
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
import com.google.android.material.chip.Chip
import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.lang.IndexOutOfBoundsException
class AnimeWatchAdapter(
private val media: Media,
@ -72,23 +56,33 @@ class AnimeWatchAdapter(
}
binding.animeSourceDubbed.isChecked = media.selected!!.preferDub
binding.animeSourceDubbedText.text = if (media.selected!!.preferDub) currActivity()!!.getString(R.string.dubbed) else currActivity()!!.getString(R.string.subbed)
binding.animeSourceDubbedText.text =
if (media.selected!!.preferDub) currActivity()!!.getString(R.string.dubbed) else currActivity()!!.getString(
R.string.subbed
)
//PreferDub
var changing = false
binding.animeSourceDubbed.setOnCheckedChangeListener { _, isChecked ->
binding.animeSourceDubbedText.text = if (isChecked) currActivity()!!.getString(R.string.dubbed) else currActivity()!!.getString(R.string.subbed)
binding.animeSourceDubbedText.text =
if (isChecked) currActivity()!!.getString(R.string.dubbed) else currActivity()!!.getString(
R.string.subbed
)
if (!changing) fragment.onDubClicked(isChecked)
}
//Wrong Title
binding.animeSourceSearch.setOnClickListener {
SourceSearchDialogFragment().show(fragment.requireActivity().supportFragmentManager, null)
SourceSearchDialogFragment().show(
fragment.requireActivity().supportFragmentManager,
null
)
}
//Source Selection
var source = media.selected!!.sourceIndex.let { if (it >= watchSources.names.size) 0 else it }
setLanguageList(media.selected!!.langIndex,source)
var source =
media.selected!!.sourceIndex.let { if (it >= watchSources.names.size) 0 else it }
setLanguageList(media.selected!!.langIndex, source)
if (watchSources.names.isNotEmpty() && source in 0 until watchSources.names.size) {
binding.animeSource.setText(watchSources.names[source])
watchSources[source].apply {
@ -100,7 +94,13 @@ class AnimeWatchAdapter(
}
}
binding.animeSource.setAdapter(ArrayAdapter(fragment.requireContext(), R.layout.item_dropdown, watchSources.names))
binding.animeSource.setAdapter(
ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
watchSources.names
)
)
binding.animeSourceTitle.isSelected = true
binding.animeSource.setOnItemClickListener { _, _, i, _ ->
fragment.onSourceChange(i).apply {
@ -109,9 +109,10 @@ class AnimeWatchAdapter(
changing = true
binding.animeSourceDubbed.isChecked = selectDub
changing = false
binding.animeSourceDubbedCont.visibility = if (isDubAvailableSeparately) View.VISIBLE else View.GONE
binding.animeSourceDubbedCont.visibility =
if (isDubAvailableSeparately) View.VISIBLE else View.GONE
source = i
setLanguageList(0,i)
setLanguageList(0, i)
}
subscribeButton(false)
fragment.loadEpisodes(i, false)
@ -124,11 +125,13 @@ class AnimeWatchAdapter(
fragment.onLangChange(i)
fragment.onSourceChange(media.selected!!.sourceIndex).apply {
binding.animeSourceTitle.text = showUserText
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } }
showUserTextListener =
{ MainScope().launch { binding.animeSourceTitle.text = it } }
changing = true
binding.animeSourceDubbed.isChecked = selectDub
changing = false
binding.animeSourceDubbedCont.visibility = if (isDubAvailableSeparately) View.VISIBLE else View.GONE
binding.animeSourceDubbedCont.visibility =
if (isDubAvailableSeparately) View.VISIBLE else View.GONE
setLanguageList(i, source)
}
subscribeButton(false)
@ -161,7 +164,7 @@ class AnimeWatchAdapter(
subscribeButton(false)
binding.animeSourceSubscribe.setOnLongClickListener {
openSettings(fragment.requireContext(),getChannelId(true,media.id))
openSettings(fragment.requireContext(), getChannelId(true, media.id))
}
//Icons
@ -200,12 +203,12 @@ class AnimeWatchAdapter(
style = 2
fragment.onIconPressed(style, reversed)
}
binding.animeScanlatorTop.visibility= View.GONE
binding.animeScanlatorTop.visibility = View.GONE
//Episode Handling
handleEpisodes()
}
fun subscribeButton(enabled : Boolean) {
fun subscribeButton(enabled: Boolean) {
subscribe?.enabled(enabled)
}
@ -219,14 +222,26 @@ class AnimeWatchAdapter(
for (position in arr.indices) {
val last = if (position + 1 == arr.size) names.size else (limit * (position + 1))
val chip =
ItemChipBinding.inflate(LayoutInflater.from(fragment.context), binding.animeSourceChipGroup, false).root
ItemChipBinding.inflate(
LayoutInflater.from(fragment.context),
binding.animeSourceChipGroup,
false
).root
chip.isCheckable = true
fun selected() {
chip.isChecked = true
binding.animeWatchChipScroll.smoothScrollTo((chip.left - screenWidth / 2) + (chip.width / 2), 0)
binding.animeWatchChipScroll.smoothScrollTo(
(chip.left - screenWidth / 2) + (chip.width / 2),
0
)
}
chip.text = "${names[limit * (position)]} - ${names[last - 1]}"
chip.setTextColor(ContextCompat.getColorStateList(fragment.requireContext(), R.color.chip_text_color))
chip.setTextColor(
ContextCompat.getColorStateList(
fragment.requireContext(),
R.color.chip_text_color
)
)
chip.setOnClickListener {
selected()
@ -239,7 +254,14 @@ class AnimeWatchAdapter(
}
}
if (select != null)
binding.animeWatchChipScroll.apply { post { scrollTo((select.left - screenWidth / 2) + (select.width / 2), 0) } }
binding.animeWatchChipScroll.apply {
post {
scrollTo(
(select.left - screenWidth / 2) + (select.width / 2),
0
)
}
}
}
}
@ -281,7 +303,9 @@ class AnimeWatchAdapter(
}
}
val ep = media.anime.episodes!![continueEp]!!
binding.itemEpisodeImage.loadImage(ep.thumb ?: FileUrl[media.banner ?: media.cover], 0)
binding.itemEpisodeImage.loadImage(
ep.thumb ?: FileUrl[media.banner ?: media.cover], 0
)
if (ep.filler) binding.itemEpisodeFillerView.visibility = View.VISIBLE
binding.animeSourceContinueText.text =
currActivity()!!.getString(R.string.continue_episode) + "${ep.number}${if (ep.filler) " - Filler" else ""}${if (ep.title != null) "\n${ep.title}" else ""}"
@ -321,10 +345,17 @@ class AnimeWatchAdapter(
}
try {
binding?.animeSourceLanguage?.setText(parser.extension.sources[lang].lang)
}catch (e: IndexOutOfBoundsException) {
binding?.animeSourceLanguage?.setText(parser.extension.sources.firstOrNull()?.lang ?: "Unknown")
} catch (e: IndexOutOfBoundsException) {
binding?.animeSourceLanguage?.setText(
parser.extension.sources.firstOrNull()?.lang ?: "Unknown"
)
}
binding?.animeSourceLanguage?.setAdapter(ArrayAdapter(fragment.requireContext(), R.layout.item_dropdown, parser.extension.sources.map { it.lang }))
binding?.animeSourceLanguage?.setAdapter(
ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
parser.extension.sources.map { it.lang })
)
}
}
@ -332,7 +363,8 @@ class AnimeWatchAdapter(
override fun getItemCount(): Int = 1
inner class ViewHolder(val binding: ItemAnimeWatchBinding) : RecyclerView.ViewHolder(binding.root) {
inner class ViewHolder(val binding: ItemAnimeWatchBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
//Timer
countDown(media, binding.animeSourceContainer)

View file

@ -8,8 +8,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Toast
import androidx.cardview.widget.CardView
import androidx.core.math.MathUtils
@ -28,8 +26,6 @@ import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.parsers.AnimeParser
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.HAnimeSources
import ani.dantotsu.settings.ExtensionsActivity
import ani.dantotsu.settings.InstalledAnimeExtensionsFragment
import ani.dantotsu.settings.PlayerSettings
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment
@ -40,17 +36,12 @@ import ani.dantotsu.subcriptions.SubscriptionHelper
import ani.dantotsu.subcriptions.SubscriptionHelper.Companion.saveSubscription
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.navigationrail.NavigationRailView
import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.roundToInt
@ -96,8 +87,10 @@ class AnimeWatchFragment : Fragment() {
maxGridSize = max(4, maxGridSize - (maxGridSize % 2))
playerSettings =
loadData("player_settings", toast = false) ?: PlayerSettings().apply { saveData("player_settings", this) }
uiSettings = loadData("ui_settings", toast = false) ?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
loadData("player_settings", toast = false)
?: PlayerSettings().apply { saveData("player_settings", this) }
uiSettings = loadData("ui_settings", toast = false)
?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
val gridLayoutManager = GridLayoutManager(requireContext(), maxGridSize)
@ -129,7 +122,8 @@ class AnimeWatchFragment : Fragment() {
media = it
media.selected = model.loadSelected(media)
subscribed = SubscriptionHelper.getSubscriptions(requireContext()).containsKey(media.id)
subscribed =
SubscriptionHelper.getSubscriptions(requireContext()).containsKey(media.id)
style = media.selected!!.recyclerStyle
reverse = media.selected!!.recyclerReversed
@ -141,9 +135,11 @@ class AnimeWatchFragment : Fragment() {
model.watchSources = if (media.isAdult) HAnimeSources else AnimeSources
headerAdapter = AnimeWatchAdapter(it, this, model.watchSources!!)
episodeAdapter = EpisodeAdapter(style ?: uiSettings.animeDefaultView, media, this)
episodeAdapter =
EpisodeAdapter(style ?: uiSettings.animeDefaultView, media, this)
binding.animeSourceRecycler.adapter = ConcatAdapter(headerAdapter, episodeAdapter)
binding.animeSourceRecycler.adapter =
ConcatAdapter(headerAdapter, episodeAdapter)
lifecycleScope.launch(Dispatchers.IO) {
awaitAll(
@ -165,15 +161,20 @@ class AnimeWatchFragment : Fragment() {
episodes.forEach { (i, episode) ->
if (media.anime?.fillerEpisodes != null) {
if (media.anime!!.fillerEpisodes!!.containsKey(i)) {
episode.title = episode.title ?: media.anime!!.fillerEpisodes!![i]?.title
episode.title =
episode.title ?: media.anime!!.fillerEpisodes!![i]?.title
episode.filler = media.anime!!.fillerEpisodes!![i]?.filler ?: false
}
}
if (media.anime?.kitsuEpisodes != null) {
if (media.anime!!.kitsuEpisodes!!.containsKey(i)) {
episode.desc = episode.desc ?: media.anime!!.kitsuEpisodes!![i]?.desc
episode.title = episode.title ?: media.anime!!.kitsuEpisodes!![i]?.title
episode.thumb = episode.thumb ?: media.anime!!.kitsuEpisodes!![i]?.thumb ?: FileUrl[media.cover]
episode.desc =
episode.desc ?: media.anime!!.kitsuEpisodes!![i]?.desc
episode.title =
episode.title ?: media.anime!!.kitsuEpisodes!![i]?.title
episode.thumb =
episode.thumb ?: media.anime!!.kitsuEpisodes!![i]?.thumb
?: FileUrl[media.cover]
}
}
}
@ -247,7 +248,12 @@ class AnimeWatchFragment : Fragment() {
selected.preferDub = checked
model.saveSelected(media.id, selected, requireActivity())
media.selected = selected
lifecycleScope.launch(Dispatchers.IO) { model.forceLoadEpisode(media, selected.sourceIndex) }
lifecycleScope.launch(Dispatchers.IO) {
model.forceLoadEpisode(
media,
selected.sourceIndex
)
}
}
fun loadEpisodes(i: Int, invalidate: Boolean) {
@ -289,7 +295,8 @@ class AnimeWatchFragment : Fragment() {
else getString(R.string.unsubscribed_notification)
)
}
fun openSettings(pkg: AnimeExtension.Installed){
fun openSettings(pkg: AnimeExtension.Installed) {
val changeUIVisibility: (Boolean) -> Unit = { show ->
val activity = requireActivity() as MediaDetailsActivity
val visibility = if (show) View.VISIBLE else View.GONE
@ -297,9 +304,9 @@ class AnimeWatchFragment : Fragment() {
activity.findViewById<ViewPager2>(R.id.mediaViewPager).visibility = visibility
activity.findViewById<CardView>(R.id.mediaCover).visibility = visibility
activity.findViewById<CardView>(R.id.mediaClose).visibility = visibility
try{
try {
activity.findViewById<CustomBottomNavBar>(R.id.mediaTab).visibility = visibility
}catch (e: ClassCastException){
} catch (e: ClassCastException) {
activity.findViewById<NavigationRailView>(R.id.mediaTab).visibility = visibility
}
activity.findViewById<FrameLayout>(R.id.fragmentExtensionsContainer).visibility =

View file

@ -14,12 +14,12 @@ data class Episode(
var selectedExtractor: String? = null,
var selectedVideo: Int = 0,
var selectedSubtitle: Int? = -1,
var extractors: MutableList<VideoExtractor>?=null,
@Transient var extractorCallback: ((VideoExtractor) -> Unit)?=null,
var extractors: MutableList<VideoExtractor>? = null,
@Transient var extractorCallback: ((VideoExtractor) -> Unit)? = null,
var allStreams: Boolean = false,
var watched: Long? = null,
var maxLength: Long? = null,
val extra: Map<String,String>?=null,
val extra: Map<String, String>? = null,
val sEpisode: eu.kanade.tachiyomi.animesource.model.SEpisode? = null
) : Serializable

View file

@ -41,8 +41,22 @@ class EpisodeAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return (when (viewType) {
0 -> EpisodeListViewHolder(ItemEpisodeListBinding.inflate(LayoutInflater.from(parent.context), parent, false))
1 -> EpisodeGridViewHolder(ItemEpisodeGridBinding.inflate(LayoutInflater.from(parent.context), parent, false))
0 -> EpisodeListViewHolder(
ItemEpisodeListBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
1 -> EpisodeGridViewHolder(
ItemEpisodeGridBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
2 -> EpisodeCompactViewHolder(
ItemEpisodeCompactBinding.inflate(
LayoutInflater.from(parent.context),
@ -50,6 +64,7 @@ class EpisodeAdapter(
false
)
)
else -> throw IllegalArgumentException()
})
}
@ -62,15 +77,21 @@ class EpisodeAdapter(
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val ep = arr[position]
val title =
"${if (!ep.title.isNullOrEmpty() && ep.title != "null") "" else currContext()!!.getString(R.string.episode_singular)} ${if (!ep.title.isNullOrEmpty() && ep.title != "null") ep.title else ep.number}"
"${
if (!ep.title.isNullOrEmpty() && ep.title != "null") "" else currContext()!!.getString(
R.string.episode_singular
)
} ${if (!ep.title.isNullOrEmpty() && ep.title != "null") ep.title else ep.number}"
when (holder) {
is EpisodeListViewHolder -> {
val binding = holder.binding
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
val thumb = ep.thumb?.let { if(it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
Glide.with(binding.itemEpisodeImage).load(thumb?:media.cover).override(400,0).into(binding.itemEpisodeImage)
val thumb =
ep.thumb?.let { if (it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
Glide.with(binding.itemEpisodeImage).load(thumb ?: media.cover).override(400, 0)
.into(binding.itemEpisodeImage)
binding.itemEpisodeNumber.text = ep.number
binding.itemEpisodeTitle.text = title
@ -81,7 +102,8 @@ class EpisodeAdapter(
binding.itemEpisodeFiller.visibility = View.GONE
binding.itemEpisodeFillerView.visibility = View.GONE
}
binding.itemEpisodeDesc.visibility = if (ep.desc != null && ep.desc?.trim(' ') != "") View.VISIBLE else View.GONE
binding.itemEpisodeDesc.visibility =
if (ep.desc != null && ep.desc?.trim(' ') != "") View.VISIBLE else View.GONE
binding.itemEpisodeDesc.text = ep.desc ?: ""
if (media.userProgress != null) {
@ -114,8 +136,10 @@ class EpisodeAdapter(
val binding = holder.binding
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
val thumb = ep.thumb?.let { if(it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
Glide.with(binding.itemEpisodeImage).load(thumb?:media.cover).override(400,0).into(binding.itemEpisodeImage)
val thumb =
ep.thumb?.let { if (it.url.isNotEmpty()) GlideUrl(it.url) { it.headers } else null }
Glide.with(binding.itemEpisodeImage).load(thumb ?: media.cover).override(400, 0)
.into(binding.itemEpisodeImage)
binding.itemEpisodeNumber.text = ep.number
binding.itemEpisodeTitle.text = title
@ -155,7 +179,8 @@ class EpisodeAdapter(
val binding = holder.binding
setAnimation(fragment.requireContext(), holder.binding.root, fragment.uiSettings)
binding.itemEpisodeNumber.text = ep.number
binding.itemEpisodeFillerView.visibility = if (ep.filler) View.VISIBLE else View.GONE
binding.itemEpisodeFillerView.visibility =
if (ep.filler) View.VISIBLE else View.GONE
if (media.userProgress != null) {
if ((ep.number.toFloatOrNull() ?: 9999f) <= media.userProgress!!.toFloat())
binding.itemEpisodeViewedCover.visibility = View.VISIBLE
@ -180,7 +205,8 @@ class EpisodeAdapter(
override fun getItemCount(): Int = arr.size
inner class EpisodeCompactViewHolder(val binding: ItemEpisodeCompactBinding) : RecyclerView.ViewHolder(binding.root) {
inner class EpisodeCompactViewHolder(val binding: ItemEpisodeCompactBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
if (bindingAdapterPosition < arr.size && bindingAdapterPosition >= 0)
@ -189,7 +215,8 @@ class EpisodeAdapter(
}
}
inner class EpisodeGridViewHolder(val binding: ItemEpisodeGridBinding) : RecyclerView.ViewHolder(binding.root) {
inner class EpisodeGridViewHolder(val binding: ItemEpisodeGridBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
if (bindingAdapterPosition < arr.size && bindingAdapterPosition >= 0)
@ -198,7 +225,8 @@ class EpisodeAdapter(
}
}
inner class EpisodeListViewHolder(val binding: ItemEpisodeListBinding) : RecyclerView.ViewHolder(binding.root) {
inner class EpisodeListViewHolder(val binding: ItemEpisodeListBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
if (bindingAdapterPosition < arr.size && bindingAdapterPosition >= 0)

View file

@ -17,7 +17,6 @@ import android.graphics.drawable.Animatable
import android.hardware.SensorManager
import android.media.AudioManager
import android.media.AudioManager.*
import android.media.PlaybackParams
import android.net.Uri
import android.os.Build
import android.os.Bundle
@ -74,6 +73,7 @@ import ani.dantotsu.media.SubtitleDownloader
import ani.dantotsu.others.AniSkip
import ani.dantotsu.others.AniSkip.getType
import ani.dantotsu.others.Download.download
import ani.dantotsu.others.LangSet
import ani.dantotsu.others.ResettableTimer
import ani.dantotsu.others.getSerialized
import ani.dantotsu.parsers.*
@ -81,7 +81,6 @@ import ani.dantotsu.settings.PlayerSettings
import ani.dantotsu.settings.PlayerSettingsActivity
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import com.bumptech.glide.Glide
import com.google.android.material.slider.Slider
import com.google.firebase.crashlytics.FirebaseCrashlytics
@ -1325,7 +1324,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
.setAllowMultipleAdaptiveSelections(true)
.setPreferredTextLanguage(subtitle?.language ?: "en")
.setPreferredTextRoleFlags(C.ROLE_FLAG_SUBTITLE)
.setRendererDisabled(C.TRACK_TYPE_VIDEO, false)
.setRendererDisabled(TRACK_TYPE_VIDEO, false)
.setRendererDisabled(C.TRACK_TYPE_AUDIO, false)
.setRendererDisabled(C.TRACK_TYPE_TEXT, false)
.setMinVideoSize(

View file

@ -25,8 +25,6 @@ import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.others.Download.download
import ani.dantotsu.parsers.VideoExtractor
import ani.dantotsu.parsers.VideoType
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -54,7 +52,11 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetSelectorBinding.inflate(inflater, container, false)
val window = dialog?.window
window?.statusBarColor = Color.TRANSPARENT
@ -92,10 +94,13 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
fun load() {
val size = ep.extractors?.find { it.server.name == selected }?.videos?.size
if (size!=null && size >= media!!.selected!!.video) {
media!!.anime!!.episodes?.get(media!!.anime!!.selectedEpisode!!)?.selectedExtractor = selected
media!!.anime!!.episodes?.get(media!!.anime!!.selectedEpisode!!)?.selectedVideo = media!!.selected!!.video
val size =
ep.extractors?.find { it.server.name == selected }?.videos?.size
if (size != null && size >= media!!.selected!!.video) {
media!!.anime!!.episodes?.get(media!!.anime!!.selectedEpisode!!)?.selectedExtractor =
selected
media!!.anime!!.episodes?.get(media!!.anime!!.selectedEpisode!!)?.selectedVideo =
media!!.selected!!.video
startExoplayer(media!!)
} else fail()
}
@ -116,8 +121,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}) fail()
}
} else load()
}
else {
} else {
binding.selectorRecyclerView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = navBarHeight
}
@ -130,10 +134,14 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
saveData("make_default", makeDefault)
}
binding.selectorRecyclerView.layoutManager =
LinearLayoutManager(requireActivity(), LinearLayoutManager.VERTICAL, false)
LinearLayoutManager(
requireActivity(),
LinearLayoutManager.VERTICAL,
false
)
val adapter = ExtractorAdapter()
binding.selectorRecyclerView.adapter = adapter
if (!ep.allStreams ) {
if (!ep.allStreams) {
ep.extractorCallback = {
scope.launch {
adapter.add(it)
@ -141,12 +149,15 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
model.getEpisode().observe(this) {
if (it != null) {
media!!.anime?.episodes?.set(media!!.anime?.selectedEpisode!!, ep)
media!!.anime?.episodes?.set(
media!!.anime?.selectedEpisode!!,
ep
)
}
}
scope.launch(Dispatchers.IO) {
model.loadEpisodeVideos(ep, media!!.selected!!.sourceIndex)
withContext(Dispatchers.Main){
withContext(Dispatchers.Main) {
binding.selectorProgressBar.visibility = View.GONE
}
}
@ -175,7 +186,10 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
ExoplayerView.initialized = true
startActivity(intent)
} else {
model.setEpisode(media.anime!!.episodes!![media.anime.selectedEpisode!!]!!, "startExo no launch")
model.setEpisode(
media.anime!!.episodes!![media.anime.selectedEpisode!!]!!,
"startExo no launch"
)
}
}
@ -186,10 +200,17 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
}
private inner class ExtractorAdapter : RecyclerView.Adapter<ExtractorAdapter.StreamViewHolder>() {
private inner class ExtractorAdapter :
RecyclerView.Adapter<ExtractorAdapter.StreamViewHolder>() {
val links = mutableListOf<VideoExtractor>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StreamViewHolder =
StreamViewHolder(ItemStreamBinding.inflate(LayoutInflater.from(parent.context), parent, false))
StreamViewHolder(
ItemStreamBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: StreamViewHolder, position: Int) {
val extractor = links[position]
@ -202,38 +223,49 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
override fun getItemCount(): Int = links.size
fun add(videoExtractor: VideoExtractor){
if(videoExtractor.videos.isNotEmpty()) {
fun add(videoExtractor: VideoExtractor) {
if (videoExtractor.videos.isNotEmpty()) {
links.add(videoExtractor)
notifyItemInserted(links.size - 1)
}
}
fun addAll(extractors: List<VideoExtractor>?) {
links.addAll(extractors?:return)
notifyItemRangeInserted(0,extractors.size)
links.addAll(extractors ?: return)
notifyItemRangeInserted(0, extractors.size)
}
private inner class StreamViewHolder(val binding: ItemStreamBinding) : RecyclerView.ViewHolder(binding.root)
private inner class StreamViewHolder(val binding: ItemStreamBinding) :
RecyclerView.ViewHolder(binding.root)
}
private inner class VideoAdapter(private val extractor : VideoExtractor) : RecyclerView.Adapter<VideoAdapter.UrlViewHolder>() {
private inner class VideoAdapter(private val extractor: VideoExtractor) :
RecyclerView.Adapter<VideoAdapter.UrlViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UrlViewHolder {
return UrlViewHolder(ItemUrlBinding.inflate(LayoutInflater.from(parent.context), parent, false))
return UrlViewHolder(
ItemUrlBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: UrlViewHolder, position: Int) {
val binding = holder.binding
val video = extractor.videos[position]
binding.urlQuality.text = if(video.quality!=null) "${video.quality}p" else "Default Quality"
binding.urlQuality.text =
if (video.quality != null) "${video.quality}p" else "Default Quality"
binding.urlNote.text = video.extraNote ?: ""
binding.urlNote.visibility = if (video.extraNote != null) View.VISIBLE else View.GONE
binding.urlDownload.visibility = View.VISIBLE
binding.urlDownload.setSafeOnClickListener {
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedExtractor = extractor.server.name
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedVideo = position
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedExtractor =
extractor.server.name
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedVideo =
position
binding.urlDownload.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
download(
requireActivity(),
@ -246,9 +278,10 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
binding.urlSize.visibility = if (video.size != null) View.VISIBLE else View.GONE
binding.urlSize.text =
// if video size is null or 0, show "Unknown Size" else show the size in MB
(if (video.extraNote != null) " : " else "") + (if (video.size == 0.0) "Unknown Size" else (DecimalFormat("#.##").format(video.size ?: 0).toString()+ " MB"))
}
else {
(if (video.extraNote != null) " : " else "") + (if (video.size == 0.0) "Unknown Size" else (DecimalFormat(
"#.##"
).format(video.size ?: 0).toString() + " MB"))
} else {
binding.urlQuality.text = "Multi Quality"
if ((loadData<Int>("settings_download_manager") ?: 0) == 0) {
binding.urlDownload.visibility = View.GONE
@ -258,12 +291,15 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
override fun getItemCount(): Int = extractor.videos.size
private inner class UrlViewHolder(val binding: ItemUrlBinding) : RecyclerView.ViewHolder(binding.root) {
private inner class UrlViewHolder(val binding: ItemUrlBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setSafeOnClickListener {
tryWith(true) {
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedExtractor = extractor.server.name
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedVideo = bindingAdapterPosition
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedExtractor =
extractor.server.name
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]?.selectedVideo =
bindingAdapterPosition
if (makeDefault) {
media!!.selected!!.server = extractor.server.name
media!!.selected!!.video = bindingAdapterPosition
@ -274,12 +310,12 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
itemView.setOnLongClickListener {
val video = extractor.videos[bindingAdapterPosition]
val intent= Intent(Intent.ACTION_VIEW).apply {
setDataAndType(Uri.parse(video.file.url),"video/*")
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(Uri.parse(video.file.url), "video/*")
}
copyToClipboard(video.file.url,true)
copyToClipboard(video.file.url, true)
dismiss()
startActivity(Intent.createChooser(intent,"Open Video in :"))
startActivity(Intent.createChooser(intent, "Open Video in :"))
true
}
}
@ -287,7 +323,11 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
}
companion object {
fun newInstance(server: String? = null, la: Boolean = true, prev: String? = null): SelectorDialogFragment =
fun newInstance(
server: String? = null,
la: Boolean = true,
prev: String? = null
): SelectorDialogFragment =
SelectorDialogFragment().apply {
arguments = Bundle().apply {
putString("server", server)

View file

@ -24,7 +24,11 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
val model: MediaDetailsViewModel by activityViewModels()
private lateinit var episode: Episode
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetSubtitlesBinding.inflate(inflater, container, false)
return binding.root
}
@ -34,17 +38,27 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
model.getMedia().observe(viewLifecycleOwner) { media ->
episode = media?.anime?.episodes?.get(media.anime.selectedEpisode) ?: return@observe
val currentExtractor = episode.extractors?.find { it.server.name == episode.selectedExtractor } ?: return@observe
val currentExtractor =
episode.extractors?.find { it.server.name == episode.selectedExtractor }
?: return@observe
binding.subtitlesRecycler.layoutManager = LinearLayoutManager(requireContext())
binding.subtitlesRecycler.adapter = SubtitleAdapter(currentExtractor.subtitles)
}
}
inner class SubtitleAdapter(val subtitles: List<Subtitle>) : RecyclerView.Adapter<SubtitleAdapter.StreamViewHolder>() {
inner class StreamViewHolder(val binding: ItemSubtitleTextBinding) : RecyclerView.ViewHolder(binding.root)
inner class SubtitleAdapter(val subtitles: List<Subtitle>) :
RecyclerView.Adapter<SubtitleAdapter.StreamViewHolder>() {
inner class StreamViewHolder(val binding: ItemSubtitleTextBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StreamViewHolder =
StreamViewHolder(ItemSubtitleTextBinding.inflate(LayoutInflater.from(parent.context), parent, false))
StreamViewHolder(
ItemSubtitleTextBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: StreamViewHolder, position: Int) {
val binding = holder.binding
@ -60,7 +74,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
binding.root.setOnClickListener {
episode.selectedSubtitle = null
model.setEpisode(episode, "Subtitle")
model.getMedia().observe(viewLifecycleOwner){media ->
model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id
saveData("subLang_${mediaID}", "None", activity)
}
@ -87,7 +101,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
"pl-PL" -> "[pl-PL] Polish"
"ro-RO" -> "[ro-RO] Romanian"
"sv-SE" -> "[sv-SE] Swedish"
else -> if(subtitles[position - 1].language matches Regex("([a-z]{2})-([A-Z]{2}|\\d{3})")) "[${subtitles[position - 1].language}]" else subtitles[position - 1].language
else -> if (subtitles[position - 1].language matches Regex("([a-z]{2})-([A-Z]{2}|\\d{3})")) "[${subtitles[position - 1].language}]" else subtitles[position - 1].language
}
model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id
@ -100,7 +114,7 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
binding.root.setOnClickListener {
episode.selectedSubtitle = position - 1
model.setEpisode(episode, "Subtitle")
model.getMedia().observe(viewLifecycleOwner){media ->
model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id
saveData("subLang_${mediaID}", subtitles[position - 1].language, activity)
}

View file

@ -14,7 +14,10 @@ object VideoCache {
val databaseProvider = StandaloneDatabaseProvider(context)
if (simpleCache == null)
simpleCache = SimpleCache(
File(context.cacheDir, "exoplayer").also { it.deleteOnExit() }, // Ensures always fresh file
File(
context.cacheDir,
"exoplayer"
).also { it.deleteOnExit() }, // Ensures always fresh file
LeastRecentlyUsedCacheEvictor(300L * 1024L * 1024L),
databaseProvider
)

View file

@ -8,5 +8,5 @@ data class Manga(
var selectedChapter: String? = null,
var chapters: MutableMap<String, MangaChapter>? = null,
var slug: String? = null,
var author: Author?=null,
var author: Author? = null,
) : Serializable

View file

@ -10,7 +10,6 @@ import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.util.LruCache
import android.widget.Toast
import ani.dantotsu.logger
import ani.dantotsu.snackString
import eu.kanade.tachiyomi.source.model.Page
@ -23,8 +22,12 @@ import java.io.FileOutputStream
data class ImageData(
val page: Page,
val source: HttpSource
){
suspend fun fetchAndProcessImage(page: Page, httpSource: HttpSource, context: Context): Bitmap? {
) {
suspend fun fetchAndProcessImage(
page: Page,
httpSource: HttpSource,
context: Context
): Bitmap? {
return withContext(Dispatchers.IO) {
try {
// Fetch the image
@ -52,16 +55,26 @@ data class ImageData(
}
}
fun saveImage(bitmap: Bitmap, contentResolver: ContentResolver, filename: String, format: Bitmap.CompressFormat, quality: Int) {
fun saveImage(
bitmap: Bitmap,
contentResolver: ContentResolver,
filename: String,
format: Bitmap.CompressFormat,
quality: Int
) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/${format.name.lowercase()}")
put(MediaStore.MediaColumns.RELATIVE_PATH, "${Environment.DIRECTORY_DOWNLOADS}/Dantotsu/Manga")
put(
MediaStore.MediaColumns.RELATIVE_PATH,
"${Environment.DIRECTORY_DOWNLOADS}/Dantotsu/Manga"
)
}
val uri: Uri? = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
val uri: Uri? =
contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
uri?.let {
contentResolver.openOutputStream(it)?.use { os ->
@ -69,7 +82,8 @@ fun saveImage(bitmap: Bitmap, contentResolver: ContentResolver, filename: String
}
}
} else {
val directory = File("${Environment.getExternalStorageDirectory()}${File.separator}Dantotsu${File.separator}Manga")
val directory =
File("${Environment.getExternalStorageDirectory()}${File.separator}Dantotsu${File.separator}Manga")
if (!directory.exists()) {
directory.mkdirs()
}
@ -85,7 +99,7 @@ fun saveImage(bitmap: Bitmap, contentResolver: ContentResolver, filename: String
}
}
class MangaCache() {
class MangaCache {
private val maxMemory = (Runtime.getRuntime().maxMemory() / 1024 / 2).toInt()
private val cache = LruCache<String, ImageData>(maxMemory)

View file

@ -15,7 +15,14 @@ data class MangaChapter(
val scanlator: String? = null,
var progress: String? = ""
) : Serializable {
constructor(chapter: MangaChapter) : this(chapter.number, chapter.link, chapter.title, chapter.description, chapter.sChapter, chapter.scanlator)
constructor(chapter: MangaChapter) : this(
chapter.number,
chapter.link,
chapter.title,
chapter.description,
chapter.sChapter,
chapter.scanlator
)
private val images = mutableListOf<MangaImage>()
fun images(): List<MangaImage> = images

View file

@ -5,16 +5,15 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import androidx.core.content.ContentProviderCompat.requireContext
import androidx.lifecycle.coroutineScope
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.R
import ani.dantotsu.connections.updateProgress
import ani.dantotsu.currContext
import ani.dantotsu.databinding.ItemChapterListBinding
import ani.dantotsu.databinding.ItemEpisodeCompactBinding
import ani.dantotsu.media.Media
import ani.dantotsu.setAnimation
import ani.dantotsu.connections.updateProgress
import ani.dantotsu.currContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -124,7 +123,7 @@ class MangaChapterAdapter(
if (progress != null) {
binding.itemChapterTitle.visibility = View.VISIBLE
binding.itemChapterTitle.text = "$progress"
}else{
} else {
binding.itemChapterTitle.visibility = View.GONE
binding.itemChapterTitle.text = ""
}
@ -154,7 +153,8 @@ class MangaChapterAdapter(
// Add chapter number to active coroutines set
activeCoroutines.add(chapterNumber)
while (activeDownloads.contains(chapterNumber)) {
binding.itemDownload.animate().rotationBy(360f).setDuration(1000).setInterpolator(
binding.itemDownload.animate().rotationBy(360f).setDuration(1000)
.setInterpolator(
LinearInterpolator()
).start()
delay(1000)
@ -171,8 +171,16 @@ class MangaChapterAdapter(
init {
val theme = currContext()?.theme
theme?.resolveAttribute(com.google.android.material.R.attr.colorError, typedValue1, true)
theme?.resolveAttribute(com.google.android.material.R.attr.colorPrimary, typedValue2, true)
theme?.resolveAttribute(
com.google.android.material.R.attr.colorError,
typedValue1,
true
)
theme?.resolveAttribute(
com.google.android.material.R.attr.colorPrimary,
typedValue2,
true
)
itemView.setOnClickListener {
if (0 <= bindingAdapterPosition && bindingAdapterPosition < arr.size)
fragment.onMangaChapterClick(arr[bindingAdapterPosition].number)

View file

@ -13,15 +13,12 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.*
import ani.dantotsu.App.Companion.context
import ani.dantotsu.media.anime.handleProgress
import ani.dantotsu.databinding.ItemAnimeWatchBinding
import ani.dantotsu.databinding.ItemChipBinding
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.SourceSearchDialogFragment
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.DynamicAnimeParser
import ani.dantotsu.media.anime.handleProgress
import ani.dantotsu.parsers.DynamicMangaParser
import ani.dantotsu.parsers.MangaReadSources
import ani.dantotsu.parsers.MangaSources
@ -30,7 +27,6 @@ import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
import com.google.android.material.chip.Chip
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import java.lang.IndexOutOfBoundsException
class MangaReadAdapter(
private val media: Media,
@ -57,12 +53,16 @@ class MangaReadAdapter(
//Wrong Title
binding.animeSourceSearch.setOnClickListener {
SourceSearchDialogFragment().show(fragment.requireActivity().supportFragmentManager, null)
SourceSearchDialogFragment().show(
fragment.requireActivity().supportFragmentManager,
null
)
}
//Source Selection
var source = media.selected!!.sourceIndex.let { if (it >= mangaReadSources.names.size) 0 else it }
setLanguageList(media.selected!!.langIndex,source)
var source =
media.selected!!.sourceIndex.let { if (it >= mangaReadSources.names.size) 0 else it }
setLanguageList(media.selected!!.langIndex, source)
if (mangaReadSources.names.isNotEmpty() && source in 0 until mangaReadSources.names.size) {
binding.animeSource.setText(mangaReadSources.names[source])
mangaReadSources[source].apply {
@ -70,14 +70,20 @@ class MangaReadAdapter(
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } }
}
}
binding.animeSource.setAdapter(ArrayAdapter(fragment.requireContext(), R.layout.item_dropdown, mangaReadSources.names))
binding.animeSource.setAdapter(
ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
mangaReadSources.names
)
)
binding.animeSourceTitle.isSelected = true
binding.animeSource.setOnItemClickListener { _, _, i, _ ->
fragment.onSourceChange(i).apply {
binding.animeSourceTitle.text = showUserText
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } }
source = i
setLanguageList(0,i)
setLanguageList(0, i)
}
subscribeButton(false)
//invalidate if it's the last source
@ -92,7 +98,8 @@ class MangaReadAdapter(
fragment.onLangChange(i)
fragment.onSourceChange(media.selected!!.sourceIndex).apply {
binding.animeSourceTitle.text = showUserText
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } }
showUserTextListener =
{ MainScope().launch { binding.animeSourceTitle.text = it } }
setLanguageList(i, source)
}
subscribeButton(false)
@ -139,7 +146,8 @@ class MangaReadAdapter(
}
binding.animeScanlatorTop.setOnClickListener {
val dialogView = LayoutInflater.from(currContext()).inflate(R.layout.custom_dialog_layout, null)
val dialogView =
LayoutInflater.from(currContext()).inflate(R.layout.custom_dialog_layout, null)
val checkboxContainer = dialogView.findViewById<LinearLayout>(R.id.checkboxContainer)
// Dynamically add checkboxes
@ -149,10 +157,10 @@ class MangaReadAdapter(
text = option
}
//set checked if it's already selected
if(media.selected!!.scanlators != null){
if (media.selected!!.scanlators != null) {
checkBox.isChecked = media.selected!!.scanlators?.contains(option) != true
scanlatorSelectionListener?.onScanlatorsSelected()
}else{
} else {
checkBox.isChecked = true
}
checkboxContainer.addView(checkBox)
@ -217,14 +225,26 @@ class MangaReadAdapter(
for (position in arr.indices) {
val last = if (position + 1 == arr.size) names.size else (limit * (position + 1))
val chip =
ItemChipBinding.inflate(LayoutInflater.from(fragment.context), binding.animeSourceChipGroup, false).root
ItemChipBinding.inflate(
LayoutInflater.from(fragment.context),
binding.animeSourceChipGroup,
false
).root
chip.isCheckable = true
fun selected() {
chip.isChecked = true
binding.animeWatchChipScroll.smoothScrollTo((chip.left - screenWidth / 2) + (chip.width / 2), 0)
binding.animeWatchChipScroll.smoothScrollTo(
(chip.left - screenWidth / 2) + (chip.width / 2),
0
)
}
chip.text = "${names[limit * (position)]} - ${names[last - 1]}"
chip.setTextColor(ContextCompat.getColorStateList(fragment.requireContext(), R.color.chip_text_color))
chip.setTextColor(
ContextCompat.getColorStateList(
fragment.requireContext(),
R.color.chip_text_color
)
)
chip.setOnClickListener {
selected()
@ -237,7 +257,14 @@ class MangaReadAdapter(
}
}
if (select != null)
binding.animeWatchChipScroll.apply { post { scrollTo((select.left - screenWidth / 2) + (select.width / 2), 0) } }
binding.animeWatchChipScroll.apply {
post {
scrollTo(
(select.left - screenWidth / 2) + (select.width / 2),
0
)
}
}
}
}
@ -259,7 +286,9 @@ class MangaReadAdapter(
val chapter = media.manga.chapters!![chapterKey]!!
chapter.scanlator !in hiddenScanlators
}
val formattedChapters = filteredChapters.map { MangaNameAdapter.findChapterNumber(it)?.toInt()?.toString() }
val formattedChapters = filteredChapters.map {
MangaNameAdapter.findChapterNumber(it)?.toInt()?.toString()
}
if (formattedChapters.contains(continueEp)) {
continueEp = chapters[formattedChapters.indexOf(continueEp)]
binding.animeSourceContinue.visibility = View.VISIBLE
@ -317,10 +346,17 @@ class MangaReadAdapter(
}
try {
binding?.animeSourceLanguage?.setText(parser.extension.sources[lang].lang)
}catch (e: IndexOutOfBoundsException) {
binding?.animeSourceLanguage?.setText(parser.extension.sources.firstOrNull()?.lang ?: "Unknown")
} catch (e: IndexOutOfBoundsException) {
binding?.animeSourceLanguage?.setText(
parser.extension.sources.firstOrNull()?.lang ?: "Unknown"
)
}
binding?.animeSourceLanguage?.setAdapter(ArrayAdapter(fragment.requireContext(), R.layout.item_dropdown, parser.extension.sources.map { it.lang }))
binding?.animeSourceLanguage?.setAdapter(
ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
parser.extension.sources.map { it.lang })
)
}
}
@ -328,7 +364,8 @@ class MangaReadAdapter(
override fun getItemCount(): Int = 1
inner class ViewHolder(val binding: ItemAnimeWatchBinding) : RecyclerView.ViewHolder(binding.root)
inner class ViewHolder(val binding: ItemAnimeWatchBinding) :
RecyclerView.ViewHolder(binding.root)
}
interface ScanlatorSelectionListener {

View file

@ -1,5 +1,6 @@
package ani.dantotsu.media.manga
import android.Manifest
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.BroadcastReceiver
@ -16,6 +17,7 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast
import androidx.cardview.widget.CardView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.math.MathUtils.clamp
import androidx.core.view.updatePadding
@ -31,10 +33,10 @@ import ani.dantotsu.download.Download
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.manga.MangaDownloaderService
import ani.dantotsu.download.manga.MangaServiceDataSingleton
import ani.dantotsu.media.manga.mangareader.ChapterLoaderDialog
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.manga.mangareader.ChapterLoaderDialog
import ani.dantotsu.parsers.DynamicMangaParser
import ani.dantotsu.parsers.HMangaSources
import ani.dantotsu.parsers.MangaParser
@ -59,8 +61,6 @@ import uy.kohesive.injekt.api.get
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.roundToInt
import android.Manifest
import androidx.core.app.ActivityCompat
open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
private var _binding: FragmentAnimeWatchBinding? = null
@ -85,7 +85,8 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
var continueEp: Boolean = false
var loaded = false
val uiSettings = loadData("ui_settings", toast = false) ?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
val uiSettings = loadData("ui_settings", toast = false)
?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
override fun onCreateView(
inflater: LayoutInflater,
@ -105,7 +106,12 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
addAction(ACTION_DOWNLOAD_PROGRESS)
}
ContextCompat.registerReceiver(requireContext(), downloadStatusReceiver, intentFilter, ContextCompat.RECEIVER_EXPORTED)
ContextCompat.registerReceiver(
requireContext(),
downloadStatusReceiver,
intentFilter,
ContextCompat.RECEIVER_EXPORTED
)
binding.animeSourceRecycler.updatePadding(bottom = binding.animeSourceRecycler.paddingBottom + navBarHeight)
screenWidth = resources.displayMetrics.widthPixels.dp
@ -146,7 +152,8 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
if (media.format == "MANGA" || media.format == "ONE SHOT") {
media.selected = model.loadSelected(media)
subscribed = SubscriptionHelper.getSubscriptions(requireContext()).containsKey(media.id)
subscribed =
SubscriptionHelper.getSubscriptions(requireContext()).containsKey(media.id)
style = media.selected!!.recyclerStyle
reverse = media.selected!!.recyclerReversed
@ -156,13 +163,15 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
headerAdapter = MangaReadAdapter(it, this, model.mangaReadSources!!)
headerAdapter.scanlatorSelectionListener = this
chapterAdapter = MangaChapterAdapter(style ?: uiSettings.mangaDefaultView, media, this)
chapterAdapter =
MangaChapterAdapter(style ?: uiSettings.mangaDefaultView, media, this)
for (download in downloadManager.mangaDownloads){
for (download in downloadManager.mangaDownloads) {
chapterAdapter.stopDownload(download.chapter)
}
binding.animeSourceRecycler.adapter = ConcatAdapter(headerAdapter, chapterAdapter)
binding.animeSourceRecycler.adapter =
ConcatAdapter(headerAdapter, chapterAdapter)
lifecycleScope.launch(Dispatchers.IO) {
model.loadMangaChapters(media, media.selected!!.sourceIndex)
@ -173,7 +182,8 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
}
} else {
binding.animeNotSupported.visibility = View.VISIBLE
binding.animeNotSupported.text = getString(R.string.not_supported, media.format ?: "")
binding.animeNotSupported.text =
getString(R.string.not_supported, media.format ?: "")
}
}
}
@ -302,7 +312,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
)
}
fun openSettings(pkg: MangaExtension.Installed){
fun openSettings(pkg: MangaExtension.Installed) {
val changeUIVisibility: (Boolean) -> Unit = { show ->
val activity = requireActivity() as MediaDetailsActivity
val visibility = if (show) View.VISIBLE else View.GONE
@ -310,9 +320,9 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
activity.findViewById<ViewPager2>(R.id.mediaViewPager).visibility = visibility
activity.findViewById<CardView>(R.id.mediaCover).visibility = visibility
activity.findViewById<CardView>(R.id.mediaClose).visibility = visibility
try{
try {
activity.findViewById<CustomBottomNavBar>(R.id.mediaTab).visibility = visibility
}catch (e: ClassCastException){
} catch (e: ClassCastException) {
activity.findViewById<NavigationRailView>(R.id.mediaTab).visibility = visibility
}
activity.findViewById<FrameLayout>(R.id.fragmentExtensionsContainer).visibility =
@ -335,7 +345,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
// Move the fragment transaction here
val fragment =
MangaSourcePreferencesFragment().getInstance(selectedSetting.id){
MangaSourcePreferencesFragment().getInstance(selectedSetting.id) {
changeUIVisibility(true)
loadChapters(media.selected!!.sourceIndex, true)
}
@ -353,7 +363,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
.show()
} else {
// If there's only one setting, proceed with the fragment transaction
val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id){
val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id) {
changeUIVisibility(true)
loadChapters(media.selected!!.sourceIndex, true)
}
@ -376,7 +386,8 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
media.manga?.chapters?.get(i)?.let {
media.manga?.selectedChapter = i
model.saveSelected(media.id, media.selected!!, requireActivity())
ChapterLoaderDialog.newInstance(it, true).show(requireActivity().supportFragmentManager, "dialog")
ChapterLoaderDialog.newInstance(it, true)
.show(requireActivity().supportFragmentManager, "dialog")
}
}
@ -393,7 +404,8 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
model.continueMedia = false
media.manga?.chapters?.get(i)?.let { chapter ->
val parser = model.mangaReadSources?.get(media.selected!!.sourceIndex) as? DynamicMangaParser
val parser =
model.mangaReadSources?.get(media.selected!!.sourceIndex) as? DynamicMangaParser
parser?.let {
CoroutineScope(Dispatchers.IO).launch {
val images = parser.imageList("", chapter.sChapter)
@ -439,10 +451,17 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
}
fun onMangaChapterRemoveDownloadClick(i: String){
downloadManager.removeDownload(Download(media.nameMAL?:media.nameRomaji, i, Download.Type.MANGA))
fun onMangaChapterRemoveDownloadClick(i: String) {
downloadManager.removeDownload(
Download(
media.nameMAL ?: media.nameRomaji,
i,
Download.Type.MANGA
)
)
chapterAdapter.deleteDownload(i)
}
fun onMangaChapterStopDownloadClick(i: String) {
val cancelIntent = Intent().apply {
action = MangaDownloaderService.ACTION_CANCEL_DOWNLOAD
@ -451,12 +470,19 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
requireContext().sendBroadcast(cancelIntent)
// Remove the download from the manager and update the UI
downloadManager.removeDownload(Download(media.nameMAL?:media.nameRomaji, i, Download.Type.MANGA))
downloadManager.removeDownload(
Download(
media.nameMAL ?: media.nameRomaji,
i,
Download.Type.MANGA
)
)
chapterAdapter.purgeDownload(i)
}
private val downloadStatusReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if(!this@MangaReadFragment::chapterAdapter.isInitialized) return
if (!this@MangaReadFragment::chapterAdapter.isInitialized) return
when (intent.action) {
ACTION_DOWNLOAD_STARTED -> {
val chapterNumber = intent.getStringExtra(EXTRA_CHAPTER_NUMBER)
@ -492,8 +518,10 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
val selected = model.loadSelected(media)
//Find latest chapter for subscription
selected.latest = media.manga?.chapters?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f
selected.latest = media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest
selected.latest =
media.manga?.chapters?.values?.maxOfOrNull { it.number.toFloatOrNull() ?: 0f } ?: 0f
selected.latest =
media.userProgress?.toFloat()?.takeIf { selected.latest < it } ?: selected.latest
model.saveSelected(media.id, selected, requireActivity())
headerAdapter.handleChapters()
@ -502,7 +530,8 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
if (media.manga!!.chapters != null) {
val end = if (end != null && end!! < media.manga!!.chapters!!.size) end else null
arr.addAll(
media.manga!!.chapters!!.values.toList().slice(start..(end ?: (media.manga!!.chapters!!.size - 1)))
media.manga!!.chapters!!.values.toList()
.slice(start..(end ?: (media.manga!!.chapters!!.size - 1)))
)
if (reverse)
arr = (arr.reversed() as? ArrayList<MangaChapter>) ?: arr

View file

@ -14,8 +14,8 @@ import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.*
import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.parsers.DynamicMangaParser
import ani.dantotsu.settings.CurrentReaderSettings
import com.alexvasilkov.gestures.views.GestureFrameLayout
import com.bumptech.glide.Glide
@ -23,12 +23,9 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ani.dantotsu.media.manga.MangaCache
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
@ -118,7 +115,10 @@ abstract class BaseImageAdapter(
abstract suspend fun loadImage(position: Int, parent: View): Boolean
companion object {
suspend fun Context.loadBitmap_old(link: FileUrl, transforms: List<BitmapTransformation>): Bitmap? { //still used in some places
suspend fun Context.loadBitmap_old(
link: FileUrl,
transforms: List<BitmapTransformation>
): Bitmap? { //still used in some places
return tryWithSuspend {
withContext(Dispatchers.IO) {
Glide.with(this@loadBitmap_old)
@ -135,8 +135,7 @@ abstract class BaseImageAdapter(
.let {
if (transforms.isNotEmpty()) {
it.transform(*transforms.toTypedArray())
}
else {
} else {
it
}
}
@ -146,7 +145,10 @@ abstract class BaseImageAdapter(
}
}
suspend fun Context.loadBitmap(link: FileUrl, transforms: List<BitmapTransformation>): Bitmap? {
suspend fun Context.loadBitmap(
link: FileUrl,
transforms: List<BitmapTransformation>
): Bitmap? {
return tryWithSuspend {
val mangaCache = uy.kohesive.injekt.Injekt.get<MangaCache>()
withContext(Dispatchers.IO) {
@ -161,7 +163,11 @@ abstract class BaseImageAdapter(
.diskCacheStrategy(DiskCacheStrategy.NONE)
} else {
mangaCache.get(link.url)?.let { imageData ->
val bitmap = imageData.fetchAndProcessImage(imageData.page, imageData.source, context = this@loadBitmap)
val bitmap = imageData.fetchAndProcessImage(
imageData.page,
imageData.source,
context = this@loadBitmap
)
it.load(bitmap)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)

View file

@ -14,9 +14,9 @@ import ani.dantotsu.BottomSheetDialogFragment
import ani.dantotsu.R
import ani.dantotsu.currActivity
import ani.dantotsu.databinding.BottomSheetSelectorBinding
import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.MediaSingleton
import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.others.getSerialized
import ani.dantotsu.tryWith
import kotlinx.coroutines.Dispatchers
@ -29,8 +29,8 @@ class ChapterLoaderDialog : BottomSheetDialogFragment() {
val model: MediaDetailsViewModel by activityViewModels()
private val launch : Boolean by lazy { arguments?.getBoolean("launch", false) ?: false }
private val chp : MangaChapter by lazy { arguments?.getSerialized("next")!! }
private val launch: Boolean by lazy { arguments?.getBoolean("launch", false) ?: false }
private val chp: MangaChapter by lazy { arguments?.getSerialized("next")!! }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
var loaded = false
@ -47,13 +47,21 @@ class ChapterLoaderDialog : BottomSheetDialogFragment() {
loaded = true
binding.selectorAutoText.text = chp.title
lifecycleScope.launch(Dispatchers.IO) {
if(model.loadMangaChapterImages(chp, m.selected!!, m.nameMAL?:m.nameRomaji)) {
if (model.loadMangaChapterImages(
chp,
m.selected!!,
m.nameMAL ?: m.nameRomaji
)
) {
val activity = currActivity()
activity?.runOnUiThread {
tryWith { dismiss() }
if(launch) {
if (launch) {
MediaSingleton.media = m
val intent = Intent(activity, MangaReaderActivity::class.java)//.apply { putExtra("media", m) }
val intent = Intent(
activity,
MangaReaderActivity::class.java
)//.apply { putExtra("media", m) }
activity.startActivity(intent)
}
}
@ -63,7 +71,11 @@ class ChapterLoaderDialog : BottomSheetDialogFragment() {
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetSelectorBinding.inflate(inflater, container, false)
val window = dialog?.window
window?.statusBarColor = Color.TRANSPARENT

View file

@ -30,15 +30,15 @@ open class ImageAdapter(
inner class ImageViewHolder(binding: ItemImageBinding) : RecyclerView.ViewHolder(binding.root)
open suspend fun loadBitmap(position: Int, parent: View) : Bitmap? {
open suspend fun loadBitmap(position: Int, parent: View): Bitmap? {
val link = images.getOrNull(position)?.url ?: return null
if (link.url.isEmpty()) return null
val transforms = mutableListOf<BitmapTransformation>()
val parserTransformation = activity.getTransformation(images[position])
if(parserTransformation!=null) transforms.add(parserTransformation)
if(settings.cropBorders) {
if (parserTransformation != null) transforms.add(parserTransformation)
if (settings.cropBorders) {
transforms.add(RemoveBordersTransformation(true, settings.cropBorderThreshold))
transforms.add(RemoveBordersTransformation(false, settings.cropBorderThreshold))
}
@ -47,7 +47,8 @@ open class ImageAdapter(
}
override suspend fun loadImage(position: Int, parent: View): Boolean {
val imageView = parent.findViewById<SubsamplingScaleImageView>(R.id.imgProgImageNoGestures) ?: return false
val imageView = parent.findViewById<SubsamplingScaleImageView>(R.id.imgProgImageNoGestures)
?: return false
val progress = parent.findViewById<View>(R.id.imgProgProgress) ?: return false
imageView.recycle()
imageView.visibility = View.GONE
@ -60,10 +61,12 @@ open class ImageAdapter(
if (settings.layout != PAGED)
parent.updateLayoutParams {
if (settings.direction != LEFT_TO_RIGHT && settings.direction != RIGHT_TO_LEFT) {
sHeight = if (settings.wrapImages) bitmap.height else (sWidth * bitmap.height * 1f / bitmap.width).toInt()
sHeight =
if (settings.wrapImages) bitmap.height else (sWidth * bitmap.height * 1f / bitmap.width).toInt()
height = sHeight
} else {
sWidth = if (settings.wrapImages) bitmap.width else (sHeight * bitmap.width * 1f / bitmap.height).toInt()
sWidth =
if (settings.wrapImages) bitmap.width else (sHeight * bitmap.width * 1f / bitmap.height).toInt()
width = sWidth
}
}
@ -73,7 +76,8 @@ open class ImageAdapter(
val parentArea = sWidth * sHeight * 1f
val bitmapArea = bitmap.width * bitmap.height * 1f
val scale = if (parentArea < bitmapArea) (bitmapArea / parentArea) else (parentArea / bitmapArea)
val scale =
if (parentArea < bitmapArea) (bitmapArea / parentArea) else (parentArea / bitmapArea)
imageView.maxScale = scale * 1.1f
imageView.minScale = scale

View file

@ -34,12 +34,11 @@ import ani.dantotsu.databinding.ActivityMangaReaderBinding
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.MediaSingleton
import ani.dantotsu.media.anime.ExoplayerView
import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.media.manga.MangaNameAdapter
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.others.getSerialized
import ani.dantotsu.others.LangSet
import ani.dantotsu.parsers.HMangaSources
import ani.dantotsu.parsers.MangaImage
import ani.dantotsu.parsers.MangaSources
@ -50,7 +49,6 @@ import ani.dantotsu.settings.CurrentReaderSettings.Layouts.*
import ani.dantotsu.settings.ReaderSettings
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import com.alexvasilkov.gestures.views.GestureFrameLayout
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
@ -58,8 +56,6 @@ import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get

View file

@ -10,14 +10,15 @@ import kotlin.math.max
class PreloadLinearLayoutManager(context: Context, orientation: Int, reverseLayout: Boolean) :
LinearLayoutManager(context, orientation, reverseLayout) {
private val mOrientationHelper: OrientationHelper = OrientationHelper.createOrientationHelper(this, orientation)
private val mOrientationHelper: OrientationHelper =
OrientationHelper.createOrientationHelper(this, orientation)
/**
* As [LinearLayoutManager.collectAdjacentPrefetchPositions] will prefetch one view for us,
* we only need to prefetch additional ones.
*/
var preloadItemCount = 1
set(count){
set(count) {
require(count >= 1) { "preloadItemCount must not be smaller than 1!" }
field = count - 1
}
@ -37,7 +38,8 @@ class PreloadLinearLayoutManager(context: Context, orientation: Int, reverseLayo
val currentPosition: Int = getPosition(child ?: return) + layoutDirection
if (layoutDirection == 1) {
val scrollingOffset = (mOrientationHelper.getDecoratedEnd(child) - mOrientationHelper.endAfterPadding)
val scrollingOffset =
(mOrientationHelper.getDecoratedEnd(child) - mOrientationHelper.endAfterPadding)
((currentPosition + 1) until (currentPosition + preloadItemCount + 1)).forEach {
if (it >= 0 && it < state.itemCount) {
layoutPrefetchRegistry.addPosition(it, max(0, scrollingOffset))

View file

@ -14,7 +14,11 @@ class ReaderSettingsDialogFragment : BottomSheetDialogFragment() {
private var _binding: BottomSheetCurrentReaderSettingsBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetCurrentReaderSettingsBinding.inflate(inflater, container, false)
return binding.root
}
@ -24,11 +28,14 @@ class ReaderSettingsDialogFragment : BottomSheetDialogFragment() {
val activity = requireActivity() as MangaReaderActivity
val settings = activity.settings.default
binding.readerDirectionText.text = resources.getStringArray(R.array.manga_directions)[settings.direction.ordinal]
binding.readerDirectionText.text =
resources.getStringArray(R.array.manga_directions)[settings.direction.ordinal]
binding.readerDirection.rotation = 90f * (settings.direction.ordinal)
binding.readerDirection.setOnClickListener {
settings.direction = Directions[settings.direction.ordinal + 1] ?: Directions.TOP_TO_BOTTOM
binding.readerDirectionText.text = resources.getStringArray(R.array.manga_directions)[settings.direction.ordinal]
settings.direction =
Directions[settings.direction.ordinal + 1] ?: Directions.TOP_TO_BOTTOM
binding.readerDirectionText.text =
resources.getStringArray(R.array.manga_directions)[settings.direction.ordinal]
binding.readerDirection.rotation = 90f * (settings.direction.ordinal)
activity.applySettings()
}
@ -39,36 +46,39 @@ class ReaderSettingsDialogFragment : BottomSheetDialogFragment() {
binding.readerContinuous
)
binding.readerPadding.isEnabled = settings.layout.ordinal!=0
fun paddingAvailable(enable:Boolean){
binding.readerPadding.isEnabled = settings.layout.ordinal != 0
fun paddingAvailable(enable: Boolean) {
binding.readerPadding.isEnabled = enable
}
binding.readerPadding.isChecked = settings.padding
binding.readerPadding.setOnCheckedChangeListener { _,isChecked ->
binding.readerPadding.setOnCheckedChangeListener { _, isChecked ->
settings.padding = isChecked
activity.applySettings()
}
binding.readerCropBorders.isChecked = settings.cropBorders
binding.readerCropBorders.setOnCheckedChangeListener { _,isChecked ->
binding.readerCropBorders.setOnCheckedChangeListener { _, isChecked ->
settings.cropBorders = isChecked
activity.applySettings()
}
binding.readerLayoutText.text = resources.getStringArray(R.array.manga_layouts)[settings.layout.ordinal]
binding.readerLayoutText.text =
resources.getStringArray(R.array.manga_layouts)[settings.layout.ordinal]
var selected = list[settings.layout.ordinal]
selected.alpha = 1f
list.forEachIndexed { index , imageButton ->
list.forEachIndexed { index, imageButton ->
imageButton.setOnClickListener {
selected.alpha = 0.33f
selected = imageButton
selected.alpha = 1f
settings.layout = CurrentReaderSettings.Layouts[index]?:CurrentReaderSettings.Layouts.CONTINUOUS
binding.readerLayoutText.text = resources.getStringArray(R.array.manga_layouts)[settings.layout.ordinal]
settings.layout =
CurrentReaderSettings.Layouts[index] ?: CurrentReaderSettings.Layouts.CONTINUOUS
binding.readerLayoutText.text =
resources.getStringArray(R.array.manga_layouts)[settings.layout.ordinal]
activity.applySettings()
paddingAvailable(settings.layout.ordinal!=0)
paddingAvailable(settings.layout.ordinal != 0)
}
}
@ -87,7 +97,8 @@ class ReaderSettingsDialogFragment : BottomSheetDialogFragment() {
selectedDual.alpha = 0.33f
selectedDual = imageButton
selectedDual.alpha = 1f
settings.dualPageMode = CurrentReaderSettings.DualPageModes[index] ?: CurrentReaderSettings.DualPageModes.Automatic
settings.dualPageMode = CurrentReaderSettings.DualPageModes[index]
?: CurrentReaderSettings.DualPageModes.Automatic
binding.readerDualPageText.text = settings.dualPageMode.toString()
activity.applySettings()
}
@ -111,37 +122,37 @@ class ReaderSettingsDialogFragment : BottomSheetDialogFragment() {
}
binding.readerKeepScreenOn.isChecked = settings.keepScreenOn
binding.readerKeepScreenOn.setOnCheckedChangeListener { _,isChecked ->
binding.readerKeepScreenOn.setOnCheckedChangeListener { _, isChecked ->
settings.keepScreenOn = isChecked
activity.applySettings()
}
binding.readerHidePageNumbers.isChecked = settings.hidePageNumbers
binding.readerHidePageNumbers.setOnCheckedChangeListener { _,isChecked ->
binding.readerHidePageNumbers.setOnCheckedChangeListener { _, isChecked ->
settings.hidePageNumbers = isChecked
activity.applySettings()
}
binding.readerOverscroll.isChecked = settings.overScrollMode
binding.readerOverscroll.setOnCheckedChangeListener { _,isChecked ->
binding.readerOverscroll.setOnCheckedChangeListener { _, isChecked ->
settings.overScrollMode = isChecked
activity.applySettings()
}
binding.readerVolumeButton.isChecked = settings.volumeButtons
binding.readerVolumeButton.setOnCheckedChangeListener { _,isChecked ->
binding.readerVolumeButton.setOnCheckedChangeListener { _, isChecked ->
settings.volumeButtons = isChecked
activity.applySettings()
}
binding.readerWrapImage.isChecked = settings.wrapImages
binding.readerWrapImage.setOnCheckedChangeListener { _,isChecked ->
binding.readerWrapImage.setOnCheckedChangeListener { _, isChecked ->
settings.wrapImages = isChecked
activity.applySettings()
}
binding.readerLongClickImage.isChecked = settings.longClickImage
binding.readerLongClickImage.setOnCheckedChangeListener { _,isChecked ->
binding.readerLongClickImage.setOnCheckedChangeListener { _, isChecked ->
settings.longClickImage = isChecked
activity.applySettings()
}
@ -152,7 +163,7 @@ class ReaderSettingsDialogFragment : BottomSheetDialogFragment() {
super.onDestroy()
}
companion object{
companion object {
fun newInstance() = ReaderSettingsDialogFragment()
}
}

View file

@ -6,7 +6,8 @@ import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import java.security.MessageDigest
class RemoveBordersTransformation(private val white:Boolean, private val threshHold:Int) : BitmapTransformation() {
class RemoveBordersTransformation(private val white: Boolean, private val threshHold: Int) :
BitmapTransformation() {
override fun transform(
pool: BitmapPool,
@ -95,6 +96,6 @@ class RemoveBordersTransformation(private val white:Boolean, private val threshH
private fun isPixelNotWhite(pixel: Int): Boolean {
val brightness = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel)
return if(white) brightness < (255-threshHold) else brightness > threshHold
return if (white) brightness < (255 - threshHold) else brightness > threshHold
}
}

View file

@ -13,7 +13,7 @@ class Swipy @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {
var dragDivider : Int = 5
var dragDivider: Int = 5
var vertical = true
//public, in case a different sub child needs to be considered
@ -109,6 +109,7 @@ class Swipy @JvmOverloads constructor(
}
initialDown = if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex)
}
MotionEvent.ACTION_MOVE -> {
if (activePointerId == INVALID_POINTER) {
//("Got ACTION_MOVE event but don't have an active pointer id.")
@ -121,6 +122,7 @@ class Swipy @JvmOverloads constructor(
val pos = if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex)
startDragging(pos)
}
MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev)
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
isBeingDragged = false
@ -142,6 +144,7 @@ class Swipy @JvmOverloads constructor(
activePointerId = ev.getPointerId(0)
isBeingDragged = false
}
MotionEvent.ACTION_MOVE -> {
pointerIndex = ev.findPointerIndex(activePointerId)
if (pointerIndex < 0) {
@ -160,16 +163,16 @@ class Swipy @JvmOverloads constructor(
if (overscroll > 0) {
parent.requestDisallowInterceptTouchEvent(true)
if (vertical){
val totalDragDistance = Resources.getSystem().displayMetrics.heightPixels / dragDivider
if (vertical) {
val totalDragDistance =
Resources.getSystem().displayMetrics.heightPixels / dragDivider
if (verticalPos == VerticalPosition.Top)
topBeingSwiped.invoke(overscroll / totalDragDistance)
else
bottomBeingSwiped.invoke(overscroll / totalDragDistance)
}
else {
val totalDragDistance = Resources.getSystem().displayMetrics.widthPixels / dragDivider
} else {
val totalDragDistance =
Resources.getSystem().displayMetrics.widthPixels / dragDivider
if (horizontalPos == HorizontalPosition.Left)
leftBeingSwiped.invoke(overscroll / totalDragDistance)
else
@ -180,6 +183,7 @@ class Swipy @JvmOverloads constructor(
}
}
}
MotionEvent.ACTION_POINTER_DOWN -> {
pointerIndex = ev.actionIndex
if (pointerIndex < 0) {
@ -188,6 +192,7 @@ class Swipy @JvmOverloads constructor(
}
activePointerId = ev.getPointerId(pointerIndex)
}
MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev)
MotionEvent.ACTION_UP -> {
if (vertical) {
@ -216,6 +221,7 @@ class Swipy @JvmOverloads constructor(
activePointerId = INVALID_POINTER
return false
}
MotionEvent.ACTION_CANCEL -> return false
}
return true
@ -242,8 +248,7 @@ class Swipy @JvmOverloads constructor(
onTopSwiped.invoke()
else
onBottomSwiped.invoke()
}
else {
} else {
val totalDragDistance = Resources.getSystem().displayMetrics.widthPixels / dragDivider
if (overscrollDistance > totalDragDistance)
if (horizontalPos == HorizontalPosition.Left)

View file

@ -13,8 +13,6 @@ import ani.dantotsu.loadImage
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.others.getSerialized
import ani.dantotsu.parsers.ShowResponse
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -25,13 +23,14 @@ class BookDialog : BottomSheetDialogFragment() {
private val viewModel by activityViewModels<MediaDetailsViewModel>()
private lateinit var novelName:String
private lateinit var novelName: String
private lateinit var novel: ShowResponse
private var source:Int = 0
private var source: Int = 0
interface Callback {
fun onDownloadTriggered(link: String)
}
private var callback: Callback? = null
fun setCallback(callback: Callback) {
this.callback = callback
@ -46,7 +45,11 @@ class BookDialog : BottomSheetDialogFragment() {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetBookBinding.inflate(inflater, container, false)
return binding.root
}
@ -55,7 +58,7 @@ class BookDialog : BottomSheetDialogFragment() {
binding.bookRecyclerView.layoutManager = LinearLayoutManager(requireContext())
viewModel.book.observe(viewLifecycleOwner) {
if(it!=null){
if (it != null) {
binding.itemBookTitle.text = it.name
binding.itemBookDesc.text = it.description
binding.itemBookImage.loadImage(it.img)
@ -73,7 +76,7 @@ class BookDialog : BottomSheetDialogFragment() {
}
companion object {
fun newInstance(novelName:String, novel:ShowResponse, source: Int) : BookDialog{
fun newInstance(novelName: String, novel: ShowResponse, source: Int): BookDialog {
val bundle = Bundle().apply {
putString("novelName", novelName)
putInt("source", source)

View file

@ -22,12 +22,14 @@ class NovelReadAdapter(
var progress: View? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NovelReadAdapter.ViewHolder {
val binding = ItemNovelHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding =
ItemNovelHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
progress = binding.progress.root
return ViewHolder(binding)
}
private val imm = fragment.requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
private val imm = fragment.requireContext()
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val binding = holder.binding
@ -35,7 +37,8 @@ class NovelReadAdapter(
fun search(): Boolean {
val query = binding.searchBarText.text.toString()
val source = media.selected!!.sourceIndex.let { if (it >= novelReadSources.names.size) 0 else it }
val source =
media.selected!!.sourceIndex.let { if (it >= novelReadSources.names.size) 0 else it }
fragment.source = source
binding.searchBarText.clearFocus()
@ -44,11 +47,18 @@ class NovelReadAdapter(
return true
}
val source = media.selected!!.sourceIndex.let { if (it >= novelReadSources.names.size) 0 else it }
val source =
media.selected!!.sourceIndex.let { if (it >= novelReadSources.names.size) 0 else it }
if (novelReadSources.names.isNotEmpty() && source in 0 until novelReadSources.names.size) {
binding.animeSource.setText(novelReadSources.names[source], false)
}
binding.animeSource.setAdapter(ArrayAdapter(fragment.requireContext(), R.layout.item_dropdown, novelReadSources.names))
binding.animeSource.setAdapter(
ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
novelReadSources.names
)
)
binding.animeSource.setOnItemClickListener { _, _, i, _ ->
fragment.onSourceChange(i)
search()
@ -66,5 +76,6 @@ class NovelReadAdapter(
override fun getItemCount(): Int = 1
inner class ViewHolder(val binding: ItemNovelHeaderBinding) : RecyclerView.ViewHolder(binding.root)
inner class ViewHolder(val binding: ItemNovelHeaderBinding) :
RecyclerView.ViewHolder(binding.root)
}

View file

@ -61,7 +61,8 @@ class NovelReadFragment : Fragment(),
private var continueEp: Boolean = false
var loaded = false
val uiSettings = loadData("ui_settings", toast = false) ?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
val uiSettings = loadData("ui_settings", toast = false)
?: UserInterfaceSettings().apply { saveData("ui_settings", this) }
override fun downloadTrigger(novelDownloadPackage: NovelDownloadPackage) {
Log.e("downloadTrigger", novelDownloadPackage.link)
@ -90,10 +91,24 @@ class NovelReadFragment : Fragment(),
override fun downloadedCheckWithStart(novel: ShowResponse): Boolean {
val downloadsManager = Injekt.get<DownloadsManager>()
if(downloadsManager.queryDownload(Download(media.nameMAL ?: media.nameRomaji, novel.name, Download.Type.NOVEL))) {
val file = File(context?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "${DownloadsManager.novelLocation}/${media.nameMAL ?: media.nameRomaji}/${novel.name}/0.epub")
if (downloadsManager.queryDownload(
Download(
media.nameMAL ?: media.nameRomaji,
novel.name,
Download.Type.NOVEL
)
)
) {
val file = File(
context?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"${DownloadsManager.novelLocation}/${media.nameMAL ?: media.nameRomaji}/${novel.name}/0.epub"
)
if (!file.exists()) return false
val fileUri = FileProvider.getUriForFile(requireContext(), "${requireContext().packageName}.provider", file)
val fileUri = FileProvider.getUriForFile(
requireContext(),
"${requireContext().packageName}.provider",
file
)
val intent = Intent(context, NovelReaderActivity::class.java).apply {
action = Intent.ACTION_VIEW
setDataAndType(fileUri, "application/epub+zip")
@ -108,12 +123,24 @@ class NovelReadFragment : Fragment(),
override fun downloadedCheck(novel: ShowResponse): Boolean {
val downloadsManager = Injekt.get<DownloadsManager>()
return downloadsManager.queryDownload(Download(media.nameMAL ?: media.nameRomaji, novel.name, Download.Type.NOVEL))
return downloadsManager.queryDownload(
Download(
media.nameMAL ?: media.nameRomaji,
novel.name,
Download.Type.NOVEL
)
)
}
override fun deleteDownload(novel: ShowResponse) {
val downloadsManager = Injekt.get<DownloadsManager>()
downloadsManager.removeDownload(Download(media.nameMAL ?: media.nameRomaji, novel.name, Download.Type.NOVEL))
downloadsManager.removeDownload(
Download(
media.nameMAL ?: media.nameRomaji,
novel.name,
Download.Type.NOVEL
)
)
}
private val downloadStatusReceiver = object : BroadcastReceiver() {
@ -126,18 +153,21 @@ class NovelReadFragment : Fragment(),
novelResponseAdapter.startDownload(it)
}
}
ACTION_DOWNLOAD_FINISHED -> {
val link = intent.getStringExtra(EXTRA_NOVEL_LINK)
link?.let {
novelResponseAdapter.stopDownload(it)
}
}
ACTION_DOWNLOAD_FAILED -> {
val link = intent.getStringExtra(EXTRA_NOVEL_LINK)
link?.let {
novelResponseAdapter.purgeDownload(it)
}
}
ACTION_DOWNLOAD_PROGRESS -> {
val link = intent.getStringExtra(EXTRA_NOVEL_LINK)
val progress = intent.getIntExtra("progress", 0)
@ -159,7 +189,12 @@ class NovelReadFragment : Fragment(),
addAction(ACTION_DOWNLOAD_PROGRESS)
}
ContextCompat.registerReceiver(requireContext(), downloadStatusReceiver, intentFilter ,ContextCompat.RECEIVER_EXPORTED)
ContextCompat.registerReceiver(
requireContext(),
downloadStatusReceiver,
intentFilter,
ContextCompat.RECEIVER_EXPORTED
)
binding.animeSourceRecycler.updatePadding(bottom = binding.animeSourceRecycler.paddingBottom + navBarHeight)
@ -179,8 +214,13 @@ class NovelReadFragment : Fragment(),
val sel = media.selected
searchQuery = sel?.server ?: media.name ?: media.nameRomaji
headerAdapter = NovelReadAdapter(media, this, model.novelSources)
novelResponseAdapter = NovelResponseAdapter(this, this, this) // probably a better way to do this but it works
binding.animeSourceRecycler.adapter = ConcatAdapter(headerAdapter, novelResponseAdapter)
novelResponseAdapter = NovelResponseAdapter(
this,
this,
this
) // probably a better way to do this but it works
binding.animeSourceRecycler.adapter =
ConcatAdapter(headerAdapter, novelResponseAdapter)
loaded = true
Handler(Looper.getMainLooper()).postDelayed({
search(searchQuery, sel?.sourceIndex ?: 0, auto = sel?.server == null)
@ -206,7 +246,7 @@ class NovelReadFragment : Fragment(),
searchQuery = query
headerAdapter.progress?.visibility = View.VISIBLE
lifecycleScope.launch(Dispatchers.IO) {
if (auto || query=="") model.autoSearchNovels(media)
if (auto || query == "") model.autoSearchNovels(media)
else model.searchNovels(query, source)
}
searching = true

View file

@ -13,11 +13,22 @@ import ani.dantotsu.parsers.Book
import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.tryWith
class UrlAdapter(private val urls: List<FileUrl>, val book: Book, val novel: String, val callback: BookDialog.Callback?) :
class UrlAdapter(
private val urls: List<FileUrl>,
val book: Book,
val novel: String,
val callback: BookDialog.Callback?
) :
RecyclerView.Adapter<UrlAdapter.UrlViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UrlViewHolder {
return UrlViewHolder(ItemUrlBinding.inflate(LayoutInflater.from(parent.context), parent, false))
return UrlViewHolder(
ItemUrlBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
@SuppressLint("SetTextI18n")

View file

@ -27,6 +27,7 @@ import ani.dantotsu.databinding.ActivityNovelReaderBinding
import ani.dantotsu.hideSystemBars
import ani.dantotsu.loadData
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.others.LangSet
import ani.dantotsu.saveData
import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.settings.CurrentNovelReaderSettings
@ -35,7 +36,6 @@ import ani.dantotsu.settings.NovelReaderSettings
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.snackString
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import ani.dantotsu.tryWith
import com.google.android.material.slider.Slider
import com.vipulog.ebookreader.Book
@ -161,7 +161,8 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
binding.novelReaderBack.setOnClickListener { finish() }
binding.novelReaderSettings.setSafeOnClickListener {
NovelReaderSettingsDialogFragment.newInstance().show(supportFragmentManager, NovelReaderSettingsDialogFragment.TAG)
NovelReaderSettingsDialogFragment.newInstance()
.show(supportFragmentManager, NovelReaderSettingsDialogFragment.TAG)
}
val gestureDetector = GestureDetectorCompat(this, object : GesturesListener() {
@ -233,9 +234,16 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
binding.novelReaderSource.text = book.author?.joinToString(", ")
val tocLabels = book.toc.map { it.label ?: "" }
binding.novelReaderChapterSelect.adapter = NoPaddingArrayAdapter(this, R.layout.item_dropdown, tocLabels)
binding.novelReaderChapterSelect.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
binding.novelReaderChapterSelect.adapter =
NoPaddingArrayAdapter(this, R.layout.item_dropdown, tocLabels)
binding.novelReaderChapterSelect.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
binding.bookReader.goto(book.toc[position].href)
}
@ -326,7 +334,8 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
saveData("${sanitizedBookId}_current_settings", settings.default)
hideBars()
currentTheme = themes.first { it.name.equals(settings.default.currentThemeName, ignoreCase = true) }
currentTheme =
themes.first { it.name.equals(settings.default.currentThemeName, ignoreCase = true) }
when (settings.default.layout) {
CurrentNovelReaderSettings.Layouts.PAGED -> {
@ -342,7 +351,8 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
when (settings.default.dualPageMode) {
CurrentReaderSettings.DualPageModes.No -> currentTheme?.maxColumnCount = 1
CurrentReaderSettings.DualPageModes.Automatic -> currentTheme?.maxColumnCount = 2
CurrentReaderSettings.DualPageModes.Force -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
CurrentReaderSettings.DualPageModes.Force -> requestedOrientation =
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
}
currentTheme?.lineHeight = settings.default.lineHeight
@ -393,7 +403,8 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
isContVisible = false
if (!isAnimating) {
isAnimating = true
ObjectAnimator.ofFloat(binding.novelReaderCont, "alpha", 1f, 0f).setDuration(controllerDuration).start()
ObjectAnimator.ofFloat(binding.novelReaderCont, "alpha", 1f, 0f)
.setDuration(controllerDuration).start()
ObjectAnimator.ofFloat(binding.novelReaderBottomCont, "translationY", 0f, 128f)
.apply { interpolator = overshoot;duration = controllerDuration;start() }
ObjectAnimator.ofFloat(binding.novelReaderTopLayout, "translationY", 0f, -128f)
@ -403,7 +414,8 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
} else {
isContVisible = true
binding.novelReaderCont.visibility = View.VISIBLE
ObjectAnimator.ofFloat(binding.novelReaderCont, "alpha", 0f, 1f).setDuration(controllerDuration).start()
ObjectAnimator.ofFloat(binding.novelReaderCont, "alpha", 0f, 1f)
.setDuration(controllerDuration).start()
ObjectAnimator.ofFloat(binding.novelReaderTopLayout, "translationY", -128f, 0f)
.apply { interpolator = overshoot;duration = controllerDuration;start() }
ObjectAnimator.ofFloat(binding.novelReaderBottomCont, "translationY", 128f, 0f)
@ -418,7 +430,10 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
val displayCutout = window.decorView.rootWindowInsets.displayCutout
if (displayCutout != null) {
if (displayCutout.boundingRects.size > 0) {
notchHeight = min(displayCutout.boundingRects[0].width(), displayCutout.boundingRects[0].height())
notchHeight = min(
displayCutout.boundingRects[0].width(),
displayCutout.boundingRects[0].height()
)
applyNotchMargin()
}
}

View file

@ -17,7 +17,11 @@ class NovelReaderSettingsDialogFragment : BottomSheetDialogFragment() {
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetCurrentNovelReaderSettingsBinding.inflate(inflater, container, false)
return binding.root
}
@ -29,10 +33,16 @@ class NovelReaderSettingsDialogFragment : BottomSheetDialogFragment() {
val settings = activity.settings.default
val themeLabels = activity.themes.map { it.name }
binding.themeSelect.adapter = NoPaddingArrayAdapter(activity, R.layout.item_dropdown, themeLabels)
binding.themeSelect.adapter =
NoPaddingArrayAdapter(activity, R.layout.item_dropdown, themeLabels)
binding.themeSelect.setSelection(themeLabels.indexOfFirst { it == settings.currentThemeName })
binding.themeSelect.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
settings.currentThemeName = themeLabels[position]
activity.applySettings()
}
@ -54,7 +64,8 @@ class NovelReaderSettingsDialogFragment : BottomSheetDialogFragment() {
selected.alpha = 0.33f
selected = imageButton
selected.alpha = 1f
settings.layout = CurrentNovelReaderSettings.Layouts[index]?:CurrentNovelReaderSettings.Layouts.PAGED
settings.layout = CurrentNovelReaderSettings.Layouts[index]
?: CurrentNovelReaderSettings.Layouts.PAGED
binding.layoutText.text = settings.layout.string
activity.applySettings()
}
@ -75,7 +86,8 @@ class NovelReaderSettingsDialogFragment : BottomSheetDialogFragment() {
selectedDual.alpha = 0.33f
selectedDual = imageButton
selectedDual.alpha = 1f
settings.dualPageMode = CurrentReaderSettings.DualPageModes[index] ?: CurrentReaderSettings.DualPageModes.Automatic
settings.dualPageMode = CurrentReaderSettings.DualPageModes[index]
?: CurrentReaderSettings.DualPageModes.Automatic
binding.dualPageText.text = settings.dualPageMode.toString()
activity.applySettings()
}
@ -164,19 +176,19 @@ class NovelReaderSettingsDialogFragment : BottomSheetDialogFragment() {
}
binding.useDarkTheme.isChecked = settings.useDarkTheme
binding.useDarkTheme.setOnCheckedChangeListener { _,isChecked ->
binding.useDarkTheme.setOnCheckedChangeListener { _, isChecked ->
settings.useDarkTheme = isChecked
activity.applySettings()
}
binding.keepScreenOn.isChecked = settings.keepScreenOn
binding.keepScreenOn.setOnCheckedChangeListener { _,isChecked ->
binding.keepScreenOn.setOnCheckedChangeListener { _, isChecked ->
settings.keepScreenOn = isChecked
activity.applySettings()
}
binding.volumeButton.isChecked = settings.volumeButtons
binding.volumeButton.setOnCheckedChangeListener { _,isChecked ->
binding.volumeButton.setOnCheckedChangeListener { _, isChecked ->
settings.volumeButtons = isChecked
activity.applySettings()
}
@ -189,7 +201,7 @@ class NovelReaderSettingsDialogFragment : BottomSheetDialogFragment() {
}
companion object{
companion object {
fun newInstance() = NovelReaderSettingsDialogFragment()
const val TAG = "NovelReaderSettingsDialogFragment"
}

View file

@ -16,9 +16,9 @@ import ani.dantotsu.R
import ani.dantotsu.Refresh
import ani.dantotsu.databinding.ActivityListBinding
import ani.dantotsu.loadData
import ani.dantotsu.others.LangSet
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.Dispatchers
@ -34,14 +34,18 @@ class ListActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
ThemeManager(this).applyTheme()
ThemeManager(this).applyTheme()
binding = ActivityListBinding.inflate(layoutInflater)
val typedValue = TypedValue()
theme.resolveAttribute(com.google.android.material.R.attr.colorSurface, typedValue, true)
val primaryColor = typedValue.data
val typedValue2 = TypedValue()
theme.resolveAttribute(com.google.android.material.R.attr.colorOnBackground, typedValue2, true)
theme.resolveAttribute(
com.google.android.material.R.attr.colorOnBackground,
typedValue2,
true
)
val titleTextColor = typedValue2.data
val typedValue3 = TypedValue()
theme.resolveAttribute(com.google.android.material.R.attr.colorPrimary, typedValue3, true)
@ -63,33 +67,50 @@ ThemeManager(this).applyTheme()
ContextCompat.getColor(this, R.color.nav_bg_inv)
binding.root.fitsSystemWindows = true
}else{
} else {
binding.root.fitsSystemWindows = false
requestWindowFeature(Window.FEATURE_NO_TITLE)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
}
setContentView(binding.root)
val anime = intent.getBooleanExtra("anime", true)
binding.listTitle.text = intent.getStringExtra("username") + "'s " + (if (anime) "Anime" else "Manga") + " List"
binding.listTitle.text =
intent.getStringExtra("username") + "'s " + (if (anime) "Anime" else "Manga") + " List"
binding.listTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
this@ListActivity.selectedTabIdx = tab?.position ?: 0
}
override fun onTabUnselected(tab: TabLayout.Tab?) { }
override fun onTabReselected(tab: TabLayout.Tab?) { }
override fun onTabUnselected(tab: TabLayout.Tab?) {}
override fun onTabReselected(tab: TabLayout.Tab?) {}
})
val model: ListViewModel by viewModels()
model.getLists().observe(this) {
val defaultKeys = listOf("Reading", "Watching", "Completed", "Paused", "Dropped", "Planning", "Favourites", "Rewatching", "Rereading", "All")
val userKeys : Array<String> = resources.getStringArray(R.array.keys)
val defaultKeys = listOf(
"Reading",
"Watching",
"Completed",
"Paused",
"Dropped",
"Planning",
"Favourites",
"Rewatching",
"Rereading",
"All"
)
val userKeys: Array<String> = resources.getStringArray(R.array.keys)
if (it != null) {
binding.listProgressBar.visibility = View.GONE
binding.listViewPager.adapter = ListViewPagerAdapter(it.size, false,this)
val keys = it.keys.toList().map { key -> userKeys.getOrNull(defaultKeys.indexOf(key))?: key }
binding.listViewPager.adapter = ListViewPagerAdapter(it.size, false, this)
val keys = it.keys.toList()
.map { key -> userKeys.getOrNull(defaultKeys.indexOf(key)) ?: key }
val values = it.values.toList()
val savedTab = this.selectedTabIdx
TabLayoutMediator(binding.listTabLayout, binding.listViewPager) { tab, position ->
@ -103,7 +124,12 @@ ThemeManager(this).applyTheme()
live.observe(this) {
if (it) {
scope.launch {
withContext(Dispatchers.IO) { model.loadLists(anime, intent.getIntExtra("userId", 0)) }
withContext(Dispatchers.IO) {
model.loadLists(
anime,
intent.getIntExtra("userId", 0)
)
}
live.postValue(false)
}
}
@ -123,7 +149,13 @@ ThemeManager(this).applyTheme()
binding.listProgressBar.visibility = View.VISIBLE
binding.listViewPager.adapter = null
scope.launch {
withContext(Dispatchers.IO) { model.loadLists(anime, intent.getIntExtra("userId", 0), sort) }
withContext(Dispatchers.IO) {
model.loadLists(
anime,
intent.getIntExtra("userId", 0),
sort
)
}
}
true
}

View file

@ -11,8 +11,6 @@ import ani.dantotsu.databinding.FragmentListBinding
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaAdaptor
import ani.dantotsu.media.OtherDetailsViewModel
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.others.LangSet
class ListFragment : Fragment() {
private var _binding: FragmentListBinding? = null
@ -29,7 +27,11 @@ class ListFragment : Fragment() {
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentListBinding.inflate(inflater, container, false)
return binding.root
}
@ -42,7 +44,10 @@ class ListFragment : Fragment() {
if (grid != null && list != null) {
val adapter = MediaAdaptor(if (grid!!) 0 else 1, list!!, requireActivity(), true)
binding.listRecyclerView.layoutManager =
GridLayoutManager(requireContext(), if (grid!!) (screenWidth / 124f).toInt() else 1)
GridLayoutManager(
requireContext(),
if (grid!!) (screenWidth / 124f).toInt() else 1
)
binding.listRecyclerView.adapter = adapter
}
}

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