chore: code cleanup

This commit is contained in:
rebelonion 2024-04-19 06:03:40 -05:00
parent 386e02a564
commit 24147e746a
198 changed files with 1367 additions and 965 deletions

View file

@ -7,11 +7,11 @@ import android.os.Bundle
import androidx.multidex.MultiDex import androidx.multidex.MultiDex
import androidx.multidex.MultiDexApplication import androidx.multidex.MultiDexApplication
import ani.dantotsu.addons.download.DownloadAddonManager import ani.dantotsu.addons.download.DownloadAddonManager
import ani.dantotsu.addons.torrent.TorrentAddonManager
import ani.dantotsu.aniyomi.anime.custom.AppModule import ani.dantotsu.aniyomi.anime.custom.AppModule
import ani.dantotsu.aniyomi.anime.custom.PreferenceModule import ani.dantotsu.aniyomi.anime.custom.PreferenceModule
import ani.dantotsu.connections.comments.CommentsAPI import ani.dantotsu.connections.comments.CommentsAPI
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.addons.torrent.TorrentAddonManager
import ani.dantotsu.notifications.TaskScheduler import ani.dantotsu.notifications.TaskScheduler
import ani.dantotsu.others.DisabledReports import ani.dantotsu.others.DisabledReports
import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.parsers.AnimeSources

View file

