diff --git a/app/src/main/java/ani/dantotsu/App.kt b/app/src/main/java/ani/dantotsu/App.kt index 3c5b5616..89a5d441 100644 --- a/app/src/main/java/ani/dantotsu/App.kt +++ b/app/src/main/java/ani/dantotsu/App.kt @@ -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 } diff --git a/app/src/main/java/ani/dantotsu/Functions.kt b/app/src/main/java/ani/dantotsu/Functions.kt index d7b7469a..6a789743 100644 --- a/app/src/main/java/ani/dantotsu/Functions.kt +++ b/app/src/main/java/ani/dantotsu/Functions.kt @@ -132,9 +132,10 @@ fun loadData(fileName: String, context: Context? = null, toast: Boolean = tr fun initActivity(a: Activity) { val window = a.window WindowCompat.setDecorFitsSystemWindows(window, 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) + } uiSettings.darkMode.apply { AppCompatDelegate.setDefaultNightMode( when (this) { @@ -146,9 +147,10 @@ fun initActivity(a: Activity) { } if (uiSettings.immersiveMode) { if (navBarHeight == 0) { - ViewCompat.getRootWindowInsets(window.decorView.findViewById(android.R.id.content))?.apply { - navBarHeight = this.getInsets(WindowInsetsCompat.Type.systemBars()).bottom - } + ViewCompat.getRootWindowInsets(window.decorView.findViewById(android.R.id.content)) + ?.apply { + navBarHeight = this.getInsets(WindowInsetsCompat.Type.systemBars()).bottom + } } a.hideStatusBar() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && statusBarHeight == 0 && a.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) { @@ -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) { @@ -219,7 +223,7 @@ fun isOnline(context: Context): Boolean { cap.hasTransport(TRANSPORT_WIFI) || cap.hasTransport(TRANSPORT_WIFI_AWARE) -> true - else -> false + else -> false } } else false } ?: false @@ -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.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 { gravity = (Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM) @@ -769,7 +815,8 @@ fun snackString(s: String?, activity: Activity? = null, clipboard: String? = nul } } -open class NoPaddingArrayAdapter(context: Context, layoutId: Int, items: List) : ArrayAdapter(context, layoutId, items) { +open class NoPaddingArrayAdapter(context: Context, layoutId: Int, items: List) : + ArrayAdapter(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,16 +837,21 @@ 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() { - override fun onSingleTapUp(e: MotionEvent): Boolean { - return performClick() - } - }) + mGestureDetector = + GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() { + override fun onSingleTapUp(e: MotionEvent): Boolean { + return performClick() + } + }) } override fun onTouchEvent(event: MotionEvent): Boolean { @@ -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) @@ -865,12 +921,12 @@ fun checkCountry(context: Context): Boolean { tz.equals("Asia/Kolkata", ignoreCase = true) } - TelephonyManager.SIM_STATE_READY -> { + TelephonyManager.SIM_STATE_READY -> { val countryCodeValue = telMgr.networkCountryIso countryCodeValue.equals("in", ignoreCase = true) } - else -> false + else -> false } } diff --git a/app/src/main/java/ani/dantotsu/MainActivity.kt b/app/src/main/java/ani/dantotsu/MainActivity.kt index 90805d8d..b27ee17d 100644 --- a/app/src/main/java/ani/dantotsu/MainActivity.kt +++ b/app/src/main/java/ani/dantotsu/MainActivity.kt @@ -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) : diff --git a/app/src/main/java/ani/dantotsu/Network.kt b/app/src/main/java/ani/dantotsu/Network.kt index d53f33c9..dd58849a 100644 --- a/app/src/main/java/ani/dantotsu/Network.kt +++ b/app/src/main/java/ani/dantotsu/Network.kt @@ -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 tryWith(post: Boolean = false, snackbar: Boolean = true, call: () -> T): } } -suspend fun tryWithSuspend(post: Boolean = false, snackbar: Boolean = true, call: suspend () -> T): T? { +suspend fun tryWithSuspend( + post: Boolean = false, + snackbar: Boolean = true, + call: suspend () -> T +): T? { return try { call.invoke() } catch (e: Throwable) { @@ -194,28 +199,29 @@ fun OkHttpClient.Builder.addAdGuardDns() = ( @Suppress("BlockingMethodInNonBlockingContext") suspend fun webViewInterface(webViewDialog: WebViewBottomDialog): Map? { - var map : Map? = null + var map: Map? = 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 } suspend fun webViewInterface(type: String, url: FileUrl): Map? { val webViewDialog: WebViewBottomDialog = when (type) { "Cloudflare" -> CloudFlare.newInstance(url) - else -> return null + else -> return null } return webViewInterface(webViewDialog) } suspend fun webViewInterface(type: String, url: String): Map? { - return webViewInterface(type,FileUrl(url)) + return webViewInterface(type, FileUrl(url)) } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt b/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt index 1f5778ad..38b9fc4c 100644 --- a/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt +++ b/app/src/main/java/ani/dantotsu/aniyomi/anime/custom/InjektModules.kt @@ -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() { diff --git a/app/src/main/java/ani/dantotsu/connections/UpdateProgress.kt b/app/src/main/java/ani/dantotsu/connections/UpdateProgress.kt index c930ab67..6917aade 100644 --- a/app/src/main/java/ani/dantotsu/connections/UpdateProgress.kt +++ b/app/src/main/java/ani/dantotsu/connections/UpdateProgress.kt @@ -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, diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt b/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt index 231a9483..77325d0b 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt @@ -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>? = 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( @@ -51,11 +56,11 @@ object Anilist { private val cal: Calendar = Calendar.getInstance() private val currentYear = cal.get(Calendar.YEAR) private val currentSeason: Int = when (cal.get(Calendar.MONTH)) { - 0, 1, 2 -> 0 - 3, 4, 5 -> 1 - 6, 7, 8 -> 2 + 0, 1, 2 -> 0 + 3, 4, 5 -> 1 + 6, 7, 8 -> 2 9, 10, 11 -> 3 - else -> 0 + else -> 0 } private fun getSeason(next: Boolean): Pair { @@ -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() diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt index da50162c..686381f1 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt @@ -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? = 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(query, variables, show = true) diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt index 21bcccc3..a0c1c270 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt @@ -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) - "vrv" -> media.vrvId = i.url?.split("/")?.getOrNull(4) + "youtube" -> media.anime.youtube = i.url + "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 { + suspend fun continueMedia(type: String, planned: Boolean = false): ArrayList { val returnArray = arrayListOf() val map = mutableMapOf() - 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(""" { 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 { + suspend fun getNextPage(page: Int): List { val response = executeQuery("""{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() - 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> { + suspend fun getMediaLists( + anime: Boolean, + userId: Int, + sortOrder: String? = null + ): MutableMap> { val response = executeQuery("""{ 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>() @@ -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,11 +414,18 @@ 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 }) } - "title" -> sorted[i]?.sortWith(compareBy { it.userPreferredName }) + "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 }) - "id" -> sorted[i]?.sortWith(compareBy { it.id }) + "release" -> sorted[i]?.sortWith(compareByDescending { it.startDate }) + "id" -> sorted[i]?.sortWith(compareBy { it.id }) } } return sorted @@ -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? { - 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, force = true)?.data?.page } - if(smaller) { + if (smaller) { val response = execute()?.airingSchedules ?: return null val idArr = mutableListOf() 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() - 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,19 +862,20 @@ Page(page:$page,perPage:50) { var page = 0 while (hasNextPage) { page++ - hasNextPage = executeQuery(query(page), force = true)?.data?.studio?.media?.let { - it.edges?.forEach { i -> - i.node?.apply { - val status = status.toString() - val year = startDate?.year?.toString() ?: "TBA" - val title = if (status != "CANCELLED") year else status - if (!yearMedia.containsKey(title)) - yearMedia[title] = arrayListOf() - yearMedia[title]?.add(Media(this)) + hasNextPage = + executeQuery(query(page), force = true)?.data?.studio?.media?.let { + it.edges?.forEach { i -> + i.node?.apply { + val status = status.toString() + val year = startDate?.year?.toString() ?: "TBA" + val title = if (status != "CANCELLED") year else status + if (!yearMedia.containsKey(title)) + yearMedia[title] = arrayListOf() + yearMedia[title]?.add(Media(this)) + } } - } - it.pageInfo?.hasNextPage == true - } ?: false + it.pageInfo?.hasNextPage == true + } ?: false } if (yearMedia.contains("CANCELLED")) { val a = yearMedia["CANCELLED"]!! @@ -896,7 +937,10 @@ Page(page:$page,perPage:50) { while (hasNextPage) { page++ - hasNextPage = executeQuery(query(page), force = true)?.data?.author?.staffMedia?.let { + hasNextPage = executeQuery( + query(page), + force = true + )?.data?.author?.staffMedia?.let { it.edges?.forEach { i -> i.node?.apply { val status = status.toString() diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt index 20ec880e..4cc8451f 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt @@ -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> = MutableLiveData>(arrayListOf()) + private val listImages: MutableLiveData> = + MutableLiveData>(arrayListOf()) + fun getListImages(): LiveData> = listImages suspend fun setListImages() = listImages.postValue(Anilist.query.getBannerImages()) - private val animeContinue: MutableLiveData> = MutableLiveData>(null) + private val animeContinue: MutableLiveData> = + MutableLiveData>(null) + fun getAnimeContinue(): LiveData> = animeContinue suspend fun setAnimeContinue() = animeContinue.postValue(Anilist.query.continueMedia("ANIME")) - private val animeFav: MutableLiveData> = MutableLiveData>(null) + private val animeFav: MutableLiveData> = + MutableLiveData>(null) + fun getAnimeFav(): LiveData> = animeFav suspend fun setAnimeFav() = animeFav.postValue(Anilist.query.favMedia(true)) - private val animePlanned: MutableLiveData> = MutableLiveData>(null) - fun getAnimePlanned(): LiveData> = animePlanned - suspend fun setAnimePlanned() = animePlanned.postValue(Anilist.query.continueMedia("ANIME", true)) + private val animePlanned: MutableLiveData> = + MutableLiveData>(null) + + fun getAnimePlanned(): LiveData> = animePlanned + suspend fun setAnimePlanned() = + animePlanned.postValue(Anilist.query.continueMedia("ANIME", true)) + + private val mangaContinue: MutableLiveData> = + MutableLiveData>(null) - private val mangaContinue: MutableLiveData> = MutableLiveData>(null) fun getMangaContinue(): LiveData> = mangaContinue suspend fun setMangaContinue() = mangaContinue.postValue(Anilist.query.continueMedia("MANGA")) - private val mangaFav: MutableLiveData> = MutableLiveData>(null) + private val mangaFav: MutableLiveData> = + MutableLiveData>(null) + fun getMangaFav(): LiveData> = mangaFav suspend fun setMangaFav() = mangaFav.postValue(Anilist.query.favMedia(false)) - private val mangaPlanned: MutableLiveData> = MutableLiveData>(null) - fun getMangaPlanned(): LiveData> = mangaPlanned - suspend fun setMangaPlanned() = mangaPlanned.postValue(Anilist.query.continueMedia("MANGA", true)) + private val mangaPlanned: MutableLiveData> = + MutableLiveData>(null) + + fun getMangaPlanned(): LiveData> = mangaPlanned + suspend fun setMangaPlanned() = + mangaPlanned.postValue(Anilist.query.continueMedia("MANGA", true)) + + private val recommendation: MutableLiveData> = + MutableLiveData>(null) - private val recommendation: MutableLiveData> = MutableLiveData>(null) fun getRecommendation(): LiveData> = 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> = MutableLiveData>(null) + private val trending: MutableLiveData> = + MutableLiveData>(null) + fun getTrending(): LiveData> = trending suspend fun loadTrending(i: Int) { val (season, year) = Anilist.currentSeasons[i] @@ -116,7 +136,9 @@ class AnilistAnimeViewModel : ViewModel() { ) } - private val updated: MutableLiveData> = MutableLiveData>(null) + private val updated: MutableLiveData> = + MutableLiveData>(null) + fun getUpdated(): LiveData> = 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> = MutableLiveData>(null) + private val trending: MutableLiveData> = + MutableLiveData>(null) + fun getTrending(): LiveData> = 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> = + MutableLiveData>(null) - private val updated: MutableLiveData> = MutableLiveData>(null) fun getTrendingNovel(): LiveData> = 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(null) fun getPopular(): LiveData = mangaPopular diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/Login.kt b/app/src/main/java/ani/dantotsu/connections/anilist/Login.kt index abeafbe6..fdba6d3a 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/Login.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/Login.kt @@ -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()) diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/SearchResults.kt b/app/src/main/java/ani/dantotsu/connections/anilist/SearchResults.kt index 86b146ca..32bc1757 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/SearchResults.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/SearchResults.kt @@ -27,7 +27,15 @@ data class SearchResults( val list = mutableListOf() 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,27 +50,37 @@ 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 } fun removeChip(chip: SearchChip) { when (chip.type) { - "SORT" -> sort = null - "FORMAT" -> format = null - "SEASON" -> season = null - "SEASON_YEAR" -> seasonYear = null - "GENRE" -> genres?.remove(chip.text) + "SORT" -> sort = null + "FORMAT" -> format = null + "SEASON" -> season = null + "SEASON_YEAR" -> seasonYear = null + "GENRE" -> genres?.remove(chip.text) "EXCLUDED_GENRE" -> excludedGenres?.remove(chip.text) - "TAG" -> tags?.remove(chip.text) - "EXCLUDED_TAG" -> excludedTags?.remove(chip.text) + "TAG" -> tags?.remove(chip.text) + "EXCLUDED_TAG" -> excludedTags?.remove(chip.text) } } diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt b/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt index addaebed..30be1706 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/UrlMedia.kt @@ -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) + ) } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt index 0d0b10e2..5b3a16ea 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Data.kt @@ -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") diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/FuzzyDate.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/FuzzyDate.kt index 0be30778..9de2c0c4 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/FuzzyDate.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/FuzzyDate.kt @@ -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 { @@ -54,8 +61,8 @@ data class FuzzyDate( // } override fun compareTo(other: FuzzyDate): Int = when { - year != other.year -> (year ?: 0) - (other.year ?: 0) + year != other.year -> (year ?: 0) - (other.year ?: 0) month != other.month -> (month ?: 0) - (other.month ?: 0) - else -> (day ?: 0) - (other.day ?: 0) + else -> (day ?: 0) - (other.day ?: 0) } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt index 311ef02c..491f4df4 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt @@ -116,7 +116,7 @@ data class Media( @SerialName("characters") var characters: CharacterConnection?, // The staff who produced the media - @SerialName("staffPreview") var staff: StaffConnection?, + @SerialName("staffPreview") var staff: StaffConnection?, // The companies who produced the media @SerialName("studios") var studios: StudioConnection?, @@ -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?, + @SerialName("customLists") var customLists: Map?, // 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? + @SerialName("tags") var tags: List? ) @Serializable diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Recommendations.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Recommendations.kt index c9e9f321..413a6095 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Recommendations.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Recommendations.kt @@ -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?, diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Staff.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Staff.kt index b9c6bc80..b4742e5b 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Staff.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Staff.kt @@ -9,7 +9,7 @@ data class Staff( @SerialName("id") var id: Int, // The names of the staff member - @SerialName("name") var name: StaffName?, + @SerialName("name") var name: StaffName?, // The primary language of the staff member. Current values: Japanese, English, Korean, Italian, Spanish, Portuguese, French, German, Hebrew, Hungarian, Chinese, Arabic, Filipino, Catalan, Finnish, Turkish, Dutch, Swedish, Thai, Tagalog, Malaysian, Indonesian, Vietnamese, Nepali, Hindi, Urdu @SerialName("languageV2") var languageV2: String?, @@ -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? ) \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt index 99a66eb8..dddef0d5 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/User.kt @@ -80,10 +80,10 @@ data class UserOptions( @SerialName("displayAdultContent") var displayAdultContent: Boolean?, // Whether the user receives notifications when a show they are watching aires - @SerialName("airingNotifications") var airingNotifications: Boolean?, + @SerialName("airingNotifications") var airingNotifications: Boolean?, // - // Profile highlight color (blue, purple, pink, orange, red, green, gray) - @SerialName("profileColor") var profileColor: String?, + // Profile highlight color (blue, purple, pink, orange, red, green, gray) + @SerialName("profileColor") var profileColor: String?, // // // Notification options // // @SerialName("notificationOptions") var notificationOptions: List?, diff --git a/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt b/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt index 6d5e3fb1..95450305 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt @@ -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 { diff --git a/app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt b/app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt index 1f24670d..26545c2f 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt @@ -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( - Presence.Response( - 3, - Presence(status = "offline") + 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( @@ -311,23 +324,24 @@ class DiscordService : Service() { context.getString(R.string.preference_file_key), Context.MODE_PRIVATE ) - val token = sharedPref.getString(Discord.TOKEN, null) - if(token == null) { + val token = sharedPref.getString(Discord.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) { + } } } diff --git a/app/src/main/java/ani/dantotsu/connections/discord/Login.kt b/app/src/main/java/ani/dantotsu/connections/discord/Login.kt index a636adec..98048844 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/Login.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/Login.kt @@ -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 diff --git a/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt b/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt index 6961282e..822218c5 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt @@ -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 = 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() return json?.id } + suspend fun createPresence(data: RPCData): String { val json = Json { encodeDefaults = true diff --git a/app/src/main/java/ani/dantotsu/connections/discord/serializers/Activity.kt b/app/src/main/java/ani/dantotsu/connections/discord/serializers/Activity.kt index 2debdfd7..2e6366ac 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/serializers/Activity.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/serializers/Activity.kt @@ -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, diff --git a/app/src/main/java/ani/dantotsu/connections/discord/serializers/Identity.kt b/app/src/main/java/ani/dantotsu/connections/discord/serializers/Identity.kt index 838ba214..1e31a6d8 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/serializers/Identity.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/serializers/Identity.kt @@ -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, diff --git a/app/src/main/java/ani/dantotsu/connections/discord/serializers/Presence.kt b/app/src/main/java/ani/dantotsu/connections/discord/serializers/Presence.kt index fa78a948..1d06a192 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/serializers/Presence.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/serializers/Presence.kt @@ -3,14 +3,14 @@ package ani.dantotsu.connections.discord.serializers import kotlinx.serialization.Serializable @Serializable -data class Presence ( +data class Presence( val activities: List = 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 ) diff --git a/app/src/main/java/ani/dantotsu/connections/discord/serializers/User.kt b/app/src/main/java/ani/dantotsu/connections/discord/serializers/User.kt index 9148de8d..6ce3a9ef 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/serializers/User.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/serializers/User.kt @@ -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" } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/connections/mal/Login.kt b/app/src/main/java/ani/dantotsu/connections/mal/Login.kt index f6249727..9ec2ca5a 100644 --- a/app/src/main/java/ani/dantotsu/connections/mal/Login.kt +++ b/app/src/main/java/ani/dantotsu/connections/mal/Login.kt @@ -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) } } diff --git a/app/src/main/java/ani/dantotsu/connections/mal/MAL.kt b/app/src/main/java/ani/dantotsu/connections/mal/MAL.kt index 26034cd2..0391cd47 100644 --- a/app/src/main/java/ani/dantotsu/connections/mal/MAL.kt +++ b/app/src/main/java/ani/dantotsu/connections/mal/MAL.kt @@ -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 } diff --git a/app/src/main/java/ani/dantotsu/connections/mal/MALQueries.kt b/app/src/main/java/ani/dantotsu/connections/mal/MALQueries.kt index 28662cf6..c35cdcb9 100644 --- a/app/src/main/java/ani/dantotsu/connections/mal/MALQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/mal/MALQueries.kt @@ -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", diff --git a/app/src/main/java/ani/dantotsu/download/DownloadContainerActivity.kt b/app/src/main/java/ani/dantotsu/download/DownloadContainerActivity.kt index 2fb9df4e..21f0a910 100644 --- a/app/src/main/java/ani/dantotsu/download/DownloadContainerActivity.kt +++ b/app/src/main/java/ani/dantotsu/download/DownloadContainerActivity.kt @@ -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") diff --git a/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt b/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt index 23b50034..89a07b0e 100644 --- a/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt +++ b/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt @@ -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") diff --git a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt index 99460325..aa53a3f7 100644 --- a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt +++ b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt @@ -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,33 +304,38 @@ class MangaDownloaderService : Service() { } - private suspend fun downloadImage(url: String, directory: File, name: String): String? = withContext(Dispatchers.IO) { - var connection: HttpURLConnection? = null - println("Downloading url $url") - try { - connection = URL(url).openConnection() as HttpURLConnection - connection.connect() - if (connection.responseCode != HttpURLConnection.HTTP_OK) { - throw Exception("Server returned HTTP ${connection.responseCode} ${connection.responseMessage}") - } - - val file = File(directory, name) - FileOutputStream(file).use { output -> - connection.inputStream.use { input -> - input.copyTo(output) + private suspend fun downloadImage(url: String, directory: File, name: String): String? = + withContext(Dispatchers.IO) { + var connection: HttpURLConnection? = null + println("Downloading url $url") + try { + connection = URL(url).openConnection() as HttpURLConnection + connection.connect() + if (connection.responseCode != HttpURLConnection.HTTP_OK) { + throw Exception("Server returned HTTP ${connection.responseCode} ${connection.responseMessage}") } + + val file = File(directory, name) + FileOutputStream(file).use { output -> + connection.inputStream.use { input -> + input.copyTo(output) + } + } + return@withContext file.absolutePath + } catch (e: Exception) { + e.printStackTrace() + withContext(Dispatchers.Main) { + Toast.makeText( + this@MangaDownloaderService, + "Exception while saving ${name}: ${e.message}", + Toast.LENGTH_LONG + ).show() + } + null + } finally { + connection?.disconnect() } - return@withContext file.absolutePath - } catch (e: Exception) { - e.printStackTrace() - withContext(Dispatchers.Main) { - Toast.makeText(this@MangaDownloaderService, "Exception while saving ${name}: ${e.message}", Toast.LENGTH_LONG).show() - } - null - } finally { - connection?.disconnect() } - } private fun broadcastDownloadStarted(chapterNumber: String) { val intent = Intent(ACTION_DOWNLOAD_STARTED).apply { @@ -385,6 +398,7 @@ object MangaServiceDataSingleton { var imageData: List = listOf() var sourceMedia: Media? = null var downloadQueue: Queue = ConcurrentLinkedQueue() + @Volatile var isServiceRunning: Boolean = false } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt index a9933dfa..7f304b11 100644 --- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt +++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt @@ -11,8 +11,13 @@ import androidx.cardview.widget.CardView import ani.dantotsu.R -class OfflineMangaAdapter(private val context: Context, private val items: List) : BaseAdapter() { - private val inflater: LayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater +class OfflineMangaAdapter( + private val context: Context, + private val items: List +) : BaseAdapter() { + private val inflater: LayoutInflater = + context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + override fun getCount(): Int { return items.size } diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt index 4167a94e..3b869ca0 100644 --- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt +++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt @@ -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() private var downloads: List = 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(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(R.id.offlineMangaUserAvatar) + val animeUserAvatar = view.findViewById(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() @@ -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) diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaModel.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaModel.kt index 30b97911..568081ee 100644 --- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaModel.kt +++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaModel.kt @@ -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?) { -} \ No newline at end of file +data class OfflineMangaModel( + val title: String, + val score: String, + val isOngoing: Boolean, + val isUserScored: Boolean, + val image: Uri? +) \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt index ef1a3c11..25158142 100644 --- a/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt +++ b/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt @@ -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,19 +72,29 @@ class NovelDownloaderService : Service() { override fun onCreate() { super.onCreate() notificationManager = NotificationManagerCompat.from(this) - builder = NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply { - setContentTitle("Novel Download Progress") - setSmallIcon(R.drawable.ic_round_download_24) - priority = NotificationCompat.PRIORITY_DEFAULT - setOnlyAlertOnce(true) - setProgress(0, 0, false) - } + builder = + NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply { + setContentTitle("Novel Download Progress") + setSmallIcon(R.drawable.ic_round_download_24) + priority = NotificationCompat.PRIORITY_DEFAULT + setOnlyAlertOnce(true) + 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,34 +348,40 @@ class NovelDownloaderService : Service() { } - private suspend fun downloadImage(url: String, directory: File, name: String): String? = withContext( - Dispatchers.IO) { - var connection: HttpURLConnection? = null - println("Downloading url $url") - try { - connection = URL(url).openConnection() as HttpURLConnection - connection.connect() - if (connection.responseCode != HttpURLConnection.HTTP_OK) { - throw Exception("Server returned HTTP ${connection.responseCode} ${connection.responseMessage}") - } - - val file = File(directory, name) - FileOutputStream(file).use { output -> - connection.inputStream.use { input -> - input.copyTo(output) + private suspend fun downloadImage(url: String, directory: File, name: String): String? = + withContext( + Dispatchers.IO + ) { + var connection: HttpURLConnection? = null + println("Downloading url $url") + try { + connection = URL(url).openConnection() as HttpURLConnection + connection.connect() + if (connection.responseCode != HttpURLConnection.HTTP_OK) { + throw Exception("Server returned HTTP ${connection.responseCode} ${connection.responseMessage}") } + + val file = File(directory, name) + FileOutputStream(file).use { output -> + connection.inputStream.use { input -> + input.copyTo(output) + } + } + return@withContext file.absolutePath + } catch (e: Exception) { + e.printStackTrace() + withContext(Dispatchers.Main) { + Toast.makeText( + this@NovelDownloaderService, + "Exception while saving ${name}: ${e.message}", + Toast.LENGTH_LONG + ).show() + } + null + } finally { + connection?.disconnect() } - return@withContext file.absolutePath - } catch (e: Exception) { - e.printStackTrace() - withContext(Dispatchers.Main) { - Toast.makeText(this@NovelDownloaderService, "Exception while saving ${name}: ${e.message}", Toast.LENGTH_LONG).show() - } - null - } finally { - connection?.disconnect() } - } private fun broadcastDownloadStarted(link: String) { val intent = Intent(NovelReadFragment.ACTION_DOWNLOAD_STARTED).apply { @@ -429,6 +444,7 @@ class NovelDownloaderService : Service() { object NovelServiceDataSingleton { var sourceMedia: Media? = null var downloadQueue: Queue = ConcurrentLinkedQueue() + @Volatile var isServiceRunning: Boolean = false } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/download/video/Helper.kt b/app/src/main/java/ani/dantotsu/download/video/Helper.kt index 15cbda59..2dcf31c6 100644 --- a/app/src/main/java/ani/dantotsu/download/video/Helper.kt +++ b/app/src/main/java/ani/dantotsu/download/video/Helper.kt @@ -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) } @@ -52,7 +53,7 @@ object Helper { val mimeType = when (video.format) { VideoType.M3U8 -> MimeTypes.APPLICATION_M3U8 VideoType.DASH -> MimeTypes.APPLICATION_MPD - else -> MimeTypes.APPLICATION_MP4 + else -> MimeTypes.APPLICATION_MP4 } val builder = MediaItem.Builder().setUri(video.file.url).setMimeType(mimeType) @@ -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() 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 } } diff --git a/app/src/main/java/ani/dantotsu/download/video/MyDownloadService.kt b/app/src/main/java/ani/dantotsu/download/video/MyDownloadService.kt index 18daad3a..3a26ac1d 100644 --- a/app/src/main/java/ani/dantotsu/download/video/MyDownloadService.kt +++ b/app/src/main/java/ani/dantotsu/download/video/MyDownloadService.kt @@ -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, notMetRequirements: Int): Notification = + override fun getForegroundNotification( + downloads: MutableList, + notMetRequirements: Int + ): Notification = DownloadNotificationHelper(this, "download_service").buildProgressNotification( this, R.drawable.mono, diff --git a/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt b/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt index 3e80d341..1a08934d 100644 --- a/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt +++ b/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt @@ -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() } } diff --git a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt index 8cda9988..63ee9b84 100644 --- a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt +++ b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt @@ -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(R.id.animeUserAvatarContainer) + val materialCardView = + holder.itemView.findViewById(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 onIncludeListClick.invoke(isChecked) } @@ -133,9 +140,9 @@ class AnimePageAdapter : RecyclerView.AdapterUnit) - 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 { @@ -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("ui_settings") ?: UserInterfaceSettings() + uiSettings = + loadData("ui_settings") ?: UserInterfaceSettings() withContext(Dispatchers.IO) { //Get userData First getUserId(requireContext()) { diff --git a/app/src/main/java/ani/dantotsu/home/LoginFragment.kt b/app/src/main/java/ani/dantotsu/home/LoginFragment.kt index 40b97cc1..d3144237 100644 --- a/app/src/main/java/ani/dantotsu/home/LoginFragment.kt +++ b/app/src/main/java/ani/dantotsu/home/LoginFragment.kt @@ -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 } diff --git a/app/src/main/java/ani/dantotsu/home/MangaFragment.kt b/app/src/main/java/ani/dantotsu/home/MangaFragment.kt index a9d01448..14870b7f 100644 --- a/app/src/main/java/ani/dantotsu/home/MangaFragment.kt +++ b/app/src/main/java/ani/dantotsu/home/MangaFragment.kt @@ -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() } } diff --git a/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt b/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt index 5103c72b..7053df8a 100644 --- a/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt +++ b/app/src/main/java/ani/dantotsu/home/MangaPageAdapter.kt @@ -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(R.id.mangaUserAvatarContainer) + val materialCardView = + holder.itemView.findViewById(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 onIncludeListClick.invoke(isChecked) } @@ -126,7 +134,7 @@ class MangaPageAdapter : RecyclerView.AdapterUnit) + lateinit var onIncludeListClick: ((Boolean) -> Unit) override fun getItemCount(): Int = 1 @@ -142,7 +150,8 @@ class MangaPageAdapter : RecyclerView.Adapter ) : RecyclerView.Adapter() { 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("ui_settings") ?: UserInterfaceSettings() + private val uiSettings = + loadData("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() ) } diff --git a/app/src/main/java/ani/dantotsu/media/CharacterDetailsActivity.kt b/app/src/main/java/ani/dantotsu/media/CharacterDetailsActivity.kt index 36439bb3..3ccba6e1 100644 --- a/app/src/main/java/ani/dantotsu/media/CharacterDetailsActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/CharacterDetailsActivity.kt @@ -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 { 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,14 +88,15 @@ 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) gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { override fun getSpanSize(position: Int): Int { return when (position) { - 0 -> gridSize + 0 -> gridSize else -> 1 } } @@ -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) } } diff --git a/app/src/main/java/ani/dantotsu/media/CharacterDetailsAdapter.kt b/app/src/main/java/ani/dantotsu/media/CharacterDetailsAdapter.kt index 6734765d..15781d8b 100644 --- a/app/src/main/java/ani/dantotsu/media/CharacterDetailsAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/CharacterDetailsAdapter.kt @@ -15,7 +15,8 @@ import io.noties.markwon.SoftBreakAddsNewLinePlugin class CharacterDetailsAdapter(private val character: Character, private val activity: Activity) : RecyclerView.Adapter() { 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) } @@ -23,20 +24,22 @@ class CharacterDetailsAdapter(private val character: Character, private val acti override fun onBindViewHolder(holder: GenreViewHolder, position: Int) { val binding = holder.binding val desc = - (if (character.age != "null") currActivity()!!.getString(R.string.age) + " " + character.age else "") + + (if (character.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) } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/media/GenreActivity.kt b/app/src/main/java/ani/dantotsu/media/GenreActivity.kt index ef193136..e5960502 100644 --- a/app/src/main/java/ani/dantotsu/media/GenreActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/GenreActivity.kt @@ -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()) { diff --git a/app/src/main/java/ani/dantotsu/media/GenreAdapter.kt b/app/src/main/java/ani/dantotsu/media/GenreAdapter.kt index dfb8fcab..331b230c 100644 --- a/app/src/main/java/ani/dantotsu/media/GenreAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/GenreAdapter.kt @@ -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( @@ -48,15 +49,15 @@ class GenreAdapter( .putExtra("sortBy", Anilist.sortBy[2]) .putExtra("search", true) .also { - if (pos[bindingAdapterPosition].lowercase() == "hentai") { - if (!Anilist.adult) Toast.makeText( - itemView.context, - currActivity()?.getString(R.string.content_18), - Toast.LENGTH_SHORT - ).show() - it.putExtra("hentai", true) - } - }, + if (pos[bindingAdapterPosition].lowercase() == "hentai") { + if (!Anilist.adult) Toast.makeText( + itemView.context, + currActivity()?.getString(R.string.content_18), + Toast.LENGTH_SHORT + ).show() + it.putExtra("hentai", true) + } + }, null ) } diff --git a/app/src/main/java/ani/dantotsu/media/Media.kt b/app/src/main/java/ani/dantotsu/media/Media.kt index 4dbc9459..3360a94a 100644 --- a/app/src/main/java/ani/dantotsu/media/Media.kt +++ b/app/src/main/java/ani/dantotsu/media/Media.kt @@ -41,7 +41,7 @@ data class Media( var userUpdatedAt: Long? = null, var userStartedAt: FuzzyDate = FuzzyDate(), var userCompletedAt: FuzzyDate = FuzzyDate(), - var inCustomListsOf: MutableMap?= null, + var inCustomListsOf: MutableMap? = 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 { diff --git a/app/src/main/java/ani/dantotsu/media/MediaAdaptor.kt b/app/src/main/java/ani/dantotsu/media/MediaAdaptor.kt index b2a75655..509b40a7 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaAdaptor.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaAdaptor.kt @@ -41,20 +41,43 @@ class MediaAdaptor( private val viewPager: ViewPager2? = null, ) : RecyclerView.Adapter() { - private val uiSettings = loadData("ui_settings") ?: UserInterfaceSettings() + private val uiSettings = + loadData("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)) - 3 -> MediaPageSmallViewHolder( + 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), parent, 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) - else currActivity()!!.getString(R.string.episode_singular) + 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) diff --git a/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt b/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt index fbea5dbb..a902a1cf 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt @@ -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 diff --git a/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt b/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt index 5235e6b5..e6aec94a 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaDetailsViewModel.kt @@ -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() val data = loadData("${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> = MutableLiveData>(null) + private val kitsuEpisodes: MutableLiveData> = + MutableLiveData>(null) + fun getKitsuEpisodes(): LiveData> = kitsuEpisodes suspend fun loadKitsuEpisodes(s: Media) { tryWithSuspend { @@ -114,7 +106,9 @@ class MediaDetailsViewModel : ViewModel() { } } - private val fillerEpisodes: MutableLiveData> = MutableLiveData>(null) + private val fillerEpisodes: MutableLiveData> = + MutableLiveData>(null) + fun getFillerEpisodes(): LiveData> = 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?>() private val timeStampsMap: MutableMap?> = 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>>(null) + private val mangaChapters = + MutableLiveData>>(null) private val mangaLoaded = mutableMapOf>() - fun getMangaChapters(): LiveData>> = mangaChapters + fun getMangaChapters(): LiveData>> = + 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(null) fun getMangaChapter(): LiveData = 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 = 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 + ) } } diff --git a/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt b/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt index 75d452b3..a7b78f8e 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt @@ -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 { 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, _, _, _, _ -> diff --git a/app/src/main/java/ani/dantotsu/media/MediaListDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/MediaListDialogFragment.kt index da7d08fd..b1576e40 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaListDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaListDialogFragment.kt @@ -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,9 +50,13 @@ class MediaListDialogFragment : BottomSheetDialogFragment() { binding.mediaListLayout.visibility = View.VISIBLE val statuses: Array = 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( ArrayAdapter( @@ -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)) diff --git a/app/src/main/java/ani/dantotsu/media/MediaListDialogSmallFragment.kt b/app/src/main/java/ani/dantotsu/media/MediaListDialogSmallFragment.kt index 55692759..cb7390ce 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaListDialogSmallFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaListDialogSmallFragment.kt @@ -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 = 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() diff --git a/app/src/main/java/ani/dantotsu/media/OtherDetailsViewModel.kt b/app/src/main/java/ani/dantotsu/media/OtherDetailsViewModel.kt index 61c1c5c6..677d700f 100644 --- a/app/src/main/java/ani/dantotsu/media/OtherDetailsViewModel.kt +++ b/app/src/main/java/ani/dantotsu/media/OtherDetailsViewModel.kt @@ -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 = 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 = MutableLiveData(null) fun getAuthor(): LiveData = author suspend fun loadAuthor(m: Author) { if (author.value == null) author.postValue(Anilist.query.getAuthorDetails(m)) } - private val calendar: MutableLiveData>> = MutableLiveData(null) - fun getCalendar(): LiveData>> = calendar + + private val calendar: MutableLiveData>> = MutableLiveData(null) + fun getCalendar(): LiveData>> = 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>() - val idMap = mutableMapOf>() + val map = mutableMapOf>() + val idMap = mutableMapOf>() 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) } diff --git a/app/src/main/java/ani/dantotsu/media/ProgressAdapter.kt b/app/src/main/java/ani/dantotsu/media/ProgressAdapter.kt index 1558da64..081d47fc 100644 --- a/app/src/main/java/ani/dantotsu/media/ProgressAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/ProgressAdapter.kt @@ -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 } } diff --git a/app/src/main/java/ani/dantotsu/media/SearchActivity.kt b/app/src/main/java/ani/dantotsu/media/SearchActivity.kt index 1394f14a..b3cf36f5 100644 --- a/app/src/main/java/ani/dantotsu/media/SearchActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/SearchActivity.kt @@ -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) @@ -83,10 +83,10 @@ ThemeManager(this).applyTheme() gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { override fun getSpanSize(position: Int): Int { return when (position) { - 0 -> gridSize + 0 -> gridSize concatAdapter.itemCount - 1 -> gridSize - else -> when (style) { - 0 -> 1 + else -> when (style) { + 0 -> 1 else -> gridSize } } @@ -149,7 +149,7 @@ ThemeManager(this).applyTheme() } else headerAdaptor.requestFocus?.run() - if(intent.getBooleanExtra("search",false)) search() + if (intent.getBooleanExtra("search", false)) search() } } } diff --git a/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt b/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt index cd47c907..67c523e6 100644 --- a/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt @@ -20,14 +20,16 @@ import ani.dantotsu.saveData import com.google.android.material.checkbox.MaterialCheckBox.* -class SearchAdapter(private val activity: SearchActivity) : RecyclerView.Adapter() { +class SearchAdapter(private val activity: SearchActivity) : + RecyclerView.Adapter() { 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,7 +102,8 @@ class SearchAdapter(private val activity: SearchActivity) : RecyclerView.Adapter imm.hideSoftInputFromWindow(binding.searchBarText.windowToken, 0) true } - else -> false + + else -> false } } binding.searchBar.setEndIconOnClickListener { searchTitle() } @@ -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 @@ -135,10 +142,10 @@ class SearchAdapter(private val activity: SearchActivity) : RecyclerView.Adapter addOnCheckedStateChangedListener { _, state -> listOnly = when (state) { - STATE_CHECKED -> true + STATE_CHECKED -> true STATE_INDETERMINATE -> false - STATE_UNCHECKED -> null - else -> null + STATE_UNCHECKED -> null + else -> null } } @@ -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() { + class SearchChipAdapter(val activity: SearchActivity) : + RecyclerView.Adapter() { 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) } diff --git a/app/src/main/java/ani/dantotsu/media/SearchFilterBottomDialog.kt b/app/src/main/java/ani/dantotsu/media/SearchFilterBottomDialog.kt index 14bc386c..79fd8712 100644 --- a/app/src/main/java/ani/dantotsu/media/SearchFilterBottomDialog.kt +++ b/app/src/main/java/ani/dantotsu/media/SearchFilterBottomDialog.kt @@ -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 } @@ -99,7 +103,7 @@ class SearchFilterBottomDialog() : BottomSheetDialogFragment() { ArrayAdapter( binding.root.context, R.layout.item_dropdown, - (1970 until 2025).map { it.toString() }.reversed().toTypedArray() + (1970 until 2025).map { it.toString() }.reversed().toTypedArray() ) ) } @@ -129,24 +133,25 @@ class SearchFilterBottomDialog() : BottomSheetDialogFragment() { } binding.searchGenresGrid.isChecked = false - 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) - chip.setOnCheckedChangeListener { _, isChecked -> - if (isChecked) { - chip.isCloseIconVisible = false - exTags.remove(tag) - selectedTags.add(tag) - } else - selectedTags.remove(tag) + 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) + chip.setOnCheckedChangeListener { _, isChecked -> + if (isChecked) { + chip.isCloseIconVisible = false + exTags.remove(tag) + selectedTags.add(tag) + } else + selectedTags.remove(tag) + } + chip.setOnLongClickListener { + chip.isChecked = false + chip.isCloseIconVisible = true + exTags.add(tag) + } } - chip.setOnLongClickListener { - chip.isChecked = false - chip.isCloseIconVisible = true - exTags.add(tag) - } - } binding.searchTagsGrid.setOnCheckedChangeListener { _, isChecked -> binding.searchFilterTags.layoutManager = if (!isChecked) LinearLayoutManager(binding.root.context, HORIZONTAL, false) @@ -158,10 +163,12 @@ class SearchFilterBottomDialog() : BottomSheetDialogFragment() { class FilterChipAdapter(val list: List, private val perform: ((Chip) -> Unit)) : RecyclerView.Adapter() { - 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) } diff --git a/app/src/main/java/ani/dantotsu/media/SourceAdapter.kt b/app/src/main/java/ani/dantotsu/media/SourceAdapter.kt index 0b487ff0..c1dd18b2 100644 --- a/app/src/main/java/ani/dantotsu/media/SourceAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/SourceAdapter.kt @@ -17,7 +17,8 @@ abstract class SourceAdapter( private val scope: CoroutineScope ) : RecyclerView.Adapter() { 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() diff --git a/app/src/main/java/ani/dantotsu/media/SourceSearchDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/SourceSearchDialogFragment.kt index 46d8454a..3925bcce 100644 --- a/app/src/main/java/ani/dantotsu/media/SourceSearchDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/SourceSearchDialogFragment.kt @@ -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 { 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,7 +92,8 @@ class SourceSearchDialogFragment : BottomSheetDialogFragment() { search() true } - else -> false + + else -> false } } binding.searchBar.setEndIconOnClickListener { search() } @@ -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 + ) ) } } diff --git a/app/src/main/java/ani/dantotsu/media/StudioActivity.kt b/app/src/main/java/ani/dantotsu/media/StudioActivity.kt index 6839e117..53e04a37 100644 --- a/app/src/main/java/ani/dantotsu/media/StudioActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/StudioActivity.kt @@ -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) diff --git a/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt b/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt index 25059937..17759141 100644 --- a/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt +++ b/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt @@ -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 { diff --git a/app/src/main/java/ani/dantotsu/media/TitleAdapter.kt b/app/src/main/java/ani/dantotsu/media/TitleAdapter.kt index 45e8b1fa..03416ec5 100644 --- a/app/src/main/java/ani/dantotsu/media/TitleAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/TitleAdapter.kt @@ -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() { - inner class TitleViewHolder(val binding: ItemTitleBinding) : RecyclerView.ViewHolder(binding.root) +class TitleAdapter(private val text: String) : + RecyclerView.Adapter() { + 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) diff --git a/app/src/main/java/ani/dantotsu/media/anime/Anime.kt b/app/src/main/java/ani/dantotsu/media/anime/Anime.kt index 9a014c90..bee71d03 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/Anime.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/Anime.kt @@ -15,7 +15,7 @@ data class Anime( var ed: ArrayList = arrayListOf(), var mainStudio: Studio? = null, - var author: Author?=null, + var author: Author? = null, var youtube: String? = null, var nextAiringEpisode: Int? = null, diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt index a4573ff0..e01548da 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchAdapter.kt @@ -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) @@ -146,7 +149,7 @@ class AnimeWatchAdapter( //Subscription - subscribe = MediaDetailsActivity.PopImageButton( + subscribe = MediaDetailsActivity.PopImageButton( fragment.lifecycleScope, binding.animeSourceSubscribe, R.drawable.ic_round_notifications_active_24, @@ -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) diff --git a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt index 0dc1fda7..199f4ffd 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/AnimeWatchFragment.kt @@ -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) @@ -106,11 +99,11 @@ class AnimeWatchFragment : Fragment() { val style = episodeAdapter.getItemViewType(position) return when (position) { - 0 -> maxGridSize + 0 -> maxGridSize else -> when (style) { - 0 -> maxGridSize - 1 -> 2 - 2 -> 1 + 0 -> maxGridSize + 1 -> 2 + 2 -> 1 else -> 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] } } } @@ -187,7 +188,7 @@ class AnimeWatchFragment : Fragment() { val limit = when { (divisions < 25) -> 25 (divisions < 50) -> 50 - else -> 100 + else -> 100 } headerAdapter.clearChips() if (total > limit) { @@ -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(R.id.mediaViewPager).visibility = visibility activity.findViewById(R.id.mediaCover).visibility = visibility activity.findViewById(R.id.mediaClose).visibility = visibility - try{ + try { activity.findViewById(R.id.mediaTab).visibility = visibility - }catch (e: ClassCastException){ + } catch (e: ClassCastException) { activity.findViewById(R.id.mediaTab).visibility = visibility } activity.findViewById(R.id.fragmentExtensionsContainer).visibility = @@ -363,46 +370,46 @@ class AnimeWatchFragment : Fragment() { } } - fun onEpisodeClick(i: String) { - model.continueMedia = false - model.saveSelected(media.id, media.selected!!, requireActivity()) - model.onEpisodeClick(media, i, requireActivity().supportFragmentManager) + fun onEpisodeClick(i: String) { + model.continueMedia = false + model.saveSelected(media.id, media.selected!!, requireActivity()) + model.onEpisodeClick(media, i, requireActivity().supportFragmentManager) + } + + @SuppressLint("NotifyDataSetChanged") + private fun reload() { + val selected = model.loadSelected(media) + + //Find latest episode for subscription + selected.latest = + media.anime?.episodes?.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.handleEpisodes() + episodeAdapter.notifyItemRangeRemoved(0, episodeAdapter.arr.size) + var arr: ArrayList = arrayListOf() + if (media.anime!!.episodes != null) { + val end = if (end != null && end!! < media.anime!!.episodes!!.size) end else null + arr.addAll( + media.anime!!.episodes!!.values.toList() + .slice(start..(end ?: (media.anime!!.episodes!!.size - 1))) + ) + if (reverse) + arr = (arr.reversed() as? ArrayList) ?: arr } + episodeAdapter.arr = arr + episodeAdapter.updateType(style ?: uiSettings.animeDefaultView) + episodeAdapter.notifyItemRangeInserted(0, arr.size) + } - @SuppressLint("NotifyDataSetChanged") - private fun reload() { - val selected = model.loadSelected(media) + override fun onDestroy() { + model.watchSources?.flushText() + super.onDestroy() + } - //Find latest episode for subscription - selected.latest = - media.anime?.episodes?.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.handleEpisodes() - episodeAdapter.notifyItemRangeRemoved(0, episodeAdapter.arr.size) - var arr: ArrayList = arrayListOf() - if (media.anime!!.episodes != null) { - val end = if (end != null && end!! < media.anime!!.episodes!!.size) end else null - arr.addAll( - media.anime!!.episodes!!.values.toList() - .slice(start..(end ?: (media.anime!!.episodes!!.size - 1))) - ) - if (reverse) - arr = (arr.reversed() as? ArrayList) ?: arr - } - episodeAdapter.arr = arr - episodeAdapter.updateType(style ?: uiSettings.animeDefaultView) - episodeAdapter.notifyItemRangeInserted(0, arr.size) - } - - override fun onDestroy() { - model.watchSources?.flushText() - super.onDestroy() - } - - var state: Parcelable? = null + var state: Parcelable? = null override fun onResume() { super.onResume() binding.mediaInfoProgressBar.visibility = progress diff --git a/app/src/main/java/ani/dantotsu/media/anime/Episode.kt b/app/src/main/java/ani/dantotsu/media/anime/Episode.kt index a1112338..14615246 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/Episode.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/Episode.kt @@ -14,12 +14,12 @@ data class Episode( var selectedExtractor: String? = null, var selectedVideo: Int = 0, var selectedSubtitle: Int? = -1, - var extractors: MutableList?=null, - @Transient var extractorCallback: ((VideoExtractor) -> Unit)?=null, + var extractors: MutableList? = null, + @Transient var extractorCallback: ((VideoExtractor) -> Unit)? = null, var allStreams: Boolean = false, var watched: Long? = null, var maxLength: Long? = null, - val extra: Map?=null, + val extra: Map? = null, val sEpisode: eu.kanade.tachiyomi.animesource.model.SEpisode? = null ) : Serializable diff --git a/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt b/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt index 17924c80..1a85b9bf 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/EpisodeAdapters.kt @@ -41,15 +41,30 @@ 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)) - 2 -> EpisodeCompactViewHolder( + 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), parent, 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 -> { + 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) { @@ -110,12 +132,14 @@ class EpisodeAdapter( ) } - is EpisodeGridViewHolder -> { + is EpisodeGridViewHolder -> { 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) diff --git a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt index 3033f1dd..68a71cb2 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt @@ -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( diff --git a/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt index 23ac9f04..d42bcd94 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt @@ -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 @@ -71,7 +73,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() { media = m if (media != null && !loaded) { loaded = true - val ep = media?.anime?.episodes?.get(media?.anime?.selectedEpisode) + val ep = media?.anime?.episodes?.get(media?.anime?.selectedEpisode) episode = ep if (ep != null) { @@ -92,14 +94,17 @@ 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() } - + if (ep.extractors.isNullOrEmpty()) { model.getEpisode().observe(this) { if (it != null) { @@ -116,8 +121,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() { }) fail() } } else load() - } - else { + } else { binding.selectorRecyclerView.updateLayoutParams { 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,54 +200,72 @@ class SelectorDialogFragment : BottomSheetDialogFragment() { } } - private inner class ExtractorAdapter : RecyclerView.Adapter() { + private inner class ExtractorAdapter : + RecyclerView.Adapter() { val links = mutableListOf() 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] holder.binding.streamName.text = extractor.server.name - holder.binding.streamRecyclerView.layoutManager = LinearLayoutManager(requireContext()) - holder.binding.streamRecyclerView.adapter = VideoAdapter(extractor) + holder.binding.streamRecyclerView.layoutManager = LinearLayoutManager(requireContext()) + holder.binding.streamRecyclerView.adapter = VideoAdapter(extractor) } 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?) { - 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() { + private inner class VideoAdapter(private val extractor: VideoExtractor) : + RecyclerView.Adapter() { 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(), @@ -245,10 +277,11 @@ class SelectorDialogFragment : BottomSheetDialogFragment() { if (video.format == VideoType.CONTAINER) { 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 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 { binding.urlQuality.text = "Multi Quality" if ((loadData("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) diff --git a/app/src/main/java/ani/dantotsu/media/anime/SubtitleDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/anime/SubtitleDialogFragment.kt index 9314eb6e..406efb3c 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/SubtitleDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/SubtitleDialogFragment.kt @@ -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) : RecyclerView.Adapter() { - inner class StreamViewHolder(val binding: ItemSubtitleTextBinding) : RecyclerView.ViewHolder(binding.root) + inner class SubtitleAdapter(val subtitles: List) : + RecyclerView.Adapter() { + 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) } diff --git a/app/src/main/java/ani/dantotsu/media/anime/VideoCache.kt b/app/src/main/java/ani/dantotsu/media/anime/VideoCache.kt index 6c463a5f..340966fa 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/VideoCache.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/VideoCache.kt @@ -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 ) diff --git a/app/src/main/java/ani/dantotsu/media/manga/Manga.kt b/app/src/main/java/ani/dantotsu/media/manga/Manga.kt index 3179a1ec..24275add 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/Manga.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/Manga.kt @@ -8,5 +8,5 @@ data class Manga( var selectedChapter: String? = null, var chapters: MutableMap? = null, var slug: String? = null, - var author: Author?=null, + var author: Author? = null, ) : Serializable \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaCache.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaCache.kt index 16fb83b9..50eec776 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaCache.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaCache.kt @@ -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(maxMemory) diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaChapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaChapter.kt index 0bfc3237..6af71788 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaChapter.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaChapter.kt @@ -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() fun images(): List = images diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt index b14a81f1..c4752a04 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt @@ -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,9 +153,10 @@ class MangaChapterAdapter( // Add chapter number to active coroutines set activeCoroutines.add(chapterNumber) while (activeDownloads.contains(chapterNumber)) { - binding.itemDownload.animate().rotationBy(360f).setDuration(1000).setInterpolator( - LinearInterpolator() - ).start() + binding.itemDownload.animate().rotationBy(360f).setDuration(1000) + .setInterpolator( + LinearInterpolator() + ).start() delay(1000) } // Remove chapter number from active coroutines set @@ -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) diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt index 8ef56d92..265122d6 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt @@ -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(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) @@ -178,8 +186,8 @@ class MangaReadAdapter( } var selected = when (style) { - 0 -> binding.animeSourceList - 1 -> binding.animeSourceCompact + 0 -> binding.animeSourceList + 1 -> binding.animeSourceCompact else -> binding.animeSourceList } selected.alpha = 1f @@ -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 { diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt index 4a62648e..d59b84ba 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt @@ -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,10 +61,8 @@ 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 { +open class MangaReadFragment : Fragment(), ScanlatorSelectionListener { private var _binding: FragmentAnimeWatchBinding? = null private val binding get() = _binding!! private val model: MediaDetailsViewModel by activityViewModels() @@ -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 @@ -120,10 +126,10 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener { val style = chapterAdapter.getItemViewType(position) return when (position) { - 0 -> maxGridSize + 0 -> maxGridSize else -> when (style) { - 0 -> maxGridSize - 1 -> 1 + 0 -> maxGridSize + 1 -> 1 else -> maxGridSize } } @@ -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 ?: "") } } } @@ -207,7 +217,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener { val limit = when { (divisions < 25) -> 25 (divisions < 50) -> 50 - else -> 100 + else -> 100 } headerAdapter.clearChips() if (total > limit) { @@ -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(R.id.mediaViewPager).visibility = visibility activity.findViewById(R.id.mediaCover).visibility = visibility activity.findViewById(R.id.mediaClose).visibility = visibility - try{ + try { activity.findViewById(R.id.mediaTab).visibility = visibility - }catch (e: ClassCastException){ + } catch (e: ClassCastException) { activity.findViewById(R.id.mediaTab).visibility = visibility } activity.findViewById(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) ?: arr diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt index bae545bb..6a18b9e8 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt @@ -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): Bitmap? { //still used in some places + suspend fun Context.loadBitmap_old( + link: FileUrl, + transforms: List + ): 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): Bitmap? { + suspend fun Context.loadBitmap( + link: FileUrl, + transforms: List + ): Bitmap? { return tryWithSuspend { val mangaCache = uy.kohesive.injekt.Injekt.get() 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) diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ChapterLoaderDialog.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ChapterLoaderDialog.kt index 3cf0c0e1..8d31e9d1 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ChapterLoaderDialog.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ChapterLoaderDialog.kt @@ -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 diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt index 81ec8548..97651499 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt @@ -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() 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(R.id.imgProgImageNoGestures) ?: return false + val imageView = parent.findViewById(R.id.imgProgImageNoGestures) + ?: return false val progress = parent.findViewById(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 diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt index 271da755..dc0f1de8 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt @@ -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 @@ -262,16 +258,16 @@ class MangaReaderActivity : AppCompatActivity() { if (showProgressDialog && Anilist.userid != null && if (media.isAdult) settings.updateForH else true) AlertDialog.Builder(this, R.style.MyPopup) .setTitle(getString(R.string.title_update_progress)).apply { - setMultiChoiceItems( - arrayOf(getString(R.string.dont_ask_again, media.userPreferredName)), - booleanArrayOf(false) - ) { _, _, isChecked -> - if (isChecked) progressDialog = null - saveData("${media.id}_progressDialog", isChecked) - showProgressDialog = isChecked + setMultiChoiceItems( + arrayOf(getString(R.string.dont_ask_again, media.userPreferredName)), + booleanArrayOf(false) + ) { _, _, isChecked -> + if (isChecked) progressDialog = null + saveData("${media.id}_progressDialog", isChecked) + showProgressDialog = isChecked + } + setOnCancelListener { hideBars() } } - setOnCancelListener { hideBars() } - } else null //Chapter Change diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/PreloadLinearLayoutManager.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/PreloadLinearLayoutManager.kt index 3a9792a3..7609149f 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/PreloadLinearLayoutManager.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/PreloadLinearLayoutManager.kt @@ -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)) diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ReaderSettingsDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ReaderSettingsDialogFragment.kt index ec27ecae..fc399e19 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ReaderSettingsDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ReaderSettingsDialogFragment.kt @@ -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() } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/RemoveBordersTransformation.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/RemoveBordersTransformation.kt index e90c80bf..223f2760 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/RemoveBordersTransformation.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/RemoveBordersTransformation.kt @@ -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 } } diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/Swipy.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/Swipy.kt index bab71149..be481c6c 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/Swipy.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/Swipy.kt @@ -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 @@ -100,7 +100,7 @@ class Swipy @JvmOverloads constructor( } when (action) { - MotionEvent.ACTION_DOWN -> { + MotionEvent.ACTION_DOWN -> { activePointerId = ev.getPointerId(0) isBeingDragged = false pointerIndex = ev.findPointerIndex(activePointerId) @@ -109,7 +109,8 @@ class Swipy @JvmOverloads constructor( } initialDown = if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex) } - MotionEvent.ACTION_MOVE -> { + + MotionEvent.ACTION_MOVE -> { if (activePointerId == INVALID_POINTER) { //("Got ACTION_MOVE event but don't have an active pointer id.") return false @@ -121,7 +122,8 @@ 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_POINTER_UP -> onSecondaryPointerUp(ev) MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { isBeingDragged = false activePointerId = INVALID_POINTER @@ -138,11 +140,12 @@ class Swipy @JvmOverloads constructor( return false } when (action) { - MotionEvent.ACTION_DOWN -> { + MotionEvent.ACTION_DOWN -> { activePointerId = ev.getPointerId(0) isBeingDragged = false } - MotionEvent.ACTION_MOVE -> { + + MotionEvent.ACTION_MOVE -> { pointerIndex = ev.findPointerIndex(activePointerId) if (pointerIndex < 0) { //("Got ACTION_MOVE event but have an invalid active pointer id.") @@ -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,8 +192,9 @@ class Swipy @JvmOverloads constructor( } activePointerId = ev.getPointerId(pointerIndex) } - MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev) - MotionEvent.ACTION_UP -> { + + MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev) + MotionEvent.ACTION_UP -> { if (vertical) { topBeingSwiped.invoke(0f) bottomBeingSwiped.invoke(0f) @@ -216,7 +221,8 @@ class Swipy @JvmOverloads constructor( activePointerId = INVALID_POINTER return false } - MotionEvent.ACTION_CANCEL -> return false + + MotionEvent.ACTION_CANCEL -> return false } return true } @@ -235,21 +241,20 @@ class Swipy @JvmOverloads constructor( private fun finishSpinner(overscrollDistance: Float) { - if (vertical) { - val totalDragDistance = Resources.getSystem().displayMetrics.heightPixels / dragDivider - if (overscrollDistance > totalDragDistance) - if (verticalPos == VerticalPosition.Top) - onTopSwiped.invoke() - else - onBottomSwiped.invoke() - } - else { - val totalDragDistance = Resources.getSystem().displayMetrics.widthPixels / dragDivider - if (overscrollDistance > totalDragDistance) + if (vertical) { + val totalDragDistance = Resources.getSystem().displayMetrics.heightPixels / dragDivider + if (overscrollDistance > totalDragDistance) + if (verticalPos == VerticalPosition.Top) + onTopSwiped.invoke() + else + onBottomSwiped.invoke() + } else { + val totalDragDistance = Resources.getSystem().displayMetrics.widthPixels / dragDivider + if (overscrollDistance > totalDragDistance) if (horizontalPos == HorizontalPosition.Left) onLeftSwiped.invoke() else onRightSwiped.invoke() - } + } } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/media/novel/BookDialog.kt b/app/src/main/java/ani/dantotsu/media/novel/BookDialog.kt index 467321dc..163275a6 100644 --- a/app/src/main/java/ani/dantotsu/media/novel/BookDialog.kt +++ b/app/src/main/java/ani/dantotsu/media/novel/BookDialog.kt @@ -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() - 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) diff --git a/app/src/main/java/ani/dantotsu/media/novel/NovelReadAdapter.kt b/app/src/main/java/ani/dantotsu/media/novel/NovelReadAdapter.kt index 09c67510..7f9d34f4 100644 --- a/app/src/main/java/ani/dantotsu/media/novel/NovelReadAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/novel/NovelReadAdapter.kt @@ -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() @@ -58,7 +68,7 @@ class NovelReadAdapter( binding.searchBarText.setOnEditorActionListener { _, actionId, _ -> return@setOnEditorActionListener when (actionId) { IME_ACTION_SEARCH -> search() - else -> false + else -> false } } binding.searchBar.setEndIconOnClickListener { 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) } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt b/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt index 515d2294..fd970a34 100644 --- a/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt @@ -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() - 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() - 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.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 diff --git a/app/src/main/java/ani/dantotsu/media/novel/UrlAdapter.kt b/app/src/main/java/ani/dantotsu/media/novel/UrlAdapter.kt index 7caa1b5c..96e9bcf8 100644 --- a/app/src/main/java/ani/dantotsu/media/novel/UrlAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/novel/UrlAdapter.kt @@ -13,11 +13,22 @@ import ani.dantotsu.parsers.Book import ani.dantotsu.setSafeOnClickListener import ani.dantotsu.tryWith -class UrlAdapter(private val urls: List, val book: Book, val novel: String, val callback: BookDialog.Callback?) : +class UrlAdapter( + private val urls: List, + val book: Book, + val novel: String, + val callback: BookDialog.Callback? +) : RecyclerView.Adapter() { 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") diff --git a/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderActivity.kt b/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderActivity.kt index 442714ca..8cc990e5 100644 --- a/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderActivity.kt @@ -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,14 +234,21 @@ 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.bookReader.goto(book.toc[position].href) - } + 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) + } - override fun onNothingSelected(parent: AdapterView<*>?) {} - } + override fun onNothingSelected(parent: AdapterView<*>?) {} + } binding.bookReader.getAppearance { currentTheme = it @@ -295,7 +303,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener { private var onVolumeDown: (() -> Unit)? = null override fun dispatchKeyEvent(event: KeyEvent): Boolean { return when (event.keyCode) { - KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_PAGE_UP -> { + KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_PAGE_UP -> { if (event.keyCode == KeyEvent.KEYCODE_VOLUME_UP) if (!settings.default.volumeButtons) return false @@ -315,7 +323,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener { } else false } - else -> { + else -> { super.dispatchKeyEvent(event) } } @@ -326,10 +334,11 @@ 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 -> { + CurrentNovelReaderSettings.Layouts.PAGED -> { currentTheme?.flow = ReaderFlow.PAGINATED } @@ -340,9 +349,10 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener { requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER when (settings.default.dualPageMode) { - CurrentReaderSettings.DualPageModes.No -> currentTheme?.maxColumnCount = 1 + 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() } } diff --git a/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderSettingsDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderSettingsDialogFragment.kt index be609ff5..27a9caaa 100644 --- a/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderSettingsDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/novel/novelreader/NovelReaderSettingsDialogFragment.kt @@ -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" } diff --git a/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt b/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt index c89e2188..047efc05 100644 --- a/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/user/ListActivity.kt @@ -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 = resources.getStringArray(R.array.keys) + val defaultKeys = listOf( + "Reading", + "Watching", + "Completed", + "Paused", + "Dropped", + "Planning", + "Favourites", + "Rewatching", + "Rereading", + "All" + ) + val userKeys: Array = 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,32 +124,43 @@ 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) } } } - binding.listSort.setOnClickListener { - val popup = PopupMenu(this, it) - popup.setOnMenuItemClickListener { item -> - val sort = when (item.itemId) { - R.id.score -> "score" - R.id.title -> "title" - R.id.updated -> "updatedAt" - R.id.release -> "release" - else -> null - } + binding.listSort.setOnClickListener { + val popup = PopupMenu(this, it) + popup.setOnMenuItemClickListener { item -> + val sort = when (item.itemId) { + R.id.score -> "score" + R.id.title -> "title" + R.id.updated -> "updatedAt" + R.id.release -> "release" + else -> null + } - binding.listProgressBar.visibility = View.VISIBLE - binding.listViewPager.adapter = null - scope.launch { - withContext(Dispatchers.IO) { model.loadLists(anime, intent.getIntExtra("userId", 0), sort) } - } - true - } - popup.inflate(R.menu.list_sort_menu) - popup.show() - } + binding.listProgressBar.visibility = View.VISIBLE + binding.listViewPager.adapter = null + scope.launch { + withContext(Dispatchers.IO) { + model.loadLists( + anime, + intent.getIntExtra("userId", 0), + sort + ) + } + } + true + } + popup.inflate(R.menu.list_sort_menu) + popup.show() + } } } diff --git a/app/src/main/java/ani/dantotsu/media/user/ListFragment.kt b/app/src/main/java/ani/dantotsu/media/user/ListFragment.kt index 6b0f87de..8053996a 100644 --- a/app/src/main/java/ani/dantotsu/media/user/ListFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/user/ListFragment.kt @@ -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 } } diff --git a/app/src/main/java/ani/dantotsu/media/user/ListViewPagerAdapter.kt b/app/src/main/java/ani/dantotsu/media/user/ListViewPagerAdapter.kt index 45bf00b0..979059dc 100644 --- a/app/src/main/java/ani/dantotsu/media/user/ListViewPagerAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/user/ListViewPagerAdapter.kt @@ -4,8 +4,13 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.viewpager2.adapter.FragmentStateAdapter -class ListViewPagerAdapter(private val size: Int, private val calendar: Boolean, fragment: FragmentActivity) : +class ListViewPagerAdapter( + private val size: Int, + private val calendar: Boolean, + fragment: FragmentActivity +) : FragmentStateAdapter(fragment) { override fun getItemCount(): Int = size - override fun createFragment(position: Int): Fragment = ListFragment.newInstance(position, calendar) + override fun createFragment(position: Int): Fragment = + ListFragment.newInstance(position, calendar) } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/offline/OfflineFragment.kt b/app/src/main/java/ani/dantotsu/offline/OfflineFragment.kt index 5a5579b1..fb780651 100644 --- a/app/src/main/java/ani/dantotsu/offline/OfflineFragment.kt +++ b/app/src/main/java/ani/dantotsu/offline/OfflineFragment.kt @@ -13,7 +13,11 @@ import ani.dantotsu.startMainActivity import ani.dantotsu.statusBarHeight class OfflineFragment : Fragment() { - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { val binding = FragmentOfflineBinding.inflate(inflater, container, false) binding.refreshContainer.updateLayoutParams { topMargin = statusBarHeight diff --git a/app/src/main/java/ani/dantotsu/others/AniSkip.kt b/app/src/main/java/ani/dantotsu/others/AniSkip.kt index b1cb3f3f..84448f09 100644 --- a/app/src/main/java/ani/dantotsu/others/AniSkip.kt +++ b/app/src/main/java/ani/dantotsu/others/AniSkip.kt @@ -8,12 +8,21 @@ import java.net.URLEncoder object AniSkip { @Suppress("BlockingMethodInNonBlockingContext") - suspend fun getResult(malId: Int, episodeNumber: Int, episodeLength: Long, useProxyForTimeStamps: Boolean): List? { + suspend fun getResult( + malId: Int, + episodeNumber: Int, + episodeLength: Long, + useProxyForTimeStamps: Boolean + ): List? { val url = "https://api.aniskip.com/v2/skip-times/$malId/$episodeNumber?types[]=ed&types[]=mixed-ed&types[]=mixed-op&types[]=op&types[]=recap&episodeLength=$episodeLength" return tryWithSuspend { - val a = if(useProxyForTimeStamps) - client.get("https://corsproxy.io/?${URLEncoder.encode(url, "utf-8").replace("+", "%20")}") + val a = if (useProxyForTimeStamps) + client.get( + "https://corsproxy.io/?${ + URLEncoder.encode(url, "utf-8").replace("+", "%20") + }" + ) else client.get(url) val res = a.parsed() @@ -40,8 +49,8 @@ object AniSkip { fun String.getType(): String { return when (this) { - "op" -> "Opening" - "ed" -> "Ending" + "op" -> "Opening" + "ed" -> "Ending" "recap" -> "Recap" "mixed-ed" -> "Mixed Ending" "mixed-op" -> "Mixed Opening" diff --git a/app/src/main/java/ani/dantotsu/others/AppUpdater.kt b/app/src/main/java/ani/dantotsu/others/AppUpdater.kt index dca947d3..b753e908 100644 --- a/app/src/main/java/ani/dantotsu/others/AppUpdater.kt +++ b/app/src/main/java/ani/dantotsu/others/AppUpdater.kt @@ -30,11 +30,11 @@ import java.text.SimpleDateFormat import java.util.* object AppUpdater { - suspend fun check(activity: FragmentActivity, post:Boolean=false) { - if(post) snackString(currContext()?.getString(R.string.checking_for_update)) + suspend fun check(activity: FragmentActivity, post: Boolean = false) { + if (post) snackString(currContext()?.getString(R.string.checking_for_update)) val repo = activity.getString(R.string.repo) tryWithSuspend { - val (md, version) = if(BuildConfig.DEBUG){ + val (md, version) = if (BuildConfig.DEBUG) { val res = client.get("https://api.github.com/repos/$repo/releases") .parsed().map { Mapper.json.decodeFromJsonElement(it) @@ -42,9 +42,9 @@ object AppUpdater { val r = res.filter { it.prerelease }.maxByOrNull { it.timeStamp() } ?: throw Exception("No Pre Release Found") - val v = r.tagName.substringAfter("v","") + val v = r.tagName.substringAfter("v", "") (r.body ?: "") to v.ifEmpty { throw Exception("Weird Version : ${r.tagName}") } - }else{ + } else { val res = client.get("https://raw.githubusercontent.com/$repo/main/stable.md").text res to res.substringAfter("# ").substringBefore("\n") @@ -54,15 +54,23 @@ object AppUpdater { val dontShow = loadData("dont_ask_for_update_$version") ?: false if (compareVersion(version) && !dontShow && !activity.isDestroyed) activity.runOnUiThread { CustomBottomDialog.newInstance().apply { - setTitleText("${if (BuildConfig.DEBUG) "Beta " else ""}Update " + currContext()!!.getString(R.string.available)) + setTitleText( + "${if (BuildConfig.DEBUG) "Beta " else ""}Update " + currContext()!!.getString( + R.string.available + ) + ) addView( TextView(activity).apply { - val markWon = Markwon.builder(activity).usePlugin(SoftBreakAddsNewLinePlugin.create()).build() + val markWon = Markwon.builder(activity) + .usePlugin(SoftBreakAddsNewLinePlugin.create()).build() markWon.setMarkdown(this, md) } ) - setCheck(currContext()!!.getString(R.string.dont_show_again, version), false) { isChecked -> + setCheck( + currContext()!!.getString(R.string.dont_show_again, version), + false + ) { isChecked -> if (isChecked) { saveData("dont_ask_for_update_$version", true) } @@ -72,11 +80,11 @@ object AppUpdater { try { client.get("https://api.github.com/repos/$repo/releases/tags/v$version") .parsed().assets?.find { - it.browserDownloadURL.endsWith("apk") - }?.browserDownloadURL.apply { - if (this != null) activity.downloadUpdate(version, this) - else openLinkInBrowser("https://github.com/repos/$repo/releases/tag/v$version") - } + it.browserDownloadURL.endsWith("apk") + }?.browserDownloadURL.apply { + if (this != null) activity.downloadUpdate(version, this) + else openLinkInBrowser("https://github.com/repos/$repo/releases/tag/v$version") + } } catch (e: Exception) { logError(e) } @@ -89,25 +97,24 @@ object AppUpdater { show(activity.supportFragmentManager, "dialog") } } - else{ - if(post) snackString(currContext()?.getString(R.string.no_update_found)) + else { + if (post) snackString(currContext()?.getString(R.string.no_update_found)) } } } private fun compareVersion(version: String): Boolean { - if(BuildConfig.DEBUG) { + if (BuildConfig.DEBUG) { return BuildConfig.VERSION_NAME != version - } - else { + } else { fun toDouble(list: List): Double { return list.mapIndexed { i: Int, s: String -> when (i) { - 0 -> s.toDouble() * 100 - 1 -> s.toDouble() * 10 - 2 -> s.toDouble() - else -> s.toDoubleOrNull()?: 0.0 + 0 -> s.toDouble() * 100 + 1 -> s.toDouble() * 10 + 2 -> s.toDouble() + else -> s.toDoubleOrNull() ?: 0.0 } }.sum() } @@ -211,7 +218,7 @@ object AppUpdater { val tagName: String, val prerelease: Boolean, @SerialName("created_at") - val createdAt : String, + val createdAt: String, val body: String? = null, val assets: List? = null ) { diff --git a/app/src/main/java/ani/dantotsu/others/CustomBottomDialog.kt b/app/src/main/java/ani/dantotsu/others/CustomBottomDialog.kt index 3c758f60..e5446bd0 100644 --- a/app/src/main/java/ani/dantotsu/others/CustomBottomDialog.kt +++ b/app/src/main/java/ani/dantotsu/others/CustomBottomDialog.kt @@ -17,8 +17,9 @@ open class CustomBottomDialog : BottomSheetDialogFragment() { fun addView(view: View) { viewList.add(view) } - var title: String?=null - fun setTitleText(string: String){ + + var title: String? = null + fun setTitleText(string: String) { title = string } @@ -46,7 +47,11 @@ open class CustomBottomDialog : BottomSheetDialogFragment() { positiveCallback = callback } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { _binding = BottomSheetCustomBinding.inflate(inflater, container, false) val window = dialog?.window window?.statusBarColor = Color.TRANSPARENT @@ -70,16 +75,16 @@ open class CustomBottomDialog : BottomSheetDialogFragment() { checkCallback?.invoke(checked) } } - - if(negativeText!=null) binding.bottomDialogCustomNegative.apply { + + if (negativeText != null) binding.bottomDialogCustomNegative.apply { visibility = View.VISIBLE text = negativeText - setOnClickListener { + setOnClickListener { negativeCallback?.invoke() } } - if(positiveText!=null) binding.bottomDialogCustomPositive.apply { + if (positiveText != null) binding.bottomDialogCustomPositive.apply { visibility = View.VISIBLE text = positiveText setOnClickListener { diff --git a/app/src/main/java/ani/dantotsu/others/Download.kt b/app/src/main/java/ani/dantotsu/others/Download.kt index b9d9a27a..8a8b496a 100644 --- a/app/src/main/java/ani/dantotsu/others/Download.kt +++ b/app/src/main/java/ani/dantotsu/others/Download.kt @@ -12,10 +12,10 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import ani.dantotsu.FileUrl import ani.dantotsu.R -import ani.dantotsu.media.anime.Episode import ani.dantotsu.currContext import ani.dantotsu.defaultHeaders import ani.dantotsu.loadData +import ani.dantotsu.media.anime.Episode import ani.dantotsu.parsers.Book import ani.dantotsu.toast import kotlinx.coroutines.CoroutineScope @@ -50,12 +50,17 @@ object Download { fun download(context: Context, episode: Episode, animeTitle: String) { toast(context.getString(R.string.downloading)) - val extractor = episode.extractors?.find { it.server.name == episode.selectedExtractor } ?: return + val extractor = + episode.extractors?.find { it.server.name == episode.selectedExtractor } ?: return val video = if (extractor.videos.size > episode.selectedVideo) extractor.videos[episode.selectedVideo] else return val regex = "[\\\\/:*?\"<>|]".toRegex() val aTitle = animeTitle.replace(regex, "") - val title = "Episode ${episode.number}${if (episode.title != null) " - ${episode.title}" else ""}".replace(regex, "") + val title = + "Episode ${episode.number}${if (episode.title != null) " - ${episode.title}" else ""}".replace( + regex, + "" + ) val notif = "$title : $aTitle" val folder = "/Anime/${aTitle}/" @@ -64,7 +69,7 @@ object Download { download(context, file, fileName, folder, notif) } - fun download(context: Context, book:Book, pos:Int, novelTitle:String){ + fun download(context: Context, book: Book, pos: Int, novelTitle: String) { toast(currContext()?.getString(R.string.downloading)) val regex = "[\\\\/:*?\"<>|]".toRegex() val nTitle = novelTitle.replace(regex, "") @@ -77,19 +82,32 @@ object Download { download(context, file, fileName, folder, notif) } - fun download(context: Context, file: FileUrl, fileName: String, folder: String, notif: String? = null) { - if(!file.url.startsWith("http")) + fun download( + context: Context, + file: FileUrl, + fileName: String, + folder: String, + notif: String? = null + ) { + if (!file.url.startsWith("http")) toast(context.getString(R.string.invalid_url)) else when (loadData("settings_download_manager", context, false) ?: 0) { - 1 -> oneDM(context, file, notif ?: fileName) - 2 -> adm(context, file, fileName, folder) + 1 -> oneDM(context, file, notif ?: fileName) + 2 -> adm(context, file, fileName, folder) else -> defaultDownload(context, file, fileName, folder, notif ?: fileName) } } - private fun defaultDownload(context: Context, file: FileUrl, fileName: String, folder: String, notif: String) { - val manager = context.getSystemService(AppCompatActivity.DOWNLOAD_SERVICE) as DownloadManager + private fun defaultDownload( + context: Context, + file: FileUrl, + fileName: String, + folder: String, + notif: String + ) { + val manager = + context.getSystemService(AppCompatActivity.DOWNLOAD_SERVICE) as DownloadManager val request: DownloadManager.Request = DownloadManager.Request(Uri.parse(file.url)) file.headers.forEach { request.addRequestHeader(it.key, it.value) @@ -124,15 +142,24 @@ object Download { } private fun oneDM(context: Context, file: FileUrl, notif: String) { - val appName = if (isPackageInstalled("idm.internet.download.manager.plus", context.packageManager)) { - "idm.internet.download.manager.plus" - } else if (isPackageInstalled("idm.internet.download.manager", context.packageManager)) { - "idm.internet.download.manager" - } else if (isPackageInstalled("idm.internet.download.manager.adm.lite", context.packageManager)) { - "idm.internet.download.manager.adm.lite" - } else { - "" - } + val appName = + if (isPackageInstalled("idm.internet.download.manager.plus", context.packageManager)) { + "idm.internet.download.manager.plus" + } else if (isPackageInstalled( + "idm.internet.download.manager", + context.packageManager + ) + ) { + "idm.internet.download.manager" + } else if (isPackageInstalled( + "idm.internet.download.manager.adm.lite", + context.packageManager + ) + ) { + "idm.internet.download.manager.adm.lite" + } else { + "" + } if (appName.isNotEmpty()) { val bundle = Bundle() defaultHeaders.forEach { a -> bundle.putString(a.key, a.value) } @@ -177,7 +204,9 @@ object Download { } else { ContextCompat.startActivity( context, - Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.dv.adm")).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), + Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.dv.adm")).addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK + ), null ) toast(currContext()?.getString(R.string.install_adm)) diff --git a/app/src/main/java/ani/dantotsu/others/GlideApp.kt b/app/src/main/java/ani/dantotsu/others/GlideApp.kt index edc2747d..97e26ad4 100644 --- a/app/src/main/java/ani/dantotsu/others/GlideApp.kt +++ b/app/src/main/java/ani/dantotsu/others/GlideApp.kt @@ -15,7 +15,7 @@ import java.io.InputStream @GlideModule -class DantotsuGlideApp : AppGlideModule(){ +class DantotsuGlideApp : AppGlideModule() { @SuppressLint("CheckResult") override fun applyOptions(context: Context, builder: GlideBuilder) { super.applyOptions(context, builder) diff --git a/app/src/main/java/ani/dantotsu/others/ImageViewDialog.kt b/app/src/main/java/ani/dantotsu/others/ImageViewDialog.kt index 03013d08..aa792d9f 100644 --- a/app/src/main/java/ani/dantotsu/others/ImageViewDialog.kt +++ b/app/src/main/java/ani/dantotsu/others/ImageViewDialog.kt @@ -19,8 +19,6 @@ import ani.dantotsu.saveImageToDownloads import ani.dantotsu.setSafeOnClickListener import ani.dantotsu.shareImage import ani.dantotsu.snackString -import ani.dantotsu.themes.ThemeManager -import ani.dantotsu.others.LangSet import ani.dantotsu.toast import com.bumptech.glide.load.resource.bitmap.BitmapTransformation import com.davemorrissey.labs.subscaleview.ImageSource @@ -50,7 +48,11 @@ class ImageViewDialog : BottomSheetDialogFragment() { } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { _binding = BottomSheetImageBinding.inflate(inflater, container, false) return binding.root } @@ -81,13 +83,16 @@ class ImageViewDialog : BottomSheetDialogFragment() { val binding = _binding ?: return@launch var bitmap = context.loadBitmap_old(image, trans1 ?: listOf()) - var bitmap2 = if (image2 != null) context.loadBitmap_old(image2, trans2 ?: listOf()) else null + var bitmap2 = + if (image2 != null) context.loadBitmap_old(image2, trans2 ?: listOf()) else null if (bitmap == null) { bitmap = context.loadBitmap(image, trans1 ?: listOf()) - bitmap2 = if (image2 != null) context.loadBitmap(image2, trans2 ?: listOf()) else null + bitmap2 = + if (image2 != null) context.loadBitmap(image2, trans2 ?: listOf()) else null } - bitmap = if (bitmap2 != null && bitmap != null) mergeBitmap(bitmap, bitmap2,) else bitmap + bitmap = + if (bitmap2 != null && bitmap != null) mergeBitmap(bitmap, bitmap2) else bitmap if (bitmap != null) { binding.bottomImageShare.isEnabled = true @@ -100,10 +105,11 @@ class ImageViewDialog : BottomSheetDialogFragment() { } binding.bottomImageView.setImage(ImageSource.cachedBitmap(bitmap)) - ObjectAnimator.ofFloat(binding.bottomImageView, "alpha", 0f, 1f).setDuration(400L).start() + ObjectAnimator.ofFloat(binding.bottomImageView, "alpha", 0f, 1f).setDuration(400L) + .start() binding.bottomImageProgress.visibility = View.GONE } else { - toast(context?.getString(R.string.loading_image_failed)) + toast(context.getString(R.string.loading_image_failed)) binding.bottomImageNo.visibility = View.VISIBLE binding.bottomImageProgress.visibility = View.GONE } @@ -116,7 +122,12 @@ class ImageViewDialog : BottomSheetDialogFragment() { } companion object { - fun newInstance(title: String, image: FileUrl, showReload: Boolean = false, image2: FileUrl?) = ImageViewDialog().apply { + fun newInstance( + title: String, + image: FileUrl, + showReload: Boolean = false, + image2: FileUrl? + ) = ImageViewDialog().apply { arguments = Bundle().apply { putString("title", title) putBoolean("reload", showReload) diff --git a/app/src/main/java/ani/dantotsu/others/Jikan.kt b/app/src/main/java/ani/dantotsu/others/Jikan.kt index e08231a0..518dca3d 100644 --- a/app/src/main/java/ani/dantotsu/others/Jikan.kt +++ b/app/src/main/java/ani/dantotsu/others/Jikan.kt @@ -1,7 +1,7 @@ package ani.dantotsu.others -import ani.dantotsu.media.anime.Episode import ani.dantotsu.client +import ani.dantotsu.media.anime.Episode import ani.dantotsu.tryWithSuspend import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -23,9 +23,10 @@ object Jikan { val res = query("anime/$malId/episodes?page=$page") res?.data?.forEach { val ep = it.malID.toString() - eps[ep] = Episode(ep, title = it.title, + eps[ep] = Episode( + ep, title = it.title, //Personal revenge with 34566 :prayge: - filler = if(malId!=34566) it.filler else true, + filler = if (malId != 34566) it.filler else true, ) } hasNextPage = res?.pagination?.hasNextPage == true diff --git a/app/src/main/java/ani/dantotsu/others/JsUnpacker.kt b/app/src/main/java/ani/dantotsu/others/JsUnpacker.kt index 790958a3..35ebe7b5 100644 --- a/app/src/main/java/ani/dantotsu/others/JsUnpacker.kt +++ b/app/src/main/java/ani/dantotsu/others/JsUnpacker.kt @@ -1,7 +1,7 @@ package ani.dantotsu.others import ani.dantotsu.logger -import java.util.regex.* +import java.util.regex.Pattern import kotlin.math.pow // https://github.com/cylonu87/JsUnpacker @@ -31,7 +31,10 @@ class JsUnpacker(packedJS: String?) { val js = packedJS ?: return null try { var p = - Pattern.compile("""\}\s*\('(.*)',\s*(.*?),\s*(\d+),\s*'(.*?)'\.split\('\|'\)""", Pattern.DOTALL) + Pattern.compile( + """\}\s*\('(.*)',\s*(.*?),\s*(\d+),\s*'(.*?)'\.split\('\|'\)""", + Pattern.DOTALL + ) var m = p.matcher(js) if (m.find() && m.groupCount() == 4) { val payload = m.group(1)?.replace("\\'", "'") ?: return null @@ -79,7 +82,10 @@ class JsUnpacker(packedJS: String?) { } else { val tmp = StringBuilder(str).reverse().toString() for (i in tmp.indices) { - ret += (radix.toDouble().pow(i.toDouble()) * dictionary!![tmp.substring(i, i + 1)]!!).toInt() + ret += (radix.toDouble().pow(i.toDouble()) * dictionary!![tmp.substring( + i, + i + 1 + )]!!).toInt() } } return ret @@ -91,12 +97,15 @@ class JsUnpacker(packedJS: String?) { radix < 62 -> { alphabet = a62.substring(0, radix) } + radix in 63..94 -> { alphabet = a95.substring(0, radix) } + radix == 62 -> { alphabet = a62 } + radix == 95 -> { alphabet = a95 } diff --git a/app/src/main/java/ani/dantotsu/others/Kitsu.kt b/app/src/main/java/ani/dantotsu/others/Kitsu.kt index 6f0ae8cf..e8155729 100644 --- a/app/src/main/java/ani/dantotsu/others/Kitsu.kt +++ b/app/src/main/java/ani/dantotsu/others/Kitsu.kt @@ -18,7 +18,13 @@ object Kitsu { "DNT" to "1", "Origin" to "https://kitsu.io" ) - val json = tryWithSuspend { client.post("https://kitsu.io/api/graphql", headers, data = mapOf("query" to query)) } + val json = tryWithSuspend { + client.post( + "https://kitsu.io/api/graphql", + headers, + data = mapOf("query" to query) + ) + } return json?.parsed() } @@ -54,8 +60,8 @@ query { val result = getKitsuData(query) ?: return null logger("Kitsu : result=$result", print) media.idKitsu = result.data?.lookupMapping?.id - return (result.data?.lookupMapping?.episodes?.nodes?:return null).mapNotNull { ep -> - val num = ep?.num?.toString()?:return@mapNotNull null + return (result.data?.lookupMapping?.episodes?.nodes ?: return null).mapNotNull { ep -> + val num = ep?.num?.toString() ?: return@mapNotNull null num to Episode( number = num, title = ep.titles?.canonical, @@ -70,39 +76,46 @@ query { @SerialName("data") val data: Data? = null ) { @Serializable - data class Data ( + data class Data( @SerialName("lookupMapping") val lookupMapping: LookupMapping? = null ) + @Serializable - data class LookupMapping ( + data class LookupMapping( @SerialName("id") val id: String? = null, @SerialName("episodes") val episodes: Episodes? = null ) + @Serializable - data class Episodes ( + data class Episodes( @SerialName("nodes") val nodes: List? = null ) + @Serializable - data class Node ( + data class Node( @SerialName("number") val num: Long? = null, @SerialName("titles") val titles: Titles? = null, @SerialName("description") val description: Description? = null, @SerialName("thumbnail") val thumbnail: Thumbnail? = null ) + @Serializable - data class Description ( + data class Description( @SerialName("en") val en: String? = null ) + @Serializable - data class Thumbnail ( + data class Thumbnail( @SerialName("original") val original: Original? = null ) + @Serializable - data class Original ( + data class Original( @SerialName("url") val url: String? = null ) + @Serializable - data class Titles ( + data class Titles( @SerialName("canonical") val canonical: String? = null ) diff --git a/app/src/main/java/ani/dantotsu/others/LangSet.kt b/app/src/main/java/ani/dantotsu/others/LangSet.kt index fd5ba8a3..3e7857da 100644 --- a/app/src/main/java/ani/dantotsu/others/LangSet.kt +++ b/app/src/main/java/ani/dantotsu/others/LangSet.kt @@ -6,13 +6,12 @@ import android.content.res.Resources import java.util.Locale - - class LangSet { - companion object{ + companion object { fun setLocale(activity: Activity) { - val useCursedLang = activity.getSharedPreferences("Dantotsu", Activity.MODE_PRIVATE).getBoolean("use_cursed_lang", false) - val locale = if(useCursedLang) Locale("en", "DW") else Locale("en", "US") + val useCursedLang = activity.getSharedPreferences("Dantotsu", Activity.MODE_PRIVATE) + .getBoolean("use_cursed_lang", false) + val locale = if (useCursedLang) Locale("en", "DW") else Locale("en", "US") Locale.setDefault(locale) val resources: Resources = activity.resources val config: Configuration = resources.configuration diff --git a/app/src/main/java/ani/dantotsu/others/MalScraper.kt b/app/src/main/java/ani/dantotsu/others/MalScraper.kt index 048dc0ed..8b39ac64 100644 --- a/app/src/main/java/ani/dantotsu/others/MalScraper.kt +++ b/app/src/main/java/ani/dantotsu/others/MalScraper.kt @@ -17,11 +17,14 @@ object MalScraper { try { withTimeout(6000) { if (media.anime != null) { - val res = client.get("https://myanimelist.net/anime/${media.idMAL}", headers).document + val res = + client.get("https://myanimelist.net/anime/${media.idMAL}", headers).document val a = res.select(".title-english").text() media.nameMAL = if (a != "") a else res.select(".title-name").text() media.typeMAL = - if (res.select("div.spaceit_pad > a").isNotEmpty()) res.select("div.spaceit_pad > a")[0].text() else null + if (res.select("div.spaceit_pad > a") + .isNotEmpty() + ) res.select("div.spaceit_pad > a")[0].text() else null media.anime.op = arrayListOf() res.select(".opnening > table > tbody > tr").forEach { val text = it.text() @@ -35,12 +38,15 @@ object MalScraper { media.anime.ed.add(it.text()) } } else { - val res = client.get("https://myanimelist.net/manga/${media.idMAL}", headers).document + val res = + client.get("https://myanimelist.net/manga/${media.idMAL}", headers).document val b = res.select(".title-english").text() val a = res.select(".h1-title").text().removeSuffix(b) media.nameMAL = a media.typeMAL = - if (res.select("div.spaceit_pad > a").isNotEmpty()) res.select("div.spaceit_pad > a")[0].text() else null + if (res.select("div.spaceit_pad > a") + .isNotEmpty() + ) res.select("div.spaceit_pad > a")[0].text() else null } } } catch (e: Exception) { diff --git a/app/src/main/java/ani/dantotsu/others/MalSyncBackup.kt b/app/src/main/java/ani/dantotsu/others/MalSyncBackup.kt index 5ff7d9c1..66e48603 100644 --- a/app/src/main/java/ani/dantotsu/others/MalSyncBackup.kt +++ b/app/src/main/java/ani/dantotsu/others/MalSyncBackup.kt @@ -31,8 +31,8 @@ object MalSyncBackup { val isDub = page.title.lowercase().replace(" ", "").endsWith("(dub)") val slug = if (dub == isDub) page.identifier else null if (slug != null && page.active == true && page.url != null) { - val url = when(name){ - "Gogoanime" -> slug + val url = when (name) { + "Gogoanime" -> slug "Tenshi" -> slug else -> page.url } diff --git a/app/src/main/java/ani/dantotsu/others/OutlineTextView.kt b/app/src/main/java/ani/dantotsu/others/OutlineTextView.kt index c5ee8efd..e2c17cc0 100644 --- a/app/src/main/java/ani/dantotsu/others/OutlineTextView.kt +++ b/app/src/main/java/ani/dantotsu/others/OutlineTextView.kt @@ -25,7 +25,11 @@ class OutlineTextView : AppCompatTextView { } - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( + context, + attrs, + defStyleAttr + ) { initResources(context, attrs) } @@ -55,7 +59,8 @@ class OutlineTextView : AppCompatTextView { strokeWidth = width.toPx(context) } - private fun Float.toPx(context: Context) = (this * context.resources.displayMetrics.scaledDensity + 0.5F) + private fun Float.toPx(context: Context) = + (this * context.resources.displayMetrics.scaledDensity + 0.5F) override fun invalidate() { if (isDrawing) return diff --git a/app/src/main/java/ani/dantotsu/others/ResettableTimer.kt b/app/src/main/java/ani/dantotsu/others/ResettableTimer.kt index af3a1162..cb0066c0 100644 --- a/app/src/main/java/ani/dantotsu/others/ResettableTimer.kt +++ b/app/src/main/java/ani/dantotsu/others/ResettableTimer.kt @@ -1,7 +1,8 @@ package ani.dantotsu.others -import java.util.* -import java.util.concurrent.atomic.* +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.atomic.AtomicBoolean class ResettableTimer { var resetLock = AtomicBoolean(false) diff --git a/app/src/main/java/ani/dantotsu/others/SpoilerPlugin.kt b/app/src/main/java/ani/dantotsu/others/SpoilerPlugin.kt index 635150fe..fd746664 100644 --- a/app/src/main/java/ani/dantotsu/others/SpoilerPlugin.kt +++ b/app/src/main/java/ani/dantotsu/others/SpoilerPlugin.kt @@ -10,7 +10,7 @@ import android.view.View import android.widget.TextView import io.noties.markwon.AbstractMarkwonPlugin import io.noties.markwon.utils.ColorUtils -import java.util.regex.* +import java.util.regex.Pattern class SpoilerPlugin : AbstractMarkwonPlugin() { override fun beforeSetText(textView: TextView, markdown: Spanned) { diff --git a/app/src/main/java/ani/dantotsu/others/Xpandable.kt b/app/src/main/java/ani/dantotsu/others/Xpandable.kt index ea6bfa7b..527380ef 100644 --- a/app/src/main/java/ani/dantotsu/others/Xpandable.kt +++ b/app/src/main/java/ani/dantotsu/others/Xpandable.kt @@ -29,8 +29,8 @@ class Xpandable @JvmOverloads constructor( }, 300) } - if(!expanded) children.forEach { - if (it != getChildAt(0)){ + if (!expanded) children.forEach { + if (it != getChildAt(0)) { it.visibility = GONE } } @@ -40,7 +40,7 @@ class Xpandable @JvmOverloads constructor( private fun hideAll() { children.forEach { - if (it != getChildAt(0)){ + if (it != getChildAt(0)) { ObjectAnimator.ofFloat(it, "scaleY", 1f, 0.5f).setDuration(200).start() ObjectAnimator.ofFloat(it, "translationY", 0f, -32f).setDuration(200).start() ObjectAnimator.ofFloat(it, "alpha", 1f, 0f).setDuration(200).start() @@ -54,7 +54,7 @@ class Xpandable @JvmOverloads constructor( private fun showAll() { children.forEach { - if (it != getChildAt(0)){ + if (it != getChildAt(0)) { it.visibility = VISIBLE ObjectAnimator.ofFloat(it, "scaleY", 0.5f, 1f).setDuration(200).start() ObjectAnimator.ofFloat(it, "translationY", -32f, 0f).setDuration(200).start() diff --git a/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchActivity.kt b/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchActivity.kt index f1d846eb..1e38fc0e 100644 --- a/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchActivity.kt +++ b/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchActivity.kt @@ -15,8 +15,8 @@ import ani.dantotsu.R import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.databinding.ActivityImageSearchBinding import ani.dantotsu.media.MediaDetailsActivity -import ani.dantotsu.themes.ThemeManager import ani.dantotsu.others.LangSet +import ani.dantotsu.themes.ThemeManager import ani.dantotsu.toast import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -36,7 +36,7 @@ class ImageSearchActivity : AppCompatActivity() { } val inputStream = contentResolver.openInputStream(imageUri) - if(inputStream != null) viewModel.analyzeImage(inputStream) + if (inputStream != null) viewModel.analyzeImage(inputStream) else toast(getString(R.string.error_loading_image)) withContext(Dispatchers.Main) { @@ -49,7 +49,7 @@ class ImageSearchActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) LangSet.setLocale(this) -ThemeManager(this).applyTheme() + ThemeManager(this).applyTheme() binding = ActivityImageSearchBinding.inflate(layoutInflater) setContentView(binding.root) @@ -76,7 +76,7 @@ ThemeManager(this).applyTheme() override fun onItemClick(searchResult: ImageSearchViewModel.ImageResult) { lifecycleScope.launch(Dispatchers.IO) { val id = searchResult.anilist?.id?.toInt() - if (id==null){ + if (id == null) { toast(getString(R.string.no_anilist_id_found)) return@launch } diff --git a/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchResultAdapter.kt b/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchResultAdapter.kt index 917e9cfe..df996a08 100644 --- a/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchResultAdapter.kt +++ b/app/src/main/java/ani/dantotsu/others/imagesearch/ImageSearchResultAdapter.kt @@ -20,10 +20,12 @@ class ImageSearchResultAdapter(private val searchResults: List?, sAnime: SAnime): List + abstract suspend fun loadEpisodes( + animeLink: String, + extra: Map?, + sAnime: SAnime + ): List /** * Takes ShowResponse.link, ShowResponse.extra & the Last Largest Episode Number known by app as arguments @@ -31,10 +35,15 @@ abstract class AnimeParser : BaseParser() { * Returns the latest episode (If overriding, Make sure the episode is actually the latest episode) * Returns null, if no latest episode is found. * **/ - open suspend fun getLatestEpisode(animeLink: String, extra: Map?, sAnime: SAnime, latest: Float): Episode?{ + open suspend fun getLatestEpisode( + animeLink: String, + extra: Map?, + sAnime: SAnime, + latest: Float + ): Episode? { val episodes = loadEpisodes(animeLink, extra, sAnime) val max = episodes - .maxByOrNull { it.number.toFloatOrNull()?:0f } + .maxByOrNull { it.number.toFloatOrNull() ?: 0f } return max ?.takeIf { latest < (it.number.toFloatOrNull() ?: 0.001f) } } @@ -44,7 +53,11 @@ abstract class AnimeParser : BaseParser() { * * This returns a Map of "Video Server's Name" & "Link/Data" of all the Video Servers present on the site, which can be further used by loadVideoServers() & loadSingleVideoServer() * **/ - abstract suspend fun loadVideoServers(episodeLink: String, extra: Map?, sEpisode: SEpisode): List + abstract suspend fun loadVideoServers( + episodeLink: String, + extra: Map?, + sEpisode: SEpisode + ): List /** @@ -75,10 +88,12 @@ abstract class AnimeParser : BaseParser() { * **/ open suspend fun getVideoExtractor(server: VideoServer): VideoExtractor? { var domain = Uri.parse(server.embed.url).host ?: return null - if (domain.startsWith("www.")) {domain = domain.substring(4)} + if (domain.startsWith("www.")) { + domain = domain.substring(4) + } val extractor: VideoExtractor? = when (domain) { - else -> { + else -> { println("$name : No extractor found for: $domain | ${server.embed.url}") null } @@ -98,7 +113,12 @@ abstract class AnimeParser : BaseParser() { * * Doesn't need to be overridden, if the parser is following the norm. * **/ - open suspend fun loadByVideoServers(episodeUrl: String, extra: Map?, sEpisode: SEpisode, callback: (VideoExtractor) -> Unit) { + open suspend fun loadByVideoServers( + episodeUrl: String, + extra: Map?, + sEpisode: SEpisode, + callback: (VideoExtractor) -> Unit + ) { tryWithSuspend(true) { loadVideoServers(episodeUrl, extra, sEpisode).asyncMap { getVideoExtractor(it)?.apply { @@ -116,7 +136,13 @@ abstract class AnimeParser : BaseParser() { * * Doesn't need to be overridden, if the parser is following the norm. * **/ - open suspend fun loadSingleVideoServer(serverName: String, episodeUrl: String, extra: Map?, sEpisode: SEpisode, post: Boolean): VideoExtractor? { + open suspend fun loadSingleVideoServer( + serverName: String, + episodeUrl: String, + extra: Map?, + sEpisode: SEpisode, + post: Boolean + ): VideoExtractor? { return tryWithSuspend(post) { loadVideoServers(episodeUrl, extra, sEpisode).apply { find { it.name == serverName }?.also { @@ -159,27 +185,43 @@ abstract class AnimeParser : BaseParser() { val dub = if (isDubAvailableSeparately) "_${if (selectDub) "dub" else "sub"}" else "" var loaded = loadData("${saveName}${dub}_$mediaId") if (loaded == null && malSyncBackupName.isNotEmpty()) - loaded = MalSyncBackup.get(mediaId, malSyncBackupName, selectDub)?.also { saveShowResponse(mediaId, it, true) } + loaded = MalSyncBackup.get(mediaId, malSyncBackupName, selectDub) + ?.also { saveShowResponse(mediaId, it, true) } return loaded } override fun saveShowResponse(mediaId: Int, response: ShowResponse?, selected: Boolean) { if (response != null) { checkIfVariablesAreEmpty() - setUserText("${if (selected) currContext()!!.getString(R.string.selected) else currContext()!!.getString(R.string.found)} : ${response.name}") + setUserText( + "${ + if (selected) currContext()!!.getString(R.string.selected) else currContext()!!.getString( + R.string.found + ) + } : ${response.name}" + ) val dub = if (isDubAvailableSeparately) "_${if (selectDub) "dub" else "sub"}" else "" saveData("${saveName}${dub}_$mediaId", response) } } } -class EmptyAnimeParser: AnimeParser() { +class EmptyAnimeParser : AnimeParser() { override val name: String = "None" override val saveName: String = "None" override val isDubAvailableSeparately: Boolean = false - override suspend fun loadEpisodes(animeLink: String, extra: Map?, sAnime: SAnime): List = emptyList() - override suspend fun loadVideoServers(episodeLink: String, extra: Map?, sEpisode: SEpisode): List = emptyList() + override suspend fun loadEpisodes( + animeLink: String, + extra: Map?, + sAnime: SAnime + ): List = emptyList() + + override suspend fun loadVideoServers( + episodeLink: String, + extra: Map?, + sEpisode: SEpisode + ): List = emptyList() override suspend fun search(query: String): List = emptyList() } @@ -209,7 +251,7 @@ data class Episode( /** * In case, you want to pass extra data * **/ - val extra: Map? = null, + val extra: Map? = null, //SEpisode from Aniyomi val sEpisode: SEpisode? = null @@ -221,7 +263,7 @@ data class Episode( thumbnail: String, description: String? = null, isFiller: Boolean = false, - extra: Map? = null + extra: Map? = null ) : this(number, link, title, FileUrl(thumbnail), description, isFiller, extra) constructor( @@ -231,7 +273,7 @@ data class Episode( thumbnail: String, description: String? = null, isFiller: Boolean = false, - extra: Map? = null, + extra: Map? = null, sEpisode: SEpisode? = null ) : this(number, link, title, FileUrl(thumbnail), description, isFiller, extra, sEpisode) } diff --git a/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt b/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt index 1b66928c..c48a4b3e 100644 --- a/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt +++ b/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt @@ -1,8 +1,8 @@ package ani.dantotsu.parsers import ani.dantotsu.Lazier -import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import ani.dantotsu.lazyList +import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first @@ -30,8 +30,8 @@ object AnimeSources : WatchSources() { object HAnimeSources : WatchSources() { - private val aList: List> = lazyList( + private val aList: List> = lazyList( ) - override val list = listOf(aList,AnimeSources.list).flatten() + override val list = listOf(aList, AnimeSources.list).flatten() } diff --git a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt index 239290c9..ea2fa1a0 100644 --- a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt +++ b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt @@ -3,7 +3,6 @@ package ani.dantotsu.parsers import android.content.ContentResolver import android.content.ContentValues import android.content.Context -import android.content.Intent import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri @@ -12,21 +11,21 @@ import android.os.Environment import android.provider.MediaStore import android.widget.Toast import ani.dantotsu.FileUrl -import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension -import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource -import eu.kanade.tachiyomi.network.interceptor.CloudflareBypassException import ani.dantotsu.currContext import ani.dantotsu.logger import ani.dantotsu.media.anime.AnimeNameAdapter import ani.dantotsu.media.manga.ImageData import ani.dantotsu.media.manga.MangaCache import com.google.firebase.crashlytics.FirebaseCrashlytics -import eu.kanade.tachiyomi.animesource.model.SEpisode +import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource import eu.kanade.tachiyomi.animesource.model.AnimesPage import eu.kanade.tachiyomi.animesource.model.SAnime +import eu.kanade.tachiyomi.animesource.model.SEpisode import eu.kanade.tachiyomi.animesource.model.Track import eu.kanade.tachiyomi.animesource.model.Video +import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import eu.kanade.tachiyomi.extension.manga.model.MangaExtension +import eu.kanade.tachiyomi.network.interceptor.CloudflareBypassException import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter @@ -41,13 +40,13 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking 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.io.UnsupportedEncodingException import java.net.URL import java.net.URLDecoder -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.* -import java.io.UnsupportedEncodingException import java.util.regex.Pattern class AniyomiAdapter { @@ -107,7 +106,8 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() { } else { var episodeCounter = 1f // Group by season, sort within each season, and then renumber while keeping episode number 0 as is - val seasonGroups = res.groupBy { AnimeNameAdapter.findSeasonNumber(it.name) ?: 0 } + val seasonGroups = + res.groupBy { AnimeNameAdapter.findSeasonNumber(it.name) ?: 0 } seasonGroups.keys.sorted().flatMap { season -> seasonGroups[season]?.sortedBy { it.episode_number }?.map { episode -> if (episode.episode_number != 0f) { // Skip renumbering for episode number 0 @@ -302,7 +302,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() { return ret } - suspend fun imageList(chapterLink: String, sChapter: SChapter): List{ + suspend fun imageList(chapterLink: String, sChapter: SChapter): List { val source = try { extension.sources[sourceLanguage] } catch (e: Exception) { @@ -348,7 +348,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() { println("Response: ${response.message}") // Convert the Response to an InputStream - val inputStream = response.body?.byteStream() + val inputStream = response.body.byteStream() // Convert InputStream to Bitmap val bitmap = BitmapFactory.decodeStream(inputStream) @@ -379,7 +379,7 @@ class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() { val response = httpSource.getImage(page) // Convert the Response to an InputStream - val inputStream = response.body?.byteStream() + val inputStream = response.body.byteStream() // Convert InputStream to Bitmap val bitmap = BitmapFactory.decodeStream(inputStream) @@ -578,7 +578,7 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() { } } - private fun AniVideoToSaiVideo(aniVideo: eu.kanade.tachiyomi.animesource.model.Video): ani.dantotsu.parsers.Video { + private fun AniVideoToSaiVideo(aniVideo: Video): ani.dantotsu.parsers.Video { // Find the number value from the .quality string val number = Regex("""\d+""").find(aniVideo.quality)?.value?.toInt() ?: 0 @@ -608,14 +608,15 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() { // If the format is still undetermined, log an error or handle it appropriately if (format == null) { logger("Unknown video format: $videoUrl") - FirebaseCrashlytics.getInstance().recordException(Exception("Unknown video format: $videoUrl")) + FirebaseCrashlytics.getInstance() + .recordException(Exception("Unknown video format: $videoUrl")) format = VideoType.CONTAINER } val headersMap: Map = aniVideo.headers?.toMultimap()?.mapValues { it.value.joinToString() } ?: mapOf() - return ani.dantotsu.parsers.Video( + return Video( number, format, FileUrl(videoUrl, headersMap), @@ -647,7 +648,7 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() { private fun findSubtitleType(url: String): SubtitleType? { // First, try to determine the type based on the URL file extension - val type: SubtitleType? = when { + val type: SubtitleType = when { url.endsWith(".vtt", true) -> SubtitleType.VTT url.endsWith(".ass", true) -> SubtitleType.ASS url.endsWith(".srt", true) -> SubtitleType.SRT diff --git a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt index 798d9c77..a28ef295 100644 --- a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt @@ -1,13 +1,18 @@ package ani.dantotsu.parsers -import ani.dantotsu.* +import ani.dantotsu.FileUrl +import ani.dantotsu.R +import ani.dantotsu.currContext +import ani.dantotsu.loadData +import ani.dantotsu.logger import ani.dantotsu.media.Media +import ani.dantotsu.saveData import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.source.model.SManga +import me.xdrop.fuzzywuzzy.FuzzySearch import java.io.Serializable import java.net.URLDecoder import java.net.URLEncoder -import me.xdrop.fuzzywuzzy.FuzzySearch abstract class BaseParser { @@ -62,18 +67,32 @@ abstract class BaseParser { logger("Result: ${it.name}") } val sortedResults = if (results.isNotEmpty()) { - results.sortedByDescending { FuzzySearch.ratio(it.name.lowercase(), mediaObj.mainName().lowercase()) } + results.sortedByDescending { + FuzzySearch.ratio( + it.name.lowercase(), + mediaObj.mainName().lowercase() + ) + } } else { emptyList() } response = sortedResults.firstOrNull() - if (response == null || FuzzySearch.ratio(response.name.lowercase(), mediaObj.mainName().lowercase()) < 100) { + if (response == null || FuzzySearch.ratio( + response.name.lowercase(), + mediaObj.mainName().lowercase() + ) < 100 + ) { setUserText("Searching : ${mediaObj.nameRomaji}") logger("Searching : ${mediaObj.nameRomaji}") val romajiResults = search(mediaObj.nameRomaji) val sortedRomajiResults = if (romajiResults.isNotEmpty()) { - romajiResults.sortedByDescending { FuzzySearch.ratio(it.name.lowercase(), mediaObj.nameRomaji.lowercase()) } + romajiResults.sortedByDescending { + FuzzySearch.ratio( + it.name.lowercase(), + mediaObj.nameRomaji.lowercase() + ) + } } else { emptyList() } @@ -84,8 +103,14 @@ abstract class BaseParser { logger("No exact match found in results. Using closest match from RomajiResults.") closestRomaji } else { - val romajiRatio = FuzzySearch.ratio(closestRomaji?.name?.lowercase() ?: "", mediaObj.nameRomaji.lowercase()) - val mainNameRatio = FuzzySearch.ratio(response.name.lowercase(), mediaObj.mainName().lowercase()) + val romajiRatio = FuzzySearch.ratio( + closestRomaji?.name?.lowercase() ?: "", + mediaObj.nameRomaji.lowercase() + ) + val mainNameRatio = FuzzySearch.ratio( + response.name.lowercase(), + mediaObj.mainName().lowercase() + ) logger("Fuzzy ratio for closest match in results: $mainNameRatio for ${response.name.lowercase()}") logger("Fuzzy ratio for closest match in RomajiResults: $romajiRatio for ${closestRomaji?.name?.lowercase() ?: "None"}") @@ -119,7 +144,13 @@ abstract class BaseParser { open fun saveShowResponse(mediaId: Int, response: ShowResponse?, selected: Boolean = false) { if (response != null) { checkIfVariablesAreEmpty() - setUserText("${if (selected) currContext()!!.getString(R.string.selected) else currContext()!!.getString(R.string.found)} : ${response.name}") + setUserText( + "${ + if (selected) currContext()!!.getString(R.string.selected) else currContext()!!.getString( + R.string.found + ) + } : ${response.name}" + ) saveData("${saveName}_$mediaId", response) } } @@ -167,7 +198,7 @@ data class ShowResponse( val total: Int? = null, //In case you want to sent some extra data - val extra : MutableMap?=null, + val extra: MutableMap? = null, //SAnime object from Aniyomi val sAnime: SAnime? = null, @@ -175,10 +206,23 @@ data class ShowResponse( //SManga object from Aniyomi val sManga: SManga? = null ) : Serializable { - constructor(name: String, link: String, coverUrl: String, otherNames: List = listOf(), total: Int? = null, extra: MutableMap?=null) + constructor( + name: String, + link: String, + coverUrl: String, + otherNames: List = listOf(), + total: Int? = null, + extra: MutableMap? = null + ) : this(name, link, FileUrl(coverUrl), otherNames, total, extra) - constructor(name: String, link: String, coverUrl: String, otherNames: List = listOf(), total: Int? = null) + constructor( + name: String, + link: String, + coverUrl: String, + otherNames: List = listOf(), + total: Int? = null + ) : this(name, link, FileUrl(coverUrl), otherNames, total) constructor(name: String, link: String, coverUrl: String, otherNames: List = listOf()) diff --git a/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt b/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt index c1eb6cb4..f8535772 100644 --- a/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt +++ b/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt @@ -2,9 +2,9 @@ package ani.dantotsu.parsers import ani.dantotsu.Lazier import ani.dantotsu.logger +import ani.dantotsu.media.Media import ani.dantotsu.media.anime.Episode import ani.dantotsu.media.manga.MangaChapter -import ani.dantotsu.media.Media import ani.dantotsu.tryWithSuspend import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.source.model.SManga diff --git a/app/src/main/java/ani/dantotsu/parsers/MangaParser.kt b/app/src/main/java/ani/dantotsu/parsers/MangaParser.kt index ed481c9d..8948d495 100644 --- a/app/src/main/java/ani/dantotsu/parsers/MangaParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/MangaParser.kt @@ -1,8 +1,6 @@ package ani.dantotsu.parsers -import android.graphics.Bitmap import ani.dantotsu.FileUrl -import ani.dantotsu.media.Media import ani.dantotsu.media.manga.MangaNameAdapter import com.bumptech.glide.load.resource.bitmap.BitmapTransformation import eu.kanade.tachiyomi.source.model.Page @@ -15,7 +13,11 @@ abstract class MangaParser : BaseParser() { /** * Takes ShowResponse.link and ShowResponse.extra (if any) as arguments & gives a list of total chapters present on the site. * **/ - abstract suspend fun loadChapters(mangaLink: String, extra: Map?, sManga: SManga): List + abstract suspend fun loadChapters( + mangaLink: String, + extra: Map?, + sManga: SManga + ): List /** * Takes ShowResponse.link, ShowResponse.extra & the Last Largest Chapter Number known by app as arguments @@ -23,9 +25,14 @@ abstract class MangaParser : BaseParser() { * Returns the latest chapter (If overriding, Make sure the chapter is actually the latest chapter) * Returns null, if no latest chapter is found. * **/ - open suspend fun getLatestChapter(mangaLink: String, extra: Map?, sManga: SManga, latest: Float): MangaChapter? { - val chapter = loadChapters(mangaLink, extra, sManga) - val max = chapter + open suspend fun getLatestChapter( + mangaLink: String, + extra: Map?, + sManga: SManga, + latest: Float + ): MangaChapter? { + val chapter = loadChapters(mangaLink, extra, sManga) + val max = chapter .maxByOrNull { MangaNameAdapter.findChapterNumber(it.number) ?: 0f } return max ?.takeIf { latest < (MangaNameAdapter.findChapterNumber(it.number) ?: 0.001f) } @@ -40,13 +47,18 @@ abstract class MangaParser : BaseParser() { open fun getTransformation(): BitmapTransformation? = null } -class EmptyMangaParser: MangaParser() { +class EmptyMangaParser : MangaParser() { override val name: String = "None" override val saveName: String = "None" - override suspend fun loadChapters(mangaLink: String, extra: Map?, sManga: SManga): List = emptyList() + override suspend fun loadChapters( + mangaLink: String, + extra: Map?, + sManga: SManga + ): List = emptyList() - override suspend fun loadImages(chapterLink: String, sChapter: SChapter): List = emptyList() + override suspend fun loadImages(chapterLink: String, sChapter: SChapter): List = + emptyList() override suspend fun search(query: String): List = emptyList() } @@ -82,7 +94,7 @@ data class MangaImage( val useTransformation: Boolean = false, val page: Page? = null, -) : Serializable{ - constructor(url: String,useTransformation: Boolean=false, page: Page? = null) - : this(FileUrl(url),useTransformation, page) +) : Serializable { + constructor(url: String, useTransformation: Boolean = false, page: Page? = null) + : this(FileUrl(url), useTransformation, page) } diff --git a/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt b/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt index 511049ce..0f8a5642 100644 --- a/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt +++ b/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt @@ -15,11 +15,17 @@ object MangaSources : MangaReadSources() { suspend fun init(fromExtensions: StateFlow>) { // Initialize with the first value from StateFlow val initialExtensions = fromExtensions.first() - list = createParsersFromExtensions(initialExtensions) + Lazier({ OfflineMangaParser() }, "Downloaded") + list = createParsersFromExtensions(initialExtensions) + Lazier( + { OfflineMangaParser() }, + "Downloaded" + ) // Update as StateFlow emits new values fromExtensions.collect { extensions -> - list = createParsersFromExtensions(extensions) + Lazier({ OfflineMangaParser() }, "Downloaded") + list = createParsersFromExtensions(extensions) + Lazier( + { OfflineMangaParser() }, + "Downloaded" + ) } } @@ -34,7 +40,8 @@ object MangaSources : MangaReadSources() { object HMangaSources : MangaReadSources() { val aList: List> = lazyList() suspend fun init(fromExtensions: StateFlow>) { - //todo + //todo } - override val list = listOf(aList,MangaSources.list).flatten() + + override val list = listOf(aList, MangaSources.list).flatten() } diff --git a/app/src/main/java/ani/dantotsu/parsers/NovelInterface.kt b/app/src/main/java/ani/dantotsu/parsers/NovelInterface.kt index a57bc283..af3be5cd 100644 --- a/app/src/main/java/ani/dantotsu/parsers/NovelInterface.kt +++ b/app/src/main/java/ani/dantotsu/parsers/NovelInterface.kt @@ -1,4 +1,5 @@ package ani.dantotsu.parsers + import com.lagradost.nicehttp.Requests diff --git a/app/src/main/java/ani/dantotsu/parsers/NovelParser.kt b/app/src/main/java/ani/dantotsu/parsers/NovelParser.kt index 33bc00d6..c9ed1cf1 100644 --- a/app/src/main/java/ani/dantotsu/parsers/NovelParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/NovelParser.kt @@ -9,7 +9,7 @@ abstract class NovelParser : BaseParser() { abstract suspend fun loadBook(link: String, extra: Map?): Book - fun List.sortByVolume(query:String) : List { + fun List.sortByVolume(query: String): List { val sorted = groupBy { res -> val match = volumeRegex.find(res.name)?.groupValues ?.firstOrNull { it.isNotEmpty() } @@ -42,7 +42,12 @@ data class Book( val description: String? = null, val links: List ) { - constructor (name: String, img: String, description: String? = null, links: List) : this( + constructor ( + name: String, + img: String, + description: String? = null, + links: List + ) : this( name, FileUrl(img), description, diff --git a/app/src/main/java/ani/dantotsu/parsers/NovelSources.kt b/app/src/main/java/ani/dantotsu/parsers/NovelSources.kt index 225887b5..3fdcaae2 100644 --- a/app/src/main/java/ani/dantotsu/parsers/NovelSources.kt +++ b/app/src/main/java/ani/dantotsu/parsers/NovelSources.kt @@ -2,10 +2,10 @@ package ani.dantotsu.parsers import android.util.Log import ani.dantotsu.Lazier +import ani.dantotsu.parsers.novel.DynamicNovelParser import ani.dantotsu.parsers.novel.NovelExtension import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first -import ani.dantotsu.parsers.novel.DynamicNovelParser object NovelSources : NovelReadSources() { override var list: List> = emptyList() diff --git a/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt b/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt index 3684ee8b..218a0f9a 100644 --- a/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt @@ -12,7 +12,7 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.io.File -class OfflineMangaParser: MangaParser() { +class OfflineMangaParser : MangaParser() { private val downloadManager = Injekt.get() override val hostUrl: String = "Offline" @@ -32,7 +32,14 @@ class OfflineMangaParser: MangaParser() { if (directory.exists()) { directory.listFiles()?.forEach { if (it.isDirectory) { - val chapter = MangaChapter(it.name, "$mangaLink/${it.name}", it.name, null, null, SChapter.create()) + val chapter = MangaChapter( + it.name, + "$mangaLink/${it.name}", + it.name, + null, + null, + SChapter.create() + ) chapters.add(chapter) } } diff --git a/app/src/main/java/ani/dantotsu/parsers/StringMatcher.kt b/app/src/main/java/ani/dantotsu/parsers/StringMatcher.kt index a7bf0cd3..191fecff 100644 --- a/app/src/main/java/ani/dantotsu/parsers/StringMatcher.kt +++ b/app/src/main/java/ani/dantotsu/parsers/StringMatcher.kt @@ -58,10 +58,16 @@ class StringMatcher { return shows // Return original list if no closest show found } logger("Closest show found for $target is ${closestShowAndIndex.first.name}") - return listOf(shows[closestIndex]) + shows.subList(0, closestIndex) + shows.subList(closestIndex + 1, shows.size) + return listOf(shows[closestIndex]) + shows.subList(0, closestIndex) + shows.subList( + closestIndex + 1, + shows.size + ) } - private fun closestShow(target: String, shows: List): Pair { + private fun closestShow( + target: String, + shows: List + ): Pair { var minDistance = Int.MAX_VALUE var closestShow = ShowResponse("", "", "") var closestIndex = -1 diff --git a/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt b/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt index fc6ae7c8..ce56182c 100644 --- a/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt +++ b/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt @@ -56,14 +56,19 @@ abstract class VideoExtractor : Serializable { data class VideoServer( val name: String, val embed: FileUrl, - val extraData : Map?=null, + val extraData: Map? = null, val video: eu.kanade.tachiyomi.animesource.model.Video? = null ) : Serializable { - constructor(name: String, embedUrl: String,extraData: Map?=null) - : this(name, FileUrl(embedUrl),extraData) + constructor(name: String, embedUrl: String, extraData: Map? = null) + : this(name, FileUrl(embedUrl), extraData) - constructor(name: String, embedUrl: String,extraData: Map?=null, video: eu.kanade.tachiyomi.animesource.model.Video?) - : this(name, FileUrl(embedUrl),extraData, video) + constructor( + name: String, + embedUrl: String, + extraData: Map? = null, + video: eu.kanade.tachiyomi.animesource.model.Video? + ) + : this(name, FileUrl(embedUrl), extraData, video) } /** @@ -117,7 +122,13 @@ data class Video( val extraNote: String? = null, ) : Serializable { - constructor(quality: Int? = null, videoType: VideoType, url: String, size: Double?, extraNote: String? = null) + constructor( + quality: Int? = null, + videoType: VideoType, + url: String, + size: Double?, + extraNote: String? = null + ) : this(quality, videoType, FileUrl(url), size, extraNote) constructor(quality: Int? = null, videoType: VideoType, url: String, size: Double?) @@ -149,15 +160,19 @@ data class Subtitle( * * Supports VTT, SRT & ASS * **/ - val type:SubtitleType = SubtitleType.VTT, + val type: SubtitleType = SubtitleType.VTT, ) : Serializable { - constructor(language: String, url: String, type: SubtitleType = SubtitleType.VTT) : this(language, FileUrl(url), type) + constructor(language: String, url: String, type: SubtitleType = SubtitleType.VTT) : this( + language, + FileUrl(url), + type + ) } -enum class VideoType{ +enum class VideoType { CONTAINER, M3U8, DASH } -enum class SubtitleType{ +enum class SubtitleType { VTT, ASS, SRT, UNKNOWN } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/parsers/novel/NovelAdapter.kt b/app/src/main/java/ani/dantotsu/parsers/novel/NovelAdapter.kt index 87bab797..13da2ce2 100644 --- a/app/src/main/java/ani/dantotsu/parsers/novel/NovelAdapter.kt +++ b/app/src/main/java/ani/dantotsu/parsers/novel/NovelAdapter.kt @@ -1,6 +1,5 @@ package ani.dantotsu.parsers.novel -import ani.dantotsu.FileUrl import ani.dantotsu.parsers.Book import ani.dantotsu.parsers.NovelInterface import ani.dantotsu.parsers.NovelParser @@ -9,13 +8,14 @@ import eu.kanade.tachiyomi.network.NetworkHelper import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -class NovelAdapter { -} +class NovelAdapter class DynamicNovelParser(extension: NovelExtension.Installed) : NovelParser() { - override val volumeRegex = Regex("vol\\.? (\\d+(\\.\\d+)?)|volume (\\d+(\\.\\d+)?)", RegexOption.IGNORE_CASE) + override val volumeRegex = + Regex("vol\\.? (\\d+(\\.\\d+)?)|volume (\\d+(\\.\\d+)?)", RegexOption.IGNORE_CASE) var extension: NovelExtension.Installed val client = Injekt.get().requestClient + init { this.extension = extension } diff --git a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionGithubApi.kt b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionGithubApi.kt index 2354a311..f4a2edcc 100644 --- a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionGithubApi.kt +++ b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionGithubApi.kt @@ -7,7 +7,6 @@ import ani.dantotsu.logger import eu.kanade.tachiyomi.extension.ExtensionUpdateNotifier import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension import eu.kanade.tachiyomi.extension.anime.model.AnimeLoadResult -import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionLoader import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.awaitSuccess @@ -27,7 +26,9 @@ class NovelExtensionGithubApi { private val novelExtensionManager: NovelExtensionManager by injectLazy() private val json: Json by injectLazy() - private val lastExtCheck: Long = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.getLong("last_ext_check", 0)?:0 + private val lastExtCheck: Long = + currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + ?.getLong("last_ext_check", 0) ?: 0 private var requiresFallbackSource = false @@ -72,7 +73,10 @@ class NovelExtensionGithubApi { } } - suspend fun checkForUpdates(context: Context, fromAvailableExtensionList: Boolean = false): List? { + suspend fun checkForUpdates( + context: Context, + fromAvailableExtensionList: Boolean = false + ): List? { // Limit checks to once a day at most if (fromAvailableExtensionList && Date().time < lastExtCheck + 1.days.inWholeMilliseconds) { return null @@ -81,7 +85,10 @@ class NovelExtensionGithubApi { val extensions = if (fromAvailableExtensionList) { novelExtensionManager.availableExtensionsFlow.value } else { - findExtensions().also { context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()?.putLong("last_ext_check", Date().time)?.apply() } + findExtensions().also { + context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit() + ?.putLong("last_ext_check", Date().time)?.apply() + } } val installedExtensions = NovelExtensionLoader.loadExtensions(context) @@ -143,6 +150,7 @@ class NovelExtensionGithubApi { fun getApkUrl(extension: NovelExtension.Available): String { return "${getUrlPrefix()}apk/${extension.pkgName}.apk" } + private fun getUrlPrefix(): String { return if (requiresFallbackSource) { FALLBACK_REPO_URL_PREFIX @@ -152,8 +160,11 @@ class NovelExtensionGithubApi { } } -private const val REPO_URL_PREFIX = "https://raw.githubusercontent.com/dannovels/novel-extensions/main/" -private const val FALLBACK_REPO_URL_PREFIX = "https://gcore.jsdelivr.net/gh/dannovels/novel-extensions@latest/" +private const val REPO_URL_PREFIX = + "https://raw.githubusercontent.com/dannovels/novel-extensions/main/" +private const val FALLBACK_REPO_URL_PREFIX = + "https://gcore.jsdelivr.net/gh/dannovels/novel-extensions@latest/" + @Serializable private data class NovelExtensionJsonObject( val name: String, diff --git a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionInstallReceiver.kt b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionInstallReceiver.kt index f1f52083..7835cb1d 100644 --- a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionInstallReceiver.kt +++ b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionInstallReceiver.kt @@ -1,34 +1,18 @@ package ani.dantotsu.parsers.novel -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.os.Build import android.os.FileObserver import android.util.Log -import androidx.annotation.RequiresApi -import androidx.core.content.ContextCompat import ani.dantotsu.parsers.novel.FileObserver.fileObserver -import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension -import eu.kanade.tachiyomi.extension.anime.model.AnimeLoadResult -import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionLoader -import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async -import logcat.LogPriority -import tachiyomi.core.util.lang.launchNow -import tachiyomi.core.util.system.logcat import java.io.File -import java.lang.Exception -class NovelExtensionFileObserver(private val listener: Listener, private val path: String) : FileObserver(path, CREATE or DELETE or MOVED_FROM or MOVED_TO or MODIFY) { +class NovelExtensionFileObserver(private val listener: Listener, private val path: String) : + FileObserver(path, CREATE or DELETE or MOVED_FROM or MOVED_TO or MODIFY) { init { fileObserver = this } + /** * Starts observing the file changes in the directory. */ @@ -48,10 +32,12 @@ class NovelExtensionFileObserver(private val listener: Listener, private val pat Log.e("NovelExtensionFileObserver", "File created: $fullPath") listener.onExtensionFileCreated(fullPath) } + DELETE -> { Log.e("NovelExtensionFileObserver", "File deleted: $fullPath") listener.onExtensionFileDeleted(fullPath) } + MODIFY -> { Log.e("NovelExtensionFileObserver", "File modified: $fullPath") listener.onExtensionFileModified(fullPath) diff --git a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionInstaller.kt b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionInstaller.kt index 7fef6f22..8bbf0561 100644 --- a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionInstaller.kt +++ b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionInstaller.kt @@ -91,8 +91,12 @@ internal class NovelExtensionInstaller(private val context: Context) { val downloadUri = url.toUri() val request = DownloadManager.Request(downloadUri) .setTitle(extension.name) - .setMimeType(NovelExtensionInstaller.APK_MIME) - .setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, downloadUri.lastPathSegment) + .setMimeType(APK_MIME) + .setDestinationInExternalFilesDir( + context, + Environment.DIRECTORY_DOWNLOADS, + downloadUri.lastPathSegment + ) .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) val id = downloadManager.enqueue(request) activeDownloads[pkgName] = id @@ -144,11 +148,14 @@ internal class NovelExtensionInstaller(private val context: Context) { } } - fun installApk(downloadId: Long, uri: Uri, context: Context, pkgName: String) : InstallStep { - val sourcePath = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath + "/" + uri.lastPathSegment - val destinationPath = context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/$pkgName.apk" + fun installApk(downloadId: Long, uri: Uri, context: Context, pkgName: String): InstallStep { + val sourcePath = + context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath + "/" + uri.lastPathSegment + val destinationPath = + context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/$pkgName.apk" - val destinationPathDirectory = context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/" + val destinationPathDirectory = + context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/" val destinationPathDirectoryFile = File(destinationPathDirectory) @@ -164,7 +171,7 @@ internal class NovelExtensionInstaller(private val context: Context) { if (destinationDir?.exists() == false) { destinationDir.mkdirs() } - if(destinationDir?.setWritable(true) == false) { + if (destinationDir?.setWritable(true) == false) { Log.e("Install APK", "Failed to set destinationDir to writable.") downloadsRelay.call(downloadId to InstallStep.Error) return InstallStep.Error @@ -186,7 +193,8 @@ internal class NovelExtensionInstaller(private val context: Context) { } fun uninstallApk(pkgName: String, context: Context) { - val apkPath = context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/$pkgName.apk" + val apkPath = + context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/$pkgName.apk" val fileToDelete = File(apkPath) //give write permission to the file if (fileToDelete.exists() && !fileToDelete.canWrite()) { diff --git a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionLoader.kt b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionLoader.kt index c0d5ac18..638617dd 100644 --- a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionLoader.kt +++ b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionLoader.kt @@ -11,9 +11,7 @@ import ani.dantotsu.parsers.NovelInterface import ani.dantotsu.snackString import com.google.firebase.crashlytics.FirebaseCrashlytics import dalvik.system.PathClassLoader -import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionLoader import eu.kanade.tachiyomi.util.lang.Hash -import tachiyomi.core.util.system.logcat import java.io.File import java.util.Locale @@ -27,7 +25,10 @@ internal object NovelExtensionLoader { val results = mutableListOf() //the number of files Log.e("NovelExtensionLoader", "Loading extensions from $installDir") - Log.e("NovelExtensionLoader", "Loading extensions from ${File(installDir).listFiles()?.size}") + Log.e( + "NovelExtensionLoader", + "Loading extensions from ${File(installDir).listFiles()?.size}" + ) File(installDir).setWritable(false) File(installDir).listFiles()?.forEach { //set the file to read only @@ -48,7 +49,8 @@ internal object NovelExtensionLoader { * contains the required feature flag before trying to load it. */ fun loadExtensionFromPkgName(context: Context, pkgName: String): NovelLoadResult { - val path = context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/$pkgName.apk" + val path = + context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/$pkgName.apk" //make /extensions/novel read only context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/".let { File(it).setWritable(false) @@ -67,7 +69,10 @@ internal object NovelExtensionLoader { @Suppress("DEPRECATION") fun loadExtension(context: Context, file: File): NovelLoadResult { val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - context.packageManager.getPackageArchiveInfo(file.absolutePath, GET_SIGNING_CERTIFICATES) + context.packageManager.getPackageArchiveInfo( + file.absolutePath, + GET_SIGNING_CERTIFICATES + ) ?: return NovelLoadResult.Error(Exception("Failed to load extension")) } else { context.packageManager.getPackageArchiveInfo(file.absolutePath, GET_SIGNATURES) @@ -75,8 +80,8 @@ internal object NovelExtensionLoader { } val appInfo = packageInfo.applicationInfo ?: return NovelLoadResult.Error(Exception("Failed to load Extension Info")) - appInfo.sourceDir = file.absolutePath; - appInfo.publicSourceDir = file.absolutePath; + appInfo.sourceDir = file.absolutePath + appInfo.publicSourceDir = file.absolutePath val signatureHash = getSignatureHash(packageInfo) @@ -88,13 +93,14 @@ internal object NovelExtensionLoader { } val extension = NovelExtension.Installed( - packageInfo.applicationInfo?.loadLabel(context.packageManager)?.toString() ?: - return NovelLoadResult.Error(Exception("Failed to load Extension Info")), + packageInfo.applicationInfo?.loadLabel(context.packageManager)?.toString() + ?: return NovelLoadResult.Error(Exception("Failed to load Extension Info")), packageInfo.packageName ?: return NovelLoadResult.Error(Exception("Failed to load Extension Info")), packageInfo.versionName ?: "", - packageInfo.versionCode.toLong() ?: 0, - loadSources(context, file, + packageInfo.versionCode.toLong(), + loadSources( + context, file, packageInfo.applicationInfo?.loadLabel(context.packageManager)?.toString()!! ), packageInfo.applicationInfo?.loadIcon(context.packageManager) @@ -126,7 +132,8 @@ internal object NovelExtensionLoader { } Log.e("NovelExtensionLoader", "isFileWritable: ${file.canWrite()}") val classLoader = PathClassLoader(file.absolutePath, null, context.classLoader) - val className = "some.random.novelextensions.${className.lowercase(Locale.getDefault())}.$className" + val className = + "some.random.novelextensions.${className.lowercase(Locale.getDefault())}.$className" val loadedClass = classLoader.loadClass(className) val instance = loadedClass.newInstance() val novelInterfaceInstance = instance as? NovelInterface diff --git a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionManager.kt b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionManager.kt index 843e6fed..c7a9723c 100644 --- a/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionManager.kt +++ b/app/src/main/java/ani/dantotsu/parsers/novel/NovelExtensionManager.kt @@ -2,7 +2,6 @@ package ani.dantotsu.parsers.novel import android.content.Context import android.graphics.drawable.Drawable -import android.os.Build import ani.dantotsu.logger import ani.dantotsu.snackString import eu.kanade.tachiyomi.extension.InstallStep @@ -51,7 +50,7 @@ class NovelExtensionManager(private val context: Context) { init { initNovelExtensions() val path = context.getExternalFilesDir(null)?.absolutePath + "/extensions/novel/" - NovelExtensionFileObserver(NovelInstallationListener(),path).register() + NovelExtensionFileObserver(NovelInstallationListener(), path).register() } private fun initNovelExtensions() { @@ -129,8 +128,9 @@ class NovelExtensionManager(private val context: Context) { * @param extension The anime extension to be updated. */ fun updateExtension(extension: NovelExtension.Installed): Observable { - val availableExt = _availableNovelExtensionsFlow.value.find { it.pkgName == extension.pkgName } - ?: return Observable.empty() + val availableExt = + _availableNovelExtensionsFlow.value.find { it.pkgName == extension.pkgName } + ?: return Observable.empty() return installExtension(availableExt) } @@ -192,7 +192,8 @@ class NovelExtensionManager(private val context: Context) { * @param pkgName The package name of the uninstalled application. */ private fun unregisterNovelExtension(pkgName: String) { - val installedNovelExtension = _installedNovelExtensionsFlow.value.find { it.pkgName == pkgName } + val installedNovelExtension = + _installedNovelExtensionsFlow.value.find { it.pkgName == pkgName } if (installedNovelExtension != null) { _installedNovelExtensionsFlow.value -= installedNovelExtension } @@ -210,10 +211,12 @@ class NovelExtensionManager(private val context: Context) { } } } + override fun onExtensionFileDeleted(file: File) { val pkgName = file.nameWithoutExtension unregisterNovelExtension(pkgName) } + override fun onExtensionFileModified(file: File) { NovelExtensionLoader.loadExtension(context, file).let { if (it is NovelLoadResult.Success) { @@ -235,7 +238,8 @@ class NovelExtensionManager(private val context: Context) { } private fun NovelExtension.Installed.updateExists(availableNovelExtension: NovelExtension.Available? = null): Boolean { - val availableExt = availableNovelExtension ?: _availableNovelExtensionsFlow.value.find { it.pkgName == pkgName } + val availableExt = availableNovelExtension + ?: _availableNovelExtensionsFlow.value.find { it.pkgName == pkgName } if (isUnofficial || availableExt == null) return false return (availableExt.versionCode > versionCode) diff --git a/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt index c706d6cf..e46b1d71 100644 --- a/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/AnimeExtensionsFragment.kt @@ -53,7 +53,8 @@ class AnimeExtensionsFragment : Fragment(), binding.allAnimeExtensionsRecyclerView.isNestedScrollingEnabled = false binding.allAnimeExtensionsRecyclerView.adapter = adapter binding.allAnimeExtensionsRecyclerView.layoutManager = LinearLayoutManager(context) - (binding.allAnimeExtensionsRecyclerView.layoutManager as LinearLayoutManager).isItemPrefetchEnabled = true + (binding.allAnimeExtensionsRecyclerView.layoutManager as LinearLayoutManager).isItemPrefetchEnabled = + true lifecycleScope.launch { viewModel.pagerFlow.collectLatest { diff --git a/app/src/main/java/ani/dantotsu/settings/DevelopersAdapter.kt b/app/src/main/java/ani/dantotsu/settings/DevelopersAdapter.kt index 545b5208..7375d101 100644 --- a/app/src/main/java/ani/dantotsu/settings/DevelopersAdapter.kt +++ b/app/src/main/java/ani/dantotsu/settings/DevelopersAdapter.kt @@ -11,9 +11,11 @@ import ani.dantotsu.setAnimation class DevelopersAdapter(private val developers: Array) : RecyclerView.Adapter() { - private val uiSettings = loadData("ui_settings") ?: UserInterfaceSettings() + private val uiSettings = + loadData("ui_settings") ?: UserInterfaceSettings() - inner class DeveloperViewHolder(val binding: ItemDeveloperBinding) : RecyclerView.ViewHolder(binding.root) { + inner class DeveloperViewHolder(val binding: ItemDeveloperBinding) : + RecyclerView.ViewHolder(binding.root) { init { itemView.setOnClickListener { openLinkInBrowser(developers[bindingAdapterPosition].url) @@ -22,7 +24,13 @@ class DevelopersAdapter(private val developers: Array) : } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DeveloperViewHolder { - return DeveloperViewHolder(ItemDeveloperBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + return DeveloperViewHolder( + ItemDeveloperBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) } override fun onBindViewHolder(holder: DeveloperViewHolder, position: Int) { diff --git a/app/src/main/java/ani/dantotsu/settings/DevelopersDialogFragment.kt b/app/src/main/java/ani/dantotsu/settings/DevelopersDialogFragment.kt index 0ddd0227..68542836 100644 --- a/app/src/main/java/ani/dantotsu/settings/DevelopersDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/DevelopersDialogFragment.kt @@ -13,13 +13,37 @@ class DevelopersDialogFragment : BottomSheetDialogFragment() { private val binding get() = _binding!! private val developers = arrayOf( - Developer("rebelonion","https://avatars.githubusercontent.com/u/87634197?v=4","Owner and Maintainer","https://github.com/rebelonion"), - Developer("Wai What", "https://avatars.githubusercontent.com/u/149729762?v=4", "Icon Designer", "https://github.com/WaiWhat"), - Developer("Aayush262", "https://avatars.githubusercontent.com/u/99584765?v=4", "Contributor", "https://github.com/aayush2622"), - Developer("MarshMeadow", "https://avatars.githubusercontent.com/u/88599122?v=4", "Beta Icon Designer", "https://github.com/MarshMeadow?tab=repositories"), + Developer( + "rebelonion", + "https://avatars.githubusercontent.com/u/87634197?v=4", + "Owner and Maintainer", + "https://github.com/rebelonion" + ), + Developer( + "Wai What", + "https://avatars.githubusercontent.com/u/149729762?v=4", + "Icon Designer", + "https://github.com/WaiWhat" + ), + Developer( + "Aayush262", + "https://avatars.githubusercontent.com/u/99584765?v=4", + "Contributor", + "https://github.com/aayush2622" + ), + Developer( + "MarshMeadow", + "https://avatars.githubusercontent.com/u/88599122?v=4", + "Beta Icon Designer", + "https://github.com/MarshMeadow?tab=repositories" + ), ) - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { _binding = BottomSheetDevelopersBinding.inflate(inflater, container, false) return binding.root } diff --git a/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt b/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt index 90d7656f..b3221425 100644 --- a/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/ExtensionsActivity.kt @@ -6,11 +6,8 @@ import android.os.Build.VERSION.* import android.os.Bundle import android.text.Editable import android.text.TextWatcher -import android.view.View import android.view.ViewGroup import android.widget.AutoCompleteTextView -import android.widget.LinearLayout -import android.widget.SearchView import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.PopupMenu @@ -20,16 +17,13 @@ import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.* import ani.dantotsu.databinding.ActivityExtensionsBinding -import ani.dantotsu.themes.ThemeManager import ani.dantotsu.others.LangSet +import ani.dantotsu.themes.ThemeManager import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -class ExtensionsActivity : AppCompatActivity() { +class ExtensionsActivity : AppCompatActivity() { private val restartMainActivity = object : OnBackPressedCallback(false) { override fun handleOnBackPressed() = startMainActivity(this@ExtensionsActivity) } @@ -104,7 +98,8 @@ class ExtensionsActivity : AppCompatActivity() { } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - val currentFragment = supportFragmentManager.findFragmentByTag("f${viewPager.currentItem}") + val currentFragment = + supportFragmentManager.findFragmentByTag("f${viewPager.currentItem}") if (currentFragment is SearchQueryHandler) { currentFragment.updateContentBasedOnQuery(s?.toString()?.trim()) } diff --git a/app/src/main/java/ani/dantotsu/settings/FAQActivity.kt b/app/src/main/java/ani/dantotsu/settings/FAQActivity.kt index bd10b561..c03ba9e1 100644 --- a/app/src/main/java/ani/dantotsu/settings/FAQActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/FAQActivity.kt @@ -7,8 +7,8 @@ import ani.dantotsu.R import ani.dantotsu.currContext import ani.dantotsu.databinding.ActivityFaqBinding import ani.dantotsu.initActivity -import ani.dantotsu.themes.ThemeManager import ani.dantotsu.others.LangSet +import ani.dantotsu.themes.ThemeManager class FAQActivity : AppCompatActivity() { private lateinit var binding: ActivityFaqBinding @@ -107,7 +107,7 @@ class FAQActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) LangSet.setLocale(this) -ThemeManager(this).applyTheme() + ThemeManager(this).applyTheme() binding = ActivityFaqBinding.inflate(layoutInflater) setContentView(binding.root) diff --git a/app/src/main/java/ani/dantotsu/settings/FAQAdapter.kt b/app/src/main/java/ani/dantotsu/settings/FAQAdapter.kt index 39f91e90..f854bd89 100644 --- a/app/src/main/java/ani/dantotsu/settings/FAQAdapter.kt +++ b/app/src/main/java/ani/dantotsu/settings/FAQAdapter.kt @@ -12,14 +12,25 @@ import ani.dantotsu.setAnimation import io.noties.markwon.Markwon import io.noties.markwon.SoftBreakAddsNewLinePlugin -class FAQAdapter(private val questions: List>, private val manager: FragmentManager) : +class FAQAdapter( + private val questions: List>, + private val manager: FragmentManager +) : RecyclerView.Adapter() { - private val uiSettings = loadData("ui_settings") ?: UserInterfaceSettings() + private val uiSettings = + loadData("ui_settings") ?: UserInterfaceSettings() - inner class FAQViewHolder(val binding: ItemQuestionBinding) : RecyclerView.ViewHolder(binding.root) + inner class FAQViewHolder(val binding: ItemQuestionBinding) : + RecyclerView.ViewHolder(binding.root) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FAQViewHolder { - return FAQViewHolder(ItemQuestionBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + return FAQViewHolder( + ItemQuestionBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) } override fun onBindViewHolder(holder: FAQViewHolder, position: Int) { @@ -33,7 +44,8 @@ class FAQAdapter(private val questions: List>, priva setTitleText(faq.second) addView( TextView(b.context).apply { - val markWon = Markwon.builder(b.context).usePlugin(SoftBreakAddsNewLinePlugin.create()).build() + val markWon = Markwon.builder(b.context) + .usePlugin(SoftBreakAddsNewLinePlugin.create()).build() markWon.setMarkdown(this, faq.third) } ) diff --git a/app/src/main/java/ani/dantotsu/settings/ForksDialogFragment.kt b/app/src/main/java/ani/dantotsu/settings/ForksDialogFragment.kt index d4994edb..b1a4db01 100644 --- a/app/src/main/java/ani/dantotsu/settings/ForksDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/ForksDialogFragment.kt @@ -14,10 +14,19 @@ class ForksDialogFragment : BottomSheetDialogFragment() { private val binding get() = _binding!! private val developers = arrayOf( - Developer("Dantotsu","https://avatars.githubusercontent.com/u/87634197?v=4","rebelonion","https://github.com/rebelonion/Dantotsu"), + Developer( + "Dantotsu", + "https://avatars.githubusercontent.com/u/87634197?v=4", + "rebelonion", + "https://github.com/rebelonion/Dantotsu" + ), ) - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { _binding = BottomSheetDevelopersBinding.inflate(inflater, container, false) return binding.root } diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt index ee7cfa1c..696fb614 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledAnimeExtensionsFragment.kt @@ -24,7 +24,6 @@ import ani.dantotsu.R import ani.dantotsu.databinding.FragmentAnimeExtensionsBinding import ani.dantotsu.loadData import ani.dantotsu.others.LanguageMapper -import ani.dantotsu.saveData import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment import ani.dantotsu.snackString import com.google.android.material.tabs.TabLayout @@ -47,84 +46,86 @@ class InstalledAnimeExtensionsFragment : Fragment() { private lateinit var extensionsRecyclerView: RecyclerView val skipIcons = loadData("skip_extension_icons") ?: false private val animeExtensionManager: AnimeExtensionManager = Injekt.get() - private val extensionsAdapter = AnimeExtensionsAdapter({ pkg -> - val allSettings = pkg.sources.filterIsInstance() - if (allSettings.isNotEmpty()) { - var selectedSetting = allSettings[0] - if (allSettings.size > 1) { - val names = allSettings.map { it.lang }.toTypedArray() - var selectedIndex = 0 - AlertDialog.Builder(requireContext(), R.style.MyPopup) - .setTitle("Select a Source") - .setSingleChoiceItems(names, selectedIndex) { _, which -> - selectedIndex = which - } - .setPositiveButton("OK") { dialog, _ -> - selectedSetting = allSettings[selectedIndex] - dialog.dismiss() - - // Move the fragment transaction here - val eActivity = requireActivity() as ExtensionsActivity - eActivity.runOnUiThread { - val fragment = - AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) { - - eActivity.findViewById(R.id.viewPager).visibility = - View.VISIBLE - eActivity.findViewById(R.id.tabLayout).visibility = - View.VISIBLE - eActivity.findViewById(R.id.searchView).visibility = - View.VISIBLE - eActivity.findViewById(R.id.fragmentExtensionsContainer).visibility = - View.GONE - } - parentFragmentManager.beginTransaction() - .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) - .replace(R.id.fragmentExtensionsContainer, fragment) - .addToBackStack(null) - .commit() + private val extensionsAdapter = AnimeExtensionsAdapter( + { pkg -> + val allSettings = pkg.sources.filterIsInstance() + if (allSettings.isNotEmpty()) { + var selectedSetting = allSettings[0] + if (allSettings.size > 1) { + val names = allSettings.map { it.lang }.toTypedArray() + var selectedIndex = 0 + AlertDialog.Builder(requireContext(), R.style.MyPopup) + .setTitle("Select a Source") + .setSingleChoiceItems(names, selectedIndex) { _, which -> + selectedIndex = which } - } - .setNegativeButton("Cancel") { dialog, _ -> - dialog.cancel() - return@setNegativeButton - } - .show() - } else { - // If there's only one setting, proceed with the fragment transaction - val eActivity = requireActivity() as ExtensionsActivity - eActivity.runOnUiThread { - val fragment = - AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) { + .setPositiveButton("OK") { dialog, _ -> + selectedSetting = allSettings[selectedIndex] + dialog.dismiss() - eActivity.findViewById(R.id.viewPager).visibility = - View.VISIBLE - eActivity.findViewById(R.id.tabLayout).visibility = - View.VISIBLE - eActivity.findViewById(R.id.searchView).visibility = - View.VISIBLE - eActivity.findViewById(R.id.fragmentExtensionsContainer).visibility = - View.GONE + // Move the fragment transaction here + val eActivity = requireActivity() as ExtensionsActivity + eActivity.runOnUiThread { + val fragment = + AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) { + + eActivity.findViewById(R.id.viewPager).visibility = + View.VISIBLE + eActivity.findViewById(R.id.tabLayout).visibility = + View.VISIBLE + eActivity.findViewById(R.id.searchView).visibility = + View.VISIBLE + eActivity.findViewById(R.id.fragmentExtensionsContainer).visibility = + View.GONE + } + parentFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) + .replace(R.id.fragmentExtensionsContainer, fragment) + .addToBackStack(null) + .commit() + } } - parentFragmentManager.beginTransaction() - .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) - .replace(R.id.fragmentExtensionsContainer, fragment) - .addToBackStack(null) - .commit() + .setNegativeButton("Cancel") { dialog, _ -> + dialog.cancel() + return@setNegativeButton + } + .show() + } else { + // If there's only one setting, proceed with the fragment transaction + val eActivity = requireActivity() as ExtensionsActivity + eActivity.runOnUiThread { + val fragment = + AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) { + + eActivity.findViewById(R.id.viewPager).visibility = + View.VISIBLE + eActivity.findViewById(R.id.tabLayout).visibility = + View.VISIBLE + eActivity.findViewById(R.id.searchView).visibility = + View.VISIBLE + eActivity.findViewById(R.id.fragmentExtensionsContainer).visibility = + View.GONE + } + parentFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) + .replace(R.id.fragmentExtensionsContainer, fragment) + .addToBackStack(null) + .commit() + } } - } - // Hide ViewPager2 and TabLayout - val activity = requireActivity() as ExtensionsActivity - activity.findViewById(R.id.viewPager).visibility = View.GONE - activity.findViewById(R.id.tabLayout).visibility = View.GONE - activity.findViewById(R.id.searchView).visibility = View.GONE - activity.findViewById(R.id.fragmentExtensionsContainer).visibility = View.VISIBLE - } else { - Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT) - .show() - } - }, + // Hide ViewPager2 and TabLayout + val activity = requireActivity() as ExtensionsActivity + activity.findViewById(R.id.viewPager).visibility = View.GONE + activity.findViewById(R.id.tabLayout).visibility = View.GONE + activity.findViewById(R.id.searchView).visibility = View.GONE + activity.findViewById(R.id.fragmentExtensionsContainer).visibility = + View.VISIBLE + } else { + Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT) + .show() + } + }, { pkg -> if (isAdded) { // Check if the fragment is currently added to its activity val context = requireContext() // Store context in a variable @@ -228,7 +229,7 @@ class InstalledAnimeExtensionsFragment : Fragment() { override fun onBindViewHolder(holder: ViewHolder, position: Int) { val extension = getItem(position) // Use getItem() from ListAdapter val nsfw = if (extension.isNsfw) "(18+)" else "" - val lang= LanguageMapper.mapLanguageCodeToName(extension.lang) + val lang = LanguageMapper.mapLanguageCodeToName(extension.lang) holder.extensionNameTextView.text = extension.name holder.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw" if (!skipIcons) { @@ -249,10 +250,11 @@ class InstalledAnimeExtensionsFragment : Fragment() { inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView) - val extensionVersionTextView: TextView = view.findViewById(R.id.extensionVersionTextView) + val extensionVersionTextView: TextView = + view.findViewById(R.id.extensionVersionTextView) val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView) val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView) - val closeTextView: ImageView = view.findViewById(R.id.closeTextView) + val closeTextView: ImageView = view.findViewById(R.id.closeTextView) } companion object { diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt index 756dae37..73d8192f 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledMangaExtensionsFragment.kt @@ -24,8 +24,8 @@ import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.R import ani.dantotsu.databinding.FragmentMangaExtensionsBinding import ani.dantotsu.loadData -import ani.dantotsu.settings.extensionprefs.MangaSourcePreferencesFragment import ani.dantotsu.others.LanguageMapper +import ani.dantotsu.settings.extensionprefs.MangaSourcePreferencesFragment import ani.dantotsu.snackString import com.google.android.material.tabs.TabLayout import com.google.android.material.textfield.TextInputLayout @@ -71,9 +71,10 @@ class InstalledMangaExtensionsFragment : Fragment() { dialog.dismiss() // Move the fragment transaction here - val fragment = MangaSourcePreferencesFragment().getInstance(selectedSetting.id){ - changeUIVisibility(true) - } + val fragment = + MangaSourcePreferencesFragment().getInstance(selectedSetting.id) { + changeUIVisibility(true) + } parentFragmentManager.beginTransaction() .setCustomAnimations(R.anim.slide_up, R.anim.slide_down) .replace(R.id.fragmentExtensionsContainer, fragment) @@ -88,7 +89,7 @@ class InstalledMangaExtensionsFragment : Fragment() { .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) } parentFragmentManager.beginTransaction() @@ -106,59 +107,60 @@ class InstalledMangaExtensionsFragment : Fragment() { } }, { pkg -> - if (isAdded) { // Check if the fragment is currently added to its activity - val context = requireContext() // Store context in a variable - val notificationManager = - context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Initialize NotificationManager once + if (isAdded) { // Check if the fragment is currently added to its activity + val context = requireContext() // Store context in a variable + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Initialize NotificationManager once - if (pkg.hasUpdate) { - mangaExtensionManager.updateExtension(pkg) - .observeOn(AndroidSchedulers.mainThread()) // Observe on main thread - .subscribe( - { installStep -> - val builder = NotificationCompat.Builder( - context, - Notifications.CHANNEL_DOWNLOADER_PROGRESS - ) - .setSmallIcon(R.drawable.ic_round_sync_24) - .setContentTitle("Updating extension") - .setContentText("Step: $installStep") - .setPriority(NotificationCompat.PRIORITY_LOW) - notificationManager.notify(1, builder.build()) - }, - { error -> - FirebaseCrashlytics.getInstance().recordException(error) - Log.e("MangaExtensionsAdapter", "Error: ", error) // Log the error - val builder = NotificationCompat.Builder( - context, - Notifications.CHANNEL_DOWNLOADER_ERROR - ) - .setSmallIcon(R.drawable.ic_round_info_24) - .setContentTitle("Update failed: ${error.message}") - .setContentText("Error: ${error.message}") - .setPriority(NotificationCompat.PRIORITY_HIGH) - notificationManager.notify(1, builder.build()) - snackString("Update failed: ${error.message}") - }, - { - val builder = NotificationCompat.Builder( - context, - Notifications.CHANNEL_DOWNLOADER_PROGRESS - ) - .setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check) - .setContentTitle("Update complete") - .setContentText("The extension has been successfully updated.") - .setPriority(NotificationCompat.PRIORITY_LOW) - notificationManager.notify(1, builder.build()) - snackString("Extension updated") - } - ) - } else { - mangaExtensionManager.uninstallExtension(pkg.pkgName) - snackString("Extension uninstalled") + if (pkg.hasUpdate) { + mangaExtensionManager.updateExtension(pkg) + .observeOn(AndroidSchedulers.mainThread()) // Observe on main thread + .subscribe( + { installStep -> + val builder = NotificationCompat.Builder( + context, + Notifications.CHANNEL_DOWNLOADER_PROGRESS + ) + .setSmallIcon(R.drawable.ic_round_sync_24) + .setContentTitle("Updating extension") + .setContentText("Step: $installStep") + .setPriority(NotificationCompat.PRIORITY_LOW) + notificationManager.notify(1, builder.build()) + }, + { error -> + FirebaseCrashlytics.getInstance().recordException(error) + Log.e("MangaExtensionsAdapter", "Error: ", error) // Log the error + val builder = NotificationCompat.Builder( + context, + Notifications.CHANNEL_DOWNLOADER_ERROR + ) + .setSmallIcon(R.drawable.ic_round_info_24) + .setContentTitle("Update failed: ${error.message}") + .setContentText("Error: ${error.message}") + .setPriority(NotificationCompat.PRIORITY_HIGH) + notificationManager.notify(1, builder.build()) + snackString("Update failed: ${error.message}") + }, + { + val builder = NotificationCompat.Builder( + context, + Notifications.CHANNEL_DOWNLOADER_PROGRESS + ) + .setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check) + .setContentTitle("Update complete") + .setContentText("The extension has been successfully updated.") + .setPriority(NotificationCompat.PRIORITY_LOW) + notificationManager.notify(1, builder.build()) + snackString("Extension updated") + } + ) + } else { + mangaExtensionManager.uninstallExtension(pkg.pkgName) + snackString("Extension uninstalled") + } } - } - }, skipIcons) + }, skipIcons + ) override fun onCreateView( inflater: LayoutInflater, @@ -230,7 +232,8 @@ class InstalledMangaExtensionsFragment : Fragment() { inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView) - val extensionVersionTextView: TextView = view.findViewById(R.id.extensionVersionTextView) + val extensionVersionTextView: TextView = + view.findViewById(R.id.extensionVersionTextView) val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView) val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView) val closeTextView: ImageView = view.findViewById(R.id.closeTextView) diff --git a/app/src/main/java/ani/dantotsu/settings/InstalledNovelExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/InstalledNovelExtensionsFragment.kt index dbbc8871..7b4f5f56 100644 --- a/app/src/main/java/ani/dantotsu/settings/InstalledNovelExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/InstalledNovelExtensionsFragment.kt @@ -1,6 +1,5 @@ package ani.dantotsu.settings -import android.app.AlertDialog import android.app.NotificationManager import android.content.Context import android.os.Bundle @@ -8,7 +7,6 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import android.widget.Toast @@ -19,23 +17,17 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView -import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.R import ani.dantotsu.currContext -import ani.dantotsu.databinding.FragmentMangaExtensionsBinding import ani.dantotsu.databinding.FragmentNovelExtensionsBinding import ani.dantotsu.loadData import ani.dantotsu.others.LanguageMapper import ani.dantotsu.parsers.novel.NovelExtension import ani.dantotsu.parsers.novel.NovelExtensionManager import ani.dantotsu.snackString -import com.google.android.material.tabs.TabLayout -import com.google.android.material.textfield.TextInputLayout import com.google.firebase.crashlytics.FirebaseCrashlytics import eu.kanade.tachiyomi.data.notification.Notifications -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import rx.android.schedulers.AndroidSchedulers import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -47,8 +39,8 @@ class InstalledNovelExtensionsFragment : Fragment() { val skipIcons = loadData("skip_extension_icons") ?: false private val novelExtensionManager: NovelExtensionManager = Injekt.get() private val extensionsAdapter = NovelExtensionsAdapter({ pkg -> - Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT) - .show() + Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT) + .show() }, { pkg -> if (isAdded) { // Check if the fragment is currently added to its activity @@ -99,11 +91,12 @@ class InstalledNovelExtensionsFragment : Fragment() { } ) } else { - novelExtensionManager.uninstallExtension(pkg.pkgName, currContext()?:context) + novelExtensionManager.uninstallExtension(pkg.pkgName, currContext() ?: context) snackString("Extension uninstalled") } } - }, skipIcons) + }, skipIcons + ) override fun onCreateView( inflater: LayoutInflater, @@ -126,10 +119,6 @@ class InstalledNovelExtensionsFragment : Fragment() { return binding.root } - override fun onResume() { - super.onResume() - } - override fun onDestroyView() { super.onDestroyView();_binding = null } @@ -159,7 +148,7 @@ class InstalledNovelExtensionsFragment : Fragment() { override fun onBindViewHolder(holder: ViewHolder, position: Int) { val extension = getItem(position) // Use getItem() from ListAdapter - val nsfw = "" + val nsfw = "" val lang = LanguageMapper.mapLanguageCodeToName("all") holder.extensionNameTextView.text = extension.name holder.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw" @@ -181,7 +170,8 @@ class InstalledNovelExtensionsFragment : Fragment() { inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView) - val extensionVersionTextView: TextView = view.findViewById(R.id.extensionVersionTextView) + val extensionVersionTextView: TextView = + view.findViewById(R.id.extensionVersionTextView) val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView) val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView) val closeTextView: ImageView = view.findViewById(R.id.closeTextView) diff --git a/app/src/main/java/ani/dantotsu/settings/MangaExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/MangaExtensionsFragment.kt index 04e7bc88..e3d14ee6 100644 --- a/app/src/main/java/ani/dantotsu/settings/MangaExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/MangaExtensionsFragment.kt @@ -10,25 +10,23 @@ import androidx.core.app.NotificationCompat import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope -import androidx.paging.PagingData import androidx.recyclerview.widget.LinearLayoutManager import ani.dantotsu.R import ani.dantotsu.databinding.FragmentMangaExtensionsBinding -import com.google.firebase.crashlytics.FirebaseCrashlytics -import eu.kanade.tachiyomi.data.notification.Notifications -import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager -import eu.kanade.tachiyomi.extension.manga.model.MangaExtension -import kotlinx.coroutines.launch -import rx.android.schedulers.AndroidSchedulers -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import ani.dantotsu.settings.paging.MangaExtensionAdapter import ani.dantotsu.settings.paging.MangaExtensionsViewModel import ani.dantotsu.settings.paging.MangaExtensionsViewModelFactory import ani.dantotsu.settings.paging.OnMangaInstallClickListener import ani.dantotsu.snackString -import kotlinx.coroutines.flow.Flow +import com.google.firebase.crashlytics.FirebaseCrashlytics +import eu.kanade.tachiyomi.data.notification.Notifications +import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager +import eu.kanade.tachiyomi.extension.manga.model.MangaExtension import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +import rx.android.schedulers.AndroidSchedulers +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get class MangaExtensionsFragment : Fragment(), SearchQueryHandler, OnMangaInstallClickListener { @@ -56,7 +54,8 @@ class MangaExtensionsFragment : Fragment(), binding.allMangaExtensionsRecyclerView.isNestedScrollingEnabled = false binding.allMangaExtensionsRecyclerView.adapter = adapter binding.allMangaExtensionsRecyclerView.layoutManager = LinearLayoutManager(context) - (binding.allMangaExtensionsRecyclerView.layoutManager as LinearLayoutManager).isItemPrefetchEnabled = true + (binding.allMangaExtensionsRecyclerView.layoutManager as LinearLayoutManager).isItemPrefetchEnabled = + true lifecycleScope.launch { viewModel.pagerFlow.collectLatest { pagingData -> @@ -129,5 +128,4 @@ class MangaExtensionsFragment : Fragment(), } - } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/settings/NovelExtensionsFragment.kt b/app/src/main/java/ani/dantotsu/settings/NovelExtensionsFragment.kt index 03bcebd2..f042275d 100644 --- a/app/src/main/java/ani/dantotsu/settings/NovelExtensionsFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/NovelExtensionsFragment.kt @@ -13,8 +13,6 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import ani.dantotsu.R import ani.dantotsu.databinding.FragmentNovelExtensionsBinding -import ani.dantotsu.logger -import ani.dantotsu.parsers.novel.FileObserver.fileObserver import ani.dantotsu.parsers.novel.NovelExtension import ani.dantotsu.parsers.novel.NovelExtensionManager import ani.dantotsu.settings.paging.NovelExtensionAdapter @@ -24,11 +22,7 @@ import ani.dantotsu.settings.paging.OnNovelInstallClickListener import ani.dantotsu.snackString import com.google.firebase.crashlytics.FirebaseCrashlytics import eu.kanade.tachiyomi.data.notification.Notifications -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.observeOn -import kotlinx.coroutines.flow.subscribe import kotlinx.coroutines.launch import rx.android.schedulers.AndroidSchedulers import uy.kohesive.injekt.Injekt @@ -59,7 +53,8 @@ class NovelExtensionsFragment : Fragment(), binding.allNovelExtensionsRecyclerView.isNestedScrollingEnabled = false binding.allNovelExtensionsRecyclerView.adapter = adapter binding.allNovelExtensionsRecyclerView.layoutManager = LinearLayoutManager(context) - (binding.allNovelExtensionsRecyclerView.layoutManager as LinearLayoutManager).isItemPrefetchEnabled = true + (binding.allNovelExtensionsRecyclerView.layoutManager as LinearLayoutManager).isItemPrefetchEnabled = + true lifecycleScope.launch { viewModel.pagerFlow.collectLatest { pagingData -> @@ -132,5 +127,4 @@ class NovelExtensionsFragment : Fragment(), } - } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt index 17fbdb15..9b335078 100644 --- a/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/PlayerSettingsActivity.kt @@ -11,13 +11,20 @@ import androidx.activity.addCallback import androidx.appcompat.app.AppCompatActivity import androidx.core.view.updateLayoutParams import androidx.core.widget.addTextChangedListener -import ani.dantotsu.* +import ani.dantotsu.R import ani.dantotsu.databinding.ActivityPlayerSettingsBinding +import ani.dantotsu.initActivity +import ani.dantotsu.loadData import ani.dantotsu.media.Media +import ani.dantotsu.navBarHeight +import ani.dantotsu.others.LangSet import ani.dantotsu.others.getSerialized import ani.dantotsu.parsers.Subtitle +import ani.dantotsu.saveData +import ani.dantotsu.snackString +import ani.dantotsu.statusBarHeight import ani.dantotsu.themes.ThemeManager -import ani.dantotsu.others.LangSet +import ani.dantotsu.toast import com.google.android.material.snackbar.Snackbar import kotlin.math.roundToInt @@ -26,14 +33,14 @@ class PlayerSettingsActivity : AppCompatActivity() { lateinit var binding: ActivityPlayerSettingsBinding private val player = "player_settings" - var media:Media?=null - var subtitle:Subtitle?=null + var media: Media? = null + var subtitle: Subtitle? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) LangSet.setLocale(this) -ThemeManager(this).applyTheme() + ThemeManager(this).applyTheme() binding = ActivityPlayerSettingsBinding.inflate(layoutInflater) setContentView(binding.root) @@ -55,7 +62,12 @@ ThemeManager(this).applyTheme() bottomMargin = navBarHeight } - val settings = loadData(player, toast = false) ?: PlayerSettings().apply { saveData(player, this) } + val settings = loadData(player, toast = false) ?: PlayerSettings().apply { + saveData( + player, + this + ) + } binding.playerSettingsBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() @@ -68,28 +80,36 @@ ThemeManager(this).applyTheme() saveData(player, settings) } - binding.playerSettingsQualityHeight.setText((loadData("maxHeight", toast = false) ?: 480).toString()) + binding.playerSettingsQualityHeight.setText( + (loadData("maxHeight", toast = false) ?: 480).toString() + ) binding.playerSettingsQualityHeight.addTextChangedListener { val height = binding.playerSettingsQualityHeight.text.toString().toIntOrNull() saveData("maxHeight", height) } - binding.playerSettingsQualityWidth.setText((loadData("maxWidth", toast = false) ?: 720).toString()) + binding.playerSettingsQualityWidth.setText( + (loadData("maxWidth", toast = false) ?: 720).toString() + ) binding.playerSettingsQualityWidth.addTextChangedListener { val height = binding.playerSettingsQualityWidth.text.toString().toIntOrNull() saveData("maxWidth", height) } - val speeds = arrayOf(0.25f, 0.33f, 0.5f, 0.66f, 0.75f, 1f, 1.25f, 1.33f, 1.5f, 1.66f, 1.75f, 2f) + val speeds = + arrayOf(0.25f, 0.33f, 0.5f, 0.66f, 0.75f, 1f, 1.25f, 1.33f, 1.5f, 1.66f, 1.75f, 2f) val cursedSpeeds = arrayOf(1f, 1.25f, 1.5f, 1.75f, 2f, 2.5f, 3f, 4f, 5f, 10f, 25f, 50f) var curSpeedArr = if (settings.cursedSpeeds) cursedSpeeds else speeds var speedsName = curSpeedArr.map { "${it}x" }.toTypedArray() - binding.playerSettingsSpeed.text = getString(R.string.default_playback_speed, speedsName[settings.defaultSpeed]) - val speedDialog = AlertDialog.Builder(this, R.style.DialogTheme).setTitle(getString(R.string.default_speed)) + binding.playerSettingsSpeed.text = + getString(R.string.default_playback_speed, speedsName[settings.defaultSpeed]) + val speedDialog = AlertDialog.Builder(this, R.style.DialogTheme) + .setTitle(getString(R.string.default_speed)) binding.playerSettingsSpeed.setOnClickListener { speedDialog.setSingleChoiceItems(speedsName, settings.defaultSpeed) { dialog, i -> settings.defaultSpeed = i - binding.playerSettingsSpeed.text = getString(R.string.default_playback_speed, speedsName[i]) + binding.playerSettingsSpeed.text = + getString(R.string.default_playback_speed, speedsName[i]) saveData(player, settings) dialog.dismiss() }.show() @@ -101,7 +121,8 @@ ThemeManager(this).applyTheme() curSpeedArr = if (settings.cursedSpeeds) cursedSpeeds else speeds settings.defaultSpeed = if (settings.cursedSpeeds) 0 else 5 speedsName = curSpeedArr.map { "${it}x" }.toTypedArray() - binding.playerSettingsSpeed.text = getString(R.string.default_playback_speed, speedsName[settings.defaultSpeed]) + binding.playerSettingsSpeed.text = + getString(R.string.default_playback_speed, speedsName[settings.defaultSpeed]) saveData(player, settings) } @@ -155,7 +176,8 @@ ThemeManager(this).applyTheme() if (isChecked) snackString(getString(R.string.very_bold)) saveData(player, settings) } - binding.playerSettingsCompletePercentage.value = (settings.watchPercentage * 100).roundToInt().toFloat() + binding.playerSettingsCompletePercentage.value = + (settings.watchPercentage * 100).roundToInt().toFloat() binding.playerSettingsCompletePercentage.addOnChangeListener { _, value, _ -> settings.watchPercentage = value / 100 saveData(player, settings) @@ -230,7 +252,8 @@ ThemeManager(this).applyTheme() } val resizeModes = arrayOf("Original", "Zoom", "Stretch") - val resizeDialog = AlertDialog.Builder(this, R.style.DialogTheme).setTitle(getString(R.string.default_resize_mode)) + val resizeDialog = AlertDialog.Builder(this, R.style.DialogTheme) + .setTitle(getString(R.string.default_resize_mode)) binding.playerResizeMode.setOnClickListener { resizeDialog.setSingleChoiceItems(resizeModes, settings.resize) { dialog, count -> settings.resize = count @@ -244,7 +267,11 @@ ThemeManager(this).applyTheme() R.string.restart_app, Snackbar.LENGTH_SHORT ).apply { val mainIntent = - Intent.makeRestartActivityTask(context.packageManager.getLaunchIntentForPackage(context.packageName)!!.component) + Intent.makeRestartActivityTask( + context.packageManager.getLaunchIntentForPackage( + context.packageName + )!!.component + ) setAction("Do it!") { context.startActivity(mainIntent) Runtime.getRuntime().exit(0) @@ -256,7 +283,7 @@ ThemeManager(this).applyTheme() fun toggleButton(button: android.widget.Button, toggle: Boolean) { button.isClickable = toggle button.alpha = when (toggle) { - true -> 1f + true -> 1f false -> 0.5f } } @@ -269,21 +296,24 @@ ThemeManager(this).applyTheme() binding.subtitleFontSizeCard.isEnabled = isChecked binding.subtitleFontSizeCard.isClickable = isChecked binding.subtitleFontSizeCard.alpha = when (isChecked) { - true -> 1f + true -> 1f false -> 0.5f } binding.subtitleFontSize.isEnabled = isChecked binding.subtitleFontSize.isClickable = isChecked binding.subtitleFontSize.alpha = when (isChecked) { - true -> 1f - false -> 0.5f - } - ActivityPlayerSettingsBinding.bind(binding.root).subtitleFontSizeText.isEnabled = isChecked - ActivityPlayerSettingsBinding.bind(binding.root).subtitleFontSizeText.isClickable = isChecked - ActivityPlayerSettingsBinding.bind(binding.root).subtitleFontSizeText.alpha = when (isChecked) { - true -> 1f + true -> 1f false -> 0.5f } + ActivityPlayerSettingsBinding.bind(binding.root).subtitleFontSizeText.isEnabled = + isChecked + ActivityPlayerSettingsBinding.bind(binding.root).subtitleFontSizeText.isClickable = + isChecked + ActivityPlayerSettingsBinding.bind(binding.root).subtitleFontSizeText.alpha = + when (isChecked) { + true -> 1f + false -> 0.5f + } } binding.subSwitch.isChecked = settings.subtitles binding.subSwitch.setOnCheckedChangeListener { _, isChecked -> @@ -293,10 +323,26 @@ ThemeManager(this).applyTheme() restartApp() } val colorsPrimary = - arrayOf("Black", "Dark Gray", "Gray", "Light Gray", "White", "Red", "Yellow", "Green", "Cyan", "Blue", "Magenta") - val primaryColorDialog = AlertDialog.Builder(this, R.style.DialogTheme).setTitle(getString(R.string.primary_sub_color)) + arrayOf( + "Black", + "Dark Gray", + "Gray", + "Light Gray", + "White", + "Red", + "Yellow", + "Green", + "Cyan", + "Blue", + "Magenta" + ) + val primaryColorDialog = AlertDialog.Builder(this, R.style.DialogTheme) + .setTitle(getString(R.string.primary_sub_color)) binding.videoSubColorPrimary.setOnClickListener { - primaryColorDialog.setSingleChoiceItems(colorsPrimary, settings.primaryColor) { dialog, count -> + primaryColorDialog.setSingleChoiceItems( + colorsPrimary, + settings.primaryColor + ) { dialog, count -> settings.primaryColor = count saveData(player, settings) dialog.dismiss() @@ -316,16 +362,21 @@ ThemeManager(this).applyTheme() "Magenta", "Transparent" ) - val secondaryColorDialog = AlertDialog.Builder(this, R.style.DialogTheme).setTitle(getString(R.string.outline_sub_color)) + val secondaryColorDialog = AlertDialog.Builder(this, R.style.DialogTheme) + .setTitle(getString(R.string.outline_sub_color)) binding.videoSubColorSecondary.setOnClickListener { - secondaryColorDialog.setSingleChoiceItems(colorsSecondary, settings.secondaryColor) { dialog, count -> + secondaryColorDialog.setSingleChoiceItems( + colorsSecondary, + settings.secondaryColor + ) { dialog, count -> settings.secondaryColor = count saveData(player, settings) dialog.dismiss() }.show() } val typesOutline = arrayOf("Outline", "Shine", "Drop Shadow", "None") - val outlineDialog = AlertDialog.Builder(this, R.style.DialogTheme).setTitle(getString(R.string.outline_type)) + val outlineDialog = AlertDialog.Builder(this, R.style.DialogTheme) + .setTitle(getString(R.string.outline_type)) binding.videoSubOutline.setOnClickListener { outlineDialog.setSingleChoiceItems(typesOutline, settings.outline) { dialog, count -> settings.outline = count @@ -347,9 +398,13 @@ ThemeManager(this).applyTheme() "Blue", "Magenta" ) - val subBackgroundDialog = AlertDialog.Builder(this, R.style.DialogTheme).setTitle(getString(R.string.outline_sub_color)) + val subBackgroundDialog = AlertDialog.Builder(this, R.style.DialogTheme) + .setTitle(getString(R.string.outline_sub_color)) binding.videoSubColorBackground.setOnClickListener { - subBackgroundDialog.setSingleChoiceItems(colorsSubBackground, settings.subBackground) { dialog, count -> + subBackgroundDialog.setSingleChoiceItems( + colorsSubBackground, + settings.subBackground + ) { dialog, count -> settings.subBackground = count saveData(player, settings) dialog.dismiss() @@ -370,16 +425,28 @@ ThemeManager(this).applyTheme() "Blue", "Magenta" ) - val subWindowDialog = AlertDialog.Builder(this, R.style.DialogTheme).setTitle(getString(R.string.outline_sub_color)) + val subWindowDialog = AlertDialog.Builder(this, R.style.DialogTheme) + .setTitle(getString(R.string.outline_sub_color)) binding.videoSubColorWindow.setOnClickListener { - subWindowDialog.setSingleChoiceItems(colorsSubWindow, settings.subWindow) { dialog, count -> + subWindowDialog.setSingleChoiceItems( + colorsSubWindow, + settings.subWindow + ) { dialog, count -> settings.subWindow = count saveData(player, settings) dialog.dismiss() }.show() } - val fonts = arrayOf("Poppins Semi Bold", "Poppins Bold", "Poppins", "Poppins Thin","Century Gothic","Century Gothic Bold") - val fontDialog = AlertDialog.Builder(this, R.style.DialogTheme).setTitle(getString(R.string.subtitle_font)) + val fonts = arrayOf( + "Poppins Semi Bold", + "Poppins Bold", + "Poppins", + "Poppins Thin", + "Century Gothic", + "Century Gothic Bold" + ) + val fontDialog = AlertDialog.Builder(this, R.style.DialogTheme) + .setTitle(getString(R.string.subtitle_font)) binding.videoSubFont.setOnClickListener { fontDialog.setSingleChoiceItems(fonts, settings.font) { dialog, count -> settings.font = count diff --git a/app/src/main/java/ani/dantotsu/settings/ReaderSettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/ReaderSettingsActivity.kt index ebde9bbc..61c87afe 100644 --- a/app/src/main/java/ani/dantotsu/settings/ReaderSettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/ReaderSettingsActivity.kt @@ -9,11 +9,11 @@ import ani.dantotsu.databinding.ActivityReaderSettingsBinding import ani.dantotsu.initActivity import ani.dantotsu.loadData import ani.dantotsu.navBarHeight +import ani.dantotsu.others.LangSet import ani.dantotsu.saveData import ani.dantotsu.snackString import ani.dantotsu.statusBarHeight import ani.dantotsu.themes.ThemeManager -import ani.dantotsu.others.LangSet class ReaderSettingsActivity : AppCompatActivity() { lateinit var binding: ActivityReaderSettingsBinding @@ -21,7 +21,7 @@ class ReaderSettingsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) LangSet.setLocale(this) -ThemeManager(this).applyTheme() + ThemeManager(this).applyTheme() binding = ActivityReaderSettingsBinding.inflate(layoutInflater) setContentView(binding.root) @@ -31,7 +31,12 @@ ThemeManager(this).applyTheme() bottomMargin = navBarHeight } - val settings = loadData(reader, toast = false) ?: ReaderSettings().apply { saveData(reader, this) } + val settings = loadData(reader, toast = false) ?: ReaderSettings().apply { + saveData( + reader, + this + ) + } binding.readerSettingsBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() @@ -43,13 +48,13 @@ ThemeManager(this).applyTheme() settings.showSource = isChecked saveData(reader, settings) } - + binding.readerSettingsSystemBars.isChecked = settings.showSystemBars binding.readerSettingsSystemBars.setOnCheckedChangeListener { _, isChecked -> settings.showSystemBars = isChecked saveData(reader, settings) } - + binding.readerSettingsAutoWebToon.isChecked = settings.autoDetectWebtoon binding.readerSettingsAutoWebToon.setOnCheckedChangeListener { _, isChecked -> settings.autoDetectWebtoon = isChecked @@ -63,7 +68,8 @@ ThemeManager(this).applyTheme() binding.readerSettingsContinuous ) - binding.readerSettingsLayoutText.text = resources.getStringArray(R.array.manga_layouts)[settings.default.layout.ordinal] + binding.readerSettingsLayoutText.text = + resources.getStringArray(R.array.manga_layouts)[settings.default.layout.ordinal] var selectedLayout = layoutList[settings.default.layout.ordinal] selectedLayout.alpha = 1f @@ -72,17 +78,23 @@ ThemeManager(this).applyTheme() selectedLayout.alpha = 0.33f selectedLayout = imageButton selectedLayout.alpha = 1f - settings.default.layout = CurrentReaderSettings.Layouts[index]?:CurrentReaderSettings.Layouts.CONTINUOUS - binding.readerSettingsLayoutText.text = resources.getStringArray(R.array.manga_layouts)[settings.default.layout.ordinal] + settings.default.layout = + CurrentReaderSettings.Layouts[index] ?: CurrentReaderSettings.Layouts.CONTINUOUS + binding.readerSettingsLayoutText.text = + resources.getStringArray(R.array.manga_layouts)[settings.default.layout.ordinal] saveData(reader, settings) } } - binding.readerSettingsDirectionText.text = resources.getStringArray(R.array.manga_directions)[settings.default.direction.ordinal] + binding.readerSettingsDirectionText.text = + resources.getStringArray(R.array.manga_directions)[settings.default.direction.ordinal] binding.readerSettingsDirection.rotation = 90f * (settings.default.direction.ordinal) binding.readerSettingsDirection.setOnClickListener { - settings.default.direction = CurrentReaderSettings.Directions[settings.default.direction.ordinal + 1] ?: CurrentReaderSettings.Directions.TOP_TO_BOTTOM - binding.readerSettingsDirectionText.text = resources.getStringArray(R.array.manga_directions)[settings.default.direction.ordinal] + settings.default.direction = + CurrentReaderSettings.Directions[settings.default.direction.ordinal + 1] + ?: CurrentReaderSettings.Directions.TOP_TO_BOTTOM + binding.readerSettingsDirectionText.text = + resources.getStringArray(R.array.manga_directions)[settings.default.direction.ordinal] binding.readerSettingsDirection.rotation = 90f * (settings.default.direction.ordinal) saveData(reader, settings) } @@ -102,7 +114,8 @@ ThemeManager(this).applyTheme() selectedDual.alpha = 0.33f selectedDual = imageButton selectedDual.alpha = 1f - settings.default.dualPageMode = CurrentReaderSettings.DualPageModes[index] ?: CurrentReaderSettings.DualPageModes.Automatic + settings.default.dualPageMode = CurrentReaderSettings.DualPageModes[index] + ?: CurrentReaderSettings.DualPageModes.Automatic binding.readerSettingsDualPageText.text = settings.default.dualPageMode.toString() saveData(reader, settings) } @@ -149,29 +162,29 @@ ThemeManager(this).applyTheme() } binding.readerSettingsOverscroll.isChecked = settings.default.overScrollMode - binding.readerSettingsOverscroll.setOnCheckedChangeListener { _,isChecked -> + binding.readerSettingsOverscroll.setOnCheckedChangeListener { _, isChecked -> settings.default.overScrollMode = isChecked saveData(reader, settings) } binding.readerSettingsVolumeButton.isChecked = settings.default.volumeButtons - binding.readerSettingsVolumeButton.setOnCheckedChangeListener { _,isChecked -> + binding.readerSettingsVolumeButton.setOnCheckedChangeListener { _, isChecked -> settings.default.volumeButtons = isChecked saveData(reader, settings) } binding.readerSettingsWrapImages.isChecked = settings.default.wrapImages - binding.readerSettingsWrapImages.setOnCheckedChangeListener { _,isChecked -> + binding.readerSettingsWrapImages.setOnCheckedChangeListener { _, isChecked -> settings.default.wrapImages = isChecked saveData(reader, settings) } binding.readerSettingsLongClickImage.isChecked = settings.default.longClickImage - binding.readerSettingsLongClickImage.setOnCheckedChangeListener { _,isChecked -> + binding.readerSettingsLongClickImage.setOnCheckedChangeListener { _, isChecked -> settings.default.longClickImage = isChecked saveData(reader, settings) } - + //Update Progress binding.readerSettingsAskUpdateProgress.isChecked = settings.askIndividual binding.readerSettingsAskUpdateProgress.setOnCheckedChangeListener { _, isChecked -> diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt index 63ef0cd0..e9293b64 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt @@ -5,7 +5,6 @@ import android.app.AlertDialog import android.content.Context import android.content.Intent import android.graphics.drawable.Animatable -import android.os.Build import android.os.Build.* import android.os.Build.VERSION.* import android.os.Bundle @@ -27,6 +26,7 @@ import ani.dantotsu.connections.mal.MAL import ani.dantotsu.databinding.ActivitySettingsBinding import ani.dantotsu.others.AppUpdater import ani.dantotsu.others.CustomBottomDialog +import ani.dantotsu.others.LangSet import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.parsers.MangaSources import ani.dantotsu.subcriptions.Notifications @@ -35,7 +35,6 @@ import ani.dantotsu.subcriptions.Subscription.Companion.defaultTime import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription import ani.dantotsu.subcriptions.Subscription.Companion.timeMinutes import ani.dantotsu.themes.ThemeManager -import ani.dantotsu.others.LangSet import com.google.android.material.snackbar.Snackbar import com.google.android.material.textfield.TextInputEditText import com.skydoves.colorpickerview.listeners.ColorListener @@ -75,13 +74,14 @@ class SettingsActivity : AppCompatActivity() { fun getArch(): String { SUPPORTED_ABIS.forEach { when (it) { - "arm64-v8a" -> return "aarch64" + "arm64-v8a" -> return "aarch64" "armeabi-v7a" -> return "arm" - "x86_64" -> return "x86_64" - "x86" -> return "i686" + "x86_64" -> return "x86_64" + "x86" -> return "i686" } } - return System.getProperty("os.arch") ?: System.getProperty("os.product.cpu.abi") ?: "Unknown Architecture" + return System.getProperty("os.arch") ?: System.getProperty("os.product.cpu.abi") + ?: "Unknown Architecture" } val info = """ @@ -106,41 +106,68 @@ class SettingsActivity : AppCompatActivity() { onBackPressedDispatcher.onBackPressed() } - binding.settingsUseMaterialYou.isChecked = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("use_material_you", false) + binding.settingsUseMaterialYou.isChecked = + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean( + "use_material_you", + false + ) binding.settingsUseMaterialYou.setOnCheckedChangeListener { _, isChecked -> - getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putBoolean("use_material_you", isChecked).apply() - if(isChecked) binding.settingsUseCustomTheme.isChecked = false + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit() + .putBoolean("use_material_you", isChecked).apply() + if (isChecked) binding.settingsUseCustomTheme.isChecked = false restartApp() } - binding.settingsUseCustomTheme.isChecked = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("use_custom_theme", false) + binding.settingsUseCustomTheme.isChecked = + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean( + "use_custom_theme", + false + ) binding.settingsUseCustomTheme.setOnCheckedChangeListener { _, isChecked -> - getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putBoolean("use_custom_theme", isChecked).apply() - if(isChecked) { + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit() + .putBoolean("use_custom_theme", isChecked).apply() + if (isChecked) { binding.settingsUseMaterialYou.isChecked = false } restartApp() } - binding.settingsUseSourceTheme.isChecked = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("use_source_theme", false) + binding.settingsUseSourceTheme.isChecked = + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean( + "use_source_theme", + false + ) binding.settingsUseSourceTheme.setOnCheckedChangeListener { _, isChecked -> - getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putBoolean("use_source_theme", isChecked).apply() + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit() + .putBoolean("use_source_theme", isChecked).apply() } - binding.settingsUseOLED.isChecked = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("use_oled", false) + binding.settingsUseOLED.isChecked = + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("use_oled", false) binding.settingsUseOLED.setOnCheckedChangeListener { _, isChecked -> - getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putBoolean("use_oled", isChecked).apply() + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit() + .putBoolean("use_oled", isChecked).apply() restartApp() } - val themeString = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getString("theme", "PURPLE")!! - binding.themeSwitcher.setText(themeString.substring(0, 1) + themeString.substring(1).lowercase()) + val themeString = + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getString("theme", "PURPLE")!! + binding.themeSwitcher.setText( + themeString.substring(0, 1) + themeString.substring(1).lowercase() + ) - binding.themeSwitcher.setAdapter(ArrayAdapter(this, R.layout.item_dropdown, ThemeManager.Companion.Theme.values().map { it.theme.substring(0, 1) + it.theme.substring(1).lowercase() })) + binding.themeSwitcher.setAdapter( + ArrayAdapter( + this, + R.layout.item_dropdown, + ThemeManager.Companion.Theme.values() + .map { it.theme.substring(0, 1) + it.theme.substring(1).lowercase() }) + ) binding.themeSwitcher.setOnItemClickListener { _, _, i, _ -> - getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putString("theme", ThemeManager.Companion.Theme.values()[i].theme).apply() + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit() + .putString("theme", ThemeManager.Companion.Theme.values()[i].theme).apply() //ActivityHelper.shouldRefreshMainActivity = true binding.themeSwitcher.clearFocus() restartApp() @@ -148,14 +175,15 @@ class SettingsActivity : AppCompatActivity() { } - binding.customTheme.setOnClickListener{ + binding.customTheme.setOnClickListener { var passedColor: Int = 0 val dialogView = layoutInflater.inflate(R.layout.dialog_color_picker, null) - val alertDialog = AlertDialog.Builder(this ,R.style.MyPopup) + val alertDialog = AlertDialog.Builder(this, R.style.MyPopup) .setTitle("Custom Theme") .setView(dialogView) .setPositiveButton("OK") { dialog, _ -> - getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putInt("custom_theme_int", passedColor).apply() + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit() + .putInt("custom_theme_int", passedColor).apply() logger("Custom Theme: $passedColor") dialog.dismiss() restartApp() @@ -164,7 +192,8 @@ class SettingsActivity : AppCompatActivity() { dialog.dismiss() } .create() - val colorPickerView = dialogView.findViewById(R.id.colorPickerView) + val colorPickerView = + dialogView.findViewById(R.id.colorPickerView) colorPickerView.setColorListener(ColorListener { color, fromUser -> val linearLayout = dialogView.findViewById(R.id.linear) passedColor = color @@ -175,17 +204,27 @@ class SettingsActivity : AppCompatActivity() { } //val animeSource = loadData("settings_def_anime_source_s")?.let { if (it >= AnimeSources.names.size) 0 else it } ?: 0 - val animeSource = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("settings_def_anime_source_s_r", 0) + val animeSource = getSharedPreferences( + "Dantotsu", + Context.MODE_PRIVATE + ).getInt("settings_def_anime_source_s_r", 0) if (AnimeSources.names.isNotEmpty() && animeSource in 0 until AnimeSources.names.size) { binding.animeSource.setText(AnimeSources.names[animeSource], false) } - binding.animeSource.setAdapter(ArrayAdapter(this, R.layout.item_dropdown, AnimeSources.names)) + binding.animeSource.setAdapter( + ArrayAdapter( + this, + R.layout.item_dropdown, + AnimeSources.names + ) + ) binding.animeSource.setOnItemClickListener { _, _, i, _ -> //saveData("settings_def_anime_source_s", i) - getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putInt("settings_def_anime_source_s_r", i).apply() + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit() + .putInt("settings_def_anime_source_s_r", i).apply() binding.animeSource.clearFocus() } @@ -194,7 +233,8 @@ class SettingsActivity : AppCompatActivity() { } val managers = arrayOf("Default", "1DM", "ADM") - val downloadManagerDialog = AlertDialog.Builder(this, R.style.DialogTheme).setTitle("Download Manager") + val downloadManagerDialog = + AlertDialog.Builder(this, R.style.DialogTheme).setTitle("Download Manager") var downloadManager = loadData("settings_download_manager") ?: 0 binding.settingsDownloadManager.setOnClickListener { downloadManagerDialog.setSingleChoiceItems(managers, downloadManager) { dialog, count -> @@ -204,11 +244,12 @@ class SettingsActivity : AppCompatActivity() { }.show() } - binding.settingsForceLegacyInstall.isChecked = extensionInstaller.get() == BasePreferences.ExtensionInstaller.LEGACY + binding.settingsForceLegacyInstall.isChecked = + extensionInstaller.get() == BasePreferences.ExtensionInstaller.LEGACY binding.settingsForceLegacyInstall.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { extensionInstaller.set(BasePreferences.ExtensionInstaller.LEGACY) - }else{ + } else { extensionInstaller.set(BasePreferences.ExtensionInstaller.PACKAGEINSTALLER) } } @@ -223,11 +264,11 @@ class SettingsActivity : AppCompatActivity() { } - binding.userAgent.setOnClickListener{ + binding.userAgent.setOnClickListener { val dialogView = layoutInflater.inflate(R.layout.dialog_user_agent, null) val editText = dialogView.findViewById(R.id.userAgentTextBox) editText.setText(networkPreferences.defaultUserAgent().get()) - val alertDialog = AlertDialog.Builder(this ,R.style.MyPopup) + val alertDialog = AlertDialog.Builder(this, R.style.MyPopup) .setTitle("User Agent") .setView(dialogView) .setPositiveButton("OK") { dialog, _ -> @@ -235,7 +276,8 @@ class SettingsActivity : AppCompatActivity() { dialog.dismiss() } .setNeutralButton("Reset") { dialog, _ -> - networkPreferences.defaultUserAgent().set("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0") // Reset to default or empty + networkPreferences.defaultUserAgent() + .set("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0") // Reset to default or empty editText.setText("") dialog.dismiss() } @@ -248,7 +290,22 @@ class SettingsActivity : AppCompatActivity() { } - val exDns = listOf("None", "Cloudflare", "Google", "AdGuard", "Quad9", "AliDNS", "DNSPod", "360", "Quad101", "Mullvad", "Controld", "Njalla", "Shecan", "Libre") + val exDns = listOf( + "None", + "Cloudflare", + "Google", + "AdGuard", + "Quad9", + "AliDNS", + "DNSPod", + "360", + "Quad101", + "Mullvad", + "Controld", + "Njalla", + "Shecan", + "Libre" + ) binding.settingsExtensionDns.setText(exDns[networkPreferences.dohProvider().get()], false) binding.settingsExtensionDns.setAdapter(ArrayAdapter(this, R.layout.item_dropdown, exDns)) binding.settingsExtensionDns.setOnItemClickListener { _, _, i, _ -> @@ -295,19 +352,29 @@ class SettingsActivity : AppCompatActivity() { saveData("settings_prefer_dub", isChecked) } - //val mangaSource = loadData("settings_def_manga_source_s")?.let { if (it >= MangaSources.names.size) 0 else it } ?: 0 - val mangaSource = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("settings_def_manga_source_s_r", 0) - if (MangaSources.names.isNotEmpty() && mangaSource in 0 until MangaSources.names.size) { - binding.mangaSource.setText(MangaSources.names[mangaSource], false) + //val mangaSource = loadData("settings_def_manga_source_s")?.let { if (it >= MangaSources.names.size) 0 else it } ?: 0 + val mangaSource = getSharedPreferences( + "Dantotsu", + Context.MODE_PRIVATE + ).getInt("settings_def_manga_source_s_r", 0) + if (MangaSources.names.isNotEmpty() && mangaSource in 0 until MangaSources.names.size) { + binding.mangaSource.setText(MangaSources.names[mangaSource], false) } // Set up the dropdown adapter. - binding.mangaSource.setAdapter(ArrayAdapter(this, R.layout.item_dropdown, MangaSources.names)) + binding.mangaSource.setAdapter( + ArrayAdapter( + this, + R.layout.item_dropdown, + MangaSources.names + ) + ) // Set up the item click listener for the dropdown. binding.mangaSource.setOnItemClickListener { _, _, i, _ -> //saveData("settings_def_manga_source_s", i) - getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putInt("settings_def_manga_source_s_r", i).apply() + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit() + .putInt("settings_def_manga_source_s_r", i).apply() binding.mangaSource.clearFocus() } @@ -316,10 +383,11 @@ class SettingsActivity : AppCompatActivity() { } val uiSettings: UserInterfaceSettings = - loadData("ui_settings", toast = false) ?: UserInterfaceSettings().apply { saveData("ui_settings", this) } + loadData("ui_settings", toast = false) + ?: UserInterfaceSettings().apply { saveData("ui_settings", this) } var previous: View = when (uiSettings.darkMode) { - null -> binding.settingsUiAuto - true -> binding.settingsUiDark + null -> binding.settingsUiAuto + true -> binding.settingsUiDark false -> binding.settingsUiLight } previous.alpha = 1f @@ -349,9 +417,9 @@ class SettingsActivity : AppCompatActivity() { } var previousStart: View = when (uiSettings.defaultStartUpTab) { - 0 -> binding.uiSettingsAnime - 1 -> binding.uiSettingsHome - 2 -> binding.uiSettingsManga + 0 -> binding.uiSettingsAnime + 1 -> binding.uiSettingsHome + 2 -> binding.uiSettingsManga else -> binding.uiSettingsHome } previousStart.alpha = 1f @@ -383,9 +451,9 @@ class SettingsActivity : AppCompatActivity() { } var previousEp: View = when (uiSettings.animeDefaultView) { - 0 -> binding.settingsEpList - 1 -> binding.settingsEpGrid - 2 -> binding.settingsEpCompact + 0 -> binding.settingsEpList + 1 -> binding.settingsEpGrid + 2 -> binding.settingsEpCompact else -> binding.settingsEpList } previousEp.alpha = 1f @@ -410,8 +478,8 @@ class SettingsActivity : AppCompatActivity() { } var previousChp: View = when (uiSettings.mangaDefaultView) { - 0 -> binding.settingsChpList - 1 -> binding.settingsChpCompact + 0 -> binding.settingsChpList + 1 -> binding.settingsChpCompact else -> binding.settingsChpList } previousChp.alpha = 1f @@ -466,11 +534,16 @@ class SettingsActivity : AppCompatActivity() { binding.settingsLogo.setSafeOnClickListener { cursedCounter++ (binding.settingsLogo.drawable as Animatable).start() - if (cursedCounter % 7 == 0){ + if (cursedCounter % 7 == 0) { snackString("youwu have been cuwsed :pwayge:") - getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putBoolean("use_cursed_lang", - getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("use_cursed_lang", false).not()).apply() - } else{ + getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).edit().putBoolean( + "use_cursed_lang", + getSharedPreferences( + "Dantotsu", + Context.MODE_PRIVATE + ).getBoolean("use_cursed_lang", false).not() + ).apply() + } else { snackString(array[(Math.random() * array.size).toInt()], this) } @@ -504,12 +577,15 @@ class SettingsActivity : AppCompatActivity() { if (it > 0) "${if (hours > 0) "$hours hrs " else ""}${if (mins > 0) "$mins mins" else ""}" else getString(R.string.do_not_update) }.toTypedArray() - binding.settingsSubscriptionsTime.text = getString(R.string.subscriptions_checking_time_s, timeNames[curTime]) - val speedDialog = AlertDialog.Builder(this, R.style.DialogTheme).setTitle(R.string.subscriptions_checking_time) + binding.settingsSubscriptionsTime.text = + getString(R.string.subscriptions_checking_time_s, timeNames[curTime]) + val speedDialog = AlertDialog.Builder(this, R.style.DialogTheme) + .setTitle(R.string.subscriptions_checking_time) binding.settingsSubscriptionsTime.setOnClickListener { speedDialog.setSingleChoiceItems(timeNames, curTime) { dialog, i -> curTime = i - binding.settingsSubscriptionsTime.text = getString(R.string.subscriptions_checking_time_s, timeNames[i]) + binding.settingsSubscriptionsTime.text = + getString(R.string.subscriptions_checking_time_s, timeNames[i]) saveData("subscriptions_time_s", curTime) dialog.dismiss() startSubscription(true) @@ -521,7 +597,8 @@ class SettingsActivity : AppCompatActivity() { true } - binding.settingsNotificationsCheckingSubscriptions.isChecked = loadData("subscription_checking_notifications") ?: true + binding.settingsNotificationsCheckingSubscriptions.isChecked = + loadData("subscription_checking_notifications") ?: true binding.settingsNotificationsCheckingSubscriptions.setOnCheckedChangeListener { _, isChecked -> saveData("subscription_checking_notifications", isChecked) if (isChecked) @@ -570,7 +647,8 @@ class SettingsActivity : AppCompatActivity() { setTitleText(title) addView( TextView(it.context).apply { - val markWon = Markwon.builder(it.context).usePlugin(SoftBreakAddsNewLinePlugin.create()).build() + val markWon = Markwon.builder(it.context) + .usePlugin(SoftBreakAddsNewLinePlugin.create()).build() markWon.setMarkdown(this, full) } ) @@ -624,14 +702,24 @@ class SettingsActivity : AppCompatActivity() { } if (Discord.token != null) { - val id = getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE).getString("discord_id", null) - val avatar = getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE).getString("discord_avatar", null) - val username = getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE).getString("discord_username", null) + val id = getSharedPreferences( + getString(R.string.preference_file_key), + Context.MODE_PRIVATE + ).getString("discord_id", null) + val avatar = getSharedPreferences( + getString(R.string.preference_file_key), + Context.MODE_PRIVATE + ).getString("discord_avatar", null) + val username = getSharedPreferences( + getString(R.string.preference_file_key), + Context.MODE_PRIVATE + ).getString("discord_username", null) if (id != null && avatar != null) { binding.settingsDiscordAvatar.loadImage("https://cdn.discordapp.com/avatars/$id/$avatar.png") } binding.settingsDiscordUsername.visibility = View.VISIBLE - binding.settingsDiscordUsername.text = username ?: Discord.token?.replace(Regex("."),"*") + binding.settingsDiscordUsername.text = + username ?: Discord.token?.replace(Regex("."), "*") binding.settingsDiscordLogin.setText(R.string.logout) binding.settingsDiscordLogin.setOnClickListener { Discord.removeSavedToken(this) @@ -676,13 +764,18 @@ class SettingsActivity : AppCompatActivity() { } } } + private fun restartApp() { Snackbar.make( binding.root, R.string.restart_app, Snackbar.LENGTH_SHORT ).apply { val mainIntent = - Intent.makeRestartActivityTask(context.packageManager.getLaunchIntentForPackage(context.packageName)!!.component) + Intent.makeRestartActivityTask( + context.packageManager.getLaunchIntentForPackage( + context.packageName + )!!.component + ) setAction("Do it!") { context.startActivity(mainIntent) Runtime.getRuntime().exit(0) diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt b/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt index 0b10ca1d..f59f7e1d 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt @@ -10,21 +10,31 @@ import android.util.TypedValue import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Switch import androidx.core.content.ContextCompat -import ani.dantotsu.* +import ani.dantotsu.BottomSheetDialogFragment +import ani.dantotsu.R import ani.dantotsu.connections.anilist.Anilist -import ani.dantotsu.others.imagesearch.ImageSearchActivity import ani.dantotsu.databinding.BottomSheetSettingsBinding import ani.dantotsu.download.DownloadContainerActivity import ani.dantotsu.download.manga.OfflineMangaFragment +import ani.dantotsu.loadData +import ani.dantotsu.loadImage +import ani.dantotsu.openLinkInBrowser +import ani.dantotsu.others.imagesearch.ImageSearchActivity +import ani.dantotsu.setSafeOnClickListener +import ani.dantotsu.startMainActivity +import ani.dantotsu.toast class SettingsDialogFragment(val pageType: PageType) : BottomSheetDialogFragment() { private var _binding: BottomSheetSettingsBinding? = 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 = BottomSheetSettingsBinding.inflate(inflater, container, false) return binding.root } @@ -43,7 +53,7 @@ class SettingsDialogFragment(val pageType: PageType) : BottomSheetDialogFragment binding.settingsLogin.setOnClickListener { Anilist.removeSavedToken(it.context) dismiss() - startMainActivity(requireActivity(),) + startMainActivity(requireActivity()) } binding.settingsUsername.text = Anilist.username binding.settingsUserAvatar.loadImage(Anilist.avatar) @@ -73,15 +83,17 @@ class SettingsDialogFragment(val pageType: PageType) : BottomSheetDialogFragment dismiss() } binding.settingsDownloads.setSafeOnClickListener { - when(pageType) { + when (pageType) { PageType.MANGA -> { val intent = Intent(activity, DownloadContainerActivity::class.java) intent.putExtra("FRAGMENT_CLASS_NAME", OfflineMangaFragment::class.java.name) startActivity(intent) } + PageType.ANIME -> { try { - val arrayOfFiles = ContextCompat.getExternalFilesDirs(requireContext(), null) + val arrayOfFiles = + ContextCompat.getExternalFilesDirs(requireContext(), null) startActivity( if (loadData("sd_dl") == true && arrayOfFiles.size > 1 && arrayOfFiles[0] != null && arrayOfFiles[1] != null) { val parentDirectory = arrayOfFiles[1].toString() @@ -93,9 +105,11 @@ class SettingsDialogFragment(val pageType: PageType) : BottomSheetDialogFragment toast(getString(R.string.file_manager_not_found)) } } + PageType.HOME -> { try { - val arrayOfFiles = ContextCompat.getExternalFilesDirs(requireContext(), null) + val arrayOfFiles = + ContextCompat.getExternalFilesDirs(requireContext(), null) startActivity( if (loadData("sd_dl") == true && arrayOfFiles.size > 1 && arrayOfFiles[0] != null && arrayOfFiles[1] != null) { val parentDirectory = arrayOfFiles[1].toString() @@ -118,8 +132,8 @@ class SettingsDialogFragment(val pageType: PageType) : BottomSheetDialogFragment _binding = null } - companion object{ - enum class PageType{ + companion object { + enum class PageType { MANGA, ANIME, HOME } } diff --git a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt index ba71457c..39dd18b1 100644 --- a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt +++ b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt @@ -12,7 +12,15 @@ data class UserInterfaceSettings( var immersiveMode: Boolean = false, var smallView: Boolean = true, var defaultStartUpTab: Int = 1, - var homeLayoutShow: MutableList = mutableListOf(true, false, false, true, false, false, true), + var homeLayoutShow: MutableList = mutableListOf( + true, + false, + false, + true, + false, + false, + true + ), //Animations var bannerAnimations: Boolean = true, diff --git a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt index 8c238b48..d1e46215 100644 --- a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt @@ -6,10 +6,15 @@ import android.os.Bundle import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.core.view.updateLayoutParams -import ani.dantotsu.* +import ani.dantotsu.R import ani.dantotsu.databinding.ActivityUserInterfaceSettingsBinding -import ani.dantotsu.themes.ThemeManager +import ani.dantotsu.initActivity +import ani.dantotsu.loadData +import ani.dantotsu.navBarHeight import ani.dantotsu.others.LangSet +import ani.dantotsu.saveData +import ani.dantotsu.statusBarHeight +import ani.dantotsu.themes.ThemeManager import com.google.android.material.snackbar.Snackbar class UserInterfaceSettingsActivity : AppCompatActivity() { @@ -18,7 +23,7 @@ class UserInterfaceSettingsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) LangSet.setLocale(this) -ThemeManager(this).applyTheme() + ThemeManager(this).applyTheme() binding = ActivityUserInterfaceSettingsBinding.inflate(layoutInflater) setContentView(binding.root) @@ -28,7 +33,8 @@ ThemeManager(this).applyTheme() bottomMargin = navBarHeight } - val settings = loadData(ui, toast = false) ?: UserInterfaceSettings().apply { saveData(ui, this) } + val settings = loadData(ui, toast = false) + ?: UserInterfaceSettings().apply { saveData(ui, this) } binding.uiSettingsBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() @@ -36,8 +42,12 @@ ThemeManager(this).applyTheme() val views = resources.getStringArray(R.array.home_layouts) binding.uiSettingsHomeLayout.setOnClickListener { - AlertDialog.Builder(this, R.style.DialogTheme).setTitle(getString(R.string.home_layout_show)).apply { - setMultiChoiceItems(views, settings.homeLayoutShow.toBooleanArray()) { _, i, value -> + AlertDialog.Builder(this, R.style.DialogTheme) + .setTitle(getString(R.string.home_layout_show)).apply { + setMultiChoiceItems( + views, + settings.homeLayoutShow.toBooleanArray() + ) { _, i, value -> settings.homeLayoutShow[i] = value saveData(ui, settings) } @@ -100,7 +110,11 @@ ThemeManager(this).applyTheme() R.string.restart_app, Snackbar.LENGTH_SHORT ).apply { val mainIntent = - Intent.makeRestartActivityTask(context.packageManager.getLaunchIntentForPackage(context.packageName)!!.component) + Intent.makeRestartActivityTask( + context.packageManager.getLaunchIntentForPackage( + context.packageName + )!!.component + ) setAction("Do it!") { context.startActivity(mainIntent) Runtime.getRuntime().exit(0) diff --git a/app/src/main/java/ani/dantotsu/settings/extensionprefs/AnimePreferenceFragmentCompat.kt b/app/src/main/java/ani/dantotsu/settings/extensionprefs/AnimePreferenceFragmentCompat.kt index b82629f5..4f94fde6 100644 --- a/app/src/main/java/ani/dantotsu/settings/extensionprefs/AnimePreferenceFragmentCompat.kt +++ b/app/src/main/java/ani/dantotsu/settings/extensionprefs/AnimePreferenceFragmentCompat.kt @@ -1,12 +1,8 @@ package ani.dantotsu.settings.extensionprefs import android.content.Context -import android.content.SharedPreferences import android.os.Bundle -import android.util.Log import android.util.TypedValue -import android.view.View -import android.widget.FrameLayout import androidx.core.os.bundleOf import androidx.lifecycle.lifecycleScope import androidx.preference.DialogPreference @@ -14,11 +10,6 @@ import androidx.preference.EditTextPreference import androidx.preference.PreferenceFragmentCompat import androidx.preference.forEach import androidx.preference.getOnBindEditTextListener -import androidx.viewpager2.widget.ViewPager2 -import ani.dantotsu.R -import ani.dantotsu.settings.ExtensionsActivity -import com.google.android.material.tabs.TabLayout -import com.google.android.material.textfield.TextInputLayout import eu.kanade.tachiyomi.PreferenceScreen import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore @@ -33,9 +24,14 @@ class AnimeSourcePreferencesFragment : PreferenceFragmentCompat() { preferenceScreen = populateAnimePreferenceScreen() //set background color val color = TypedValue() - requireContext().theme.resolveAttribute(com.google.android.material.R.attr.backgroundColor, color, true) + requireContext().theme.resolveAttribute( + com.google.android.material.R.attr.backgroundColor, + color, + true + ) view?.setBackgroundColor(color.data) } + private var onCloseAction: (() -> Unit)? = null @@ -48,7 +44,8 @@ class AnimeSourcePreferencesFragment : PreferenceFragmentCompat() { val sourceId = requireArguments().getLong(SOURCE_ID) val source = Injekt.get().get(sourceId)!! check(source is ConfigurableAnimeSource) - val sharedPreferences = requireContext().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE) + val sharedPreferences = + requireContext().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE) val dataStore = SharedPreferencesDataStore(sharedPreferences) preferenceManager.preferenceDataStore = dataStore val sourceScreen = preferenceManager.createPreferenceScreen(requireContext()) @@ -75,7 +72,11 @@ class AnimeSourcePreferencesFragment : PreferenceFragmentCompat() { return sourceScreen } - fun getInstance(sourceId: Long, onCloseAction: (() -> Unit)? = null): AnimeSourcePreferencesFragment { + + fun getInstance( + sourceId: Long, + onCloseAction: (() -> Unit)? = null + ): AnimeSourcePreferencesFragment { val fragment = AnimeSourcePreferencesFragment() fragment.arguments = bundleOf(SOURCE_ID to sourceId) fragment.onCloseAction = onCloseAction diff --git a/app/src/main/java/ani/dantotsu/settings/extensionprefs/MangaPreferenceFragmentCompat.kt b/app/src/main/java/ani/dantotsu/settings/extensionprefs/MangaPreferenceFragmentCompat.kt index d571ef77..4ccee760 100644 --- a/app/src/main/java/ani/dantotsu/settings/extensionprefs/MangaPreferenceFragmentCompat.kt +++ b/app/src/main/java/ani/dantotsu/settings/extensionprefs/MangaPreferenceFragmentCompat.kt @@ -2,8 +2,6 @@ package ani.dantotsu.settings.extensionprefs import android.content.Context import android.os.Bundle -import android.view.View -import android.widget.FrameLayout import androidx.core.os.bundleOf import androidx.lifecycle.lifecycleScope import androidx.preference.DialogPreference @@ -11,11 +9,6 @@ import androidx.preference.EditTextPreference import androidx.preference.PreferenceFragmentCompat import androidx.preference.forEach import androidx.preference.getOnBindEditTextListener -import androidx.viewpager2.widget.ViewPager2 -import ani.dantotsu.R -import ani.dantotsu.settings.ExtensionsActivity -import com.google.android.material.tabs.TabLayout -import com.google.android.material.textfield.TextInputLayout import eu.kanade.tachiyomi.PreferenceScreen import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore import eu.kanade.tachiyomi.source.ConfigurableSource @@ -29,6 +22,7 @@ class MangaSourcePreferencesFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { preferenceScreen = populateMangaPreferenceScreen() } + private var onCloseAction: (() -> Unit)? = null override fun onDestroyView() { @@ -41,7 +35,8 @@ class MangaSourcePreferencesFragment : PreferenceFragmentCompat() { val sourceId = requireArguments().getLong(SOURCE_ID) val source = Injekt.get().get(sourceId)!! check(source is ConfigurableSource) - val sharedPreferences = requireContext().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE) + val sharedPreferences = + requireContext().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE) val dataStore = SharedPreferencesDataStore(sharedPreferences) preferenceManager.preferenceDataStore = dataStore val sourceScreen = preferenceManager.createPreferenceScreen(requireContext()) @@ -65,7 +60,11 @@ class MangaSourcePreferencesFragment : PreferenceFragmentCompat() { return sourceScreen } - fun getInstance(sourceId: Long, onCloseAction: (() -> Unit)? = null): MangaSourcePreferencesFragment { + + fun getInstance( + sourceId: Long, + onCloseAction: (() -> Unit)? = null + ): MangaSourcePreferencesFragment { val fragment = MangaSourcePreferencesFragment() fragment.arguments = bundleOf(SOURCE_ID to sourceId) fragment.onCloseAction = onCloseAction diff --git a/app/src/main/java/ani/dantotsu/settings/paging/AnimePagingSource.kt b/app/src/main/java/ani/dantotsu/settings/paging/AnimePagingSource.kt index 79f021de..f535479a 100644 --- a/app/src/main/java/ani/dantotsu/settings/paging/AnimePagingSource.kt +++ b/app/src/main/java/ani/dantotsu/settings/paging/AnimePagingSource.kt @@ -1,6 +1,5 @@ package ani.dantotsu.settings.paging -import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.ViewGroup import android.widget.ImageView @@ -47,9 +46,11 @@ class AnimeExtensionsViewModel( fun setSearchQuery(query: String) { searchQuery.value = query } + fun invalidatePager() { currentPagingSource?.invalidate() } + @OptIn(ExperimentalCoroutinesApi::class) val pagerFlow: Flow> = searchQuery.flatMapLatest { query -> Pager( @@ -77,7 +78,8 @@ class AnimeExtensionPagingSource( override suspend fun load(params: LoadParams): LoadResult { val position = params.key ?: 0 val installedExtensions = installedExtensionsFlow.first().map { it.pkgName }.toSet() - val availableExtensions = availableExtensionsFlow.first().filterNot { it.pkgName in installedExtensions } + val availableExtensions = + availableExtensionsFlow.first().filterNot { it.pkgName in installedExtensions } val query = searchQuery.first() val isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true @@ -86,7 +88,7 @@ class AnimeExtensionPagingSource( } else { availableExtensions.filter { it.name.contains(query, ignoreCase = true) } } - val filternfsw = if(isNsfwEnabled) { + val filternfsw = if (isNsfwEnabled) { filteredExtensions } else { filteredExtensions.filterNot { it.isNsfw } @@ -120,12 +122,18 @@ class AnimeExtensionAdapter(private val clickListener: OnAnimeInstallClickListen companion object { private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: AnimeExtension.Available, newItem: AnimeExtension.Available): Boolean { + override fun areItemsTheSame( + oldItem: AnimeExtension.Available, + newItem: AnimeExtension.Available + ): Boolean { // Your logic here return oldItem.pkgName == newItem.pkgName } - override fun areContentsTheSame(oldItem: AnimeExtension.Available, newItem: AnimeExtension.Available): Boolean { + override fun areContentsTheSame( + oldItem: AnimeExtension.Available, + newItem: AnimeExtension.Available + ): Boolean { // Your logic here return oldItem == newItem } @@ -133,7 +141,8 @@ class AnimeExtensionAdapter(private val clickListener: OnAnimeInstallClickListen } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnimeExtensionViewHolder { - val binding = ItemExtensionAllBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = + ItemExtensionAllBinding.inflate(LayoutInflater.from(parent.context), parent, false) return AnimeExtensionViewHolder(binding) } @@ -149,7 +158,8 @@ class AnimeExtensionAdapter(private val clickListener: OnAnimeInstallClickListen } } - inner class AnimeExtensionViewHolder(private val binding: ItemExtensionAllBinding) : RecyclerView.ViewHolder(binding.root) { + inner class AnimeExtensionViewHolder(private val binding: ItemExtensionAllBinding) : + RecyclerView.ViewHolder(binding.root) { init { binding.closeTextView.setOnClickListener { val extension = getItem(bindingAdapterPosition) @@ -158,11 +168,12 @@ class AnimeExtensionAdapter(private val clickListener: OnAnimeInstallClickListen } } } + val extensionIconImageView: ImageView = binding.extensionIconImageView - fun bind(extension: AnimeExtension.Available) { + fun bind(extension: AnimeExtension.Available) { val nsfw = if (extension.isNsfw) "(18+)" else "" - val lang= LanguageMapper.mapLanguageCodeToName(extension.lang) + val lang = LanguageMapper.mapLanguageCodeToName(extension.lang) binding.extensionNameTextView.text = extension.name binding.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw" } diff --git a/app/src/main/java/ani/dantotsu/settings/paging/MangaPagingSource.kt b/app/src/main/java/ani/dantotsu/settings/paging/MangaPagingSource.kt index de7eeab6..9e27f54f 100644 --- a/app/src/main/java/ani/dantotsu/settings/paging/MangaPagingSource.kt +++ b/app/src/main/java/ani/dantotsu/settings/paging/MangaPagingSource.kt @@ -1,6 +1,5 @@ package ani.dantotsu.settings.paging -import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.ViewGroup import android.widget.ImageView @@ -79,7 +78,8 @@ class MangaExtensionPagingSource( override suspend fun load(params: LoadParams): LoadResult { val position = params.key ?: 0 val installedExtensions = installedExtensionsFlow.first().map { it.pkgName }.toSet() - val availableExtensions = availableExtensionsFlow.first().filterNot { it.pkgName in installedExtensions } + val availableExtensions = + availableExtensionsFlow.first().filterNot { it.pkgName in installedExtensions } val query = searchQuery.first() val isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true val filteredExtensions = if (query.isEmpty()) { @@ -87,7 +87,7 @@ class MangaExtensionPagingSource( } else { availableExtensions.filter { it.name.contains(query, ignoreCase = true) } } - val filternfsw = if(isNsfwEnabled) { + val filternfsw = if (isNsfwEnabled) { filteredExtensions } else { filteredExtensions.filterNot { it.isNsfw } @@ -121,18 +121,25 @@ class MangaExtensionAdapter(private val clickListener: OnMangaInstallClickListen companion object { private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: MangaExtension.Available, newItem: MangaExtension.Available): Boolean { + override fun areItemsTheSame( + oldItem: MangaExtension.Available, + newItem: MangaExtension.Available + ): Boolean { return oldItem.pkgName == newItem.pkgName } - override fun areContentsTheSame(oldItem: MangaExtension.Available, newItem: MangaExtension.Available): Boolean { + override fun areContentsTheSame( + oldItem: MangaExtension.Available, + newItem: MangaExtension.Available + ): Boolean { return oldItem == newItem } } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MangaExtensionViewHolder { - val binding = ItemExtensionAllBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = + ItemExtensionAllBinding.inflate(LayoutInflater.from(parent.context), parent, false) return MangaExtensionViewHolder(binding) } @@ -148,7 +155,8 @@ class MangaExtensionAdapter(private val clickListener: OnMangaInstallClickListen } } - inner class MangaExtensionViewHolder(private val binding: ItemExtensionAllBinding) : RecyclerView.ViewHolder(binding.root) { + inner class MangaExtensionViewHolder(private val binding: ItemExtensionAllBinding) : + RecyclerView.ViewHolder(binding.root) { init { binding.closeTextView.setOnClickListener { val extension = getItem(bindingAdapterPosition) @@ -157,10 +165,11 @@ class MangaExtensionAdapter(private val clickListener: OnMangaInstallClickListen } } } + val extensionIconImageView: ImageView = binding.extensionIconImageView fun bind(extension: MangaExtension.Available) { val nsfw = if (extension.isNsfw) "(18+)" else "" - val lang= LanguageMapper.mapLanguageCodeToName(extension.lang) + val lang = LanguageMapper.mapLanguageCodeToName(extension.lang) binding.extensionNameTextView.text = extension.name binding.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw" } diff --git a/app/src/main/java/ani/dantotsu/settings/paging/NovelPagingSource.kt b/app/src/main/java/ani/dantotsu/settings/paging/NovelPagingSource.kt index 88649863..50fed33d 100644 --- a/app/src/main/java/ani/dantotsu/settings/paging/NovelPagingSource.kt +++ b/app/src/main/java/ani/dantotsu/settings/paging/NovelPagingSource.kt @@ -80,7 +80,8 @@ class NovelExtensionPagingSource( override suspend fun load(params: LoadParams): LoadResult { val position = params.key ?: 0 val installedExtensions = installedExtensionsFlow.first().map { it.pkgName }.toSet() - val availableExtensions = availableExtensionsFlow.first().filterNot { it.pkgName in installedExtensions } + val availableExtensions = + availableExtensionsFlow.first().filterNot { it.pkgName in installedExtensions } val query = searchQuery.first() val isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true val filteredExtensions = if (query.isEmpty()) { @@ -123,18 +124,25 @@ class NovelExtensionAdapter(private val clickListener: OnNovelInstallClickListen companion object { private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: NovelExtension.Available, newItem: NovelExtension.Available): Boolean { + override fun areItemsTheSame( + oldItem: NovelExtension.Available, + newItem: NovelExtension.Available + ): Boolean { return oldItem.pkgName == newItem.pkgName } - override fun areContentsTheSame(oldItem: NovelExtension.Available, newItem: NovelExtension.Available): Boolean { + override fun areContentsTheSame( + oldItem: NovelExtension.Available, + newItem: NovelExtension.Available + ): Boolean { return oldItem == newItem } } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NovelExtensionViewHolder { - val binding = ItemExtensionAllBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = + ItemExtensionAllBinding.inflate(LayoutInflater.from(parent.context), parent, false) return NovelExtensionViewHolder(binding) } @@ -150,7 +158,8 @@ class NovelExtensionAdapter(private val clickListener: OnNovelInstallClickListen } } - inner class NovelExtensionViewHolder(private val binding: ItemExtensionAllBinding) : RecyclerView.ViewHolder(binding.root) { + inner class NovelExtensionViewHolder(private val binding: ItemExtensionAllBinding) : + RecyclerView.ViewHolder(binding.root) { init { binding.closeTextView.setOnClickListener { val extension = getItem(bindingAdapterPosition) @@ -159,10 +168,11 @@ class NovelExtensionAdapter(private val clickListener: OnNovelInstallClickListen } } } + val extensionIconImageView: ImageView = binding.extensionIconImageView fun bind(extension: NovelExtension.Available) { val nsfw = "" - val lang= LanguageMapper.mapLanguageCodeToName("all") + val lang = LanguageMapper.mapLanguageCodeToName("all") binding.extensionNameTextView.text = extension.name binding.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw" } diff --git a/app/src/main/java/ani/dantotsu/subcriptions/AlarmReceiver.kt b/app/src/main/java/ani/dantotsu/subcriptions/AlarmReceiver.kt index 0849ca07..a326c531 100644 --- a/app/src/main/java/ani/dantotsu/subcriptions/AlarmReceiver.kt +++ b/app/src/main/java/ani/dantotsu/subcriptions/AlarmReceiver.kt @@ -5,11 +5,14 @@ import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.os.Build -import ani.dantotsu.* +import ani.dantotsu.currContext +import ani.dantotsu.isOnline +import ani.dantotsu.loadData +import ani.dantotsu.logger import ani.dantotsu.subcriptions.Subscription.Companion.defaultTime import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription import ani.dantotsu.subcriptions.Subscription.Companion.timeMinutes +import ani.dantotsu.tryWith import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/app/src/main/java/ani/dantotsu/subcriptions/Notifications.kt b/app/src/main/java/ani/dantotsu/subcriptions/Notifications.kt index 981ad580..0599a5be 100644 --- a/app/src/main/java/ani/dantotsu/subcriptions/Notifications.kt +++ b/app/src/main/java/ani/dantotsu/subcriptions/Notifications.kt @@ -58,15 +58,28 @@ class Notifications { ) } - fun createChannel(context: Context, group: Group?, id: String, name: String, silent: Boolean = false) { + fun createChannel( + context: Context, + group: Group?, + id: String, + name: String, + silent: Boolean = false + ) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val importance = if (!silent) NotificationManager.IMPORTANCE_HIGH else NotificationManager.IMPORTANCE_LOW + val importance = + if (!silent) NotificationManager.IMPORTANCE_HIGH else NotificationManager.IMPORTANCE_LOW val mChannel = NotificationChannel(id, name, importance) - val notificationManager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager + val notificationManager = + context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager if (group != null) { - notificationManager.createNotificationChannelGroup(NotificationChannelGroup(group.name, group.title)) + notificationManager.createNotificationChannelGroup( + NotificationChannelGroup( + group.name, + group.title + ) + ) mChannel.group = group.name } @@ -76,7 +89,8 @@ class Notifications { fun deleteChannel(context: Context, id: String) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val notificationManager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager + val notificationManager = + context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager notificationManager.deleteNotificationChannel(id) } } @@ -117,20 +131,21 @@ class Notifications { .submit() .get() } + @Suppress("BlockingMethodInNonBlockingContext") val largeBitmap = if (largeImg != null) Glide.with(context) - .asBitmap() - .load(GlideUrl(largeImg.url) { largeImg.headers }) - .submit() - .get() + .asBitmap() + .load(GlideUrl(largeImg.url) { largeImg.headers }) + .submit() + .get() else null - if(largeBitmap!=null) builder.setStyle( - NotificationCompat - .BigPictureStyle() - .bigPicture(largeBitmap) - .bigLargeIcon(bitmap) - ) + if (largeBitmap != null) builder.setStyle( + NotificationCompat + .BigPictureStyle() + .bigPicture(largeBitmap) + .bigLargeIcon(bitmap) + ) builder.setLargeIcon(bitmap) } else builder diff --git a/app/src/main/java/ani/dantotsu/subcriptions/Subscription.kt b/app/src/main/java/ani/dantotsu/subcriptions/Subscription.kt index 45925ba4..c28b5a49 100644 --- a/app/src/main/java/ani/dantotsu/subcriptions/Subscription.kt +++ b/app/src/main/java/ani/dantotsu/subcriptions/Subscription.kt @@ -40,7 +40,8 @@ class Subscription { val index = subscriptions.map { i++; it.key to i }.toMap() val notificationManager = NotificationManagerCompat.from(context) - val progressEnabled = loadData("subscription_checking_notifications", context) ?: true + val progressEnabled = + loadData("subscription_checking_notifications", context) ?: true val progressNotification = if (progressEnabled) getProgressNotification( context, subscriptions.size @@ -69,23 +70,26 @@ class Subscription { subscriptions.toList().map { val media = it.second val text = if (media.isAnime) { - val parser = SubscriptionHelper.getAnimeParser(context, media.isAdult, media.id) + val parser = + SubscriptionHelper.getAnimeParser(context, media.isAdult, media.id) progress(index[it.first]!!, parser.name, media.name) - val ep: Episode? = SubscriptionHelper.getEpisode(context, parser, media.id, media.isAdult) - if (ep != null) currActivity()!!.getString(R.string.episode)+"${ep.number}${ + val ep: Episode? = + SubscriptionHelper.getEpisode(context, parser, media.id, media.isAdult) + if (ep != null) currActivity()!!.getString(R.string.episode) + "${ep.number}${ if (ep.title != null) " : ${ep.title}" else "" }${ if (ep.isFiller) " [Filler]" else "" - } "+ currActivity()!!.getString(R.string.just_released) to ep.thumbnail + } " + currActivity()!!.getString(R.string.just_released) to ep.thumbnail else null } else { - val parser = SubscriptionHelper.getMangaParser(context, media.isAdult, media.id) + val parser = + SubscriptionHelper.getMangaParser(context, media.isAdult, media.id) progress(index[it.first]!!, parser.name, media.name) val ep: MangaChapter? = SubscriptionHelper.getChapter(context, parser, media.id, media.isAdult) - if (ep != null) currActivity()!!.getString(R.string.chapter)+"${ep.number}${ + if (ep != null) currActivity()!!.getString(R.string.chapter) + "${ep.number}${ if (ep.title != null) " : ${ep.title}" else "" - } "+ currActivity()!!.getString(R.string.just_released) to null + } " + currActivity()!!.getString(R.string.just_released) to null else null } ?: return@map createNotification(context.applicationContext, media, text.first, text.second) @@ -96,7 +100,8 @@ class Subscription { } } - fun getChannelId(isAnime: Boolean, mediaId: Int) = "${if (isAnime) "anime" else "manga"}_${mediaId}" + fun getChannelId(isAnime: Boolean, mediaId: Int) = + "${if (isAnime) "anime" else "manga"}_${mediaId}" private suspend fun createNotification( context: Context, @@ -124,7 +129,10 @@ class Subscription { private const val progressNotificationId = 100 - private fun getProgressNotification(context: Context, size: Int): NotificationCompat.Builder { + private fun getProgressNotification( + context: Context, + size: Int + ): NotificationCompat.Builder { return Notifications.getNotification( context, null, diff --git a/app/src/main/java/ani/dantotsu/subcriptions/SubscriptionHelper.kt b/app/src/main/java/ani/dantotsu/subcriptions/SubscriptionHelper.kt index 5f2636b6..14ee72b0 100644 --- a/app/src/main/java/ani/dantotsu/subcriptions/SubscriptionHelper.kt +++ b/app/src/main/java/ani/dantotsu/subcriptions/SubscriptionHelper.kt @@ -1,26 +1,41 @@ package ani.dantotsu.subcriptions import android.content.Context +import ani.dantotsu.R import ani.dantotsu.currContext import ani.dantotsu.loadData import ani.dantotsu.media.Media import ani.dantotsu.media.Selected -import ani.dantotsu.parsers.* +import ani.dantotsu.media.manga.MangaNameAdapter +import ani.dantotsu.parsers.AnimeParser +import ani.dantotsu.parsers.AnimeSources +import ani.dantotsu.parsers.Episode +import ani.dantotsu.parsers.HAnimeSources +import ani.dantotsu.parsers.HMangaSources +import ani.dantotsu.parsers.MangaChapter +import ani.dantotsu.parsers.MangaParser +import ani.dantotsu.parsers.MangaSources import ani.dantotsu.saveData import ani.dantotsu.tryWithSuspend -import ani.dantotsu.R -import ani.dantotsu.media.manga.MangaNameAdapter import kotlinx.coroutines.withTimeoutOrNull class SubscriptionHelper { companion object { - private fun loadSelected(context: Context, mediaId: Int, isAdult: Boolean, isAnime: Boolean): Selected { + private fun loadSelected( + context: Context, + mediaId: Int, + isAdult: Boolean, + isAnime: Boolean + ): Selected { val sharedPreferences = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) val data = loadData("${mediaId}-select", context) ?: Selected().let { it.sourceIndex = if (isAdult) 0 - else if (isAnime) {sharedPreferences.getInt("settings_def_anime_source_s_r",0)} - else {sharedPreferences.getInt("settings_def_manga_source_s_r",0)} + else if (isAnime) { + 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", context) ?: false it } @@ -39,15 +54,27 @@ class SubscriptionHelper { return parser } - suspend fun getEpisode(context: Context, parser: AnimeParser, id: Int, isAdult: Boolean): Episode? { + suspend fun getEpisode( + context: Context, + parser: AnimeParser, + id: Int, + isAdult: Boolean + ): Episode? { val selected = loadSelected(context, id, isAdult, true) val ep = withTimeoutOrNull(10 * 1000) { tryWithSuspend { - val show = parser.loadSavedShowResponse(id) ?: throw Exception(currContext()?.getString(R.string.failed_to_load_data, id)) + val show = parser.loadSavedShowResponse(id) ?: throw Exception( + currContext()?.getString( + R.string.failed_to_load_data, + id + ) + ) show.sAnime?.let { - parser.getLatestEpisode(show.link, show.extra, - it, selected.latest) + parser.getLatestEpisode( + show.link, show.extra, + it, selected.latest + ) } } } @@ -64,14 +91,26 @@ class SubscriptionHelper { return sources[selected.sourceIndex] } - suspend fun getChapter(context: Context, parser: MangaParser, id: Int, isAdult: Boolean): MangaChapter? { + suspend fun getChapter( + context: Context, + parser: MangaParser, + id: Int, + isAdult: Boolean + ): MangaChapter? { val selected = loadSelected(context, id, isAdult, true) val chp = withTimeoutOrNull(10 * 1000) { tryWithSuspend { - val show = parser.loadSavedShowResponse(id) ?: throw Exception(currContext()?.getString(R.string.failed_to_load_data, id)) + val show = parser.loadSavedShowResponse(id) ?: throw Exception( + currContext()?.getString( + R.string.failed_to_load_data, + id + ) + ) show.sManga?.let { - parser.getLatestChapter(show.link, show.extra, - it, selected.latest) + parser.getLatestChapter( + show.link, show.extra, + it, selected.latest + ) } } } @@ -91,8 +130,9 @@ class SubscriptionHelper { ) : java.io.Serializable private const val subscriptions = "subscriptions" - fun getSubscriptions(context: Context): Map = loadData(subscriptions, context) - ?: mapOf().also { saveData(subscriptions, it, context) } + fun getSubscriptions(context: Context): Map = + loadData(subscriptions, context) + ?: mapOf().also { saveData(subscriptions, it, context) } fun saveSubscription(context: Context, media: Media, subscribed: Boolean) { val data = loadData>(subscriptions, context)!!.toMutableMap() diff --git a/app/src/main/java/ani/dantotsu/subcriptions/SubscriptionWorker.kt b/app/src/main/java/ani/dantotsu/subcriptions/SubscriptionWorker.kt index 8481ed88..bed58b21 100644 --- a/app/src/main/java/ani/dantotsu/subcriptions/SubscriptionWorker.kt +++ b/app/src/main/java/ani/dantotsu/subcriptions/SubscriptionWorker.kt @@ -1,18 +1,25 @@ package ani.dantotsu.subcriptions import android.content.Context -import androidx.work.* +import androidx.work.Constraints +import androidx.work.CoroutineWorker +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.NetworkType +import androidx.work.PeriodicWorkRequest +import androidx.work.WorkManager +import androidx.work.WorkerParameters import ani.dantotsu.loadData import ani.dantotsu.subcriptions.Subscription.Companion.defaultTime import ani.dantotsu.subcriptions.Subscription.Companion.timeMinutes import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import java.util.concurrent.* +import java.util.concurrent.TimeUnit -class SubscriptionWorker(val context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { +class SubscriptionWorker(val context: Context, params: WorkerParameters) : + CoroutineWorker(context, params) { override suspend fun doWork(): Result { - withContext(Dispatchers.IO){ + withContext(Dispatchers.IO) { Subscription.perform(context) } return Result.success() @@ -23,8 +30,9 @@ class SubscriptionWorker(val context: Context, params: WorkerParameters) : Corou private const val SUBSCRIPTION_WORK_NAME = "work_subscription" fun enqueue(context: Context) { val curTime = loadData("subscriptions_time_s") ?: defaultTime - if(timeMinutes[curTime]>0L) { - val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build() + if (timeMinutes[curTime] > 0L) { + val constraints = + Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build() val periodicSyncDataWork = PeriodicWorkRequest.Builder( SubscriptionWorker::class.java, 6, TimeUnit.HOURS ).apply { @@ -32,7 +40,9 @@ class SubscriptionWorker(val context: Context, params: WorkerParameters) : Corou setConstraints(constraints) }.build() WorkManager.getInstance(context).enqueueUniquePeriodicWork( - SUBSCRIPTION_WORK_NAME, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, periodicSyncDataWork + SUBSCRIPTION_WORK_NAME, + ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, + periodicSyncDataWork ) } } diff --git a/app/src/main/java/ani/dantotsu/themes/ThemeManager.kt b/app/src/main/java/ani/dantotsu/themes/ThemeManager.kt index 69caa69a..2029177b 100644 --- a/app/src/main/java/ani/dantotsu/themes/ThemeManager.kt +++ b/app/src/main/java/ani/dantotsu/themes/ThemeManager.kt @@ -4,41 +4,42 @@ import android.app.Activity import android.content.Context import android.content.res.Configuration import android.graphics.Bitmap -import android.util.Log -import ani.dantotsu.FileUrl import ani.dantotsu.R -import ani.dantotsu.logger -import ani.dantotsu.media.manga.mangareader.BaseImageAdapter.Companion.loadBitmap -import ani.dantotsu.media.manga.mangareader.BaseImageAdapter.Companion.loadBitmap_old import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColorsOptions -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext -import kotlinx.coroutines.withTimeout class ThemeManager(private val context: Context) { fun applyTheme(fromImage: Bitmap? = null) { - val useOLED = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("use_oled", false) && isDarkThemeActive(context) - val useCustomTheme = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("use_custom_theme", false) - val customTheme = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("custom_theme_int", 16712221) - val useSource = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("use_source_theme", false) - val useMaterial = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getBoolean("use_material_you", false) - if(useSource){ - val returnedEarly = applyDynamicColors(useMaterial, context, useOLED, fromImage, useCustom = if(useCustomTheme) customTheme else null) - if(!returnedEarly) return + val useOLED = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + .getBoolean("use_oled", false) && isDarkThemeActive(context) + val useCustomTheme = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + .getBoolean("use_custom_theme", false) + val customTheme = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + .getInt("custom_theme_int", 16712221) + val useSource = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + .getBoolean("use_source_theme", false) + val useMaterial = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + .getBoolean("use_material_you", false) + if (useSource) { + val returnedEarly = applyDynamicColors( + useMaterial, + context, + useOLED, + fromImage, + useCustom = if (useCustomTheme) customTheme else null + ) + if (!returnedEarly) return } else if (useCustomTheme) { - val returnedEarly = applyDynamicColors(useMaterial, context, useOLED, useCustom = customTheme) - if(!returnedEarly) return + val returnedEarly = + applyDynamicColors(useMaterial, context, useOLED, useCustom = customTheme) + if (!returnedEarly) return } else { val returnedEarly = applyDynamicColors(useMaterial, context, useOLED, useCustom = null) - if(!returnedEarly) return + if (!returnedEarly) return } - val theme = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getString("theme", "PURPLE")!! + val theme = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + .getString("theme", "PURPLE")!! val themeToApply = when (theme) { "PURPLE" -> if (useOLED) R.style.Theme_Dantotsu_PurpleOLED else R.style.Theme_Dantotsu_Purple @@ -55,7 +56,13 @@ class ThemeManager(private val context: Context) { context.setTheme(themeToApply) } - private fun applyDynamicColors(useMaterialYou: Boolean, context: Context, useOLED: Boolean, bitmap: Bitmap? = null, useCustom: Int? = null): Boolean { + private fun applyDynamicColors( + useMaterialYou: Boolean, + context: Context, + useOLED: Boolean, + bitmap: Bitmap? = null, + useCustom: Int? = null + ): Boolean { val builder = DynamicColorsOptions.Builder() var needMaterial = true @@ -63,7 +70,7 @@ class ThemeManager(private val context: Context) { if (bitmap != null) { builder.setContentBasedSource(bitmap) needMaterial = false - }else if (useCustom != null){ + } else if (useCustom != null) { builder.setContentBasedSource(useCustom) needMaterial = false } @@ -71,7 +78,7 @@ class ThemeManager(private val context: Context) { if (useOLED) { builder.setThemeOverlay(R.style.AppTheme_Amoled) } - if(needMaterial && !useMaterialYou) return true + if (needMaterial && !useMaterialYou) return true // Build the options val options = builder.build() @@ -100,7 +107,7 @@ class ThemeManager(private val context: Context) { } - companion object{ + companion object { enum class Theme(val theme: String) { PURPLE("PURPLE"), BLUE("BLUE"),