diff --git a/app/build.gradle b/app/build.gradle index 117a1cc9..c5c77402 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -51,7 +51,7 @@ android { } debug { applicationIdSuffix ".beta" - versionNameSuffix "-beta02" + versionNameSuffix "-beta03" manifestPlaceholders.icon_placeholder = "@mipmap/ic_launcher_beta" manifestPlaceholders.icon_placeholder_round = "@mipmap/ic_launcher_beta_round" debuggable false diff --git a/app/src/main/java/ani/dantotsu/App.kt b/app/src/main/java/ani/dantotsu/App.kt index e1ea3742..92cbc6be 100644 --- a/app/src/main/java/ani/dantotsu/App.kt +++ b/app/src/main/java/ani/dantotsu/App.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.os.Bundle +import android.util.Log import androidx.multidex.MultiDex import androidx.multidex.MultiDexApplication import ani.dantotsu.addons.download.DownloadAddonManager @@ -93,7 +94,7 @@ class App : MultiDexApplication() { Logger.init(this) Thread.setDefaultUncaughtExceptionHandler(FinalExceptionHandler()) - Logger.log("App: Logging started") + Logger.log(Log.WARN, "App: Logging started") initializeNetwork() diff --git a/app/src/main/java/ani/dantotsu/MainActivity.kt b/app/src/main/java/ani/dantotsu/MainActivity.kt index 328e4a01..f882c78e 100644 --- a/app/src/main/java/ani/dantotsu/MainActivity.kt +++ b/app/src/main/java/ani/dantotsu/MainActivity.kt @@ -33,8 +33,8 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.media3.common.util.UnstableApi import androidx.viewpager2.adapter.FragmentStateAdapter -import ani.dantotsu.addons.torrent.TorrentServerService import ani.dantotsu.addons.torrent.TorrentAddonManager +import ani.dantotsu.addons.torrent.TorrentServerService import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.anilist.AnilistHomeViewModel import ani.dantotsu.databinding.ActivityMainBinding @@ -336,43 +336,35 @@ class MainActivity : AppCompatActivity() { startActivity(Intent(this, NoInternet::class.java)) } else { val model: AnilistHomeViewModel by viewModels() - model.genres.observe(this) { - if (it != null) { - if (it) { - val navbar = binding.includedNavbar.navbar - bottomBar = navbar - navbar.visibility = View.VISIBLE - binding.mainProgressBar.visibility = View.GONE - val mainViewPager = binding.viewpager - mainViewPager.isUserInputEnabled = false - mainViewPager.adapter = - ViewPagerAdapter(supportFragmentManager, lifecycle) - mainViewPager.setPageTransformer(ZoomOutPageTransformer()) - navbar.setOnTabSelectListener(object : - AnimatedBottomBar.OnTabSelectListener { - override fun onTabSelected( - lastIndex: Int, - lastTab: AnimatedBottomBar.Tab?, - newIndex: Int, - newTab: AnimatedBottomBar.Tab - ) { - navbar.animate().translationZ(12f).setDuration(200).start() - selectedOption = newIndex - mainViewPager.setCurrentItem(newIndex, false) - } - }) - if (mainViewPager.currentItem != selectedOption) { - navbar.selectTabAt(selectedOption) - mainViewPager.post { - mainViewPager.setCurrentItem( - selectedOption, - false - ) - } - } - } else { - binding.mainProgressBar.visibility = View.GONE - } + val navbar = binding.includedNavbar.navbar + bottomBar = navbar + navbar.visibility = View.VISIBLE + binding.mainProgressBar.visibility = View.GONE + val mainViewPager = binding.viewpager + mainViewPager.isUserInputEnabled = false + mainViewPager.adapter = + ViewPagerAdapter(supportFragmentManager, lifecycle) + mainViewPager.setPageTransformer(ZoomOutPageTransformer()) + navbar.setOnTabSelectListener(object : + AnimatedBottomBar.OnTabSelectListener { + override fun onTabSelected( + lastIndex: Int, + lastTab: AnimatedBottomBar.Tab?, + newIndex: Int, + newTab: AnimatedBottomBar.Tab + ) { + navbar.animate().translationZ(12f).setDuration(200).start() + selectedOption = newIndex + mainViewPager.setCurrentItem(newIndex, false) + } + }) + if (mainViewPager.currentItem != selectedOption) { + navbar.selectTabAt(selectedOption) + mainViewPager.post { + mainViewPager.setCurrentItem( + selectedOption, + false + ) } } //Load Data 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 320d715b..fa36fa1c 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt @@ -35,6 +35,8 @@ object Anilist { var rateLimitReset: Long = 0 + var initialized = false + val sortBy = listOf( "SCORE_DESC", "POPULARITY_DESC", 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 2886e66c..3c4a898a 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt @@ -57,6 +57,7 @@ class AnilistQueries { Anilist.unreadNotificationCount = user.unreadNotificationCount ?: 0 val unread = PrefManager.getVal(PrefName.UnreadCommentNotifications) Anilist.unreadNotificationCount += unread + Anilist.initialized = true return true } 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 ad8a36f8..af1c2146 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt @@ -5,6 +5,7 @@ import androidx.fragment.app.FragmentActivity import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.lifecycleScope import ani.dantotsu.BuildConfig import ani.dantotsu.R import ani.dantotsu.connections.discord.Discord @@ -12,31 +13,26 @@ import ani.dantotsu.connections.mal.MAL import ani.dantotsu.media.Media import ani.dantotsu.others.AppUpdater import ani.dantotsu.profile.User -import ani.dantotsu.profile.activity.ActivityItemBuilder import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.snackString import ani.dantotsu.tryWithSuspend -import ani.dantotsu.util.Logger -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext suspend fun getUserId(context: Context, block: () -> Unit) { - val anilist = if (Anilist.userid == null && Anilist.token != null) { + if (!Anilist.initialized) { if (Anilist.query.getUserData()) { tryWithSuspend { if (MAL.token != null && !MAL.query.getUserData()) snackString(context.getString(R.string.error_loading_mal_user_data)) } - true } else { snackString(context.getString(R.string.error_loading_anilist_user_data)) - false } - } else true - - if (anilist) block.invoke() + } + block.invoke() } class AnilistHomeViewModel : ViewModel() { @@ -90,6 +86,7 @@ class AnilistHomeViewModel : ViewModel() { MutableLiveData>(null) fun getHidden(): LiveData> = hidden + @Suppress("UNCHECKED_CAST") suspend fun initHomePage() { val res = Anilist.query.initHomePage() @@ -104,15 +101,20 @@ class AnilistHomeViewModel : ViewModel() { res["status"]?.let { userStatus.postValue(it as ArrayList?) } } - suspend fun loadMain(context: FragmentActivity) { Anilist.getSavedToken() MAL.getSavedToken() Discord.getSavedToken() if (!BuildConfig.FLAVOR.contains("fdroid")) { - if (PrefManager.getVal(PrefName.CheckUpdate)) AppUpdater.check(context) + if (PrefManager.getVal(PrefName.CheckUpdate)) + context.lifecycleScope.launch(Dispatchers.IO) { + AppUpdater.check(context, true) + } + } + val ret = Anilist.query.getGenresAndTags() + withContext(Dispatchers.Main) { + genres.value = ret } - genres.postValue(Anilist.query.getGenresAndTags()) } val empty = MutableLiveData(null) diff --git a/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt b/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt index a6c19cd7..f9a9f596 100644 --- a/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt +++ b/app/src/main/java/ani/dantotsu/home/AnimeFragment.kt @@ -36,6 +36,7 @@ import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.snackString import ani.dantotsu.statusBarHeight +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -268,13 +269,25 @@ class AnimeFragment : Fragment() { true } + var running = false val live = Refresh.activity.getOrPut(this.hashCode()) { MutableLiveData(false) } live.observe(viewLifecycleOwner) { - if (it) { + if (it && !running) { + running = true scope.launch { withContext(Dispatchers.IO) { - getUserId(requireContext()) { - load() + Anilist.userid = PrefManager.getNullableVal(PrefName.AnilistUserId, null) + ?.toIntOrNull() + if (Anilist.userid == null) { + getUserId(requireContext()) { + load() + } + } else { + CoroutineScope(Dispatchers.IO).launch { + getUserId(requireContext()) { + load() + } + } } model.loaded = true model.loadTrending(1) @@ -287,6 +300,7 @@ class AnimeFragment : Fragment() { } live.postValue(false) _binding?.animeRefresh?.isRefreshing = false + running = false } } } diff --git a/app/src/main/java/ani/dantotsu/home/HomeFragment.kt b/app/src/main/java/ani/dantotsu/home/HomeFragment.kt index fd435b68..bedc73ea 100644 --- a/app/src/main/java/ani/dantotsu/home/HomeFragment.kt +++ b/app/src/main/java/ani/dantotsu/home/HomeFragment.kt @@ -48,9 +48,9 @@ import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.snackString import ani.dantotsu.statusBarHeight import ani.dantotsu.util.Logger +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import kotlin.math.max import kotlin.math.min @@ -379,7 +379,8 @@ class HomeFragment : Fragment() { model.getHidden().observe(viewLifecycleOwner) { if (it != null) { if (it.isNotEmpty()) { - binding.homeHiddenItemsRecyclerView.adapter = MediaAdaptor(0, it, requireActivity()) + binding.homeHiddenItemsRecyclerView.adapter = + MediaAdaptor(0, it, requireActivity()) binding.homeHiddenItemsRecyclerView.layoutManager = LinearLayoutManager( requireContext(), LinearLayoutManager.HORIZONTAL, @@ -393,7 +394,8 @@ class HomeFragment : Fragment() { } binding.homeHiddenItemsMore.setSafeOnClickListener { _ -> ContextCompat.startActivity( - requireActivity(), Intent(requireActivity(), MediaListViewActivity::class.java) + requireActivity(), + Intent(requireActivity(), MediaListViewActivity::class.java) .putExtra("title", getString(R.string.hidden)) .putExtra("media", it), null @@ -403,8 +405,7 @@ class HomeFragment : Fragment() { binding.homeHiddenItemsContainer.visibility = View.GONE true } - } - else { + } else { binding.homeContinueWatch.setOnLongClickListener { snackString(getString(R.string.no_hidden_items)) true @@ -457,17 +458,29 @@ class HomeFragment : Fragment() { val live = Refresh.activity.getOrPut(1) { MutableLiveData(true) } live.observe(viewLifecycleOwner) { - if (it && !running) { + if (!running && it) { running = true scope.launch { withContext(Dispatchers.IO) { //Get userData First - getUserId(requireContext()) { - load() + Anilist.userid = + PrefManager.getNullableVal(PrefName.AnilistUserId, null) + ?.toIntOrNull() + if (Anilist.userid == null) { + getUserId(requireContext()) { + load() + } + } else { + CoroutineScope(Dispatchers.IO).launch { + getUserId(requireContext()) { + load() + } + } } model.loaded = true - model.setListImages() - Logger.log("HomeFragment: Refreshing") + CoroutineScope(Dispatchers.IO).launch { + model.setListImages() + } var empty = true val homeLayoutShow: List = PrefManager.getVal(PrefName.HomeLayout) @@ -483,9 +496,9 @@ class HomeFragment : Fragment() { } live.postValue(false) _binding?.homeRefresh?.isRefreshing = false + running = false } binding.homeHiddenItemsContainer.visibility = View.GONE - running = false } } diff --git a/app/src/main/java/ani/dantotsu/home/MangaFragment.kt b/app/src/main/java/ani/dantotsu/home/MangaFragment.kt index f74178ee..c878c584 100644 --- a/app/src/main/java/ani/dantotsu/home/MangaFragment.kt +++ b/app/src/main/java/ani/dantotsu/home/MangaFragment.kt @@ -33,6 +33,7 @@ import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.snackString import ani.dantotsu.statusBarHeight +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -253,13 +254,25 @@ class MangaFragment : Fragment() { mangaPageAdapter.updateAvatar() } - val live = Refresh.activity.getOrPut(this.hashCode()) { MutableLiveData(false) } + var running = false + val live = Refresh.activity.getOrPut(this.hashCode()) { MutableLiveData(true) } live.observe(viewLifecycleOwner) { - if (it) { + if (!running && it) { + running = true scope.launch { withContext(Dispatchers.IO) { - getUserId(requireContext()) { - load() + Anilist.userid = PrefManager.getNullableVal(PrefName.AnilistUserId, null) + ?.toIntOrNull() + if (Anilist.userid == null) { + getUserId(requireContext()) { + load() + } + } else { + CoroutineScope(Dispatchers.IO).launch { + getUserId(requireContext()) { + load() + } + } } model.loaded = true model.loadTrending() @@ -272,6 +285,7 @@ class MangaFragment : Fragment() { } live.postValue(false) _binding?.mangaRefresh?.isRefreshing = false + running = false } } } diff --git a/app/src/main/java/ani/dantotsu/util/Logger.kt b/app/src/main/java/ani/dantotsu/util/Logger.kt index b53589f4..7e4461f8 100644 --- a/app/src/main/java/ani/dantotsu/util/Logger.kt +++ b/app/src/main/java/ani/dantotsu/util/Logger.kt @@ -59,6 +59,20 @@ object Logger { } } + fun log(level: Int, message: String, tag: String = "Internal Logger") { + val trace = Thread.currentThread().stackTrace[3] + loggerExecutor.execute { + if (file == null) Log.println(level, tag, message) + else { + val className = trace.className + val methodName = trace.methodName + val lineNumber = trace.lineNumber + file?.appendText("date/time: ${Date()} | $className.$methodName($lineNumber)\n") + file?.appendText("message: $message\n-\n") + } + } + } + fun log(e: Exception) { loggerExecutor.execute { if (file == null) e.printStackTrace() else {