@ -190,7 +190,7 @@ var loadMedia: Int? = null
var loadIsMAL = false var loadIsMAL = false
val Int.toPx get() = TypedValue.applyDimension( val Int.toPx get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), Resources.getSystem().displayMetrics TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), getSystem().displayMetrics
).toInt() ).toInt()
fun initActivity(a: Activity) { fun initActivity(a: Activity) {
@ -464,7 +464,7 @@ class InputFilterMinMax(
} }
class ZoomOutPageTransformer() : class ZoomOutPageTransformer :
ViewPager2.PageTransformer { ViewPager2.PageTransformer {
override fun transformPage(view: View, position: Float) { override fun transformPage(view: View, position: Float) {
if (position == 0.0f && PrefManager.getVal(PrefName.LayoutAnimations)) { if (position == 0.0f && PrefManager.getVal(PrefName.LayoutAnimations)) {

View file

@ -1,38 +1,23 @@
package ani.dantotsu.addons package ani.dantotsu.addons
import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.DownloadManager
import android.app.NotificationManager import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Environment
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.core.net.toUri
import ani.dantotsu.BuildConfig
import ani.dantotsu.Mapper import ani.dantotsu.Mapper
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.client import ani.dantotsu.client
import ani.dantotsu.logError import ani.dantotsu.logError
import ani.dantotsu.media.MediaType
import ani.dantotsu.openLinkInBrowser import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.others.AppUpdater import ani.dantotsu.others.AppUpdater
import ani.dantotsu.settings.InstallerSteps import ani.dantotsu.settings.InstallerSteps
import ani.dantotsu.toast import ani.dantotsu.toast
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.util.ExtensionInstaller
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.decodeFromJsonElement
import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
class AddonDownloader { class AddonDownloader {
@ -44,8 +29,8 @@ class AddonDownloader {
Mapper.json.decodeFromJsonElement<AppUpdater.GithubResponse>(it) Mapper.json.decodeFromJsonElement<AppUpdater.GithubResponse>(it)
} }
val r = res.maxByOrNull { val r = res.maxByOrNull {
it.timeStamp() it.timeStamp()
} ?: throw Exception("No Pre Release Found") } ?: throw Exception("No Pre Release Found")
val v = r.tagName.substringAfter("v", "") val v = r.tagName.substringAfter("v", "")
val md = r.body ?: "" val md = r.body ?: ""
val version = v.ifEmpty { throw Exception("Weird Version : ${r.tagName}") } val version = v.ifEmpty { throw Exception("Weird Version : ${r.tagName}") }
@ -101,8 +86,7 @@ class AddonDownloader {
{ error -> installerSteps.onError(error) {} }, { error -> installerSteps.onError(error) {} },
{ installerSteps.onComplete {} } { installerSteps.onComplete {} }
) )
} } else openLinkInBrowser("https://github.com/repos/$repo/releases/tag/v$version")
else openLinkInBrowser("https://github.com/repos/$repo/releases/tag/v$version")
} }
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)

View file

@ -9,8 +9,8 @@ import ani.dantotsu.addons.download.DownloadAddon
import ani.dantotsu.addons.download.DownloadAddonApi import ani.dantotsu.addons.download.DownloadAddonApi
import ani.dantotsu.addons.download.DownloadAddonManager import ani.dantotsu.addons.download.DownloadAddonManager
import ani.dantotsu.addons.download.DownloadLoadResult import ani.dantotsu.addons.download.DownloadLoadResult
import ani.dantotsu.addons.torrent.TorrentAddonApi
import ani.dantotsu.addons.torrent.TorrentAddon import ani.dantotsu.addons.torrent.TorrentAddon
import ani.dantotsu.addons.torrent.TorrentAddonApi
import ani.dantotsu.addons.torrent.TorrentAddonManager import ani.dantotsu.addons.torrent.TorrentAddonManager
import ani.dantotsu.addons.torrent.TorrentLoadResult import ani.dantotsu.addons.torrent.TorrentLoadResult
import ani.dantotsu.media.AddonType import ani.dantotsu.media.AddonType
@ -56,7 +56,8 @@ class AddonLoader {
throw error throw error
} }
val extName = pkgManager.getApplicationLabel(appInfo).toString().substringAfter("Dantotsu: ") val extName =
pkgManager.getApplicationLabel(appInfo).toString().substringAfter("Dantotsu: ")
val versionName = pkgInfo.versionName val versionName = pkgInfo.versionName
val versionCode = PackageInfoCompat.getLongVersionCode(pkgInfo) val versionCode = PackageInfoCompat.getLongVersionCode(pkgInfo)
@ -64,7 +65,8 @@ class AddonLoader {
Logger.log("Missing versionName for extension $extName") Logger.log("Missing versionName for extension $extName")
throw IllegalStateException("Missing versionName for extension $extName") throw IllegalStateException("Missing versionName for extension $extName")
} }
val classLoader = PathClassLoader(appInfo.sourceDir, appInfo.nativeLibraryDir, context.classLoader) val classLoader =
PathClassLoader(appInfo.sourceDir, appInfo.nativeLibraryDir, context.classLoader)
val loadedClass = try { val loadedClass = try {
Class.forName(className, false, classLoader) Class.forName(className, false, classLoader)
} catch (e: ClassNotFoundException) { } catch (e: ClassNotFoundException) {
@ -75,7 +77,7 @@ class AddonLoader {
Logger.log("Extension load error: $extName ($className)") Logger.log("Extension load error: $extName ($className)")
Logger.log(e) Logger.log(e)
throw e throw e
}catch (e: Exception) { } catch (e: Exception) {
Logger.log("Extension load error: $extName ($className)") Logger.log("Extension load error: $extName ($className)")
Logger.log(e) Logger.log(e)
throw e throw e
@ -84,7 +86,8 @@ class AddonLoader {
return when (type) { return when (type) {
AddonType.TORRENT -> { AddonType.TORRENT -> {
val extension = instance as? TorrentAddonApi ?: throw IllegalStateException("Extension is not a TorrentAddonApi") val extension = instance as? TorrentAddonApi
?: throw IllegalStateException("Extension is not a TorrentAddonApi")
TorrentLoadResult.Success( TorrentLoadResult.Success(
TorrentAddon.Installed( TorrentAddon.Installed(
name = extName, name = extName,
@ -96,8 +99,10 @@ class AddonLoader {
) )
) )
} }
AddonType.DOWNLOAD -> { AddonType.DOWNLOAD -> {
val extension = instance as? DownloadAddonApi ?: throw IllegalStateException("Extension is not a DownloadAddonApi") val extension = instance as? DownloadAddonApi
?: throw IllegalStateException("Extension is not a DownloadAddonApi")
DownloadLoadResult.Success( DownloadLoadResult.Success(
DownloadAddon.Installed( DownloadAddon.Installed(
name = extName, name = extName,
@ -120,6 +125,7 @@ class AddonLoader {
TorrentAddonManager.TORRENT_CLASS, TorrentAddonManager.TORRENT_CLASS,
type type
) )
AddonType.DOWNLOAD -> loadExtension( AddonType.DOWNLOAD -> loadExtension(
context, context,
packageName, packageName,

View file

@ -6,7 +6,7 @@ import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.util.ExtensionInstaller import eu.kanade.tachiyomi.extension.util.ExtensionInstaller
import rx.Observable import rx.Observable
abstract class AddonManager<T: Addon.Installed>( abstract class AddonManager<T : Addon.Installed>(
private val context: Context private val context: Context
) { ) {
abstract var extension: T? abstract var extension: T?
@ -31,14 +31,16 @@ abstract class AddonManager<T: Addon.Installed>(
installer.uninstallApk(it) installer.uninstallApk(it)
} }
} }
fun addListenerAction(action: (AddonListener.ListenerAction) -> Unit) { fun addListenerAction(action: (AddonListener.ListenerAction) -> Unit) {
onListenerAction = action onListenerAction = action
} }
fun removeListenerAction() { fun removeListenerAction() {
onListenerAction = null onListenerAction = null
} }
fun install(url: String): Observable<InstallStep> { fun install(url: String): Observable<InstallStep> {
return installer.downloadAndInstall(url, getPackageName()?: "", name, type) return installer.downloadAndInstall(url, getPackageName() ?: "", name, type)
} }
} }

View file

@ -3,7 +3,6 @@ package ani.dantotsu.addons.download
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import ani.dantotsu.addons.AddonListener import ani.dantotsu.addons.AddonListener
import ani.dantotsu.addons.AddonLoader import ani.dantotsu.addons.AddonLoader
@ -26,7 +25,7 @@ internal class AddonInstallReceiver : BroadcastReceiver() {
ContextCompat.registerReceiver(context, this, filter, ContextCompat.RECEIVER_EXPORTED) ContextCompat.registerReceiver(context, this, filter, ContextCompat.RECEIVER_EXPORTED)
} }
fun setListener(listener: AddonListener, type: AddonType) : AddonInstallReceiver { fun setListener(listener: AddonListener, type: AddonType): AddonInstallReceiver {
this.listener = listener this.listener = listener
this.type = type this.type = type
return this return this

View file

@ -2,6 +2,6 @@ package ani.dantotsu.addons.download
import ani.dantotsu.addons.LoadResult import ani.dantotsu.addons.LoadResult
open class DownloadLoadResult: LoadResult() { open class DownloadLoadResult : LoadResult() {
class Success(val extension: DownloadAddon.Installed) : DownloadLoadResult() class Success(val extension: DownloadAddon.Installed) : DownloadLoadResult()
} }

View file

@ -14,10 +14,7 @@ import ani.dantotsu.addons.download.AddonInstallReceiver
import ani.dantotsu.media.AddonType import ani.dantotsu.media.AddonType
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.util.ExtensionInstaller
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class TorrentAddonManager( class TorrentAddonManager(
@ -98,7 +95,7 @@ class TorrentAddonManager(
} }
} }
private inner class InstallationListener : AddonListener { private inner class InstallationListener : AddonListener {
override fun onAddonInstalled(result: LoadResult?) { override fun onAddonInstalled(result: LoadResult?) {
if (result is TorrentLoadResult.Success) { if (result is TorrentLoadResult.Success) {
extension = result.extension extension = result.extension

View file

@ -2,6 +2,6 @@ package ani.dantotsu.addons.torrent
import ani.dantotsu.addons.LoadResult import ani.dantotsu.addons.LoadResult
open class TorrentLoadResult: LoadResult() { open class TorrentLoadResult : LoadResult() {
class Success(val extension: TorrentAddon.Installed) : TorrentLoadResult() class Success(val extension: TorrentAddon.Installed) : TorrentLoadResult()
} }

View file

@ -22,7 +22,7 @@ import uy.kohesive.injekt.api.get
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
class ServerService: Service() { class ServerService : Service() {
private val serviceScope = CoroutineScope(EmptyCoroutineContext) private val serviceScope = CoroutineScope(EmptyCoroutineContext)
private val applicationContext = Injekt.get<Application>() private val applicationContext = Injekt.get<Application>()
private val extension = Injekt.get<TorrentAddonManager>().extension!!.extension private val extension = Injekt.get<TorrentAddonManager>().extension!!.extension
@ -42,6 +42,7 @@ class ServerService: Service() {
notification(applicationContext) notification(applicationContext)
return START_STICKY return START_STICKY
} }
ACTION_STOP -> { ACTION_STOP -> {
stopServer() stopServer()
return START_NOT_STICKY return START_NOT_STICKY
@ -109,7 +110,7 @@ class ServerService: Service() {
const val ACTION_STOP = "stop_torrent_server" const val ACTION_STOP = "stop_torrent_server"
fun isRunning(): Boolean { fun isRunning(): Boolean {
with (Injekt.get<Application>().getSystemService(ACTIVITY_SERVICE) as ActivityManager) { with(Injekt.get<Application>().getSystemService(ACTIVITY_SERVICE) as ActivityManager) {
@Suppress("DEPRECATION") // We only need our services @Suppress("DEPRECATION") // We only need our services
getRunningServices(Int.MAX_VALUE).forEach { getRunningServices(Int.MAX_VALUE).forEach {
if (ServerService::class.java.name.equals(it.service.className)) { if (ServerService::class.java.name.equals(it.service.className)) {

View file

@ -7,9 +7,9 @@ import androidx.core.content.ContextCompat
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.database.StandaloneDatabaseProvider import androidx.media3.database.StandaloneDatabaseProvider
import ani.dantotsu.addons.download.DownloadAddonManager import ani.dantotsu.addons.download.DownloadAddonManager
import ani.dantotsu.addons.torrent.TorrentAddonManager
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.addons.torrent.TorrentAddonManager
import ani.dantotsu.media.manga.MangaCache import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.parsers.novel.NovelExtensionManager import ani.dantotsu.parsers.novel.NovelExtensionManager
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences

View file

@ -1188,7 +1188,7 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
suspend fun recentlyUpdated( suspend fun recentlyUpdated(
greater: Long = 0, greater: Long = 0,
lesser: Long = System.currentTimeMillis() / 1000 - 10000 lesser: Long = System.currentTimeMillis() / 1000 - 10000
): MutableList<Media>? { ): MutableList<Media> {
suspend fun execute(page: Int = 1): Page? { suspend fun execute(page: Int = 1): Page? {
val query = """{ val query = """{
Page(page:$page,perPage:50) { Page(page:$page,perPage:50) {

View file

@ -84,9 +84,9 @@ data class Notification(
@SerialName("createdAt") @SerialName("createdAt")
val createdAt: Int, val createdAt: Int,
@SerialName("media") @SerialName("media")
val media: ani.dantotsu.connections.anilist.api.Media? = null, val media: Media? = null,
@SerialName("user") @SerialName("user")
val user: ani.dantotsu.connections.anilist.api.User? = null, val user: User? = null,
@SerialName("message") @SerialName("message")
val message: MessageActivity? = null, val message: MessageActivity? = null,
@SerialName("activity") @SerialName("activity")

View file

@ -355,7 +355,7 @@ class NovelDownloaderService : Service() {
private fun saveMediaInfo(task: DownloadTask) { private fun saveMediaInfo(task: DownloadTask) {
launchIO { launchIO {
val directory = val directory =
DownloadsManager.getSubDirectory( getSubDirectory(
this@NovelDownloaderService, this@NovelDownloaderService,
MediaType.NOVEL, MediaType.NOVEL,
false, false,

View file

@ -101,7 +101,7 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
ContextCompat.startActivity( ContextCompat.startActivity(
view.context, view.context,
Intent(view.context, ProfileActivity::class.java) Intent(view.context, ProfileActivity::class.java)
.putExtra("userId", Anilist.userid),null .putExtra("userId", Anilist.userid), null
) )
false false
} }
@ -110,7 +110,8 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
trendingBinding.searchBar.performClick() trendingBinding.searchBar.performClick()
} }
trendingBinding.notificationCount.visibility = if (Anilist.unreadNotificationCount > 0) View.VISIBLE else View.GONE trendingBinding.notificationCount.visibility =
if (Anilist.unreadNotificationCount > 0) View.VISIBLE else View.GONE
trendingBinding.notificationCount.text = Anilist.unreadNotificationCount.toString() trendingBinding.notificationCount.text = Anilist.unreadNotificationCount.toString()
listOf( listOf(
@ -167,7 +168,8 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
trendingBinding.trendingProgressBar.visibility = View.GONE trendingBinding.trendingProgressBar.visibility = View.GONE
trendingBinding.trendingViewPager.adapter = adaptor trendingBinding.trendingViewPager.adapter = adaptor
trendingBinding.trendingViewPager.offscreenPageLimit = 3 trendingBinding.trendingViewPager.offscreenPageLimit = 3
trendingBinding.trendingViewPager.getChildAt(0).overScrollMode = RecyclerView.OVER_SCROLL_NEVER trendingBinding.trendingViewPager.getChildAt(0).overScrollMode =
RecyclerView.OVER_SCROLL_NEVER
trendingBinding.trendingViewPager.setPageTransformer(MediaPageTransformer()) trendingBinding.trendingViewPager.setPageTransformer(MediaPageTransformer())
trendHandler = Handler(Looper.getMainLooper()) trendHandler = Handler(Looper.getMainLooper())
trendRun = Runnable { trendRun = Runnable {
@ -195,7 +197,7 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
} }
fun updateRecent(adaptor: MediaAdaptor) { fun updateRecent(adaptor: MediaAdaptor) {
binding.apply{ binding.apply {
init( init(
adaptor, adaptor,
animeUpdatedRecyclerView, animeUpdatedRecyclerView,
@ -210,8 +212,9 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
} }
} }
fun updateMovies(adaptor: MediaAdaptor) { fun updateMovies(adaptor: MediaAdaptor) {
binding.apply{ binding.apply {
init( init(
adaptor, adaptor,
animeMoviesRecyclerView, animeMoviesRecyclerView,
@ -222,7 +225,7 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
} }
fun updateTopRated(adaptor: MediaAdaptor) { fun updateTopRated(adaptor: MediaAdaptor) {
binding.apply{ binding.apply {
init( init(
adaptor, adaptor,
animeTopRatedRecyclerView, animeTopRatedRecyclerView,
@ -231,8 +234,9 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
) )
} }
} }
fun updateMostFav(adaptor: MediaAdaptor) { fun updateMostFav(adaptor: MediaAdaptor) {
binding.apply{ binding.apply {
init( init(
adaptor, adaptor,
animeMostFavRecyclerView, animeMostFavRecyclerView,
@ -241,7 +245,8 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
) )
} }
} }
fun init(adaptor: MediaAdaptor,recyclerView: RecyclerView, progress: View, title: View){
fun init(adaptor: MediaAdaptor, recyclerView: RecyclerView, progress: View, title: View) {
progress.visibility = View.GONE progress.visibility = View.GONE
recyclerView.adapter = adaptor recyclerView.adapter = adaptor
recyclerView.layoutManager = recyclerView.layoutManager =
@ -256,6 +261,7 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
recyclerView.layoutAnimation = recyclerView.layoutAnimation =
LayoutAnimationController(setSlideIn(), 0.25f) LayoutAnimationController(setSlideIn(), 0.25f)
} }
fun updateAvatar() { fun updateAvatar() {
if (Anilist.avatar != null && ready.value == true) { if (Anilist.avatar != null && ready.value == true) {
trendingBinding.userAvatar.loadImage(Anilist.avatar) trendingBinding.userAvatar.loadImage(Anilist.avatar)

View file

@ -81,7 +81,10 @@ class HomeFragment : Fragment() {
binding.homeUserChaptersRead.text = Anilist.chapterRead.toString() binding.homeUserChaptersRead.text = Anilist.chapterRead.toString()
binding.homeUserAvatar.loadImage(Anilist.avatar) binding.homeUserAvatar.loadImage(Anilist.avatar)
val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations) val bannerAnimations: Boolean = PrefManager.getVal(PrefName.BannerAnimations)
blurImage(if (bannerAnimations) binding.homeUserBg else binding.homeUserBgNoKen, Anilist.bg) blurImage(
if (bannerAnimations) binding.homeUserBg else binding.homeUserBgNoKen,
Anilist.bg
)
binding.homeUserDataProgressBar.visibility = View.GONE binding.homeUserDataProgressBar.visibility = View.GONE
binding.homeNotificationCount.isVisible = Anilist.unreadNotificationCount > 0 binding.homeNotificationCount.isVisible = Anilist.unreadNotificationCount > 0
binding.homeNotificationCount.text = Anilist.unreadNotificationCount.toString() binding.homeNotificationCount.text = Anilist.unreadNotificationCount.toString()
@ -128,7 +131,7 @@ class HomeFragment : Fragment() {
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
ContextCompat.startActivity( ContextCompat.startActivity(
requireContext(), Intent(requireContext(), ProfileActivity::class.java) requireContext(), Intent(requireContext(), ProfileActivity::class.java)
.putExtra("userId", Anilist.userid),null .putExtra("userId", Anilist.userid), null
) )
false false
} }
@ -376,6 +379,7 @@ class HomeFragment : Fragment() {
} }
} }
} }
override fun onResume() { override fun onResume() {
if (!model.loaded) Refresh.activity[1]!!.postValue(true) if (!model.loaded) Refresh.activity[1]!!.postValue(true)
if (_binding != null) { if (_binding != null) {

View file

@ -172,7 +172,13 @@ class MangaFragment : Fragment() {
} }
model.getPopularManhwa().observe(viewLifecycleOwner) { model.getPopularManhwa().observe(viewLifecycleOwner) {
if (it != null) { if (it != null) {
mangaPageAdapter.updateTrendingManhwa(MediaAdaptor(0, it, requireActivity())) mangaPageAdapter.updateTrendingManhwa(
MediaAdaptor(
0,
it,
requireActivity()
)
)
} }
} }
model.getTopRated().observe(viewLifecycleOwner) { model.getTopRated().observe(viewLifecycleOwner) {

View file

@ -101,7 +101,7 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
ContextCompat.startActivity( ContextCompat.startActivity(
view.context, view.context,
Intent(view.context, ProfileActivity::class.java) Intent(view.context, ProfileActivity::class.java)
.putExtra("userId", Anilist.userid),null .putExtra("userId", Anilist.userid), null
) )
false false
} }
@ -156,7 +156,8 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
trendingBinding.trendingProgressBar.visibility = View.GONE trendingBinding.trendingProgressBar.visibility = View.GONE
trendingBinding.trendingViewPager.adapter = adaptor trendingBinding.trendingViewPager.adapter = adaptor
trendingBinding.trendingViewPager.offscreenPageLimit = 3 trendingBinding.trendingViewPager.offscreenPageLimit = 3
trendingBinding.trendingViewPager.getChildAt(0).overScrollMode = RecyclerView.OVER_SCROLL_NEVER trendingBinding.trendingViewPager.getChildAt(0).overScrollMode =
RecyclerView.OVER_SCROLL_NEVER
trendingBinding.trendingViewPager.setPageTransformer(MediaPageTransformer()) trendingBinding.trendingViewPager.setPageTransformer(MediaPageTransformer())
trendHandler = Handler(Looper.getMainLooper()) trendHandler = Handler(Looper.getMainLooper())
trendRun = Runnable { trendRun = Runnable {
@ -191,6 +192,7 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
) )
} }
} }
fun updateTrendingManhwa(adaptor: MediaAdaptor) { fun updateTrendingManhwa(adaptor: MediaAdaptor) {
binding.apply { binding.apply {
init( init(
@ -201,6 +203,7 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
) )
} }
} }
fun updateNovel(adaptor: MediaAdaptor) { fun updateNovel(adaptor: MediaAdaptor) {
binding.apply { binding.apply {
init( init(
@ -212,6 +215,7 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
} }
} }
fun updateTopRated(adaptor: MediaAdaptor) { fun updateTopRated(adaptor: MediaAdaptor) {
binding.apply { binding.apply {
init( init(
@ -222,6 +226,7 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
) )
} }
} }
fun updateMostFav(adaptor: MediaAdaptor) { fun updateMostFav(adaptor: MediaAdaptor) {
binding.apply { binding.apply {
init( init(
@ -234,7 +239,8 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
mangaPopular.startAnimation(setSlideUp()) mangaPopular.startAnimation(setSlideUp())
} }
} }
fun init(adaptor: MediaAdaptor,recyclerView: RecyclerView, progress: View, title: View){
fun init(adaptor: MediaAdaptor, recyclerView: RecyclerView, progress: View, title: View) {
progress.visibility = View.GONE progress.visibility = View.GONE
recyclerView.adapter = adaptor recyclerView.adapter = adaptor
recyclerView.layoutManager = recyclerView.layoutManager =

View file

@ -95,8 +95,10 @@ class AuthorActivity : AppCompatActivity() {
binding.charactersRecycler.visibility = View.VISIBLE binding.charactersRecycler.visibility = View.VISIBLE
binding.charactersText.visibility = View.VISIBLE binding.charactersText.visibility = View.VISIBLE
binding.charactersRecycler.adapter = CharacterAdapter(author!!.character ?: arrayListOf()) binding.charactersRecycler.adapter =
binding.charactersRecycler.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) CharacterAdapter(author!!.character ?: arrayListOf())
binding.charactersRecycler.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
if (author!!.character.isNullOrEmpty()) { if (author!!.character.isNullOrEmpty()) {
binding.charactersRecycler.visibility = View.GONE binding.charactersRecycler.visibility = View.GONE
binding.charactersText.visibility = View.GONE binding.charactersText.visibility = View.GONE
@ -108,7 +110,7 @@ class AuthorActivity : AppCompatActivity() {
if (it) { if (it) {
scope.launch { scope.launch {
if (author != null) if (author != null)
withContext(Dispatchers.IO) { model.loadAuthor(author!!)} withContext(Dispatchers.IO) { model.loadAuthor(author!!) }
live.postValue(false) live.postValue(false)
} }
} }

View file

@ -23,7 +23,7 @@ class AuthorAdapter(
return AuthorViewHolder(binding) return AuthorViewHolder(binding)
} }
override fun onBindViewHolder(holder:AuthorViewHolder, position: Int) { override fun onBindViewHolder(holder: AuthorViewHolder, position: Int) {
val binding = holder.binding val binding = holder.binding
setAnimation(binding.root.context, holder.binding.root) setAnimation(binding.root.context, holder.binding.root)
val author = authorList[position] val author = authorList[position]

View file

@ -1,7 +1,6 @@
package ani.dantotsu.media package ani.dantotsu.media
import ani.dantotsu.connections.anilist.api.FuzzyDate import ani.dantotsu.connections.anilist.api.FuzzyDate
import ani.dantotsu.connections.anilist.api.Query
import java.io.Serializable import java.io.Serializable
data class Character( data class Character(

View file

@ -95,7 +95,8 @@ class CharacterDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChang
} }
lifecycleScope.launch { lifecycleScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
character.isFav = Anilist.query.isUserFav(AnilistMutations.FavType.CHARACTER, character.id) character.isFav =
Anilist.query.isUserFav(AnilistMutations.FavType.CHARACTER, character.id)
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
binding.characterFav.setImageResource( binding.characterFav.setImageResource(

View file

@ -29,10 +29,16 @@ class CharacterDetailsAdapter(private val character: Character, private val acti
"${currActivity()!!.getString(R.string.birthday)} ${character.dateOfBirth.toString()}" else "") + "${currActivity()!!.getString(R.string.birthday)} ${character.dateOfBirth.toString()}" else "") +
(if (character.gender != "null") (if (character.gender != "null")
currActivity()!!.getString(R.string.gender) + " " + when (character.gender) { currActivity()!!.getString(R.string.gender) + " " + when (character.gender) {
currActivity()!!.getString(R.string.male) -> currActivity()!!.getString(R.string.male) currActivity()!!.getString(R.string.male) -> currActivity()!!.getString(
currActivity()!!.getString(R.string.female) -> currActivity()!!.getString(R.string.female) R.string.male
else -> character.gender )
} else "") + "\n" + character.description
currActivity()!!.getString(R.string.female) -> currActivity()!!.getString(
R.string.female
)
else -> character.gender
} else "") + "\n" + character.description
binding.characterDesc.isTextSelectable binding.characterDesc.isTextSelectable
val markWon = Markwon.builder(activity).usePlugin(SoftBreakAddsNewLinePlugin.create()) val markWon = Markwon.builder(activity).usePlugin(SoftBreakAddsNewLinePlugin.create())

View file

@ -100,7 +100,7 @@ data class Media(
startDate = apiMedia.startDate, startDate = apiMedia.startDate,
endDate = apiMedia.endDate, endDate = apiMedia.endDate,
favourites = apiMedia.favourites, favourites = apiMedia.favourites,
timeUntilAiring = apiMedia.nextAiringEpisode?.timeUntilAiring?.let { it.toLong() * 1000}, timeUntilAiring = apiMedia.nextAiringEpisode?.timeUntilAiring?.let { it.toLong() * 1000 },
anime = if (apiMedia.type == MediaType.ANIME) Anime( anime = if (apiMedia.type == MediaType.ANIME) Anime(
totalEpisodes = apiMedia.episodes, totalEpisodes = apiMedia.episodes,
nextAiringEpisode = apiMedia.nextAiringEpisode?.episode?.minus(1) nextAiringEpisode = apiMedia.nextAiringEpisode?.episode?.minus(1)
@ -115,7 +115,8 @@ data class Media(
this.userScore = mediaList.score?.toInt() ?: 0 this.userScore = mediaList.score?.toInt() ?: 0
this.userStatus = mediaList.status?.toString() this.userStatus = mediaList.status?.toString()
this.userUpdatedAt = mediaList.updatedAt?.toLong() this.userUpdatedAt = mediaList.updatedAt?.toLong()
this.genres = mediaList.media?.genres?.toMutableList() as? ArrayList<String>? ?: arrayListOf() this.genres =
mediaList.media?.genres?.toMutableList() as? ArrayList<String>? ?: arrayListOf()
} }
constructor(mediaEdge: MediaEdge) : this(mediaEdge.node!!) { constructor(mediaEdge: MediaEdge) : this(mediaEdge.node!!) {

View file

@ -149,14 +149,22 @@ class MediaAdaptor(
(if (media.userScore != 0) R.drawable.item_user_score else R.drawable.item_score) (if (media.userScore != 0) R.drawable.item_user_score else R.drawable.item_score)
) )
if (media.anime != null) { if (media.anime != null) {
val itemTotal = " " + if ((media.anime.totalEpisodes ?: 0) != 1) currActivity()!!.getString(R.string.episode_plural) else currActivity()!!.getString(R.string.episode_singular) val itemTotal = " " + if ((media.anime.totalEpisodes
?: 0) != 1
) currActivity()!!.getString(R.string.episode_plural) else currActivity()!!.getString(
R.string.episode_singular
)
b.itemTotal.text = itemTotal b.itemTotal.text = itemTotal
b.itemCompactTotal.text = b.itemCompactTotal.text =
if (media.anime.nextAiringEpisode != null) (media.anime.nextAiringEpisode.toString() + " / " + (media.anime.totalEpisodes if (media.anime.nextAiringEpisode != null) (media.anime.nextAiringEpisode.toString() + " / " + (media.anime.totalEpisodes
?: "??").toString()) else (media.anime.totalEpisodes ?: "??").toString()) else (media.anime.totalEpisodes
?: "??").toString() ?: "??").toString()
} else if (media.manga != null) { } else if (media.manga != null) {
val itemTotal = " " + if ((media.manga.totalChapters ?: 0) != 1) currActivity()!!.getString(R.string.chapter_plural) else currActivity()!!.getString(R.string.chapter_singular) val itemTotal = " " + if ((media.manga.totalChapters
?: 0) != 1
) currActivity()!!.getString(R.string.chapter_plural) else currActivity()!!.getString(
R.string.chapter_singular
)
b.itemTotal.text = itemTotal b.itemTotal.text = itemTotal
b.itemCompactTotal.text = "${media.manga.totalChapters ?: "??"}" b.itemCompactTotal.text = "${media.manga.totalChapters ?: "??"}"
} }
@ -183,7 +191,10 @@ class MediaAdaptor(
AccelerateDecelerateInterpolator() AccelerateDecelerateInterpolator()
) )
) )
blurImage(if (bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen , media.banner ?: media.cover) blurImage(
if (bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen,
media.banner ?: media.cover
)
b.itemCompactOngoing.isVisible = b.itemCompactOngoing.isVisible =
media.status == currActivity()!!.getString(R.string.status_releasing) media.status == currActivity()!!.getString(R.string.status_releasing)
b.itemCompactTitle.text = media.userPreferredName b.itemCompactTitle.text = media.userPreferredName
@ -232,7 +243,10 @@ class MediaAdaptor(
AccelerateDecelerateInterpolator() AccelerateDecelerateInterpolator()
) )
) )
blurImage(if (bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen , media.banner ?: media.cover) blurImage(
if (bannerAnimations) b.itemCompactBanner else b.itemCompactBannerNoKen,
media.banner ?: media.cover
)
b.itemCompactOngoing.isVisible = b.itemCompactOngoing.isVisible =
media.status == currActivity()!!.getString(R.string.status_releasing) media.status == currActivity()!!.getString(R.string.status_releasing)
b.itemCompactTitle.text = media.userPreferredName b.itemCompactTitle.text = media.userPreferredName

View file

@ -109,7 +109,9 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
// Ui init // Ui init
initActivity(this) initActivity(this)
binding.mediaViewPager.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin = navBarHeight } binding.mediaViewPager.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = navBarHeight
}
val oldMargin = binding.mediaViewPager.marginBottom val oldMargin = binding.mediaViewPager.marginBottom
AndroidBug5497Workaround.assistActivity(this) { AndroidBug5497Workaround.assistActivity(this) {
if (it) { if (it) {
@ -125,9 +127,11 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
} }
} }
val navBarRightMargin = if (resources.configuration.orientation == val navBarRightMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE) navBarHeight else 0 Configuration.ORIENTATION_LANDSCAPE
) navBarHeight else 0
val navBarBottomMargin = if (resources.configuration.orientation == val navBarBottomMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE) 0 else navBarHeight Configuration.ORIENTATION_LANDSCAPE
) 0 else navBarHeight
navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> { navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
rightMargin = navBarRightMargin rightMargin = navBarRightMargin
bottomMargin = navBarBottomMargin bottomMargin = navBarBottomMargin
@ -345,7 +349,13 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
adult = media.isAdult adult = media.isAdult
if (media.anime != null) { if (media.anime != null) {
viewPager.adapter = viewPager.adapter =
ViewPagerAdapter(supportFragmentManager, lifecycle, SupportedMedia.ANIME, media, intent.getIntExtra("commentId", -1)) ViewPagerAdapter(
supportFragmentManager,
lifecycle,
SupportedMedia.ANIME,
media,
intent.getIntExtra("commentId", -1)
)
} else if (media.manga != null) { } else if (media.manga != null) {
viewPager.adapter = ViewPagerAdapter( viewPager.adapter = ViewPagerAdapter(
supportFragmentManager, supportFragmentManager,
@ -368,7 +378,8 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
} else { } else {
navBar.createTab(R.drawable.ic_round_import_contacts_24, R.string.read, R.id.read) navBar.createTab(R.drawable.ic_round_import_contacts_24, R.string.read, R.id.read)
} }
val commentTab = navBar.createTab(R.drawable.ic_round_comment_24, R.string.comments, R.id.comment) val commentTab =
navBar.createTab(R.drawable.ic_round_comment_24, R.string.comments, R.id.comment)
navBar.addTab(infoTab) navBar.addTab(infoTab)
navBar.addTab(watchTab) navBar.addTab(watchTab)
navBar.addTab(commentTab) navBar.addTab(commentTab)
@ -412,10 +423,12 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
val rightMargin = if (resources.configuration.orientation == val rightMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE) navBarHeight else 0 Configuration.ORIENTATION_LANDSCAPE
) navBarHeight else 0
val bottomMargin = if (resources.configuration.orientation == val bottomMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE) 0 else navBarHeight Configuration.ORIENTATION_LANDSCAPE
val params : ViewGroup.MarginLayoutParams = ) 0 else navBarHeight
val params: ViewGroup.MarginLayoutParams =
navBar.layoutParams as ViewGroup.MarginLayoutParams navBar.layoutParams as ViewGroup.MarginLayoutParams
params.updateMargins(right = rightMargin, bottom = bottomMargin) params.updateMargins(right = rightMargin, bottom = bottomMargin)
} }
@ -448,6 +461,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
SupportedMedia.MANGA -> MangaReadFragment() SupportedMedia.MANGA -> MangaReadFragment()
SupportedMedia.NOVEL -> NovelReadFragment() SupportedMedia.NOVEL -> NovelReadFragment()
} }
2 -> { 2 -> {
val fragment = CommentsFragment() val fragment = CommentsFragment()
val bundle = Bundle() val bundle = Bundle()

View file

@ -56,9 +56,11 @@ class MediaDetailsViewModel : ViewModel() {
media.anime != null -> { media.anime != null -> {
AnimeSources.list.size - 1 AnimeSources.list.size - 1
} }
media.format == "MANGA" || media.format == "ONE_SHOT" -> { media.format == "MANGA" || media.format == "ONE_SHOT" -> {
MangaSources.list.size - 1 MangaSources.list.size - 1
} }
else -> { else -> {
NovelSources.list.size - 1 NovelSources.list.size - 1
} }

View file

@ -145,7 +145,8 @@ class MediaInfoFragment : Fragment() {
} }
binding.mediaInfoDurationContainer.visibility = View.VISIBLE binding.mediaInfoDurationContainer.visibility = View.VISIBLE
binding.mediaInfoSeasonContainer.visibility = View.VISIBLE binding.mediaInfoSeasonContainer.visibility = View.VISIBLE
val seasonInfo = "${(media.anime.season ?: "??")} ${(media.anime.seasonYear ?: "??")}" val seasonInfo =
"${(media.anime.season ?: "??")} ${(media.anime.seasonYear ?: "??")}"
binding.mediaInfoSeason.text = seasonInfo binding.mediaInfoSeason.text = seasonInfo
if (media.anime.mainStudio != null) { if (media.anime.mainStudio != null) {
@ -182,9 +183,9 @@ class MediaInfoFragment : Fragment() {
} }
binding.mediaInfoTotalTitle.setText(R.string.total_eps) binding.mediaInfoTotalTitle.setText(R.string.total_eps)
val infoTotal = if (media.anime.nextAiringEpisode != null) val infoTotal = if (media.anime.nextAiringEpisode != null)
"${media.anime.nextAiringEpisode} | ${media.anime.totalEpisodes ?: "~"}" "${media.anime.nextAiringEpisode} | ${media.anime.totalEpisodes ?: "~"}"
else else
(media.anime.totalEpisodes ?: "~").toString() (media.anime.totalEpisodes ?: "~").toString()
binding.mediaInfoTotal.text = infoTotal binding.mediaInfoTotal.text = infoTotal
} else if (media.manga != null) { } else if (media.manga != null) {
@ -213,7 +214,8 @@ class MediaInfoFragment : Fragment() {
(media.description ?: "null").replace("\\n", "<br>").replace("\\\"", "\""), (media.description ?: "null").replace("\\n", "<br>").replace("\\\"", "\""),
HtmlCompat.FROM_HTML_MODE_LEGACY HtmlCompat.FROM_HTML_MODE_LEGACY
) )
val infoDesc = tripleTab + if (desc.toString() != "null") desc else getString(R.string.no_description_available) val infoDesc =
tripleTab + if (desc.toString() != "null") desc else getString(R.string.no_description_available)
binding.mediaInfoDescription.text = infoDesc binding.mediaInfoDescription.text = infoDesc
binding.mediaInfoDescription.setOnClickListener { binding.mediaInfoDescription.setOnClickListener {
@ -570,7 +572,7 @@ class MediaInfoFragment : Fragment() {
parent.addView(root) parent.addView(root)
} }
} }
if(!media.users.isNullOrEmpty() && !offline){ if (!media.users.isNullOrEmpty() && !offline) {
ItemTitleRecyclerBinding.inflate( ItemTitleRecyclerBinding.inflate(
LayoutInflater.from(context), LayoutInflater.from(context),
parent, parent,

View file

@ -1,8 +1,6 @@
package ani.dantotsu.media package ani.dantotsu.media
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent import android.content.Intent
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View

View file

@ -4,7 +4,7 @@ interface Type {
fun asText(): String fun asText(): String
} }
enum class MediaType: Type { enum class MediaType : Type {
ANIME, ANIME,
MANGA, MANGA,
NOVEL; NOVEL;
@ -18,18 +18,20 @@ enum class MediaType: Type {
} }
companion object { companion object {
fun fromText(string : String): MediaType? { fun fromText(string: String): MediaType? {
return when (string) { return when (string) {
"Anime" -> ANIME "Anime" -> ANIME
"Manga" -> MANGA "Manga" -> MANGA
"Novel" -> NOVEL "Novel" -> NOVEL
else -> { null } else -> {
null
}
} }
} }
} }
} }
enum class AddonType: Type { enum class AddonType : Type {
TORRENT, TORRENT,
DOWNLOAD; DOWNLOAD;
@ -41,11 +43,13 @@ enum class AddonType: Type {
} }
companion object { companion object {
fun fromText(string : String): AddonType? { fun fromText(string: String): AddonType? {
return when (string) { return when (string) {
"Torrent" -> TORRENT "Torrent" -> TORRENT
"Download" -> DOWNLOAD "Download" -> DOWNLOAD
else -> { null } else -> {
null
}
} }
} }
} }

View file

@ -78,8 +78,10 @@ class SearchActivity : AppCompatActivity() {
source = intent.getStringExtra("source"), source = intent.getStringExtra("source"),
countryOfOrigin = intent.getStringExtra("country"), countryOfOrigin = intent.getStringExtra("country"),
season = intent.getStringExtra("season"), season = intent.getStringExtra("season"),
seasonYear = if (intent.getStringExtra("type") == "ANIME") intent.getStringExtra("seasonYear")?.toIntOrNull() else null, seasonYear = if (intent.getStringExtra("type") == "ANIME") intent.getStringExtra("seasonYear")
startYear = if (intent.getStringExtra("type") == "MANGA") intent.getStringExtra("seasonYear")?.toIntOrNull() else null, ?.toIntOrNull() else null,
startYear = if (intent.getStringExtra("type") == "MANGA") intent.getStringExtra("seasonYear")
?.toIntOrNull() else null,
results = mutableListOf(), results = mutableListOf(),
hasNextPage = false hasNextPage = false
) )

View file

@ -13,7 +13,6 @@ import android.view.animation.AlphaAnimation
import android.view.animation.Animation import android.view.animation.Animation
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import android.widget.PopupMenu import android.widget.PopupMenu
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
@ -60,6 +59,7 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
} }
binding.filterTextView.setCompoundDrawablesWithIntrinsicBounds(filterDrawable, 0, 0, 0) binding.filterTextView.setCompoundDrawablesWithIntrinsicBounds(filterDrawable, 0, 0, 0)
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHeaderViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHeaderViewHolder {
val binding = val binding =
ItemSearchHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false) ItemSearchHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
@ -129,36 +129,42 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
activity.search() activity.search()
updateFilterTextViewDrawable() updateFilterTextViewDrawable()
} }
R.id.sort_by_popular -> { R.id.sort_by_popular -> {
activity.result.sort = Anilist.sortBy[1] activity.result.sort = Anilist.sortBy[1]
activity.updateChips.invoke() activity.updateChips.invoke()
activity.search() activity.search()
updateFilterTextViewDrawable() updateFilterTextViewDrawable()
} }
R.id.sort_by_trending -> { R.id.sort_by_trending -> {
activity.result.sort = Anilist.sortBy[2] activity.result.sort = Anilist.sortBy[2]
activity.updateChips.invoke() activity.updateChips.invoke()
activity.search() activity.search()
updateFilterTextViewDrawable() updateFilterTextViewDrawable()
} }
R.id.sort_by_recent -> { R.id.sort_by_recent -> {
activity.result.sort = Anilist.sortBy[3] activity.result.sort = Anilist.sortBy[3]
activity.updateChips.invoke() activity.updateChips.invoke()
activity.search() activity.search()
updateFilterTextViewDrawable() updateFilterTextViewDrawable()
} }
R.id.sort_by_a_z -> { R.id.sort_by_a_z -> {
activity.result.sort = Anilist.sortBy[4] activity.result.sort = Anilist.sortBy[4]
activity.updateChips.invoke() activity.updateChips.invoke()
activity.search() activity.search()
updateFilterTextViewDrawable() updateFilterTextViewDrawable()
} }
R.id.sort_by_z_a -> { R.id.sort_by_z_a -> {
activity.result.sort = Anilist.sortBy[5] activity.result.sort = Anilist.sortBy[5]
activity.updateChips.invoke() activity.updateChips.invoke()
activity.search() activity.search()
updateFilterTextViewDrawable() updateFilterTextViewDrawable()
} }
R.id.sort_by_pure_pain -> { R.id.sort_by_pure_pain -> {
activity.result.sort = Anilist.sortBy[6] activity.result.sort = Anilist.sortBy[6]
activity.updateChips.invoke() activity.updateChips.invoke()
@ -325,7 +331,10 @@ class SearchAdapter(private val activity: SearchActivity, private val type: Stri
} }
class SearchChipAdapter(val activity: SearchActivity, private val searchAdapter: SearchAdapter) : class SearchChipAdapter(
val activity: SearchActivity,
private val searchAdapter: SearchAdapter
) :
RecyclerView.Adapter<SearchChipAdapter.SearchChipViewHolder>() { RecyclerView.Adapter<SearchChipAdapter.SearchChipViewHolder>() {
private var chips = activity.result.toChipList() private var chips = activity.result.toChipList()

View file

@ -105,7 +105,8 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
setSortByFilterImage() setSortByFilterImage()
binding.resetSearchFilter.setOnClickListener { binding.resetSearchFilter.setOnClickListener {
val rotateAnimation = ObjectAnimator.ofFloat(binding.resetSearchFilter, "rotation", 180f, 540f) val rotateAnimation =
ObjectAnimator.ofFloat(binding.resetSearchFilter, "rotation", 180f, 540f)
rotateAnimation.duration = 500 rotateAnimation.duration = 500
rotateAnimation.interpolator = AccelerateDecelerateInterpolator() rotateAnimation.interpolator = AccelerateDecelerateInterpolator()
rotateAnimation.start() rotateAnimation.start()
@ -113,7 +114,8 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
} }
binding.resetSearchFilter.setOnLongClickListener { binding.resetSearchFilter.setOnLongClickListener {
val rotateAnimation = ObjectAnimator.ofFloat(binding.resetSearchFilter, "rotation", 180f, 540f) val rotateAnimation =
ObjectAnimator.ofFloat(binding.resetSearchFilter, "rotation", 180f, 540f)
rotateAnimation.duration = 500 rotateAnimation.duration = 500
rotateAnimation.interpolator = AccelerateDecelerateInterpolator() rotateAnimation.interpolator = AccelerateDecelerateInterpolator()
rotateAnimation.start() rotateAnimation.start()
@ -125,8 +127,10 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
activity.result.apply { activity.result.apply {
status = binding.searchStatus.text.toString().replace(" ", "_").ifBlank { null } status =
source = binding.searchSource.text.toString().replace(" ", "_").ifBlank { null } binding.searchStatus.text.toString().replace(" ", "_").ifBlank { null }
source =
binding.searchSource.text.toString().replace(" ", "_").ifBlank { null }
format = binding.searchFormat.text.toString().ifBlank { null } format = binding.searchFormat.text.toString().ifBlank { null }
season = binding.searchSeason.text.toString().ifBlank { null } season = binding.searchSeason.text.toString().ifBlank { null }
startYear = binding.searchYear.text.toString().toIntOrNull() startYear = binding.searchYear.text.toString().toIntOrNull()
@ -206,21 +210,25 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_search_googlefonts) binding.countryFilter.setImageResource(R.drawable.ic_round_globe_search_googlefonts)
startBounceZoomAnimation(binding.countryFilter) startBounceZoomAnimation(binding.countryFilter)
} }
R.id.country_china -> { R.id.country_china -> {
activity.result.countryOfOrigin = "CN" activity.result.countryOfOrigin = "CN"
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_china_googlefonts) binding.countryFilter.setImageResource(R.drawable.ic_round_globe_china_googlefonts)
startBounceZoomAnimation(binding.countryFilter) startBounceZoomAnimation(binding.countryFilter)
} }
R.id.country_south_korea -> { R.id.country_south_korea -> {
activity.result.countryOfOrigin = "KR" activity.result.countryOfOrigin = "KR"
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_south_korea_googlefonts) binding.countryFilter.setImageResource(R.drawable.ic_round_globe_south_korea_googlefonts)
startBounceZoomAnimation(binding.countryFilter) startBounceZoomAnimation(binding.countryFilter)
} }
R.id.country_japan -> { R.id.country_japan -> {
activity.result.countryOfOrigin = "JP" activity.result.countryOfOrigin = "JP"
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_japan_googlefonts) binding.countryFilter.setImageResource(R.drawable.ic_round_globe_japan_googlefonts)
startBounceZoomAnimation(binding.countryFilter) startBounceZoomAnimation(binding.countryFilter)
} }
R.id.country_taiwan -> { R.id.country_taiwan -> {
activity.result.countryOfOrigin = "TW" activity.result.countryOfOrigin = "TW"
binding.countryFilter.setImageResource(R.drawable.ic_round_globe_taiwan_googlefonts) binding.countryFilter.setImageResource(R.drawable.ic_round_globe_taiwan_googlefonts)
@ -257,7 +265,8 @@ class SearchFilterBottomDialog : BottomSheetDialogFragment() {
binding.searchFilterCancel.setOnClickListener { binding.searchFilterCancel.setOnClickListener {
dismiss() dismiss()
} }
val format = if (activity.result.type == "ANIME") Anilist.animeStatus else Anilist.mangaStatus val format =
if (activity.result.type == "ANIME") Anilist.animeStatus else Anilist.mangaStatus
binding.searchStatus.setText(activity.result.status?.replace("_", " ")) binding.searchStatus.setText(activity.result.status?.replace("_", " "))
binding.searchStatus.setAdapter( binding.searchStatus.setAdapter(
ArrayAdapter( ArrayAdapter(

View file

@ -12,7 +12,6 @@ import kotlinx.coroutines.withContext
import okhttp3.Request import okhttp3.Request
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File
class SubtitleDownloader { class SubtitleDownloader {

View file

@ -330,6 +330,7 @@ class AnimeWatchAdapter(
0 0
) )
} }
val chipText = "${names[limit * (position)]} - ${names[last - 1]}" val chipText = "${names[limit * (position)]} - ${names[last - 1]}"
chip.text = chipText chip.text = chipText
chip.setTextColor( chip.setTextColor(
@ -412,10 +413,12 @@ class AnimeWatchAdapter(
if (ep.filler) binding.itemEpisodeFillerView.visibility = View.VISIBLE if (ep.filler) binding.itemEpisodeFillerView.visibility = View.VISIBLE
binding.animeSourceContinueText.text = binding.animeSourceContinueText.text =
currActivity()!!.getString(R.string.continue_episode, ep.number, if (ep.filler) currActivity()!!.getString(
currActivity()!!.getString(R.string.filler_tag) R.string.continue_episode, ep.number, if (ep.filler)
else currActivity()!!.getString(R.string.filler_tag)
"", cleanedTitle) else
"", cleanedTitle
)
binding.animeSourceContinue.setOnClickListener { binding.animeSourceContinue.setOnClickListener {
fragment.onEpisodeClick(continueEp) fragment.onEpisodeClick(continueEp)
} }
@ -441,11 +444,14 @@ class AnimeWatchAdapter(
if (!sourceFound && PrefManager.getVal(PrefName.SearchSources) && autoSelect) { if (!sourceFound && PrefManager.getVal(PrefName.SearchSources) && autoSelect) {
if (binding.animeSource.adapter.count > media.selected!!.sourceIndex + 1) { if (binding.animeSource.adapter.count > media.selected!!.sourceIndex + 1) {
val nextIndex = media.selected!!.sourceIndex + 1 val nextIndex = media.selected!!.sourceIndex + 1
binding.animeSource.setText(binding.animeSource.adapter binding.animeSource.setText(
.getItem(nextIndex).toString(), false) binding.animeSource.adapter
.getItem(nextIndex).toString(), false
)
fragment.onSourceChange(nextIndex).apply { fragment.onSourceChange(nextIndex).apply {
binding.animeSourceTitle.text = showUserText binding.animeSourceTitle.text = showUserText
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } } showUserTextListener =
{ MainScope().launch { binding.animeSourceTitle.text = it } }
binding.animeSourceDubbed.isChecked = selectDub binding.animeSourceDubbed.isChecked = selectDub
binding.animeSourceDubbedCont.isVisible = isDubAvailableSeparately() binding.animeSourceDubbedCont.isVisible = isDubAvailableSeparately()
setLanguageList(0, nextIndex) setLanguageList(0, nextIndex)

View file

@ -199,7 +199,8 @@ class AnimeWatchFragment : Fragment() {
ConcatAdapter(headerAdapter, episodeAdapter) ConcatAdapter(headerAdapter, episodeAdapter)
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val offline = !isOnline(binding.root.context) || PrefManager.getVal(PrefName.OfflineMode) val offline =
!isOnline(binding.root.context) || PrefManager.getVal(PrefName.OfflineMode)
if (offline) { if (offline) {
media.selected!!.sourceIndex = model.watchSources!!.list.lastIndex media.selected!!.sourceIndex = model.watchSources!!.list.lastIndex
} else { } else {

View file

@ -486,12 +486,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
} }
rotation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE rotation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
} }
in 225..315 -> { in 225..315 -> {
if (rotation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { if (rotation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
exoRotate.visibility = View.VISIBLE exoRotate.visibility = View.VISIBLE
} }
rotation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE rotation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
} }
in 315..360, in 0..45 -> { in 315..360, in 0..45 -> {
if (rotation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { if (rotation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
exoRotate.visibility = View.VISIBLE exoRotate.visibility = View.VISIBLE
@ -1396,7 +1398,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
subClick() subClick()
} }
} }
val sub: MutableList<MediaItem.SubtitleConfiguration> = emptyList<MediaItem.SubtitleConfiguration>().toMutableList() val sub: MutableList<MediaItem.SubtitleConfiguration> =
emptyList<MediaItem.SubtitleConfiguration>().toMutableList()
ext.subtitles.forEach { subtitle -> ext.subtitles.forEach { subtitle ->
val subtitleUrl = if (!hasExtSubtitles) video!!.file.url else subtitle.file.url val subtitleUrl = if (!hasExtSubtitles) video!!.file.url else subtitle.file.url
//var localFile: String? = null //var localFile: String? = null
@ -1865,7 +1868,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
exoPlayer.seekTo((new.interval.endTime * 1000).toLong()) exoPlayer.seekTo((new.interval.endTime * 1000).toLong())
skippedTimeStamps.add(new) skippedTimeStamps.add(new)
} }
if (PrefManager.getVal(PrefName.AutoSkipRecap) && new.skipType == "recap" && !skippedTimeStamps.contains(new)) { if (PrefManager.getVal(PrefName.AutoSkipRecap) && new.skipType == "recap" && !skippedTimeStamps.contains(
new
)
) {
exoPlayer.seekTo((new.interval.endTime * 1000).toLong()) exoPlayer.seekTo((new.interval.endTime * 1000).toLong())
skippedTimeStamps.add(new) skippedTimeStamps.add(new)
} }
@ -1910,12 +1916,15 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
val audioTracks: ArrayList<Tracks.Group> = arrayListOf() val audioTracks: ArrayList<Tracks.Group> = arrayListOf()
val subTracks: ArrayList<Tracks.Group> = arrayListOf(dummyTrack) val subTracks: ArrayList<Tracks.Group> = arrayListOf(dummyTrack)
tracks.groups.forEach { tracks.groups.forEach {
println("Track__: $it\nTrack__: ${it.length}\nTrack__: ${it.isSelected}\n" + println(
"Track__: ${it.type}\nTrack__: ${it.mediaTrackGroup.id}") "Track__: $it\nTrack__: ${it.length}\nTrack__: ${it.isSelected}\n" +
"Track__: ${it.type}\nTrack__: ${it.mediaTrackGroup.id}"
)
when (it.type) { when (it.type) {
TRACK_TYPE_AUDIO -> { TRACK_TYPE_AUDIO -> {
if (it.isSupported(true)) audioTracks.add(it) if (it.isSupported(true)) audioTracks.add(it)
} }
TRACK_TYPE_TEXT -> { TRACK_TYPE_TEXT -> {
if (!hasExtSubtitles) { if (!hasExtSubtitles) {
if (it.isSupported(true)) subTracks.add(it) if (it.isSupported(true)) subTracks.add(it)
@ -1951,7 +1960,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
onSetTrackGroupOverride(dummyTrack, TRACK_TYPE_TEXT) onSetTrackGroupOverride(dummyTrack, TRACK_TYPE_TEXT)
} }
} }
else -> { }
else -> {}
} }
} }
} }
@ -1966,6 +1976,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
isPlayerPlaying = true isPlayerPlaying = true
sourceClick() sourceClick()
} }
else -> { else -> {
toast("Player Error ${error.errorCode} (${error.errorCodeName}) : ${error.message}") toast("Player Error ${error.errorCode} (${error.errorCodeName}) : ${error.message}")
Injekt.get<CrashlyticsInterface>().logException(error) Injekt.get<CrashlyticsInterface>().logException(error)

View file

@ -24,6 +24,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.BottomSheetDialogFragment import ani.dantotsu.BottomSheetDialogFragment
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.addons.torrent.TorrentAddonManager
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
import ani.dantotsu.copyToClipboard import ani.dantotsu.copyToClipboard
import ani.dantotsu.currActivity import ani.dantotsu.currActivity
@ -32,7 +33,6 @@ import ani.dantotsu.databinding.ItemStreamBinding
import ani.dantotsu.databinding.ItemUrlBinding import ani.dantotsu.databinding.ItemUrlBinding
import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.video.Helper import ani.dantotsu.download.video.Helper
import ani.dantotsu.addons.torrent.TorrentAddonManager
import ani.dantotsu.hideSystemBars import ani.dantotsu.hideSystemBars
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsViewModel import ani.dantotsu.media.MediaDetailsViewModel
@ -233,11 +233,12 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
} }
private val externalPlayerResult = registerForActivityResult( private val externalPlayerResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> ActivityResultContracts.StartActivityForResult()
) { result: ActivityResult ->
Logger.log(result.data.toString()) Logger.log(result.data.toString())
} }
private fun exportMagnetIntent(episode: Episode, video: Video) : Intent { private fun exportMagnetIntent(episode: Episode, video: Video): Intent {
val amnis = "com.amnis" val amnis = "com.amnis"
return Intent(Intent.ACTION_VIEW).apply { return Intent(Intent.ACTION_VIEW).apply {
component = ComponentName(amnis, "$amnis.gui.player.PlayerActivity") component = ComponentName(amnis, "$amnis.gui.player.PlayerActivity")
@ -445,7 +446,11 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
SubtitleDownloader.downloadSubtitle( SubtitleDownloader.downloadSubtitle(
requireContext(), requireContext(),
subtitleToDownload!!.file.url, subtitleToDownload!!.file.url,
DownloadedType(media!!.mainName(), media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.number, MediaType.ANIME) DownloadedType(
media!!.mainName(),
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.number,
MediaType.ANIME
)
) )
} }
} }
@ -477,7 +482,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
if (extractor.videos.size > episode.selectedVideo) extractor.videos[episode.selectedVideo] else null if (extractor.videos.size > episode.selectedVideo) extractor.videos[episode.selectedVideo] else null
val subtitleNames = subtitles.map { it.language } val subtitleNames = subtitles.map { it.language }
var subtitleToDownload: Subtitle? = null var subtitleToDownload: Subtitle? = null
val activity = currActivity()?:requireActivity() val activity = currActivity() ?: requireActivity()
if (subtitles.isNotEmpty()) { if (subtitles.isNotEmpty()) {
val alertDialog = AlertDialog.Builder(context, R.style.MyPopup) val alertDialog = AlertDialog.Builder(context, R.style.MyPopup)
.setTitle("Download Subtitle") .setTitle("Download Subtitle")
@ -551,9 +556,13 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
if (video.format == VideoType.CONTAINER) { if (video.format == VideoType.CONTAINER) {
binding.urlSize.isVisible = video.size != null binding.urlSize.isVisible = video.size != null
// if video size is null or 0, show "Unknown Size" else show the size in MB // if video size is null or 0, show "Unknown Size" else show the size in MB
val sizeText = getString(R.string.mb_size, "${if (video.extraNote != null) " : " else ""}${ val sizeText = getString(
if (video.size == 0.0) getString(R.string.size_unknown) else DecimalFormat("#.##").format(video.size ?: 0) R.string.mb_size, "${if (video.extraNote != null) " : " else ""}${
}") if (video.size == 0.0) getString(R.string.size_unknown) else DecimalFormat("#.##").format(
video.size ?: 0
)
}"
)
binding.urlSize.text = sizeText binding.urlSize.text = sizeText
} }
binding.urlNote.visibility = View.VISIBLE binding.urlNote.visibility = View.VISIBLE

View file

@ -67,7 +67,11 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
binding.subtitleTitle.setText(R.string.none) binding.subtitleTitle.setText(R.string.none)
model.getMedia().observe(viewLifecycleOwner) { media -> model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id val mediaID: Int = media.id
val selSubs = PrefManager.getNullableCustomVal("subLang_${mediaID}", null, String::class.java) val selSubs = PrefManager.getNullableCustomVal(
"subLang_${mediaID}",
null,
String::class.java
)
if (episode.selectedSubtitle != null && selSubs != "None") { if (episode.selectedSubtitle != null && selSubs != "None") {
binding.root.setCardBackgroundColor(TRANSPARENT) binding.root.setCardBackgroundColor(TRANSPARENT)
} }
@ -107,7 +111,11 @@ class SubtitleDialogFragment : BottomSheetDialogFragment() {
model.getMedia().observe(viewLifecycleOwner) { media -> model.getMedia().observe(viewLifecycleOwner) { media ->
val mediaID: Int = media.id val mediaID: Int = media.id
val selSubs: String? = val selSubs: String? =
PrefManager.getNullableCustomVal("subLang_${mediaID}", null, String::class.java) PrefManager.getNullableCustomVal(
"subLang_${mediaID}",
null,
String::class.java
)
if (episode.selectedSubtitle != position - 1 && selSubs != subtitles[position - 1].language) { if (episode.selectedSubtitle != position - 1 && selSubs != subtitles[position - 1].language) {
binding.root.setCardBackgroundColor(TRANSPARENT) binding.root.setCardBackgroundColor(TRANSPARENT)
} }

View file

@ -5,7 +5,6 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.core.view.isVisible
import androidx.media3.common.C.TRACK_TYPE_AUDIO import androidx.media3.common.C.TRACK_TYPE_AUDIO
import androidx.media3.common.C.TrackType import androidx.media3.common.C.TrackType
import androidx.media3.common.Tracks import androidx.media3.common.Tracks
@ -20,7 +19,7 @@ import java.util.Locale
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
class TrackGroupDialogFragment( class TrackGroupDialogFragment(
instance: ExoplayerView, trackGroups: ArrayList<Tracks.Group>, type : @TrackType Int instance: ExoplayerView, trackGroups: ArrayList<Tracks.Group>, type: @TrackType Int
) : BottomSheetDialogFragment() { ) : BottomSheetDialogFragment() {
private var _binding: BottomSheetSubtitlesBinding? = null private var _binding: BottomSheetSubtitlesBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
@ -70,21 +69,28 @@ class TrackGroupDialogFragment(
trackGroups[position].let { trackGroup -> trackGroups[position].let { trackGroup ->
when (val language = trackGroup.getTrackFormat(0).language?.lowercase()) { when (val language = trackGroup.getTrackFormat(0).language?.lowercase()) {
null -> { null -> {
binding.subtitleTitle.text = getString(R.string.unknown_track, "Track $position") binding.subtitleTitle.text =
getString(R.string.unknown_track, "Track $position")
} }
"none" -> { "none" -> {
binding.subtitleTitle.text = getString(R.string.disabled_track) binding.subtitleTitle.text = getString(R.string.disabled_track)
} }
else -> { else -> {
val locale = if (language.contains("-")) { val locale = if (language.contains("-")) {
val parts = language.split("-") val parts = language.split("-")
try { try {
Locale(parts[0], parts[1]) Locale(parts[0], parts[1])
} catch (ignored: Exception) { null } } catch (ignored: Exception) {
null
}
} else { } else {
try { try {
Locale(language) Locale(language)
} catch (ignored: Exception) { null } } catch (ignored: Exception) {
null
}
} }
binding.subtitleTitle.text = locale?.let { binding.subtitleTitle.text = locale?.let {
"[${it.language}] ${it.displayName}" "[${it.language}] ${it.displayName}"

View file

@ -60,7 +60,8 @@ class CommentItem(
override fun bind(viewBinding: ItemCommentsBinding, position: Int) { override fun bind(viewBinding: ItemCommentsBinding, position: Int) {
binding = viewBinding binding = viewBinding
setAnimation(binding.root.context, binding.root) setAnimation(binding.root.context, binding.root)
viewBinding.commentRepliesList.layoutManager = LinearLayoutManager(commentsFragment.activity) viewBinding.commentRepliesList.layoutManager =
LinearLayoutManager(commentsFragment.activity)
viewBinding.commentRepliesList.adapter = adapter viewBinding.commentRepliesList.adapter = adapter
val isUserComment = CommentsAPI.userId == comment.userId val isUserComment = CommentsAPI.userId == comment.userId
val levelColor = getAvatarColor(comment.totalVotes, backgroundColor) val levelColor = getAvatarColor(comment.totalVotes, backgroundColor)
@ -112,16 +113,20 @@ class CommentItem(
viewBinding.commentUserName.setOnClickListener { viewBinding.commentUserName.setOnClickListener {
ContextCompat.startActivity( ContextCompat.startActivity(
commentsFragment.activity, Intent(commentsFragment.activity, ProfileActivity::class.java) commentsFragment.activity,
Intent(commentsFragment.activity, ProfileActivity::class.java)
.putExtra("userId", comment.userId.toInt()) .putExtra("userId", comment.userId.toInt())
.putExtra("userLVL","[${levelColor.second}]"), null .putExtra("userLVL", "[${levelColor.second}]"),
null
) )
} }
viewBinding.commentUserAvatar.setOnClickListener { viewBinding.commentUserAvatar.setOnClickListener {
ContextCompat.startActivity( ContextCompat.startActivity(
commentsFragment.activity, Intent(commentsFragment.activity, ProfileActivity::class.java) commentsFragment.activity,
Intent(commentsFragment.activity, ProfileActivity::class.java)
.putExtra("userId", comment.userId.toInt()) .putExtra("userId", comment.userId.toInt())
.putExtra("userLVL","[${levelColor.second}]"), null .putExtra("userLVL", "[${levelColor.second}]"),
null
) )
} }
viewBinding.commentText.setOnLongClickListener { viewBinding.commentText.setOnLongClickListener {
@ -143,8 +148,10 @@ class CommentItem(
viewBinding.commentInfo.setOnClickListener { viewBinding.commentInfo.setOnClickListener {
val popup = PopupMenu(commentsFragment.requireContext(), viewBinding.commentInfo) val popup = PopupMenu(commentsFragment.requireContext(), viewBinding.commentInfo)
popup.menuInflater.inflate(R.menu.profile_details_menu, popup.menu) popup.menuInflater.inflate(R.menu.profile_details_menu, popup.menu)
popup.menu.findItem(R.id.commentDelete)?.isVisible = isUserComment || CommentsAPI.isAdmin || CommentsAPI.isMod popup.menu.findItem(R.id.commentDelete)?.isVisible =
popup.menu.findItem(R.id.commentBanUser)?.isVisible = (CommentsAPI.isAdmin || CommentsAPI.isMod) && !isUserComment isUserComment || CommentsAPI.isAdmin || CommentsAPI.isMod
popup.menu.findItem(R.id.commentBanUser)?.isVisible =
(CommentsAPI.isAdmin || CommentsAPI.isMod) && !isUserComment
popup.menu.findItem(R.id.commentReport)?.isVisible = !isUserComment popup.menu.findItem(R.id.commentReport)?.isVisible = !isUserComment
popup.setOnMenuItemClickListener { item -> popup.setOnMenuItemClickListener { item ->
when (item.itemId) { when (item.itemId) {
@ -273,12 +280,16 @@ class CommentItem(
} }
fun replying(isReplying: Boolean) { fun replying(isReplying: Boolean) {
binding.commentReply.text = if (isReplying) commentsFragment.activity.getString(R.string.cancel) else "Reply" binding.commentReply.text =
if (isReplying) commentsFragment.activity.getString(R.string.cancel) else "Reply"
this.isReplying = isReplying this.isReplying = isReplying
} }
fun editing(isEditing: Boolean) { fun editing(isEditing: Boolean) {
binding.commentEdit.text = if (isEditing) commentsFragment.activity.getString(R.string.cancel) else commentsFragment.activity.getString(R.string.edit) binding.commentEdit.text =
if (isEditing) commentsFragment.activity.getString(R.string.cancel) else commentsFragment.activity.getString(
R.string.edit
)
this.isEditing = isEditing this.isEditing = isEditing
} }
@ -286,7 +297,7 @@ class CommentItem(
subCommentIds.add(id) subCommentIds.add(id)
} }
private fun removeSubCommentIds(){ private fun removeSubCommentIds() {
subCommentIds.forEach { id -> subCommentIds.forEach { id ->
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val parentComments = parentSection.groups as? List<CommentItem> ?: emptyList() val parentComments = parentSection.groups as? List<CommentItem> ?: emptyList()
@ -306,11 +317,13 @@ class CommentItem(
viewBinding.commentUpVote.alpha = 1f viewBinding.commentUpVote.alpha = 1f
viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_inactive_24) viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
} }
-1 -> { -1 -> {
viewBinding.commentUpVote.setImageResource(R.drawable.ic_round_upvote_inactive_24) viewBinding.commentUpVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_active_24) viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_active_24)
viewBinding.commentDownVote.alpha = 1f viewBinding.commentDownVote.alpha = 1f
} }
else -> { else -> {
viewBinding.commentUpVote.setImageResource(R.drawable.ic_round_upvote_inactive_24) viewBinding.commentUpVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_inactive_24) viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
@ -355,7 +368,8 @@ class CommentItem(
private fun getAvatarColor(voteCount: Int, backgroundColor: Int): Pair<Int, Int> { private fun getAvatarColor(voteCount: Int, backgroundColor: Int): Pair<Int, Int> {
val level = if (voteCount < 0) 0 else sqrt(abs(voteCount.toDouble()) / 0.8).toInt() val level = if (voteCount < 0) 0 else sqrt(abs(voteCount.toDouble()) / 0.8).toInt()
val colorString = if (level > usernameColors.size - 1) usernameColors[usernameColors.size - 1] else usernameColors[level] val colorString =
if (level > usernameColors.size - 1) usernameColors[usernameColors.size - 1] else usernameColors[level]
var color = Color.parseColor(colorString) var color = Color.parseColor(colorString)
val ratio = getContrastRatio(color, backgroundColor) val ratio = getContrastRatio(color, backgroundColor)
if (ratio < 4.5) { if (ratio < 4.5) {
@ -373,16 +387,17 @@ class CommentItem(
* @param callback the callback to call when the user clicks yes * @param callback the callback to call when the user clicks yes
*/ */
private fun dialogBuilder(title: String, message: String, callback: () -> Unit) { private fun dialogBuilder(title: String, message: String, callback: () -> Unit) {
val alertDialog = android.app.AlertDialog.Builder(commentsFragment.activity, R.style.MyPopup) val alertDialog =
.setTitle(title) android.app.AlertDialog.Builder(commentsFragment.activity, R.style.MyPopup)
.setMessage(message) .setTitle(title)
.setPositiveButton("Yes") { dialog, _ -> .setMessage(message)
callback() .setPositiveButton("Yes") { dialog, _ ->
dialog.dismiss() callback()
} dialog.dismiss()
.setNegativeButton("No") { dialog, _ -> }
dialog.dismiss() .setNegativeButton("No") { dialog, _ ->
} dialog.dismiss()
}
val dialog = alertDialog.show() val dialog = alertDialog.show()
dialog?.window?.setDimAmount(0.8f) dialog?.window?.setDimAmount(0.8f)
} }

View file

@ -75,7 +75,10 @@ class CommentsFragment : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
activity = requireActivity() as MediaDetailsActivity activity = requireActivity() as MediaDetailsActivity
binding.commentsListContainer.setBaseline(activity.navBar, activity.binding.commentInputLayout) binding.commentsListContainer.setBaseline(
activity.navBar,
activity.binding.commentInputLayout
)
//get the media id from the intent //get the media id from the intent
val mediaId = arguments?.getInt("mediaId") ?: -1 val mediaId = arguments?.getInt("mediaId") ?: -1
@ -301,7 +304,7 @@ class CommentsFragment : Fragment() {
activity.binding.commentLabel.setOnClickListener { activity.binding.commentLabel.setOnClickListener {
//alert dialog to enter a number, with a cancel and ok button //alert dialog to enter a number, with a cancel and ok button
val alertDialog = android.app.AlertDialog.Builder(activity, R.style.MyPopup) val alertDialog = AlertDialog.Builder(activity, R.style.MyPopup)
.setTitle("Enter a chapter/episode number tag") .setTitle("Enter a chapter/episode number tag")
.setView(R.layout.dialog_edittext) .setView(R.layout.dialog_edittext)
.setPositiveButton("OK") { dialog, _ -> .setPositiveButton("OK") { dialog, _ ->
@ -577,7 +580,7 @@ class CommentsFragment : Fragment() {
* Called when the user tries to comment for the first time * Called when the user tries to comment for the first time
*/ */
private fun showCommentRulesDialog() { private fun showCommentRulesDialog() {
val alertDialog = android.app.AlertDialog.Builder(activity, R.style.MyPopup) val alertDialog = AlertDialog.Builder(activity, R.style.MyPopup)
.setTitle("Commenting Rules") .setTitle("Commenting Rules")
.setMessage( .setMessage(
"I WILL BAN YOU WITHOUT HESITATION\n" + "I WILL BAN YOU WITHOUT HESITATION\n" +

View file

@ -343,6 +343,7 @@ class MangaChapterAdapter(
fun updateType(t: Int) { fun updateType(t: Int) {
type = t type = t
} }
private fun formatDate(timestamp: Long?): String { private fun formatDate(timestamp: Long?): String {
timestamp ?: return "" // Return empty string if timestamp is null timestamp ?: return "" // Return empty string if timestamp is null
@ -366,6 +367,7 @@ class MangaChapterAdapter(
else -> "Just now" else -> "Just now"
} }
} }
1L -> "1 day ago" 1L -> "1 day ago"
in 2..6 -> "$daysDifference days ago" in 2..6 -> "$daysDifference days ago"
else -> SimpleDateFormat("dd MMM yyyy", Locale.getDefault()).format(targetDate) else -> SimpleDateFormat("dd MMM yyyy", Locale.getDefault()).format(targetDate)

View file

@ -229,7 +229,7 @@ class MangaReadAdapter(
refresh = true refresh = true
val intent = Intent(fragment.requireContext(), CookieCatcher::class.java) val intent = Intent(fragment.requireContext(), CookieCatcher::class.java)
.putExtra("url", url) .putExtra("url", url)
ContextCompat.startActivity(fragment.requireContext(), intent, null) startActivity(fragment.requireContext(), intent, null)
} }
} }
} }
@ -258,8 +258,10 @@ class MangaReadAdapter(
dialogBinding.animeScanlatorContainer.isVisible = options.count() > 1 dialogBinding.animeScanlatorContainer.isVisible = options.count() > 1
dialogBinding.scanlatorNo.text = "${options.count()}" dialogBinding.scanlatorNo.text = "${options.count()}"
dialogBinding.animeScanlatorTop.setOnClickListener { dialogBinding.animeScanlatorTop.setOnClickListener {
val dialogView2 = LayoutInflater.from(currContext()).inflate(R.layout.custom_dialog_layout, null) val dialogView2 =
val checkboxContainer = dialogView2.findViewById<LinearLayout>(R.id.checkboxContainer) LayoutInflater.from(currContext()).inflate(R.layout.custom_dialog_layout, null)
val checkboxContainer =
dialogView2.findViewById<LinearLayout>(R.id.checkboxContainer)
val tickAllButton = dialogView2.findViewById<ImageButton>(R.id.toggleButton) val tickAllButton = dialogView2.findViewById<ImageButton>(R.id.toggleButton)
// Function to get the right image resource for the toggle button // Function to get the right image resource for the toggle button
@ -441,7 +443,11 @@ class MangaReadAdapter(
if (media.manga?.chapters != null) { if (media.manga?.chapters != null) {
val chapters = media.manga.chapters!!.keys.toTypedArray() val chapters = media.manga.chapters!!.keys.toTypedArray()
val anilistEp = (media.userProgress ?: 0).plus(1) val anilistEp = (media.userProgress ?: 0).plus(1)
val appEp = PrefManager.getNullableCustomVal("${media.id}_current_chp", null, String::class.java) val appEp = PrefManager.getNullableCustomVal(
"${media.id}_current_chp",
null,
String::class.java
)
?.toIntOrNull() ?: 1 ?.toIntOrNull() ?: 1
var continueEp = (if (anilistEp > appEp) anilistEp else appEp).toString() var continueEp = (if (anilistEp > appEp) anilistEp else appEp).toString()
val filteredChapters = chapters.filter { chapterKey -> val filteredChapters = chapters.filter { chapterKey ->
@ -470,7 +476,11 @@ class MangaReadAdapter(
val ep = media.manga.chapters!![continueEp]!! val ep = media.manga.chapters!![continueEp]!!
binding.itemEpisodeImage.loadImage(media.banner ?: media.cover) binding.itemEpisodeImage.loadImage(media.banner ?: media.cover)
binding.animeSourceContinueText.text = binding.animeSourceContinueText.text =
currActivity()!!.getString(R.string.continue_chapter, ep.number, if (!ep.title.isNullOrEmpty()) ep.title else "") currActivity()!!.getString(
R.string.continue_chapter,
ep.number,
if (!ep.title.isNullOrEmpty()) ep.title else ""
)
binding.animeSourceContinue.setOnClickListener { binding.animeSourceContinue.setOnClickListener {
fragment.onMangaChapterClick(continueEp) fragment.onMangaChapterClick(continueEp)
} }
@ -491,11 +501,14 @@ class MangaReadAdapter(
if (!sourceFound && PrefManager.getVal(PrefName.SearchSources)) { if (!sourceFound && PrefManager.getVal(PrefName.SearchSources)) {
if (binding.animeSource.adapter.count > media.selected!!.sourceIndex + 1) { if (binding.animeSource.adapter.count > media.selected!!.sourceIndex + 1) {
val nextIndex = media.selected!!.sourceIndex + 1 val nextIndex = media.selected!!.sourceIndex + 1
binding.animeSource.setText(binding.animeSource.adapter binding.animeSource.setText(
.getItem(nextIndex).toString(), false) binding.animeSource.adapter
.getItem(nextIndex).toString(), false
)
fragment.onSourceChange(nextIndex).apply { fragment.onSourceChange(nextIndex).apply {
binding.animeSourceTitle.text = showUserText binding.animeSourceTitle.text = showUserText
showUserTextListener = { MainScope().launch { binding.animeSourceTitle.text = it } } showUserTextListener =
{ MainScope().launch { binding.animeSourceTitle.text = it } }
setLanguageList(0, nextIndex) setLanguageList(0, nextIndex)
} }
subscribeButton(false) subscribeButton(false)

View file

@ -42,8 +42,8 @@ import ani.dantotsu.isOnline
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.MediaDetailsViewModel import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.MediaType
import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.media.MediaNameAdapter
import ani.dantotsu.media.MediaType
import ani.dantotsu.media.manga.mangareader.ChapterLoaderDialog import ani.dantotsu.media.manga.mangareader.ChapterLoaderDialog
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.notifications.subscription.SubscriptionHelper import ani.dantotsu.notifications.subscription.SubscriptionHelper
@ -203,8 +203,10 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
ConcatAdapter(headerAdapter, chapterAdapter) ConcatAdapter(headerAdapter, chapterAdapter)
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val offline = !isOnline(binding.root.context) || PrefManager.getVal(PrefName.OfflineMode) val offline =
if (offline) media.selected!!.sourceIndex = model.mangaReadSources!!.list.lastIndex !isOnline(binding.root.context) || PrefManager.getVal(PrefName.OfflineMode)
if (offline) media.selected!!.sourceIndex =
model.mangaReadSources!!.list.lastIndex
model.loadMangaChapters(media, media.selected!!.sourceIndex) model.loadMangaChapters(media, media.selected!!.sourceIndex)
} }
loaded = true loaded = true

View file

@ -42,7 +42,8 @@ abstract class BaseImageAdapter(
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
images = if (settings.layout == CurrentReaderSettings.Layouts.PAGED images = if (settings.layout == CurrentReaderSettings.Layouts.PAGED
&& settings.direction == CurrentReaderSettings.Directions.BOTTOM_TO_TOP) { && settings.direction == CurrentReaderSettings.Directions.BOTTOM_TO_TOP
) {
chapterImages.reversed() chapterImages.reversed()
} else { } else {
chapterImages chapterImages

View file

@ -56,8 +56,8 @@ import ani.dantotsu.isOnline
import ani.dantotsu.logError import ani.dantotsu.logError
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsViewModel import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.MediaSingleton
import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.media.MediaNameAdapter
import ani.dantotsu.media.MediaSingleton
import ani.dantotsu.media.manga.MangaCache import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.media.manga.MangaChapter import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.others.ImageViewDialog import ani.dantotsu.others.ImageViewDialog
@ -129,10 +129,12 @@ class MangaReaderActivity : AppCompatActivity() {
var sliding = false var sliding = false
var isAnimating = false var isAnimating = false
private val directionRLBT get() = defaultSettings.direction == RIGHT_TO_LEFT private val directionRLBT
|| defaultSettings.direction == BOTTOM_TO_TOP get() = defaultSettings.direction == RIGHT_TO_LEFT
private val directionPagedBT get() = defaultSettings.layout == CurrentReaderSettings.Layouts.PAGED || defaultSettings.direction == BOTTOM_TO_TOP
&& defaultSettings.direction == CurrentReaderSettings.Directions.BOTTOM_TO_TOP private val directionPagedBT
get() = defaultSettings.layout == CurrentReaderSettings.Layouts.PAGED
&& defaultSettings.direction == CurrentReaderSettings.Directions.BOTTOM_TO_TOP
override fun onAttachedToWindow() { override fun onAttachedToWindow() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !PrefManager.getVal<Boolean>(PrefName.ShowSystemBars)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !PrefManager.getVal<Boolean>(PrefName.ShowSystemBars)) {
@ -229,7 +231,7 @@ class MangaReaderActivity : AppCompatActivity() {
binding.mangaReaderRecycler.scrollToPosition((value.toInt() - 1) / (dualPage { 2 } binding.mangaReaderRecycler.scrollToPosition((value.toInt() - 1) / (dualPage { 2 }
?: 1)) ?: 1))
else else
if (defaultSettings.direction == CurrentReaderSettings.Directions.BOTTOM_TO_TOP ) { if (defaultSettings.direction == CurrentReaderSettings.Directions.BOTTOM_TO_TOP) {
binding.mangaReaderPager.currentItem = binding.mangaReaderPager.currentItem =
(maxChapterPage.toInt() - value.toInt()) / (dualPage { 2 } ?: 1) (maxChapterPage.toInt() - value.toInt()) / (dualPage { 2 } ?: 1)
} else { } else {
@ -345,7 +347,11 @@ class MangaReaderActivity : AppCompatActivity() {
if (currentChapterIndex > 0) change(currentChapterIndex - 1) if (currentChapterIndex > 0) change(currentChapterIndex - 1)
else snackString(getString(R.string.first_chapter)) else snackString(getString(R.string.first_chapter))
} else { } else {
if (chaptersArr.size > currentChapterIndex + 1) progress { change(currentChapterIndex + 1) } if (chaptersArr.size > currentChapterIndex + 1) progress {
change(
currentChapterIndex + 1
)
}
else snackString(getString(R.string.next_chapter_not_found)) else snackString(getString(R.string.next_chapter_not_found))
} }
} }
@ -355,7 +361,11 @@ class MangaReaderActivity : AppCompatActivity() {
} }
binding.mangaReaderPreviousChapter.setOnClickListener { binding.mangaReaderPreviousChapter.setOnClickListener {
if (directionRLBT) { if (directionRLBT) {
if (chaptersArr.size > currentChapterIndex + 1) progress { change(currentChapterIndex + 1) } if (chaptersArr.size > currentChapterIndex + 1) progress {
change(
currentChapterIndex + 1
)
}
else snackString(getString(R.string.next_chapter_not_found)) else snackString(getString(R.string.next_chapter_not_found))
} else { } else {
if (currentChapterIndex > 0) change(currentChapterIndex - 1) if (currentChapterIndex > 0) change(currentChapterIndex - 1)
@ -372,11 +382,15 @@ class MangaReaderActivity : AppCompatActivity() {
currentChapterIndex = chaptersArr.indexOf(chap.number) currentChapterIndex = chaptersArr.indexOf(chap.number)
binding.mangaReaderChapterSelect.setSelection(currentChapterIndex) binding.mangaReaderChapterSelect.setSelection(currentChapterIndex)
if (directionRLBT) { if (directionRLBT) {
binding.mangaReaderNextChap.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: "" binding.mangaReaderNextChap.text =
binding.mangaReaderPrevChap.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: "" chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: ""
binding.mangaReaderPrevChap.text =
chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: ""
} else { } else {
binding.mangaReaderNextChap.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: "" binding.mangaReaderNextChap.text =
binding.mangaReaderPrevChap.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: "" chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: ""
binding.mangaReaderPrevChap.text =
chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: ""
} }
applySettings() applySettings()
val context = this val context = this
@ -389,10 +403,12 @@ class MangaReaderActivity : AppCompatActivity() {
"nothing" -> mutableListOf( "nothing" -> mutableListOf(
RPC.Link(getString(R.string.view_manga), media.shareLink ?: ""), RPC.Link(getString(R.string.view_manga), media.shareLink ?: ""),
) )
"dantotsu" -> mutableListOf( "dantotsu" -> mutableListOf(
RPC.Link(getString(R.string.view_manga), media.shareLink ?: ""), RPC.Link(getString(R.string.view_manga), media.shareLink ?: ""),
RPC.Link("Read on Dantotsu", getString(R.string.dantotsu)) RPC.Link("Read on Dantotsu", getString(R.string.dantotsu))
) )
"anilist" -> { "anilist" -> {
val userId = PrefManager.getVal<String>(PrefName.AnilistUserId) val userId = PrefManager.getVal<String>(PrefName.AnilistUserId)
val anilistLink = "https://anilist.co/user/$userId/" val anilistLink = "https://anilist.co/user/$userId/"
@ -401,6 +417,7 @@ class MangaReaderActivity : AppCompatActivity() {
RPC.Link("View My AniList", anilistLink) RPC.Link("View My AniList", anilistLink)
) )
} }
else -> mutableListOf() else -> mutableListOf()
} }
val presence = RPC.createPresence( val presence = RPC.createPresence(
@ -411,7 +428,12 @@ class MangaReaderActivity : AppCompatActivity() {
details = chap.title?.takeIf { it.isNotEmpty() } details = chap.title?.takeIf { it.isNotEmpty() }
?: getString(R.string.chapter_num, chap.number), ?: getString(R.string.chapter_num, chap.number),
state = "${chap.number}/${media.manga?.totalChapters ?: "??"}", state = "${chap.number}/${media.manga?.totalChapters ?: "??"}",
largeImage = media.cover?.let { cover -> RPC.Link(media.userPreferredName, cover) }, largeImage = media.cover?.let { cover ->
RPC.Link(
media.userPreferredName,
cover
)
},
buttons = buttons buttons = buttons
) )
) )
@ -918,7 +940,12 @@ class MangaReaderActivity : AppCompatActivity() {
isAnimating = true isAnimating = true
ObjectAnimator.ofFloat(binding.mangaReaderCont, "alpha", 1f, 0f) ObjectAnimator.ofFloat(binding.mangaReaderCont, "alpha", 1f, 0f)
.setDuration(controllerDuration).start() .setDuration(controllerDuration).start()
ObjectAnimator.ofFloat(binding.mangaReaderBottomLayout, "translationY", 0f, 128f) ObjectAnimator.ofFloat(
binding.mangaReaderBottomLayout,
"translationY",
0f,
128f
)
.apply { interpolator = overshoot;duration = controllerDuration;start() } .apply { interpolator = overshoot;duration = controllerDuration;start() }
ObjectAnimator.ofFloat(binding.mangaReaderTopLayout, "translationY", 0f, -128f) ObjectAnimator.ofFloat(binding.mangaReaderTopLayout, "translationY", 0f, -128f)
.apply { interpolator = overshoot;duration = controllerDuration;start() } .apply { interpolator = overshoot;duration = controllerDuration;start() }

View file

@ -113,7 +113,12 @@ class NovelReadFragment : Fragment(),
) { ) {
try { try {
val directory = val directory =
DownloadsManager.getSubDirectory(context?:currContext()!!, MediaType.NOVEL, false, novel.name) DownloadsManager.getSubDirectory(
context ?: currContext()!!,
MediaType.NOVEL,
false,
novel.name
)
val file = directory?.findFile(novel.name) val file = directory?.findFile(novel.name)
if (file?.exists() == false) return false if (file?.exists() == false) return false
val fileUri = file?.uri ?: return false val fileUri = file?.uri ?: return false

View file

@ -181,7 +181,7 @@ class NovelResponseAdapter(
if (position != -1) { if (position != -1) {
list[position].extra?.remove("0") list[position].extra?.remove("0")
list[position].extra?.set("0", "Downloading: $progress%") list[position].extra?.set("0", "Downloading: $progress%")
Logger.log( "updateDownloadProgress: $progress, position: $position") Logger.log("updateDownloadProgress: $progress, position: $position")
notifyItemChanged(position) notifyItemChanged(position)
} }
} }

View file

@ -292,7 +292,11 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
applySettings() applySettings()
} }
val cfi = PrefManager.getNullableCustomVal("${sanitizedBookId}_progress", null, String::class.java) val cfi = PrefManager.getNullableCustomVal(
"${sanitizedBookId}_progress",
null,
String::class.java
)
cfi?.let { binding.bookReader.goto(it) } cfi?.let { binding.bookReader.goto(it) }
binding.progress.visibility = View.GONE binding.progress.visibility = View.GONE

View file

@ -70,8 +70,10 @@ class ListActivity : AppCompatActivity() {
setContentView(binding.root) setContentView(binding.root)
val anime = intent.getBooleanExtra("anime", true) val anime = intent.getBooleanExtra("anime", true)
binding.listTitle.text = getString(R.string.user_list, intent.getStringExtra("username"), binding.listTitle.text = getString(
if (anime) getString(R.string.anime) else getString(R.string.manga)) R.string.user_list, intent.getStringExtra("username"),
if (anime) getString(R.string.anime) else getString(R.string.manga)
)
binding.listTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { binding.listTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) { override fun onTabSelected(tab: TabLayout.Tab?) {
this@ListActivity.selectedTabIdx = tab?.position ?: 0 this@ListActivity.selectedTabIdx = tab?.position ?: 0
@ -158,7 +160,8 @@ class ListActivity : AppCompatActivity() {
} }
binding.filter.setOnClickListener { binding.filter.setOnClickListener {
val genres = PrefManager.getVal<Set<String>>(PrefName.GenresList).toMutableSet().sorted() val genres =
PrefManager.getVal<Set<String>>(PrefName.GenresList).toMutableSet().sorted()
val popup = PopupMenu(this, it) val popup = PopupMenu(this, it)
popup.menu.add("All") popup.menu.add("All")
genres.forEach { genre -> genres.forEach { genre ->

View file

@ -5,7 +5,10 @@ import android.graphics.Rect
import android.view.View import android.view.View
import android.widget.FrameLayout import android.widget.FrameLayout
class AndroidBug5497Workaround private constructor(activity: Activity, private val callback: (Boolean) -> Unit) { class AndroidBug5497Workaround private constructor(
activity: Activity,
private val callback: (Boolean) -> Unit
) {
private val mChildOfContent: View private val mChildOfContent: View
private var usableHeightPrevious = 0 private var usableHeightPrevious = 0
private val frameLayoutParams: FrameLayout.LayoutParams private val frameLayoutParams: FrameLayout.LayoutParams

View file

@ -1,6 +1,5 @@
package ani.dantotsu.others package ani.dantotsu.others
import android.app.DownloadManager
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -8,7 +7,6 @@ import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import ani.dantotsu.FileUrl import ani.dantotsu.FileUrl
import ani.dantotsu.R import ani.dantotsu.R
@ -19,9 +17,6 @@ import ani.dantotsu.parsers.Book
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.toast import ani.dantotsu.toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File import java.io.File
object Download { object Download {

View file

@ -2,10 +2,10 @@ package ani.dantotsu.others
import ani.dantotsu.FileUrl import ani.dantotsu.FileUrl
import ani.dantotsu.client import ani.dantotsu.client
import ani.dantotsu.util.Logger
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.anime.Episode import ani.dantotsu.media.anime.Episode
import ani.dantotsu.tryWithSuspend import ani.dantotsu.tryWithSuspend
import ani.dantotsu.util.Logger
import com.google.gson.Gson import com.google.gson.Gson
import com.lagradost.nicehttp.NiceResponse import com.lagradost.nicehttp.NiceResponse
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName

View file

@ -56,9 +56,10 @@ class OutlineTextView : AppCompatTextView {
setStrokeWidth(strokeWidth) setStrokeWidth(strokeWidth)
} }
private val Float.toPx get() = TypedValue.applyDimension( private val Float.toPx
TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics get() = TypedValue.applyDimension(
) TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics
)
private fun setStrokeWidth(width: Float) { private fun setStrokeWidth(width: Float) {
strokeWidth = width.toPx strokeWidth = width.toPx

View file

@ -50,7 +50,7 @@ class Xpandable @JvmOverloads constructor(
} }
} }
postDelayed({ postDelayed({
listeners.forEach{ listeners.forEach {
it.onRetract() it.onRetract()
} }
}, 300) }, 300)
@ -66,7 +66,7 @@ class Xpandable @JvmOverloads constructor(
} }
} }
postDelayed({ postDelayed({
listeners.forEach{ listeners.forEach {
it.onExpand() it.onExpand()
} }
}, 300) }, 300)

View file

@ -24,7 +24,8 @@ class CookieCatcher : AppCompatActivity() {
//get url from intent //get url from intent
val url = intent.getStringExtra("url") ?: getString(R.string.cursed_yt) val url = intent.getStringExtra("url") ?: getString(R.string.cursed_yt)
val headers: Map<String, String> = intent.getSerializableExtraCompat("headers") as? Map<String, String> ?: emptyMap() val headers: Map<String, String> =
intent.getSerializableExtraCompat("headers") as? Map<String, String> ?: emptyMap()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val process = Application.getProcessName() val process = Application.getProcessName()

View file

@ -5,8 +5,8 @@ import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory
import ani.dantotsu.download.anime.AnimeDownloaderService.AnimeDownloadTask.Companion.getTaskName import ani.dantotsu.download.anime.AnimeDownloaderService.AnimeDownloadTask.Companion.getTaskName
import ani.dantotsu.media.MediaType
import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.media.MediaNameAdapter
import ani.dantotsu.media.MediaType
import ani.dantotsu.tryWithSuspend import ani.dantotsu.tryWithSuspend
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.SAnime
@ -43,7 +43,7 @@ class OfflineAnimeParser : AnimeParser() {
if (it.isDirectory) { if (it.isDirectory) {
val episode = Episode( val episode = Episode(
it.name!!, it.name!!,
getTaskName(animeLink,it.name!!), getTaskName(animeLink, it.name!!),
it.name, it.name,
null, null,
null, null,

View file

@ -28,7 +28,7 @@ class OfflineNovelParser : NovelParser() {
directory.listFiles().forEach { directory.listFiles().forEach {
if (it.isDirectory) { if (it.isDirectory) {
val chapter = Book( val chapter = Book(
it.name?:"Unknown", it.name ?: "Unknown",
it.uri.toString(), it.uri.toString(),
null, null,
listOf(it.uri.toString()) listOf(it.uri.toString())
@ -63,7 +63,7 @@ class OfflineNovelParser : NovelParser() {
if (directory?.exists() == true) { if (directory?.exists() == true) {
directory.listFiles().forEach { directory.listFiles().forEach {
if (it.isDirectory) { if (it.isDirectory) {
names.add(it.name?: "Unknown") names.add(it.name ?: "Unknown")
} }
} }
} }

View file

@ -62,55 +62,57 @@ internal class NovelExtensionInstaller(private val context: Context) {
* @param url The url of the apk. * @param url The url of the apk.
* @param extension The extension to install. * @param extension The extension to install.
*/ */
fun downloadAndInstall(url: String, extension: NovelExtension): Observable<InstallStep> = Observable.defer { fun downloadAndInstall(url: String, extension: NovelExtension): Observable<InstallStep> =
val pkgName = extension.pkgName Observable.defer {
val pkgName = extension.pkgName
val oldDownload = activeDownloads[pkgName] val oldDownload = activeDownloads[pkgName]
if (oldDownload != null) { if (oldDownload != null) {
deleteDownload(pkgName) deleteDownload(pkgName)
}
val sourcePath = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath
//if the file is already downloaded, remove it
val fileToDelete = File("$sourcePath/${url.toUri().lastPathSegment}")
if (fileToDelete.exists()) {
if (fileToDelete.delete()) {
Logger.log("APK file deleted successfully.")
} else {
Logger.log("Failed to delete APK file.")
} }
} else {
Logger.log("APK file not found.") val sourcePath =
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath
//if the file is already downloaded, remove it
val fileToDelete = File("$sourcePath/${url.toUri().lastPathSegment}")
if (fileToDelete.exists()) {
if (fileToDelete.delete()) {
Logger.log("APK file deleted successfully.")
} else {
Logger.log("Failed to delete APK file.")
}
} else {
Logger.log("APK file not found.")
}
// Register the receiver after removing (and unregistering) the previous download
downloadReceiver.register()
val downloadUri = url.toUri()
val request = DownloadManager.Request(downloadUri)
.setTitle(extension.name)
.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
downloadsRelay.filter { it.first == id }
.map { it.second }
// Poll download status
.mergeWith(pollStatus(id))
// Stop when the application is installed or errors
.takeUntil { it.isCompleted() }
// Always notify on main thread
.observeOn(AndroidSchedulers.mainThread())
// Always remove the download when unsubscribed
.doOnUnsubscribe { deleteDownload(pkgName) }
} }
// Register the receiver after removing (and unregistering) the previous download
downloadReceiver.register()
val downloadUri = url.toUri()
val request = DownloadManager.Request(downloadUri)
.setTitle(extension.name)
.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
downloadsRelay.filter { it.first == id }
.map { it.second }
// Poll download status
.mergeWith(pollStatus(id))
// Stop when the application is installed or errors
.takeUntil { it.isCompleted() }
// Always notify on main thread
.observeOn(AndroidSchedulers.mainThread())
// Always remove the download when unsubscribed
.doOnUnsubscribe { deleteDownload(pkgName) }
}
/** /**
* Returns an observable that polls the given download id for its status every second, as the * Returns an observable that polls the given download id for its status every second, as the
* manager doesn't have any notification system. It'll stop once the download finishes. * manager doesn't have any notification system. It'll stop once the download finishes.

View file

@ -15,7 +15,8 @@ import com.xwray.groupie.viewbinding.GroupieViewHolder
class ChartItem( class ChartItem(
private val title: String, private val title: String,
private val aaOptions: AAOptions, private val aaOptions: AAOptions,
private val activity: ProfileActivity): BindableItem<ItemChartBinding>() { private val activity: ProfileActivity
) : BindableItem<ItemChartBinding>() {
private lateinit var binding: ItemChartBinding private lateinit var binding: ItemChartBinding
override fun bind(viewBinding: ItemChartBinding, position: Int) { override fun bind(viewBinding: ItemChartBinding, position: Int) {
binding = viewBinding binding = viewBinding
@ -78,6 +79,7 @@ class ChartItem(
viewHolder.setIsRecyclable(false) viewHolder.setIsRecyclable(false)
super.bind(viewHolder, position, payloads, onItemClickListener, onItemLongClickListener) super.bind(viewHolder, position, payloads, onItemClickListener, onItemLongClickListener)
} }
override fun getViewType(): Int { override fun getViewType(): Int {
return 0 return 0
} }

View file

@ -25,7 +25,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class FollowActivity : AppCompatActivity(){ class FollowActivity : AppCompatActivity() {
private lateinit var binding: ActivityFollowBinding private lateinit var binding: ActivityFollowBinding
val adapter = GroupieAdapter() val adapter = GroupieAdapter()
var users: List<User>? = null var users: List<User>? = null
@ -37,7 +37,9 @@ class FollowActivity : AppCompatActivity(){
initActivity(this) initActivity(this)
binding = ActivityFollowBinding.inflate(layoutInflater) binding = ActivityFollowBinding.inflate(layoutInflater)
binding.listToolbar.updateLayoutParams<MarginLayoutParams> { topMargin = statusBarHeight } binding.listToolbar.updateLayoutParams<MarginLayoutParams> { topMargin = statusBarHeight }
binding.listFrameLayout.updateLayoutParams<MarginLayoutParams> { bottomMargin = navBarHeight } binding.listFrameLayout.updateLayoutParams<MarginLayoutParams> {
bottomMargin = navBarHeight
}
setContentView(binding.root) setContentView(binding.root)
val layoutType = PrefManager.getVal<Int>(PrefName.FollowerLayout) val layoutType = PrefManager.getVal<Int>(PrefName.FollowerLayout)
selected = getSelected(layoutType) selected = getSelected(layoutType)
@ -54,7 +56,7 @@ class FollowActivity : AppCompatActivity(){
binding.listBack.setOnClickListener { finish() } binding.listBack.setOnClickListener { finish() }
val title = intent.getStringExtra("title") val title = intent.getStringExtra("title")
val userID= intent.getIntExtra("userId", 0) val userID = intent.getIntExtra("userId", 0)
binding.listTitle.text = title binding.listTitle.text = title
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
@ -93,9 +95,20 @@ class FollowActivity : AppCompatActivity(){
} }
users?.forEach { user -> users?.forEach { user ->
if (getLayoutType(selected) == 0) { if (getLayoutType(selected) == 0) {
adapter.add(FollowerItem(user.id, user.name ?: "Unknown", user.avatar?.medium, user.bannerImage ?: user.avatar?.medium ) { onUserClick(it) }) adapter.add(
FollowerItem(
user.id,
user.name ?: "Unknown",
user.avatar?.medium,
user.bannerImage ?: user.avatar?.medium
) { onUserClick(it) })
} else { } else {
adapter.add(GridFollowerItem(user.id, user.name ?: "Unknown", user.avatar?.medium) { onUserClick(it) }) adapter.add(
GridFollowerItem(
user.id,
user.name ?: "Unknown",
user.avatar?.medium
) { onUserClick(it) })
} }
} }
} }

View file

@ -14,7 +14,7 @@ class FollowerItem(
private val avatar: String?, private val avatar: String?,
private val banner: String?, private val banner: String?,
val clickCallback: (Int) -> Unit val clickCallback: (Int) -> Unit
): BindableItem<ItemFollowerBinding>() { ) : BindableItem<ItemFollowerBinding>() {
private lateinit var binding: ItemFollowerBinding private lateinit var binding: ItemFollowerBinding
override fun bind(viewBinding: ItemFollowerBinding, position: Int) { override fun bind(viewBinding: ItemFollowerBinding, position: Int) {

View file

@ -6,12 +6,12 @@ import ani.dantotsu.databinding.ItemFollowerGridBinding
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.BindableItem
class GridFollowerItem ( class GridFollowerItem(
private val id: Int, private val id: Int,
private val name: String, private val name: String,
private val avatar: String?, private val avatar: String?,
val clickCallback: (Int) -> Unit val clickCallback: (Int) -> Unit
): BindableItem<ItemFollowerGridBinding>() { ) : BindableItem<ItemFollowerGridBinding>() {
private lateinit var binding: ItemFollowerGridBinding private lateinit var binding: ItemFollowerGridBinding
override fun bind(viewBinding: ItemFollowerGridBinding, position: Int) { override fun bind(viewBinding: ItemFollowerGridBinding, position: Int) {

View file

@ -59,9 +59,11 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
screenWidth = resources.displayMetrics.widthPixels.toFloat() screenWidth = resources.displayMetrics.widthPixels.toFloat()
navBar = binding.profileNavBar navBar = binding.profileNavBar
val navBarRightMargin = if (resources.configuration.orientation == val navBarRightMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE) navBarHeight else 0 Configuration.ORIENTATION_LANDSCAPE
) navBarHeight else 0
val navBarBottomMargin = if (resources.configuration.orientation == val navBarBottomMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE) 0 else navBarHeight Configuration.ORIENTATION_LANDSCAPE
) 0 else navBarHeight
navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> { navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
rightMargin = navBarRightMargin rightMargin = navBarRightMargin
bottomMargin = navBarBottomMargin bottomMargin = navBarBottomMargin
@ -284,7 +286,7 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
if (mMaxScrollSize == 0) mMaxScrollSize = appBar.totalScrollRange if (mMaxScrollSize == 0) mMaxScrollSize = appBar.totalScrollRange
val percentage = abs(i) * 100 / mMaxScrollSize val percentage = abs(i) * 100 / mMaxScrollSize
with (bindingProfileAppBar) { with(bindingProfileAppBar) {
profileUserAvatarContainer.visibility = profileUserAvatarContainer.visibility =
if (profileUserAvatarContainer.scaleX == 0f) View.GONE else View.VISIBLE if (profileUserAvatarContainer.scaleX == 0f) View.GONE else View.VISIBLE
val duration = (200 * (PrefManager.getVal(PrefName.AnimationSpeed) as Float)).toLong() val duration = (200 * (PrefManager.getVal(PrefName.AnimationSpeed) as Float)).toLong()
@ -315,10 +317,12 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
val rightMargin = if (resources.configuration.orientation == val rightMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE) navBarHeight else 0 Configuration.ORIENTATION_LANDSCAPE
) navBarHeight else 0
val bottomMargin = if (resources.configuration.orientation == val bottomMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE) 0 else navBarHeight Configuration.ORIENTATION_LANDSCAPE
val params : ViewGroup.MarginLayoutParams = ) 0 else navBarHeight
val params: ViewGroup.MarginLayoutParams =
navBar.layoutParams as ViewGroup.MarginLayoutParams navBar.layoutParams as ViewGroup.MarginLayoutParams
params.updateMargins(right = rightMargin, bottom = bottomMargin) params.updateMargins(right = rightMargin, bottom = bottomMargin)
} }

View file

@ -1,8 +1,6 @@
package ani.dantotsu.profile package ani.dantotsu.profile
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -22,19 +20,16 @@ import ani.dantotsu.R
import ani.dantotsu.connections.anilist.ProfileViewModel import ani.dantotsu.connections.anilist.ProfileViewModel
import ani.dantotsu.connections.anilist.api.Query import ani.dantotsu.connections.anilist.api.Query
import ani.dantotsu.databinding.FragmentProfileBinding import ani.dantotsu.databinding.FragmentProfileBinding
import ani.dantotsu.loadImage
import ani.dantotsu.media.Author import ani.dantotsu.media.Author
import ani.dantotsu.media.AuthorAdapter import ani.dantotsu.media.AuthorAdapter
import ani.dantotsu.media.Character import ani.dantotsu.media.Character
import ani.dantotsu.media.CharacterAdapter import ani.dantotsu.media.CharacterAdapter
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaAdaptor import ani.dantotsu.media.MediaAdaptor
import ani.dantotsu.media.user.ListActivity
import ani.dantotsu.setBaseline import ani.dantotsu.setBaseline
import ani.dantotsu.setSlideIn import ani.dantotsu.setSlideIn
import ani.dantotsu.setSlideUp import ani.dantotsu.setSlideUp
import ani.dantotsu.util.AniMarkdown.Companion.getFullAniHTML import ani.dantotsu.util.AniMarkdown.Companion.getFullAniHTML
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.util.system.getSerializableCompat import eu.kanade.tachiyomi.util.system.getSerializableCompat
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -137,7 +132,7 @@ class ProfileFragment : Fragment() {
} }
user.favourites?.staff?.nodes?.forEach { i -> user.favourites?.staff?.nodes?.forEach { i ->
favStaff.add(Author(i.id, i.name.full, i.image.large , "" )) favStaff.add(Author(i.id, i.name.full, i.image.large, ""))
} }
setFavPeople() setFavPeople()
@ -159,7 +154,8 @@ class ProfileFragment : Fragment() {
binding.profileFavStaffRecycler.layoutManager = LinearLayoutManager( binding.profileFavStaffRecycler.layoutManager = LinearLayoutManager(
activity, LinearLayoutManager.HORIZONTAL, false activity, LinearLayoutManager.HORIZONTAL, false
) )
binding.profileFavStaffRecycler.layoutAnimation = LayoutAnimationController(setSlideIn(), 0.25f) binding.profileFavStaffRecycler.layoutAnimation =
LayoutAnimationController(setSlideIn(), 0.25f)
} }
if (favCharacter.isEmpty()) { if (favCharacter.isEmpty()) {
@ -169,7 +165,8 @@ class ProfileFragment : Fragment() {
binding.profileFavCharactersRecycler.layoutManager = LinearLayoutManager( binding.profileFavCharactersRecycler.layoutManager = LinearLayoutManager(
activity, LinearLayoutManager.HORIZONTAL, false activity, LinearLayoutManager.HORIZONTAL, false
) )
binding.profileFavCharactersRecycler.layoutAnimation = LayoutAnimationController(setSlideIn(), 0.25f) binding.profileFavCharactersRecycler.layoutAnimation =
LayoutAnimationController(setSlideIn(), 0.25f)
} }
} }
@ -189,7 +186,7 @@ class ProfileFragment : Fragment() {
recyclerView.visibility = View.GONE recyclerView.visibility = View.GONE
if (it != null) { if (it != null) {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
recyclerView.adapter = MediaAdaptor(0, it, activity, fav=true) recyclerView.adapter = MediaAdaptor(0, it, activity, fav = true)
recyclerView.layoutManager = LinearLayoutManager( recyclerView.layoutManager = LinearLayoutManager(
activity, activity,
LinearLayoutManager.HORIZONTAL, LinearLayoutManager.HORIZONTAL,

View file

@ -10,8 +10,7 @@ import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.toast import ani.dantotsu.toast
import com.github.aachartmodel.aainfographics.aachartcreator.AAOptions import com.github.aachartmodel.aainfographics.aachartcreator.AAOptions
class SingleStatActivity : AppCompatActivity() class SingleStatActivity : AppCompatActivity() {
{
private lateinit var binding: ActivitySingleStatBinding private lateinit var binding: ActivitySingleStatBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View file

@ -58,10 +58,13 @@ class StatsFragment :
binding.statisticList.adapter = adapter binding.statisticList.adapter = adapter
binding.statisticList.recycledViewPool.setMaxRecycledViews(0, 0) binding.statisticList.recycledViewPool.setMaxRecycledViews(0, 0)
binding.statisticList.isNestedScrollingEnabled = true binding.statisticList.isNestedScrollingEnabled = true
binding.statisticList.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) binding.statisticList.layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
binding.statisticProgressBar.visibility = View.VISIBLE binding.statisticProgressBar.visibility = View.VISIBLE
binding.compare.visibility = if (user.id == Anilist.userid) View.GONE else View.VISIBLE binding.compare.visibility = if (user.id == Anilist.userid) View.GONE else View.VISIBLE
binding.filterContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { topMargin = statusBarHeight } binding.filterContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = statusBarHeight
}
binding.sourceType.setAdapter( binding.sourceType.setAdapter(
ArrayAdapter( ArrayAdapter(

View file

@ -12,8 +12,8 @@ data class User(
val status: String? = null, val status: String? = null,
val score: Float? = null, val score: Float? = null,
val progress: Int? = null, val progress: Int? = null,
val totalEpisodes : Int? = null, val totalEpisodes: Int? = null,
val nextAiringEpisode : Int? = null, val nextAiringEpisode: Int? = null,
) : java.io.Serializable { ) : java.io.Serializable {
companion object { companion object {
private const val serialVersionUID: Long = 1 private const val serialVersionUID: Long = 1

View file

@ -5,13 +5,13 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.blurImage
import ani.dantotsu.databinding.ItemFollowerBinding import ani.dantotsu.databinding.ItemFollowerBinding
import ani.dantotsu.loadImage import ani.dantotsu.loadImage
import ani.dantotsu.setAnimation import ani.dantotsu.setAnimation
class UsersAdapter(private val user: ArrayList<User>) : RecyclerView.Adapter<UsersAdapter.UsersViewHolder>() { class UsersAdapter(private val user: ArrayList<User>) :
RecyclerView.Adapter<UsersAdapter.UsersViewHolder>() {
inner class UsersViewHolder(val binding: ItemFollowerBinding) : inner class UsersViewHolder(val binding: ItemFollowerBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {

View file

@ -14,9 +14,10 @@ class UsersDialogFragment : BottomSheetDialogFragment() {
private val binding get() = _binding!! private val binding get() = _binding!!
private var userList = arrayListOf<User>() private var userList = arrayListOf<User>()
fun userList(user: ArrayList<User>){ fun userList(user: ArrayList<User>) {
userList = user userList = user
} }
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -25,6 +26,7 @@ class UsersDialogFragment : BottomSheetDialogFragment() {
_binding = BottomSheetUsersBinding.inflate(inflater, container, false) _binding = BottomSheetUsersBinding.inflate(inflater, container, false)
return binding.root return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View file

@ -1,6 +1,5 @@
package ani.dantotsu.profile.activity package ani.dantotsu.profile.activity
import android.annotation.SuppressLint
import android.view.View import android.view.View
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -18,13 +17,8 @@ import ani.dantotsu.profile.UsersDialogFragment
import ani.dantotsu.setAnimation import ani.dantotsu.setAnimation
import ani.dantotsu.snackString import ani.dantotsu.snackString
import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML import ani.dantotsu.util.AniMarkdown.Companion.getBasicAniHTML
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.RequestOptions
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.BindableItem
import jp.wasabeef.glide.transformations.BlurTransformation
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@ -108,11 +102,12 @@ class ActivityItem(
} }
val context = binding.root.context val context = binding.root.context
val userList = arrayListOf<User>() val userList = arrayListOf<User>()
activity.likes?.forEach{ i -> activity.likes?.forEach { i ->
userList.add(User(i.id, i.name.toString(), i.avatar?.medium, i.bannerImage)) userList.add(User(i.id, i.name.toString(), i.avatar?.medium, i.bannerImage))
} }
binding.activityLike.setOnLongClickListener{ binding.activityLike.setOnLongClickListener {
UsersDialogFragment().apply { userList(userList) UsersDialogFragment().apply {
userList(userList)
show(fragActivity.supportFragmentManager, "dialog") show(fragActivity.supportFragmentManager, "dialog")
} }
true true
@ -126,8 +121,10 @@ class ActivityItem(
binding.activityContent.visibility = View.GONE binding.activityContent.visibility = View.GONE
binding.activityBannerContainer.visibility = View.VISIBLE binding.activityBannerContainer.visibility = View.VISIBLE
binding.activityMediaName.text = activity.media?.title?.userPreferred binding.activityMediaName.text = activity.media?.title?.userPreferred
val activityText = "${activity.user!!.name} ${activity.status} ${activity.progress val activityText = "${activity.user!!.name} ${activity.status} ${
?: activity.media?.title?.userPreferred}" activity.progress
?: activity.media?.title?.userPreferred
}"
binding.activityText.text = activityText binding.activityText.text = activityText
binding.activityCover.loadImage(cover) binding.activityCover.loadImage(cover)
blurImage(binding.activityBannerImage, banner ?: cover) blurImage(binding.activityBannerImage, banner ?: cover)

View file

@ -5,6 +5,7 @@ import ani.dantotsu.connections.anilist.api.NotificationType
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
class ActivityItemBuilder { class ActivityItemBuilder {
companion object { companion object {
@ -109,6 +110,7 @@ class ActivityItemBuilder {
else -> "Just now" else -> "Just now"
} }
} }
1L -> "1 day ago" 1L -> "1 day ago"
in 2..6 -> "$daysDifference days ago" in 2..6 -> "$daysDifference days ago"
else -> SimpleDateFormat("dd MMM yyyy", Locale.getDefault()).format(targetDate) else -> SimpleDateFormat("dd MMM yyyy", Locale.getDefault()).format(targetDate)

View file

@ -31,7 +31,8 @@ class FeedActivity : AppCompatActivity() {
setContentView(binding.root) setContentView(binding.root)
navBar = binding.feedNavBar navBar = binding.feedNavBar
val navBarMargin = if (resources.configuration.orientation == val navBarMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE) 0 else navBarHeight Configuration.ORIENTATION_LANDSCAPE
) 0 else navBarHeight
navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin = navBarMargin } navBar.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin = navBarMargin }
val personalTab = navBar.createTab(R.drawable.ic_round_person_24, "Following") val personalTab = navBar.createTab(R.drawable.ic_round_person_24, "Following")
val globalTab = navBar.createTab(R.drawable.ic_globe_24, "Global") val globalTab = navBar.createTab(R.drawable.ic_globe_24, "Global")
@ -67,10 +68,12 @@ class FeedActivity : AppCompatActivity() {
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
val margin = if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) 0 else navBarHeight val margin =
val params : ViewGroup.MarginLayoutParams = if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) 0 else navBarHeight
val params: ViewGroup.MarginLayoutParams =
binding.feedViewPager.layoutParams as ViewGroup.MarginLayoutParams binding.feedViewPager.layoutParams as ViewGroup.MarginLayoutParams
val paramsNav : ViewGroup.MarginLayoutParams = navBar.layoutParams as ViewGroup.MarginLayoutParams val paramsNav: ViewGroup.MarginLayoutParams =
navBar.layoutParams as ViewGroup.MarginLayoutParams
params.updateMargins(bottom = margin) params.updateMargins(bottom = margin)
paramsNav.updateMargins(bottom = margin) paramsNav.updateMargins(bottom = margin)
} }

View file

@ -19,7 +19,6 @@ import ani.dantotsu.databinding.FragmentFeedBinding
import ani.dantotsu.media.MediaDetailsActivity import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.setBaseline import ani.dantotsu.setBaseline
import ani.dantotsu.snackString
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.GroupieAdapter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -57,7 +56,7 @@ class FeedFragment : Fragment() {
val navBar = if (userId != null) { val navBar = if (userId != null) {
(activity as ProfileActivity).navBar (activity as ProfileActivity).navBar
}else{ } else {
(activity as FeedActivity).navBar (activity as FeedActivity).navBar
} }
binding.listRecyclerView.setBaseline(navBar) binding.listRecyclerView.setBaseline(navBar)
@ -74,7 +73,7 @@ class FeedFragment : Fragment() {
binding.root.requestLayout() binding.root.requestLayout()
val navBar = if (userId != null) { val navBar = if (userId != null) {
(activity as ProfileActivity).navBar (activity as ProfileActivity).navBar
}else{ } else {
(activity as FeedActivity).navBar (activity as FeedActivity).navBar
} }
binding.listRecyclerView.setBaseline(navBar) binding.listRecyclerView.setBaseline(navBar)
@ -85,10 +84,17 @@ class FeedFragment : Fragment() {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
res?.data?.page?.activities?.let { activities -> res?.data?.page?.activities?.let { activities ->
activityList = activities activityList = activities
val filtered = activityList.filterNot { //filter out messages that are not directed to the user val filtered =
it.recipient?.id != null && it.recipient.id != Anilist.userid activityList.filterNot { //filter out messages that are not directed to the user
} it.recipient?.id != null && it.recipient.id != Anilist.userid
adapter.update(filtered.map { ActivityItem(it, ::onActivityClick,requireActivity()) }) }
adapter.update(filtered.map {
ActivityItem(
it,
::onActivityClick,
requireActivity()
)
})
} }
binding.listProgressBar.visibility = ViewGroup.GONE binding.listProgressBar.visibility = ViewGroup.GONE
val scrollView = binding.listRecyclerView val scrollView = binding.listRecyclerView
@ -134,7 +140,13 @@ class FeedFragment : Fragment() {
val filtered = activities.filterNot { val filtered = activities.filterNot {
it.recipient?.id != null && it.recipient.id != Anilist.userid it.recipient?.id != null && it.recipient.id != Anilist.userid
} }
adapter.addAll(filtered.map { ActivityItem(it, ::onActivityClick,requireActivity()) }) adapter.addAll(filtered.map {
ActivityItem(
it,
::onActivityClick,
requireActivity()
)
})
} }
binding.feedSwipeRefresh.isRefreshing = false binding.feedSwipeRefresh.isRefreshing = false
onFinish() onFinish()
@ -150,6 +162,7 @@ class FeedFragment : Fragment() {
.putExtra("userId", id), null .putExtra("userId", id), null
) )
} }
"MEDIA" -> { "MEDIA" -> {
ContextCompat.startActivity( ContextCompat.startActivity(
activity, Intent(activity, MediaDetailsActivity::class.java) activity, Intent(activity, MediaDetailsActivity::class.java)

View file

@ -22,7 +22,6 @@ import ani.dantotsu.notifications.comment.CommentStore
import ani.dantotsu.profile.ProfileActivity import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.snackString
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger

View file

@ -1,9 +1,7 @@
package ani.dantotsu.profile.activity package ani.dantotsu.profile.activity
import android.util.TypedValue
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.updateLayoutParams
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.blurImage import ani.dantotsu.blurImage
import ani.dantotsu.connections.anilist.api.Notification import ani.dantotsu.connections.anilist.api.Notification
@ -60,7 +58,8 @@ class NotificationItem(
} }
binding.notificationBannerImage.layoutParams.height = userHeight binding.notificationBannerImage.layoutParams.height = userHeight
binding.notificationGradiant.layoutParams.height = userHeight binding.notificationGradiant.layoutParams.height = userHeight
(binding.notificationTextContainer.layoutParams as ViewGroup.MarginLayoutParams).marginStart = userHeight (binding.notificationTextContainer.layoutParams as ViewGroup.MarginLayoutParams).marginStart =
userHeight
} else { } else {
binding.notificationCover.visibility = View.VISIBLE binding.notificationCover.visibility = View.VISIBLE
binding.notificationCoverUser.visibility = View.VISIBLE binding.notificationCoverUser.visibility = View.VISIBLE
@ -68,7 +67,8 @@ class NotificationItem(
binding.notificationCover.loadImage(notification.media?.coverImage?.large) binding.notificationCover.loadImage(notification.media?.coverImage?.large)
binding.notificationBannerImage.layoutParams.height = defaultHeight binding.notificationBannerImage.layoutParams.height = defaultHeight
binding.notificationGradiant.layoutParams.height = defaultHeight binding.notificationGradiant.layoutParams.height = defaultHeight
(binding.notificationTextContainer.layoutParams as ViewGroup.MarginLayoutParams).marginStart = textMarginStart (binding.notificationTextContainer.layoutParams as ViewGroup.MarginLayoutParams).marginStart =
textMarginStart
} }
} }
@ -308,7 +308,9 @@ class NotificationItem(
if (notification.commentId != null && notification.mediaId != null) { if (notification.commentId != null && notification.mediaId != null) {
binding.notificationBannerImage.setOnClickListener { binding.notificationBannerImage.setOnClickListener {
clickCallback( clickCallback(
notification.mediaId, notification.commentId, NotificationClickType.COMMENT notification.mediaId,
notification.commentId,
NotificationClickType.COMMENT
) )
} }
} }

View file

@ -12,10 +12,10 @@ import ani.dantotsu.setAnimation
class SettingsAdapter(private val settings: ArrayList<Settings>) : class SettingsAdapter(private val settings: ArrayList<Settings>) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() { RecyclerView.Adapter<RecyclerView.ViewHolder>() {
inner class SettingsViewHolder(val binding: ItemSettingsBinding) : inner class SettingsViewHolder(val binding: ItemSettingsBinding) :
RecyclerView.ViewHolder(binding.root) {} RecyclerView.ViewHolder(binding.root)
inner class SettingsSwitchViewHolder(val binding: ItemSettingsSwitchBinding) : inner class SettingsSwitchViewHolder(val binding: ItemSettingsSwitchBinding) :
RecyclerView.ViewHolder(binding.root) {} RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) { return when (viewType) {
@ -82,7 +82,7 @@ class SettingsAdapter(private val settings: ArrayList<Settings>) :
b.settingsButton.setOnCheckedChangeListener { _, isChecked -> b.settingsButton.setOnCheckedChangeListener { _, isChecked ->
settings.switch?.invoke(isChecked, b) settings.switch?.invoke(isChecked, b)
} }
b.settingsLayout.setOnLongClickListener() { b.settingsLayout.setOnLongClickListener {
settings.onLongClick?.invoke() settings.onLongClick?.invoke()
true true
} }

View file

@ -1,7 +1,6 @@
package ani.dantotsu.util package ani.dantotsu.util
import ani.dantotsu.util.ColorEditor.Companion.toCssColor import ani.dantotsu.util.ColorEditor.Companion.toCssColor
import ani.dantotsu.util.ColorEditor.Companion.toHexColor
class AniMarkdown { //istg anilist has the worst api class AniMarkdown { //istg anilist has the worst api
companion object { companion object {

View file

@ -133,7 +133,11 @@ object Logger {
shareIntent.type = "text/plain" shareIntent.type = "text/plain"
shareIntent.putExtra( shareIntent.putExtra(
Intent.EXTRA_STREAM, Intent.EXTRA_STREAM,
FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", fileToUse!!) FileProvider.getUriForFile(
context,
"${BuildConfig.APPLICATION_ID}.provider",
fileToUse!!
)
) )
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "Log file") shareIntent.putExtra(Intent.EXTRA_SUBJECT, "Log file")
shareIntent.putExtra(Intent.EXTRA_TEXT, "Log file") shareIntent.putExtra(Intent.EXTRA_TEXT, "Log file")

View file

@ -62,9 +62,10 @@ class StoragePermissions {
return hasDirAccess(context, path) return hasDirAccess(context, path)
} }
fun AppCompatActivity.accessAlertDialog(launcher: LauncherWrapper, fun AppCompatActivity.accessAlertDialog(
force: Boolean = false, launcher: LauncherWrapper,
complete: (Boolean) -> Unit force: Boolean = false,
complete: (Boolean) -> Unit
) { ) {
if (hasDirAccess(this) && !force) { if (hasDirAccess(this) && !force) {
complete(true) complete(true)
@ -97,11 +98,12 @@ class StoragePermissions {
class LauncherWrapper( class LauncherWrapper(
activity: AppCompatActivity, activity: AppCompatActivity,
contract: ActivityResultContracts.OpenDocumentTree) contract: ActivityResultContracts.OpenDocumentTree
{ ) {
private var launcher: ActivityResultLauncher<Uri?> private var launcher: ActivityResultLauncher<Uri?>
var complete: (Boolean) -> Unit = {} var complete: (Boolean) -> Unit = {}
init{
init {
launcher = activity.registerForActivityResult(contract) { uri -> launcher = activity.registerForActivityResult(contract) { uri ->
if (uri != null) { if (uri != null) {
activity.contentResolver.takePersistableUriPermission( activity.contentResolver.takePersistableUriPermission(

View file

@ -59,7 +59,8 @@ class ProfileStatsWidget : AppWidgetProvider() {
appWidgetId: Int appWidgetId: Int
) { ) {
val prefs = context.getSharedPreferences(getPrefsName(appWidgetId), Context.MODE_PRIVATE) val prefs =
context.getSharedPreferences(getPrefsName(appWidgetId), Context.MODE_PRIVATE)
val backgroundColor = val backgroundColor =
prefs.getInt(PREF_BACKGROUND_COLOR, Color.parseColor("#80000000")) prefs.getInt(PREF_BACKGROUND_COLOR, Color.parseColor("#80000000"))
val backgroundFade = prefs.getInt(PREF_BACKGROUND_FADE, Color.parseColor("#00000000")) val backgroundFade = prefs.getInt(PREF_BACKGROUND_FADE, Color.parseColor("#00000000"))
@ -87,88 +88,95 @@ class ProfileStatsWidget : AppWidgetProvider() {
val respond = Anilist.query.getUserProfile(userPref.toInt()) val respond = Anilist.query.getUserProfile(userPref.toInt())
respond?.data?.user?.let { user -> respond?.data?.user?.let { user ->
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val views = RemoteViews(context.packageName, R.layout.statistics_widget).apply { val views =
setImageViewBitmap( RemoteViews(context.packageName, R.layout.statistics_widget).apply {
R.id.backgroundView, setImageViewBitmap(
gradientDrawable.toBitmap( R.id.backgroundView,
width, gradientDrawable.toBitmap(
height width,
height
)
) )
) setOnClickPendingIntent(
setOnClickPendingIntent( R.id.userAvatar,
R.id.userAvatar, PendingIntent.getActivity(
PendingIntent.getActivity( context,
context, 1,
1, Intent(
Intent(context, ProfileStatsConfigure::class.java).apply { context,
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) ProfileStatsConfigure::class.java
data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME)) ).apply {
}, putExtra(
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetId
)
data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
},
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
) )
) setTextColor(R.id.userLabel, titleTextColor)
setTextColor(R.id.userLabel, titleTextColor) setTextColor(R.id.topLeftItem, titleTextColor)
setTextColor(R.id.topLeftItem, titleTextColor) setTextColor(R.id.topLeftLabel, statsTextColor)
setTextColor(R.id.topLeftLabel, statsTextColor) setTextColor(R.id.topRightItem, titleTextColor)
setTextColor(R.id.topRightItem, titleTextColor) setTextColor(R.id.topRightLabel, statsTextColor)
setTextColor(R.id.topRightLabel, statsTextColor) setTextColor(R.id.bottomLeftItem, titleTextColor)
setTextColor(R.id.bottomLeftItem, titleTextColor) setTextColor(R.id.bottomLeftLabel, statsTextColor)
setTextColor(R.id.bottomLeftLabel, statsTextColor) setTextColor(R.id.bottomRightItem, titleTextColor)
setTextColor(R.id.bottomRightItem, titleTextColor) setTextColor(R.id.bottomRightLabel, statsTextColor)
setTextColor(R.id.bottomRightLabel, statsTextColor)
setImageViewBitmap( setImageViewBitmap(
R.id.userAvatar, R.id.userAvatar,
user.avatar?.medium?.let { it1 -> downloadImageAsBitmap(it1) } user.avatar?.medium?.let { it1 -> downloadImageAsBitmap(it1) }
) )
setTextViewText( setTextViewText(
R.id.userLabel, R.id.userLabel,
context.getString(R.string.user_stats, user.name) context.getString(R.string.user_stats, user.name)
) )
setTextViewText( setTextViewText(
R.id.topLeftItem, R.id.topLeftItem,
user.statistics.anime.count.toString() user.statistics.anime.count.toString()
) )
setTextViewText( setTextViewText(
R.id.topLeftLabel, R.id.topLeftLabel,
context.getString(R.string.anime_watched) context.getString(R.string.anime_watched)
) )
setTextViewText( setTextViewText(
R.id.topRightItem, R.id.topRightItem,
user.statistics.anime.episodesWatched.toString() user.statistics.anime.episodesWatched.toString()
) )
setTextViewText( setTextViewText(
R.id.topRightLabel, R.id.topRightLabel,
context.getString(R.string.episodes_watched_n) context.getString(R.string.episodes_watched_n)
) )
setTextViewText( setTextViewText(
R.id.bottomLeftItem, R.id.bottomLeftItem,
user.statistics.manga.count.toString() user.statistics.manga.count.toString()
) )
setTextViewText( setTextViewText(
R.id.bottomLeftLabel, R.id.bottomLeftLabel,
context.getString(R.string.manga_read) context.getString(R.string.manga_read)
) )
setTextViewText( setTextViewText(
R.id.bottomRightItem, R.id.bottomRightItem,
user.statistics.manga.chaptersRead.toString() user.statistics.manga.chaptersRead.toString()
) )
setTextViewText( setTextViewText(
R.id.bottomRightLabel, R.id.bottomRightLabel,
context.getString(R.string.chapters_read_n) context.getString(R.string.chapters_read_n)
) )
val intent = Intent(context, ProfileActivity::class.java) val intent = Intent(context, ProfileActivity::class.java)
.putExtra("userId", userPref.toInt()) .putExtra("userId", userPref.toInt())
val pendingIntent = PendingIntent.getActivity( val pendingIntent = PendingIntent.getActivity(
context, 0, intent, PendingIntent.FLAG_IMMUTABLE context, 0, intent, PendingIntent.FLAG_IMMUTABLE
) )
setOnClickPendingIntent(R.id.widgetContainer, pendingIntent) setOnClickPendingIntent(R.id.widgetContainer, pendingIntent)
} }
// Instruct the widget manager to update the widget // Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views) appWidgetManager.updateAppWidget(appWidgetId, views)
} }
@ -221,6 +229,7 @@ class ProfileStatsWidget : AppWidgetProvider() {
fun getPrefsName(appWidgetId: Int): String { fun getPrefsName(appWidgetId: Int): String {
return "ani.dantotsu.widgets.Statistics.${appWidgetId}" return "ani.dantotsu.widgets.Statistics.${appWidgetId}"
} }
const val PREF_BACKGROUND_COLOR = "background_color" const val PREF_BACKGROUND_COLOR = "background_color"
const val PREF_BACKGROUND_FADE = "background_fade" const val PREF_BACKGROUND_FADE = "background_fade"
const val PREF_TITLE_TEXT_COLOR = "title_text_color" const val PREF_TITLE_TEXT_COLOR = "title_text_color"

View file

@ -48,8 +48,10 @@ class UpcomingWidgetConfigure : AppCompatActivity(),
binding = UpcomingWidgetConfigureBinding.inflate(layoutInflater) binding = UpcomingWidgetConfigureBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
val prefs = getSharedPreferences(UpcomingWidget.PREFS_NAME, Context.MODE_PRIVATE) val prefs = getSharedPreferences(UpcomingWidget.PREFS_NAME, Context.MODE_PRIVATE)
val topBackground = prefs.getInt(UpcomingWidget.PREF_BACKGROUND_COLOR, Color.parseColor("#80000000")) val topBackground =
(binding.topBackgroundButton as MaterialButton).iconTint = ColorStateList.valueOf(topBackground) prefs.getInt(UpcomingWidget.PREF_BACKGROUND_COLOR, Color.parseColor("#80000000"))
(binding.topBackgroundButton as MaterialButton).iconTint =
ColorStateList.valueOf(topBackground)
binding.topBackgroundButton.setOnClickListener { binding.topBackgroundButton.setOnClickListener {
val tag = UpcomingWidget.PREF_BACKGROUND_COLOR val tag = UpcomingWidget.PREF_BACKGROUND_COLOR
SimpleColorDialog().title(R.string.custom_theme) SimpleColorDialog().title(R.string.custom_theme)
@ -66,8 +68,10 @@ class UpcomingWidgetConfigure : AppCompatActivity(),
.neg() .neg()
.show(this@UpcomingWidgetConfigure, tag) .show(this@UpcomingWidgetConfigure, tag)
} }
val bottomBackground = prefs.getInt(UpcomingWidget.PREF_BACKGROUND_FADE, Color.parseColor("#00000000")) val bottomBackground =
(binding.bottomBackgroundButton as MaterialButton).iconTint = ColorStateList.valueOf(bottomBackground) prefs.getInt(UpcomingWidget.PREF_BACKGROUND_FADE, Color.parseColor("#00000000"))
(binding.bottomBackgroundButton as MaterialButton).iconTint =
ColorStateList.valueOf(bottomBackground)
binding.bottomBackgroundButton.setOnClickListener { binding.bottomBackgroundButton.setOnClickListener {
val tag = UpcomingWidget.PREF_BACKGROUND_FADE val tag = UpcomingWidget.PREF_BACKGROUND_FADE
SimpleColorDialog().title(R.string.custom_theme) SimpleColorDialog().title(R.string.custom_theme)
@ -85,7 +89,8 @@ class UpcomingWidgetConfigure : AppCompatActivity(),
.show(this@UpcomingWidgetConfigure, tag) .show(this@UpcomingWidgetConfigure, tag)
} }
val titleTextColor = prefs.getInt(UpcomingWidget.PREF_TITLE_TEXT_COLOR, Color.WHITE) val titleTextColor = prefs.getInt(UpcomingWidget.PREF_TITLE_TEXT_COLOR, Color.WHITE)
(binding.titleColorButton as MaterialButton).iconTint = ColorStateList.valueOf(titleTextColor) (binding.titleColorButton as MaterialButton).iconTint =
ColorStateList.valueOf(titleTextColor)
binding.titleColorButton.setOnClickListener { binding.titleColorButton.setOnClickListener {
val tag = UpcomingWidget.PREF_TITLE_TEXT_COLOR val tag = UpcomingWidget.PREF_TITLE_TEXT_COLOR
SimpleColorDialog().title(R.string.custom_theme) SimpleColorDialog().title(R.string.custom_theme)
@ -102,7 +107,8 @@ class UpcomingWidgetConfigure : AppCompatActivity(),
.show(this@UpcomingWidgetConfigure, tag) .show(this@UpcomingWidgetConfigure, tag)
} }
val countdownTextColor = prefs.getInt(UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR, Color.WHITE) val countdownTextColor = prefs.getInt(UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR, Color.WHITE)
(binding.countdownColorButton as MaterialButton).iconTint = ColorStateList.valueOf(countdownTextColor) (binding.countdownColorButton as MaterialButton).iconTint =
ColorStateList.valueOf(countdownTextColor)
binding.countdownColorButton.setOnClickListener { binding.countdownColorButton.setOnClickListener {
val tag = UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR val tag = UpcomingWidget.PREF_COUNTDOWN_TEXT_COLOR
SimpleColorDialog().title(R.string.custom_theme) SimpleColorDialog().title(R.string.custom_theme)
@ -152,15 +158,27 @@ class UpcomingWidgetConfigure : AppCompatActivity(),
private fun themeColors() { private fun themeColors() {
val typedValueSurface = TypedValue() val typedValueSurface = TypedValue()
theme.resolveAttribute(com.google.android.material.R.attr.colorSurface, typedValueSurface, true) theme.resolveAttribute(
com.google.android.material.R.attr.colorSurface,
typedValueSurface,
true
)
val backgroundColor = typedValueSurface.data val backgroundColor = typedValueSurface.data
val typedValuePrimary = TypedValue() val typedValuePrimary = TypedValue()
theme.resolveAttribute(com.google.android.material.R.attr.colorPrimary, typedValuePrimary, true) theme.resolveAttribute(
com.google.android.material.R.attr.colorPrimary,
typedValuePrimary,
true
)
val textColor = typedValuePrimary.data val textColor = typedValuePrimary.data
val typedValueOutline = TypedValue() val typedValueOutline = TypedValue()
theme.resolveAttribute(com.google.android.material.R.attr.colorOutline, typedValueOutline, true) theme.resolveAttribute(
com.google.android.material.R.attr.colorOutline,
typedValueOutline,
true
)
val subTextColor = typedValueOutline.data val subTextColor = typedValueOutline.data
getSharedPreferences(UpcomingWidget.PREFS_NAME, Context.MODE_PRIVATE).edit().apply { getSharedPreferences(UpcomingWidget.PREFS_NAME, Context.MODE_PRIVATE).edit().apply {

View file

@ -23,7 +23,6 @@ import uy.kohesive.injekt.injectLazy
import java.net.URI import java.net.URI
import java.net.URISyntaxException import java.net.URISyntaxException
import java.security.MessageDigest import java.security.MessageDigest
import java.util.concurrent.TimeUnit
/** /**
* A simple implementation for sources from a website. * A simple implementation for sources from a website.
@ -89,14 +88,15 @@ abstract class AnimeHttpSource : AnimeCatalogueSource {
protected fun generateId(name: String, lang: String, versionId: Int): Long { protected fun generateId(name: String, lang: String, versionId: Int): Long {
val key = "${name.lowercase()}/$lang/$versionId" val key = "${name.lowercase()}/$lang/$versionId"
val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray()) val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray())
return (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE return (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }
.reduce(Long::or) and Long.MAX_VALUE
} }
/** /**
* Headers builder for requests. Implementations can override this method for custom headers. * Headers builder for requests. Implementations can override this method for custom headers.
*/ */
protected open fun headersBuilder() = Headers.Builder().apply { protected open fun headersBuilder() = Headers.Builder().apply {
add("User-Agent", NetworkHelper.defaultUserAgentProvider()) add("User-Agent", defaultUserAgentProvider())
} }
/** /**
@ -148,7 +148,11 @@ abstract class AnimeHttpSource : AnimeCatalogueSource {
"Use the non-RxJava API instead", "Use the non-RxJava API instead",
ReplaceWith("getSearchAnime"), ReplaceWith("getSearchAnime"),
) )
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> { override fun fetchSearchAnime(
page: Int,
query: String,
filters: AnimeFilterList
): Observable<AnimesPage> {
return Observable.defer { return Observable.defer {
try { try {
client.newCall(searchAnimeRequest(page, query, filters)).asObservableSuccess() client.newCall(searchAnimeRequest(page, query, filters)).asObservableSuccess()
@ -170,7 +174,11 @@ abstract class AnimeHttpSource : AnimeCatalogueSource {
* @param query the search query. * @param query the search query.
* @param filters the list of filters to apply. * @param filters the list of filters to apply.
*/ */
protected abstract fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request protected abstract fun searchAnimeRequest(
page: Int,
query: String,
filters: AnimeFilterList
): Request
/** /**
* Parses the response from the site and returns a [AnimesPage] object. * Parses the response from the site and returns a [AnimesPage] object.
@ -403,7 +411,8 @@ abstract class AnimeHttpSource : AnimeCatalogueSource {
video: Video, video: Video,
tries: Int, tries: Int,
): Long { ): Long {
val headers = Headers.Builder().addAll(video.headers ?: headers).add("Range", "bytes=0-1").build() val headers =
Headers.Builder().addAll(video.headers ?: headers).add("Range", "bytes=0-1").build()
val request = GET(video.videoUrl!!, headers) val request = GET(video.videoUrl!!, headers)
val response = client.newCall(request).execute() val response = client.newCall(request).execute()
// parse the response headers to get the size of the video, in particular the content-range header // parse the response headers to get the size of the video, in particular the content-range header

View file

@ -207,8 +207,10 @@ class AnimeExtensionManager(
* @param extension The anime extension to be installed. * @param extension The anime extension to be installed.
*/ */
fun installExtension(extension: AnimeExtension.Available): Observable<InstallStep> { fun installExtension(extension: AnimeExtension.Available): Observable<InstallStep> {
return installer.downloadAndInstall(api.getAnimeApkUrl(extension), extension.pkgName, return installer.downloadAndInstall(
extension.name, MediaType.ANIME) api.getAnimeApkUrl(extension), extension.pkgName,
extension.name, MediaType.ANIME
)
} }
/** /**

View file

@ -28,18 +28,19 @@ class PackageInstallerInstaller(private val service: Service) : Installer(servic
PackageInstaller.STATUS_FAILURE PackageInstaller.STATUS_FAILURE
)) { )) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> { PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val userAction = intent.getParcelableExtraCompat<Intent>(Intent.EXTRA_INTENT)?.run { val userAction =
IntentSanitizer.Builder() intent.getParcelableExtraCompat<Intent>(Intent.EXTRA_INTENT)?.run {
.allowAction(this.action!!) IntentSanitizer.Builder()
.allowExtra(PackageInstaller.EXTRA_SESSION_ID) { id -> id == activeSession?.second } .allowAction(this.action!!)
.allowAnyComponent() .allowExtra(PackageInstaller.EXTRA_SESSION_ID) { id -> id == activeSession?.second }
.allowPackage { .allowAnyComponent()
// There is no way to check the actual installer name so allow all. .allowPackage {
true // There is no way to check the actual installer name so allow all.
} true
.build() }
.sanitizeByFiltering(this) .build()
} .sanitizeByFiltering(this)
}
if (userAction == null) { if (userAction == null) {
Logger.log("Fatal error for $intent") Logger.log("Fatal error for $intent")
continueQueue(InstallStep.Error) continueQueue(InstallStep.Error)

View file

@ -204,8 +204,10 @@ class MangaExtensionManager(
* @param extension The extension to be installed. * @param extension The extension to be installed.
*/ */
fun installExtension(extension: MangaExtension.Available): Observable<InstallStep> { fun installExtension(extension: MangaExtension.Available): Observable<InstallStep> {
return installer.downloadAndInstall(api.getMangaApkUrl(extension), extension.pkgName, return installer.downloadAndInstall(
extension.name, MediaType.MANGA) api.getMangaApkUrl(extension), extension.pkgName,
extension.name, MediaType.MANGA
)
} }
/** /**

View file

@ -41,9 +41,11 @@ class ExtensionInstallActivity : AppCompatActivity() {
ThemeManager(this).applyTheme() ThemeManager(this).applyTheme()
if (intent.hasExtra(ExtensionInstaller.EXTRA_EXTENSION_TYPE)) if (intent.hasExtra(ExtensionInstaller.EXTRA_EXTENSION_TYPE))
mediaType = intent.getSerializableExtraCompat<MediaType>(ExtensionInstaller.EXTRA_EXTENSION_TYPE) mediaType =
intent.getSerializableExtraCompat<MediaType>(ExtensionInstaller.EXTRA_EXTENSION_TYPE)
if (intent.hasExtra(ExtensionInstaller.EXTRA_ADDON_TYPE)) if (intent.hasExtra(ExtensionInstaller.EXTRA_ADDON_TYPE))
addonType = intent.getSerializableExtraCompat<AddonType>(ExtensionInstaller.EXTRA_ADDON_TYPE) addonType =
intent.getSerializableExtraCompat<AddonType>(ExtensionInstaller.EXTRA_ADDON_TYPE)
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE) val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)

View file

@ -37,14 +37,14 @@ internal class ExtensionInstallReceiver : BroadcastReceiver() {
ContextCompat.registerReceiver(context, this, filter, ContextCompat.RECEIVER_EXPORTED) ContextCompat.registerReceiver(context, this, filter, ContextCompat.RECEIVER_EXPORTED)
} }
fun setAnimeListener(listener: AnimeListener) : ExtensionInstallReceiver { fun setAnimeListener(listener: AnimeListener): ExtensionInstallReceiver {
this.type = MediaType.ANIME this.type = MediaType.ANIME
animeListener = listener animeListener = listener
this.animeListener this.animeListener
return this return this
} }
fun setMangaListener(listener: MangaListener) : ExtensionInstallReceiver { fun setMangaListener(listener: MangaListener): ExtensionInstallReceiver {
this.type = MediaType.MANGA this.type = MediaType.MANGA
mangaListener = listener mangaListener = listener
return this return this
@ -66,21 +66,33 @@ internal class ExtensionInstallReceiver : BroadcastReceiver() {
when (type) { when (type) {
MediaType.ANIME -> { MediaType.ANIME -> {
when (val result = getAnimeExtensionFromIntent(context, intent)) { when (val result = getAnimeExtensionFromIntent(context, intent)) {
is AnimeLoadResult.Success -> animeListener?.onExtensionInstalled(result.extension) is AnimeLoadResult.Success -> animeListener?.onExtensionInstalled(
result.extension
)
is AnimeLoadResult.Untrusted -> animeListener?.onExtensionUntrusted(
result.extension
)
is AnimeLoadResult.Untrusted -> animeListener?.onExtensionUntrusted(result.extension)
else -> {} else -> {}
} }
} }
MediaType.MANGA -> { MediaType.MANGA -> {
when (val result = getMangaExtensionFromIntent(context, intent)) { when (val result = getMangaExtensionFromIntent(context, intent)) {
is MangaLoadResult.Success -> mangaListener?.onExtensionInstalled(result.extension) is MangaLoadResult.Success -> mangaListener?.onExtensionInstalled(
result.extension
)
is MangaLoadResult.Untrusted -> mangaListener?.onExtensionUntrusted(
result.extension
)
is MangaLoadResult.Untrusted -> mangaListener?.onExtensionUntrusted(result.extension)
else -> {} else -> {}
} }
} }
else -> { }
else -> {}
} }
} }
} }
@ -90,17 +102,25 @@ internal class ExtensionInstallReceiver : BroadcastReceiver() {
when (type) { when (type) {
MediaType.ANIME -> { MediaType.ANIME -> {
when (val result = getAnimeExtensionFromIntent(context, intent)) { when (val result = getAnimeExtensionFromIntent(context, intent)) {
is AnimeLoadResult.Success -> animeListener?.onExtensionUpdated(result.extension) is AnimeLoadResult.Success -> animeListener?.onExtensionUpdated(
result.extension
)
else -> {} else -> {}
} }
} }
MediaType.MANGA -> { MediaType.MANGA -> {
when (val result = getMangaExtensionFromIntent(context, intent)) { when (val result = getMangaExtensionFromIntent(context, intent)) {
is MangaLoadResult.Success -> mangaListener?.onExtensionUpdated(result.extension) is MangaLoadResult.Success -> mangaListener?.onExtensionUpdated(
result.extension
)
else -> {} else -> {}
} }
} }
else -> { }
else -> {}
} }
} }
} }
@ -114,10 +134,12 @@ internal class ExtensionInstallReceiver : BroadcastReceiver() {
MediaType.ANIME -> { MediaType.ANIME -> {
animeListener?.onPackageUninstalled(pkgName) animeListener?.onPackageUninstalled(pkgName)
} }
MediaType.MANGA -> { MediaType.MANGA -> {
mangaListener?.onPackageUninstalled(pkgName) mangaListener?.onPackageUninstalled(pkgName)
} }
else -> { }
else -> {}
} }
} }
} }
@ -131,7 +153,10 @@ internal class ExtensionInstallReceiver : BroadcastReceiver() {
* @param intent The intent containing the package name of the extension. * @param intent The intent containing the package name of the extension.
*/ */
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
private suspend fun getAnimeExtensionFromIntent(context: Context, intent: Intent?): AnimeLoadResult { private suspend fun getAnimeExtensionFromIntent(
context: Context,
intent: Intent?
): AnimeLoadResult {
val pkgName = getPackageNameFromIntent(intent) val pkgName = getPackageNameFromIntent(intent)
if (pkgName == null) { if (pkgName == null) {
Logger.log("Package name not found") Logger.log("Package name not found")
@ -146,7 +171,10 @@ internal class ExtensionInstallReceiver : BroadcastReceiver() {
} }
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
private suspend fun getMangaExtensionFromIntent(context: Context, intent: Intent?): MangaLoadResult { private suspend fun getMangaExtensionFromIntent(
context: Context,
intent: Intent?
): MangaLoadResult {
val pkgName = getPackageNameFromIntent(intent) val pkgName = getPackageNameFromIntent(intent)
if (pkgName == null) { if (pkgName == null) {
Logger.log("Package name not found") Logger.log("Package name not found")

View file

@ -14,8 +14,8 @@ import ani.dantotsu.media.Type
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.extension.installer.PackageInstallerInstaller
import eu.kanade.tachiyomi.extension.installer.Installer import eu.kanade.tachiyomi.extension.installer.Installer
import eu.kanade.tachiyomi.extension.installer.PackageInstallerInstaller
import eu.kanade.tachiyomi.extension.util.ExtensionInstaller.Companion.EXTRA_DOWNLOAD_ID import eu.kanade.tachiyomi.extension.util.ExtensionInstaller.Companion.EXTRA_DOWNLOAD_ID
import eu.kanade.tachiyomi.extension.util.ExtensionInstaller.Companion.EXTRA_EXTENSION_TYPE import eu.kanade.tachiyomi.extension.util.ExtensionInstaller.Companion.EXTRA_EXTENSION_TYPE
import eu.kanade.tachiyomi.util.system.getSerializableExtraCompat import eu.kanade.tachiyomi.util.system.getSerializableExtraCompat
@ -48,7 +48,8 @@ class ExtensionInstallService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val uri = intent?.data val uri = intent?.data
val mediaType = intent?.getSerializableExtraCompat<MediaType>(EXTRA_EXTENSION_TYPE) val mediaType = intent?.getSerializableExtraCompat<MediaType>(EXTRA_EXTENSION_TYPE)
val addonType = intent?.getSerializableExtraCompat<AddonType>(ExtensionInstaller.EXTRA_ADDON_TYPE) val addonType =
intent?.getSerializableExtraCompat<AddonType>(ExtensionInstaller.EXTRA_ADDON_TYPE)
val id = intent?.getLongExtra(EXTRA_DOWNLOAD_ID, -1)?.takeIf { it != -1L } val id = intent?.getLongExtra(EXTRA_DOWNLOAD_ID, -1)?.takeIf { it != -1L }
val installerUsed = intent?.getSerializableExtraCompat<BasePreferences.ExtensionInstaller>( val installerUsed = intent?.getSerializableExtraCompat<BasePreferences.ExtensionInstaller>(
EXTRA_INSTALLER EXTRA_INSTALLER

View file

@ -14,14 +14,11 @@ import androidx.core.net.toUri
import ani.dantotsu.media.AddonType import ani.dantotsu.media.AddonType
import ani.dantotsu.media.MediaType import ani.dantotsu.media.MediaType
import ani.dantotsu.media.Type import ani.dantotsu.media.Type
import ani.dantotsu.parsers.novel.NovelExtension
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import eu.kanade.tachiyomi.extension.installer.Installer import eu.kanade.tachiyomi.extension.installer.Installer
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.storage.getUriCompat
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
@ -67,7 +64,12 @@ class ExtensionInstaller(private val context: Context) {
* @param url The url of the apk. * @param url The url of the apk.
* @param extension The extension to install. * @param extension The extension to install.
*/ */
fun <T : Type> downloadAndInstall(url: String, pkgName: String, name: String, type: T): Observable<InstallStep> = Observable.defer { fun <T : Type> downloadAndInstall(
url: String,
pkgName: String,
name: String,
type: T
): Observable<InstallStep> = Observable.defer {
val oldDownload = activeDownloads[pkgName] val oldDownload = activeDownloads[pkgName]
if (oldDownload != null) { if (oldDownload != null) {
deleteDownload(pkgName) deleteDownload(pkgName)
@ -264,11 +266,15 @@ class ExtensionInstaller(private val context: Context) {
val localUri = cursor.getString( val localUri = cursor.getString(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI), cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI),
).removePrefix(FILE_SCHEME) ).removePrefix(FILE_SCHEME)
val type = MediaType.fromText(cursor.getString( val type = MediaType.fromText(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_DESCRIPTION), cursor.getString(
)) ?: AddonType.fromText(cursor.getString( cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_DESCRIPTION),
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_DESCRIPTION), )
)) ?: return ) ?: AddonType.fromText(
cursor.getString(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_DESCRIPTION),
)
) ?: return
installApk(type, id, File(localUri).getUriCompat(context)) installApk(type, id, File(localUri).getUriCompat(context))
} }

View file

@ -62,7 +62,7 @@ internal object ExtensionLoader {
val PACKAGE_FLAGS = PackageManager.GET_CONFIGURATIONS or val PACKAGE_FLAGS = PackageManager.GET_CONFIGURATIONS or
PackageManager.GET_META_DATA or PackageManager.GET_META_DATA or
@Suppress ("DEPRECATION") PackageManager.GET_SIGNATURES or @Suppress("DEPRECATION") PackageManager.GET_SIGNATURES or
(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
PackageManager.GET_SIGNING_CERTIFICATES else 0) PackageManager.GET_SIGNING_CERTIFICATES else 0)
@ -145,7 +145,7 @@ internal object ExtensionLoader {
Logger.log(error) Logger.log(error)
return AnimeLoadResult.Error return AnimeLoadResult.Error
} }
if (!isPackageAnExtension(MediaType.ANIME,pkgInfo)) { if (!isPackageAnExtension(MediaType.ANIME, pkgInfo)) {
Logger.log("Tried to load a package that wasn't a extension ($pkgName)") Logger.log("Tried to load a package that wasn't a extension ($pkgName)")
return AnimeLoadResult.Error return AnimeLoadResult.Error
} }
@ -201,8 +201,9 @@ internal object ExtensionLoader {
// Validate lib version // Validate lib version
val libVersion = versionName.substringBeforeLast('.').toDoubleOrNull() val libVersion = versionName.substringBeforeLast('.').toDoubleOrNull()
if (libVersion == null || libVersion < ANIME_LIB_VERSION_MIN || libVersion > ANIME_LIB_VERSION_MAX) { if (libVersion == null || libVersion < ANIME_LIB_VERSION_MIN || libVersion > ANIME_LIB_VERSION_MAX) {
Logger.log("Lib version is $libVersion, while only versions " + Logger.log(
"$ANIME_LIB_VERSION_MIN to $ANIME_LIB_VERSION_MAX are allowed" "Lib version is $libVersion, while only versions " +
"$ANIME_LIB_VERSION_MIN to $ANIME_LIB_VERSION_MAX are allowed"
) )
return AnimeLoadResult.Error return AnimeLoadResult.Error
} }
@ -232,7 +233,8 @@ internal object ExtensionLoader {
} }
val hasReadme = appInfo.metaData.getInt("$ANIME_PACKAGE$XX_METADATA_HAS_README", 0) == 1 val hasReadme = appInfo.metaData.getInt("$ANIME_PACKAGE$XX_METADATA_HAS_README", 0) == 1
val hasChangelog = appInfo.metaData.getInt("$ANIME_PACKAGE$XX_METADATA_HAS_CHANGELOG", 0) == 1 val hasChangelog =
appInfo.metaData.getInt("$ANIME_PACKAGE$XX_METADATA_HAS_CHANGELOG", 0) == 1
val classLoader = PathClassLoader(appInfo.sourceDir, null, context.classLoader) val classLoader = PathClassLoader(appInfo.sourceDir, null, context.classLoader)
@ -248,7 +250,8 @@ internal object ExtensionLoader {
} }
.flatMap { .flatMap {
try { try {
when (val obj = Class.forName(it, false, classLoader).getDeclaredConstructor().newInstance()) { when (val obj = Class.forName(it, false, classLoader).getDeclaredConstructor()
.newInstance()) {
is AnimeSource -> listOf(obj) is AnimeSource -> listOf(obj)
is AnimeSourceFactory -> obj.createSources() is AnimeSourceFactory -> obj.createSources()
else -> throw Exception("Unknown source class type! ${obj.javaClass}") else -> throw Exception("Unknown source class type! ${obj.javaClass}")
@ -314,8 +317,9 @@ internal object ExtensionLoader {
// Validate lib version // Validate lib version
val libVersion = versionName.substringBeforeLast('.').toDoubleOrNull() val libVersion = versionName.substringBeforeLast('.').toDoubleOrNull()
if (libVersion == null || libVersion < MANGA_LIB_VERSION_MIN || libVersion > MANGA_LIB_VERSION_MAX) { if (libVersion == null || libVersion < MANGA_LIB_VERSION_MIN || libVersion > MANGA_LIB_VERSION_MAX) {
Logger.log("Lib version is $libVersion, while only versions " + Logger.log(
"$MANGA_LIB_VERSION_MIN to $MANGA_LIB_VERSION_MAX are allowed" "Lib version is $libVersion, while only versions " +
"$MANGA_LIB_VERSION_MIN to $MANGA_LIB_VERSION_MAX are allowed"
) )
return MangaLoadResult.Error return MangaLoadResult.Error
} }
@ -340,7 +344,8 @@ internal object ExtensionLoader {
} }
val hasReadme = appInfo.metaData.getInt("$MANGA_PACKAGE$XX_METADATA_HAS_README", 0) == 1 val hasReadme = appInfo.metaData.getInt("$MANGA_PACKAGE$XX_METADATA_HAS_README", 0) == 1
val hasChangelog = appInfo.metaData.getInt("$MANGA_PACKAGE$XX_METADATA_HAS_CHANGELOG", 0) == 1 val hasChangelog =
appInfo.metaData.getInt("$MANGA_PACKAGE$XX_METADATA_HAS_CHANGELOG", 0) == 1
val classLoader = PathClassLoader(appInfo.sourceDir, null, context.classLoader) val classLoader = PathClassLoader(appInfo.sourceDir, null, context.classLoader)
@ -401,11 +406,13 @@ internal object ExtensionLoader {
* @param pkgInfo The package info of the application. * @param pkgInfo The package info of the application.
*/ */
private fun isPackageAnExtension(type: MediaType, pkgInfo: PackageInfo): Boolean { private fun isPackageAnExtension(type: MediaType, pkgInfo: PackageInfo): Boolean {
return pkgInfo.reqFeatures.orEmpty().any { it.name == when (type) { return pkgInfo.reqFeatures.orEmpty().any {
MediaType.ANIME -> ANIME_PACKAGE it.name == when (type) {
MediaType.MANGA -> MANGA_PACKAGE MediaType.ANIME -> ANIME_PACKAGE
else -> "" MediaType.MANGA -> MANGA_PACKAGE
} } else -> ""
}
}
} }
/** /**
@ -417,7 +424,7 @@ internal object ExtensionLoader {
val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
pkgInfo.signingInfo.signingCertificateHistory pkgInfo.signingInfo.signingCertificateHistory
else else
@Suppress ("DEPRECATION") pkgInfo.signatures @Suppress("DEPRECATION") pkgInfo.signatures
return if (signatures != null && signatures.isNotEmpty()) { return if (signatures != null && signatures.isNotEmpty()) {
Hash.sha256(signatures.first().toByteArray()) Hash.sha256(signatures.first().toByteArray())
} else { } else {

View file

@ -11,8 +11,8 @@ import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor
import eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor import eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor
import okhttp3.Cache import okhttp3.Cache
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.brotli.BrotliInterceptor import okhttp3.brotli.BrotliInterceptor
import okhttp3.logging.HttpLoggingInterceptor
import java.io.File import java.io.File
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit

View file

@ -41,10 +41,10 @@ class LocalAnimeSource(
override suspend fun getLatestUpdates(page: Int) = getSearchAnime(page, "", LATEST_FILTERS) override suspend fun getLatestUpdates(page: Int) = getSearchAnime(page, "", LATEST_FILTERS)
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularAnime")) @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularAnime"))
override fun fetchPopularAnime(page: Int) = fetchSearchAnime(page, "", POPULAR_FILTERS) override fun fetchPopularAnime(page: Int) = getSearchAnime
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates")) @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates"))
override fun fetchLatestUpdates(page: Int) = fetchSearchAnime(page, "", LATEST_FILTERS) override fun fetchLatestUpdates(page: Int) = getSearchAnime
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getSearchAnime")) @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getSearchAnime"))
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> { override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> {

View file

@ -1,9 +1,9 @@
<scale xmlns:android="http://schemas.android.com/apk/res/android" <scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXScale="0.3" android:fromXScale="0.3"
android:toXScale="1.0"
android:fromYScale="0.3" android:fromYScale="0.3"
android:toYScale="1.0" android:interpolator="@android:anim/overshoot_interpolator"
android:pivotX="50%" android:pivotX="50%"
android:pivotY="50%" android:pivotY="50%"
android:duration="1000" android:toXScale="1.0"
android:interpolator="@android:anim/overshoot_interpolator"/> android:toYScale="1.0" />

View file

@ -1,25 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item> <item>
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<corners <corners
android:topLeftRadius="28dp"
android:topRightRadius="28dp"
android:bottomLeftRadius="0dp" android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"/> android:bottomRightRadius="0dp"
android:topLeftRadius="28dp"
android:topRightRadius="28dp" />
</shape> </shape>
</item> </item>
<item <item
android:bottom="-50dp"
android:left="-3dp" android:left="-3dp"
android:right="-3dp" android:right="-3dp">
android:bottom="-50dp">
<shape> <shape>
<stroke android:width="2dp" android:color="@color/bg_white" /> <stroke
android:width="2dp"
android:color="@color/bg_white" />
<corners <corners
android:topLeftRadius="28dp"
android:topRightRadius="28dp"
android:bottomLeftRadius="0dp" android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"/> android:bottomRightRadius="0dp"
android:topLeftRadius="28dp"
android:topRightRadius="28dp" />
</shape> </shape>
</item> </item>
</layer-list> </layer-list>

View file

@ -1,10 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="960" android:viewportWidth="960"
android:viewportHeight="960" android:viewportHeight="960">
android:tint="?attr/colorControlNormal"> <path
<path android:fillColor="@android:color/white"
android:fillColor="@android:color/white" android:pathData="M480,600Q530,600 565,565Q600,530 600,480Q600,430 565,395Q530,360 480,360Q430,360 395,395Q360,430 360,480Q360,530 395,565Q430,600 480,600ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" />
android:pathData="M480,600Q530,600 565,565Q600,530 600,480Q600,430 565,395Q530,360 480,360Q430,360 395,395Q360,430 360,480Q360,530 395,565Q430,600 480,600ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector> </vector>

View file

@ -1,11 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportWidth="960" android:autoMirrored="true"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal" android:tint="?attr/colorControlNormal"
android:autoMirrored="true"> android:viewportWidth="960"
<path android:viewportHeight="960">
android:fillColor="@android:color/white" <path
android:pathData="M480,560Q447,560 423.5,536.5Q400,513 400,480Q400,447 423.5,423.5Q447,400 480,400Q513,400 536.5,423.5Q560,447 560,480Q560,513 536.5,536.5Q513,560 480,560ZM480,840Q341,840 239,748.5Q137,657 122,520L204,520Q218,624 296.5,692Q375,760 480,760Q597,760 678.5,678.5Q760,597 760,480Q760,363 678.5,281.5Q597,200 480,200Q411,200 351,232Q291,264 250,320L360,320L360,400L120,400L120,160L200,160L200,254Q251,190 324.5,155Q398,120 480,120Q555,120 620.5,148.5Q686,177 734.5,225.5Q783,274 811.5,339.5Q840,405 840,480Q840,555 811.5,620.5Q783,686 734.5,734.5Q686,783 620.5,811.5Q555,840 480,840Z"/> android:fillColor="@android:color/white"
android:pathData="M480,560Q447,560 423.5,536.5Q400,513 400,480Q400,447 423.5,423.5Q447,400 480,400Q513,400 536.5,423.5Q560,447 560,480Q560,513 536.5,536.5Q513,560 480,560ZM480,840Q341,840 239,748.5Q137,657 122,520L204,520Q218,624 296.5,692Q375,760 480,760Q597,760 678.5,678.5Q760,597 760,480Q760,363 678.5,281.5Q597,200 480,200Q411,200 351,232Q291,264 250,320L360,320L360,400L120,400L120,160L200,160L200,254Q251,190 324.5,155Q398,120 480,120Q555,120 620.5,148.5Q686,177 734.5,225.5Q783,274 811.5,339.5Q840,405 840,480Q840,555 811.5,620.5Q783,686 734.5,734.5Q686,783 620.5,811.5Q555,840 480,840Z" />
</vector> </vector>

View file

@ -1,10 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="960" android:viewportWidth="960"
android:viewportHeight="960" android:viewportHeight="960">
android:tint="?attr/colorControlNormal"> <path
<path android:fillColor="@android:color/white"
android:fillColor="@android:color/white" android:pathData="M120,580Q112,580 106,574Q100,568 100,560Q100,552 106,546Q112,540 120,540Q128,540 134,546Q140,552 140,560Q140,568 134,574Q128,580 120,580ZM120,420Q112,420 106,414Q100,408 100,400Q100,392 106,386Q112,380 120,380Q128,380 134,386Q140,392 140,400Q140,408 134,414Q128,420 120,420ZM240,760Q223,760 211.5,748.5Q200,737 200,720Q200,703 211.5,691.5Q223,680 240,680Q257,680 268.5,691.5Q280,703 280,720Q280,737 268.5,748.5Q257,760 240,760ZM240,600Q223,600 211.5,588.5Q200,577 200,560Q200,543 211.5,531.5Q223,520 240,520Q257,520 268.5,531.5Q280,543 280,560Q280,577 268.5,588.5Q257,600 240,600ZM240,440Q223,440 211.5,428.5Q200,417 200,400Q200,383 211.5,371.5Q223,360 240,360Q257,360 268.5,371.5Q280,383 280,400Q280,417 268.5,428.5Q257,440 240,440ZM240,280Q223,280 211.5,268.5Q200,257 200,240Q200,223 211.5,211.5Q223,200 240,200Q257,200 268.5,211.5Q280,223 280,240Q280,257 268.5,268.5Q257,280 240,280ZM400,620Q375,620 357.5,602.5Q340,585 340,560Q340,535 357.5,517.5Q375,500 400,500Q425,500 442.5,517.5Q460,535 460,560Q460,585 442.5,602.5Q425,620 400,620ZM400,460Q375,460 357.5,442.5Q340,425 340,400Q340,375 357.5,357.5Q375,340 400,340Q425,340 442.5,357.5Q460,375 460,400Q460,425 442.5,442.5Q425,460 400,460ZM400,760Q383,760 371.5,748.5Q360,737 360,720Q360,703 371.5,691.5Q383,680 400,680Q417,680 428.5,691.5Q440,703 440,720Q440,737 428.5,748.5Q417,760 400,760ZM400,280Q383,280 371.5,268.5Q360,257 360,240Q360,223 371.5,211.5Q383,200 400,200Q417,200 428.5,211.5Q440,223 440,240Q440,257 428.5,268.5Q417,280 400,280ZM400,860Q392,860 386,854Q380,848 380,840Q380,832 386,826Q392,820 400,820Q408,820 414,826Q420,832 420,840Q420,848 414,854Q408,860 400,860ZM400,140Q392,140 386,134Q380,128 380,120Q380,112 386,106Q392,100 400,100Q408,100 414,106Q420,112 420,120Q420,128 414,134Q408,140 400,140ZM560,620Q535,620 517.5,602.5Q500,585 500,560Q500,535 517.5,517.5Q535,500 560,500Q585,500 602.5,517.5Q620,535 620,560Q620,585 602.5,602.5Q585,620 560,620ZM560,460Q535,460 517.5,442.5Q500,425 500,400Q500,375 517.5,357.5Q535,340 560,340Q585,340 602.5,357.5Q620,375 620,400Q620,425 602.5,442.5Q585,460 560,460ZM560,760Q543,760 531.5,748.5Q520,737 520,720Q520,703 531.5,691.5Q543,680 560,680Q577,680 588.5,691.5Q600,703 600,720Q600,737 588.5,748.5Q577,760 560,760ZM560,280Q543,280 531.5,268.5Q520,257 520,240Q520,223 531.5,211.5Q543,200 560,200Q577,200 588.5,211.5Q600,223 600,240Q600,257 588.5,268.5Q577,280 560,280ZM560,860Q552,860 546,854Q540,848 540,840Q540,832 546,826Q552,820 560,820Q568,820 574,826Q580,832 580,840Q580,848 574,854Q568,860 560,860ZM560,140Q552,140 546,134Q540,128 540,120Q540,112 546,106Q552,100 560,100Q568,100 574,106Q580,112 580,120Q580,128 574,134Q568,140 560,140ZM720,760Q703,760 691.5,748.5Q680,737 680,720Q680,703 691.5,691.5Q703,680 720,680Q737,680 748.5,691.5Q760,703 760,720Q760,737 748.5,748.5Q737,760 720,760ZM720,600Q703,600 691.5,588.5Q680,577 680,560Q680,543 691.5,531.5Q703,520 720,520Q737,520 748.5,531.5Q760,543 760,560Q760,577 748.5,588.5Q737,600 720,600ZM720,440Q703,440 691.5,428.5Q680,417 680,400Q680,383 691.5,371.5Q703,360 720,360Q737,360 748.5,371.5Q760,383 760,400Q760,417 748.5,428.5Q737,440 720,440ZM720,280Q703,280 691.5,268.5Q680,257 680,240Q680,223 691.5,211.5Q703,200 720,200Q737,200 748.5,211.5Q760,223 760,240Q760,257 748.5,268.5Q737,280 720,280ZM840,580Q832,580 826,574Q820,568 820,560Q820,552 826,546Q832,540 840,540Q848,540 854,546Q860,552 860,560Q860,568 854,574Q848,580 840,580ZM840,420Q832,420 826,414Q820,408 820,400Q820,392 826,386Q832,380 840,380Q848,380 854,386Q860,392 860,400Q860,408 854,414Q848,420 840,420Z" />
android:pathData="M120,580Q112,580 106,574Q100,568 100,560Q100,552 106,546Q112,540 120,540Q128,540 134,546Q140,552 140,560Q140,568 134,574Q128,580 120,580ZM120,420Q112,420 106,414Q100,408 100,400Q100,392 106,386Q112,380 120,380Q128,380 134,386Q140,392 140,400Q140,408 134,414Q128,420 120,420ZM240,760Q223,760 211.5,748.5Q200,737 200,720Q200,703 211.5,691.5Q223,680 240,680Q257,680 268.5,691.5Q280,703 280,720Q280,737 268.5,748.5Q257,760 240,760ZM240,600Q223,600 211.5,588.5Q200,577 200,560Q200,543 211.5,531.5Q223,520 240,520Q257,520 268.5,531.5Q280,543 280,560Q280,577 268.5,588.5Q257,600 240,600ZM240,440Q223,440 211.5,428.5Q200,417 200,400Q200,383 211.5,371.5Q223,360 240,360Q257,360 268.5,371.5Q280,383 280,400Q280,417 268.5,428.5Q257,440 240,440ZM240,280Q223,280 211.5,268.5Q200,257 200,240Q200,223 211.5,211.5Q223,200 240,200Q257,200 268.5,211.5Q280,223 280,240Q280,257 268.5,268.5Q257,280 240,280ZM400,620Q375,620 357.5,602.5Q340,585 340,560Q340,535 357.5,517.5Q375,500 400,500Q425,500 442.5,517.5Q460,535 460,560Q460,585 442.5,602.5Q425,620 400,620ZM400,460Q375,460 357.5,442.5Q340,425 340,400Q340,375 357.5,357.5Q375,340 400,340Q425,340 442.5,357.5Q460,375 460,400Q460,425 442.5,442.5Q425,460 400,460ZM400,760Q383,760 371.5,748.5Q360,737 360,720Q360,703 371.5,691.5Q383,680 400,680Q417,680 428.5,691.5Q440,703 440,720Q440,737 428.5,748.5Q417,760 400,760ZM400,280Q383,280 371.5,268.5Q360,257 360,240Q360,223 371.5,211.5Q383,200 400,200Q417,200 428.5,211.5Q440,223 440,240Q440,257 428.5,268.5Q417,280 400,280ZM400,860Q392,860 386,854Q380,848 380,840Q380,832 386,826Q392,820 400,820Q408,820 414,826Q420,832 420,840Q420,848 414,854Q408,860 400,860ZM400,140Q392,140 386,134Q380,128 380,120Q380,112 386,106Q392,100 400,100Q408,100 414,106Q420,112 420,120Q420,128 414,134Q408,140 400,140ZM560,620Q535,620 517.5,602.5Q500,585 500,560Q500,535 517.5,517.5Q535,500 560,500Q585,500 602.5,517.5Q620,535 620,560Q620,585 602.5,602.5Q585,620 560,620ZM560,460Q535,460 517.5,442.5Q500,425 500,400Q500,375 517.5,357.5Q535,340 560,340Q585,340 602.5,357.5Q620,375 620,400Q620,425 602.5,442.5Q585,460 560,460ZM560,760Q543,760 531.5,748.5Q520,737 520,720Q520,703 531.5,691.5Q543,680 560,680Q577,680 588.5,691.5Q600,703 600,720Q600,737 588.5,748.5Q577,760 560,760ZM560,280Q543,280 531.5,268.5Q520,257 520,240Q520,223 531.5,211.5Q543,200 560,200Q577,200 588.5,211.5Q600,223 600,240Q600,257 588.5,268.5Q577,280 560,280ZM560,860Q552,860 546,854Q540,848 540,840Q540,832 546,826Q552,820 560,820Q568,820 574,826Q580,832 580,840Q580,848 574,854Q568,860 560,860ZM560,140Q552,140 546,134Q540,128 540,120Q540,112 546,106Q552,100 560,100Q568,100 574,106Q580,112 580,120Q580,128 574,134Q568,140 560,140ZM720,760Q703,760 691.5,748.5Q680,737 680,720Q680,703 691.5,691.5Q703,680 720,680Q737,680 748.5,691.5Q760,703 760,720Q760,737 748.5,748.5Q737,760 720,760ZM720,600Q703,600 691.5,588.5Q680,577 680,560Q680,543 691.5,531.5Q703,520 720,520Q737,520 748.5,531.5Q760,543 760,560Q760,577 748.5,588.5Q737,600 720,600ZM720,440Q703,440 691.5,428.5Q680,417 680,400Q680,383 691.5,371.5Q703,360 720,360Q737,360 748.5,371.5Q760,383 760,400Q760,417 748.5,428.5Q737,440 720,440ZM720,280Q703,280 691.5,268.5Q680,257 680,240Q680,223 691.5,211.5Q703,200 720,200Q737,200 748.5,211.5Q760,223 760,240Q760,257 748.5,268.5Q737,280 720,280ZM840,580Q832,580 826,574Q820,568 820,560Q820,552 826,546Q832,540 840,540Q848,540 854,546Q860,552 860,560Q860,568 854,574Q848,580 840,580ZM840,420Q832,420 826,414Q820,408 820,400Q820,392 826,386Q832,380 840,380Q848,380 854,386Q860,392 860,400Q860,408 854,414Q848,420 840,420Z"/>
</vector> </vector>

View file

@ -2,8 +2,8 @@
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="?attr/colorSurface" /> <solid android:color="?attr/colorSurface" />
<corners <corners
android:topLeftRadius="16dp"
android:topRightRadius="16dp"
android:bottomLeftRadius="0dp" android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"/> android:bottomRightRadius="0dp"
android:topLeftRadius="16dp"
android:topRightRadius="16dp" />
</shape> </shape>

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