Merge branch 'dev' into Dunno

This commit is contained in:
Sadwhy
2024-01-05 11:55:26 +06:00
committed by GitHub
96 changed files with 4224 additions and 2391 deletions

View File

@@ -3,7 +3,10 @@ name: Build APK and Notify Discord
on:
push:
branches:
- Dunno
- dev
paths-ignore:
- '**/README.md'
jobs:
build:
@@ -50,7 +53,7 @@ jobs:
shell: bash
run: |
contentbody=$( jq -Rsa . <<< "${{ github.event.head_commit.message }}" )
curl -F "payload_json={\"content\":\" everyone **${{ env.VERSION }}**\n\n${contentbody:1:-1}\"}" -F "dantotsu_debug=@app/build/outputs/apk/debug/app-debug.apk" ${{ secrets.DISCORD_WEBHOOK }}
curl -F "payload_json={\"content\":\" Debug-Build **${{ env.VERSION }}**\n\n${contentbody:1:-1}\"}" -F "dantotsu_debug=@app/build/outputs/apk/debug/app-debug.apk" ${{ secrets.DISCORD_WEBHOOK }}
- name: Delete Old Pre-Releases
id: delete-pre-releases

View File

@@ -21,7 +21,7 @@ android {
minSdk 23
targetSdk 34
versionCode ((System.currentTimeMillis() / 60000).toInteger())
versionName "2.0.0-beta00-i"
versionName "2.0.0-beta00-iv2"
signingConfig signingConfigs.debug
}
@@ -90,6 +90,9 @@ dependencies {
implementation "androidx.media3:media3-exoplayer-dash:$exo_version"
implementation "androidx.media3:media3-datasource-okhttp:$exo_version"
implementation "androidx.media3:media3-session:$exo_version"
//media3 casting
implementation "androidx.media3:media3-cast:$exo_version"
implementation "androidx.mediarouter:mediarouter:1.6.0"
// UI
implementation 'com.google.android.material:material:1.10.0'

View File

@@ -10,6 +10,8 @@
android:required="false" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"
tools:ignore="LeanbackUsesWifi" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
@@ -45,7 +47,7 @@
<application
android:name=".App"
android:allowBackup="true"
android:banner="@drawable/ic_banner_foreground"
android:banner="@mipmap/ic_banner_foreground"
android:icon="${icon_placeholder}"
android:label="@string/app_name"
android:largeHeap="true"
@@ -271,9 +273,10 @@
android:permission="android.permission.BIND_REMOTEVIEWS"
android:exported="true" />
<service
android:name=".download.video.MyDownloadService"
android:exported="false">
<intent-filter>
android:name=".download.video.ExoplayerDownloadService"
android:exported="false"
android:foregroundServiceType="dataSync">
<intent-filter>
<action android:name="androidx.media3.exoplayer.downloadService.action.RESTART" />
<category android:name="android.intent.category.DEFAULT" />
@@ -295,10 +298,16 @@
android:name=".download.novel.NovelDownloaderService"
android:exported="false"
android:foregroundServiceType="dataSync" />
<service android:name=".download.anime.AnimeDownloaderService"
android:exported="false"
android:foregroundServiceType="dataSync" />
<service
android:name=".connections.discord.DiscordService"
android:exported="false"
android:foregroundServiceType="dataSync" />
<meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="androidx.media3.cast.DefaultCastOptionsProvider"/>
</application>
</manifest>

View File

@@ -53,6 +53,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.internal.ViewUtils
import com.google.android.material.snackbar.Snackbar
import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.coroutines.*
import nl.joery.animatedbottombar.AnimatedBottomBar
import java.io.*
@@ -673,7 +674,7 @@ fun copyToClipboard(string: String, toast: Boolean = true) {
@SuppressLint("SetTextI18n")
fun countDown(media: Media, view: ViewGroup) {
if (media.anime?.nextAiringEpisode != null && media.anime.nextAiringEpisodeTime != null && (media.anime.nextAiringEpisodeTime!! - System.currentTimeMillis() / 1000) <= 86400 * 7.toLong()) {
if (media.anime?.nextAiringEpisode != null && media.anime.nextAiringEpisodeTime != null && (media.anime.nextAiringEpisodeTime!! - System.currentTimeMillis() / 1000) <= 86400 * 28.toLong()) {
val v = ItemCountDownBinding.inflate(LayoutInflater.from(view.context), view, false)
view.addView(v.root, 0)
v.mediaCountdownText.text =
@@ -783,35 +784,40 @@ fun toast(string: String?) {
}
fun snackString(s: String?, activity: Activity? = null, clipboard: String? = null) {
if (s != null) {
(activity ?: currActivity())?.apply {
runOnUiThread {
val snackBar = Snackbar.make(
window.decorView.findViewById(android.R.id.content),
s,
Snackbar.LENGTH_SHORT
)
snackBar.view.apply {
updateLayoutParams<FrameLayout.LayoutParams> {
gravity = (Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM)
width = WRAP_CONTENT
}
translationY = -(navBarHeight.dp + 32f)
translationZ = 32f
updatePadding(16f.px, right = 16f.px)
setOnClickListener {
snackBar.dismiss()
}
setOnLongClickListener {
copyToClipboard(clipboard ?: s, false)
toast(getString(R.string.copied_to_clipboard))
true
try { //I have no idea why this sometimes crashes for some people...
if (s != null) {
(activity ?: currActivity())?.apply {
runOnUiThread {
val snackBar = Snackbar.make(
window.decorView.findViewById(android.R.id.content),
s,
Snackbar.LENGTH_SHORT
)
snackBar.view.apply {
updateLayoutParams<FrameLayout.LayoutParams> {
gravity = (Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM)
width = WRAP_CONTENT
}
translationY = -(navBarHeight.dp + 32f)
translationZ = 32f
updatePadding(16f.px, right = 16f.px)
setOnClickListener {
snackBar.dismiss()
}
setOnLongClickListener {
copyToClipboard(clipboard ?: s, false)
toast(getString(R.string.copied_to_clipboard))
true
}
}
snackBar.show()
}
snackBar.show()
}
logger(s)
}
logger(s)
} catch (e: Exception) {
logger(e.stackTraceToString())
FirebaseCrashlytics.getInstance().recordException(e)
}
}

View File

@@ -11,12 +11,14 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnticipateInterpolator
import android.widget.TextView
import androidx.activity.addCallback
import androidx.activity.viewModels
import androidx.annotation.OptIn
import androidx.appcompat.app.AppCompatActivity
import androidx.core.animation.doOnEnd
import androidx.core.content.ContextCompat
@@ -26,11 +28,14 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.offline.Download
import androidx.viewpager2.adapter.FragmentStateAdapter
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistHomeViewModel
import ani.dantotsu.databinding.ActivityMainBinding
import ani.dantotsu.databinding.SplashScreenBinding
import ani.dantotsu.download.video.Helper
import ani.dantotsu.home.AnimeFragment
import ani.dantotsu.home.HomeFragment
import ani.dantotsu.home.LoginFragment
@@ -45,6 +50,7 @@ import ani.dantotsu.themes.ThemeManager
import io.noties.markwon.Markwon
import io.noties.markwon.SoftBreakAddsNewLinePlugin
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -60,7 +66,7 @@ class MainActivity : AppCompatActivity() {
private var uiSettings = UserInterfaceSettings()
override fun onCreate(savedInstanceState: Bundle?) {
@OptIn(UnstableApi::class) override fun onCreate(savedInstanceState: Bundle?) {
ThemeManager(this).applyTheme()
LangSet.setLocale(this)
super.onCreate(savedInstanceState)
@@ -73,16 +79,10 @@ class MainActivity : AppCompatActivity() {
val backgroundDrawable = _bottomBar.background as GradientDrawable
val currentColor = backgroundDrawable.color?.defaultColor ?: 0
val semiTransparentColor = (currentColor and 0x00FFFFFF) or 0xE8000000.toInt()
val semiTransparentColor = (currentColor and 0x00FFFFFF) or 0xF0000000.toInt()
backgroundDrawable.setColor(semiTransparentColor)
_bottomBar.background = backgroundDrawable
}
val colorOverflow = this.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
.getBoolean("colorOverflow", false)
if (!colorOverflow) {
_bottomBar.background = ContextCompat.getDrawable(this, R.drawable.bottom_nav_gray)
}
var doubleBackToExitPressedOnce = false
@@ -242,6 +242,25 @@ class MainActivity : AppCompatActivity() {
}
}
GlobalScope.launch(Dispatchers.IO) {
val index = Helper.downloadManager(this@MainActivity).downloadIndex
val downloadCursor = index.getDownloads()
while (downloadCursor.moveToNext()) {
val download = downloadCursor.download
Log.e("Downloader", download.request.uri.toString())
Log.e("Downloader", download.request.id.toString())
Log.e("Downloader", download.request.mimeType.toString())
Log.e("Downloader", download.request.data.size.toString())
Log.e("Downloader", download.bytesDownloaded.toString())
Log.e("Downloader", download.state.toString())
Log.e("Downloader", download.failureReason.toString())
if (download.state == Download.STATE_FAILED) { //simple cleanup
Helper.downloadManager(this@MainActivity).removeDownload(download.request.id)
}
}
}
}

View File

@@ -3,7 +3,11 @@ package ani.dantotsu.aniyomi.anime.custom
import android.app.Application
import android.content.Context
import androidx.annotation.OptIn
import androidx.core.content.ContextCompat
import androidx.media3.common.util.UnstableApi
import androidx.media3.database.StandaloneDatabaseProvider
import androidx.media3.datasource.cache.SimpleCache
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.media.manga.MangaCache
import ani.dantotsu.parsers.novel.NovelExtensionManager
@@ -27,7 +31,7 @@ import uy.kohesive.injekt.api.addSingletonFactory
import uy.kohesive.injekt.api.get
class AppModule(val app: Application) : InjektModule {
override fun InjektRegistrar.registerInjectables() {
@OptIn(UnstableApi::class) override fun InjektRegistrar.registerInjectables() {
addSingleton(app)
addSingletonFactory { DownloadsManager(app) }
@@ -51,6 +55,8 @@ class AppModule(val app: Application) : InjektModule {
}
}
addSingletonFactory { StandaloneDatabaseProvider(app) }
addSingletonFactory { MangaCache() }
ContextCompat.getMainExecutor(app).execute {

View File

@@ -1,6 +1,7 @@
package ani.dantotsu.connections.anilist
import android.app.Activity
import android.content.Context
import ani.dantotsu.R
import ani.dantotsu.checkGenreTime
import ani.dantotsu.checkId
@@ -410,8 +411,9 @@ class AnilistQueries {
sorted["Favourites"]?.sortWith(compareBy { it.userFavOrder })
sorted["All"] = all
val sort = sortOrder ?: options?.rowOrder
val listsort = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getString("sort_order", "score")
val sort = listsort ?: sortOrder ?: options?.rowOrder
for (i in sorted.keys) {
when (sort) {
"score" -> sorted[i]?.sortWith { b, a ->

View File

@@ -15,43 +15,43 @@ class DownloadsManager(private val context: Context) {
private val gson = Gson()
private val downloadsList = loadDownloads().toMutableList()
val mangaDownloads: List<Download>
get() = downloadsList.filter { it.type == Download.Type.MANGA }
val animeDownloads: List<Download>
get() = downloadsList.filter { it.type == Download.Type.ANIME }
val novelDownloads: List<Download>
get() = downloadsList.filter { it.type == Download.Type.NOVEL }
val mangaDownloadedTypes: List<DownloadedType>
get() = downloadsList.filter { it.type == DownloadedType.Type.MANGA }
val animeDownloadedTypes: List<DownloadedType>
get() = downloadsList.filter { it.type == DownloadedType.Type.ANIME }
val novelDownloadedTypes: List<DownloadedType>
get() = downloadsList.filter { it.type == DownloadedType.Type.NOVEL }
private fun saveDownloads() {
val jsonString = gson.toJson(downloadsList)
prefs.edit().putString("downloads_key", jsonString).apply()
}
private fun loadDownloads(): List<Download> {
private fun loadDownloads(): List<DownloadedType> {
val jsonString = prefs.getString("downloads_key", null)
return if (jsonString != null) {
val type = object : TypeToken<List<Download>>() {}.type
val type = object : TypeToken<List<DownloadedType>>() {}.type
gson.fromJson(jsonString, type)
} else {
emptyList()
}
}
fun addDownload(download: Download) {
downloadsList.add(download)
fun addDownload(downloadedType: DownloadedType) {
downloadsList.add(downloadedType)
saveDownloads()
}
fun removeDownload(download: Download) {
downloadsList.remove(download)
removeDirectory(download)
fun removeDownload(downloadedType: DownloadedType) {
downloadsList.remove(downloadedType)
removeDirectory(downloadedType)
saveDownloads()
}
fun removeMedia(title: String, type: Download.Type) {
val subDirectory = if (type == Download.Type.MANGA) {
fun removeMedia(title: String, type: DownloadedType.Type) {
val subDirectory = if (type == DownloadedType.Type.MANGA) {
"Manga"
} else if (type == Download.Type.ANIME) {
} else if (type == DownloadedType.Type.ANIME) {
"Anime"
} else {
"Novel"
@@ -76,16 +76,16 @@ class DownloadsManager(private val context: Context) {
}
private fun cleanDownloads() {
cleanDownload(Download.Type.MANGA)
cleanDownload(Download.Type.ANIME)
cleanDownload(Download.Type.NOVEL)
cleanDownload(DownloadedType.Type.MANGA)
cleanDownload(DownloadedType.Type.ANIME)
cleanDownload(DownloadedType.Type.NOVEL)
}
private fun cleanDownload(type: Download.Type) {
private fun cleanDownload(type: DownloadedType.Type) {
// remove all folders that are not in the downloads list
val subDirectory = if (type == Download.Type.MANGA) {
val subDirectory = if (type == DownloadedType.Type.MANGA) {
"Manga"
} else if (type == Download.Type.ANIME) {
} else if (type == DownloadedType.Type.ANIME) {
"Anime"
} else {
"Novel"
@@ -94,18 +94,18 @@ class DownloadsManager(private val context: Context) {
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/$subDirectory"
)
val downloadsSubList = if (type == Download.Type.MANGA) {
mangaDownloads
} else if (type == Download.Type.ANIME) {
animeDownloads
val downloadsSubLists = if (type == DownloadedType.Type.MANGA) {
mangaDownloadedTypes
} else if (type == DownloadedType.Type.ANIME) {
animeDownloadedTypes
} else {
novelDownloads
novelDownloadedTypes
}
if (directory.exists()) {
val files = directory.listFiles()
if (files != null) {
for (file in files) {
if (!downloadsSubList.any { it.title == file.name }) {
if (!downloadsSubLists.any { it.title == file.name }) {
val deleted = file.deleteRecursively()
}
}
@@ -122,7 +122,7 @@ class DownloadsManager(private val context: Context) {
}
}
fun saveDownloadsListToJSONFileInDownloadsFolder(downloadsList: List<Download>) //for debugging
fun saveDownloadsListToJSONFileInDownloadsFolder(downloadsList: List<DownloadedType>) //for debugging
{
val jsonString = gson.toJson(downloadsList)
val file = File(
@@ -138,25 +138,33 @@ class DownloadsManager(private val context: Context) {
file.writeText(jsonString)
}
fun queryDownload(download: Download): Boolean {
return downloadsList.contains(download)
fun queryDownload(downloadedType: DownloadedType): Boolean {
return downloadsList.contains(downloadedType)
}
private fun removeDirectory(download: Download) {
val directory = if (download.type == Download.Type.MANGA) {
fun queryDownload(title: String, chapter: String, type: DownloadedType.Type? = null): Boolean {
return if (type == null) {
downloadsList.any { it.title == title && it.chapter == chapter }
} else {
downloadsList.any { it.title == title && it.chapter == chapter && it.type == type }
}
}
private fun removeDirectory(downloadedType: DownloadedType) {
val directory = if (downloadedType.type == DownloadedType.Type.MANGA) {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Manga/${download.title}/${download.chapter}"
"Dantotsu/Manga/${downloadedType.title}/${downloadedType.chapter}"
)
} else if (download.type == Download.Type.ANIME) {
} else if (downloadedType.type == DownloadedType.Type.ANIME) {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Anime/${download.title}/${download.chapter}"
"Dantotsu/Anime/${downloadedType.title}/${downloadedType.chapter}"
)
} else {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Novel/${download.title}/${download.chapter}"
"Dantotsu/Novel/${downloadedType.title}/${downloadedType.chapter}"
)
}
@@ -173,26 +181,26 @@ class DownloadsManager(private val context: Context) {
}
}
fun exportDownloads(download: Download) { //copies to the downloads folder available to the user
val directory = if (download.type == Download.Type.MANGA) {
fun exportDownloads(downloadedType: DownloadedType) { //copies to the downloads folder available to the user
val directory = if (downloadedType.type == DownloadedType.Type.MANGA) {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Manga/${download.title}/${download.chapter}"
"Dantotsu/Manga/${downloadedType.title}/${downloadedType.chapter}"
)
} else if (download.type == Download.Type.ANIME) {
} else if (downloadedType.type == DownloadedType.Type.ANIME) {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Anime/${download.title}/${download.chapter}"
"Dantotsu/Anime/${downloadedType.title}/${downloadedType.chapter}"
)
} else {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/Novel/${download.title}/${download.chapter}"
"Dantotsu/Novel/${downloadedType.title}/${downloadedType.chapter}"
)
}
val destination = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/${download.title}/${download.chapter}"
"Dantotsu/${downloadedType.title}/${downloadedType.chapter}"
)
if (directory.exists()) {
val copied = directory.copyRecursively(destination, true)
@@ -206,10 +214,10 @@ class DownloadsManager(private val context: Context) {
}
}
fun purgeDownloads(type: Download.Type) {
val directory = if (type == Download.Type.MANGA) {
fun purgeDownloads(type: DownloadedType.Type) {
val directory = if (type == DownloadedType.Type.MANGA) {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Manga")
} else if (type == Download.Type.ANIME) {
} else if (type == DownloadedType.Type.ANIME) {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Anime")
} else {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Novel")
@@ -237,7 +245,7 @@ class DownloadsManager(private val context: Context) {
}
data class Download(val title: String, val chapter: String, val type: Type) : Serializable {
data class DownloadedType(val title: String, val chapter: String, val type: Type) : Serializable {
enum class Type {
MANGA,
ANIME,

View File

@@ -0,0 +1,455 @@
package ani.dantotsu.download.anime
import android.Manifest
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
import android.os.Build
import android.os.Environment
import android.os.IBinder
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.offline.Download
import androidx.media3.exoplayer.offline.DownloadManager
import androidx.media3.exoplayer.offline.DownloadService
import ani.dantotsu.FileUrl
import ani.dantotsu.R
import ani.dantotsu.currActivity
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.video.Helper
import ani.dantotsu.download.video.ExoplayerDownloadService
import ani.dantotsu.logger
import ani.dantotsu.media.Media
import ani.dantotsu.media.anime.AnimeWatchFragment
import ani.dantotsu.parsers.Subtitle
import ani.dantotsu.parsers.Video
import ani.dantotsu.snackString
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.gson.GsonBuilder
import com.google.gson.InstanceCreator
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SAnimeImpl
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.SEpisodeImpl
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SChapterImpl
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
import java.io.FileOutputStream
import java.net.HttpURLConnection
import java.net.URL
import java.util.Queue
import java.util.concurrent.ConcurrentLinkedQueue
class AnimeDownloaderService : Service() {
private lateinit var notificationManager: NotificationManagerCompat
private lateinit var builder: NotificationCompat.Builder
private val downloadsManager: DownloadsManager = Injekt.get<DownloadsManager>()
private val downloadJobs = mutableMapOf<String, Job>()
private val mutex = Mutex()
private var isCurrentlyProcessing = false
override fun onBind(intent: Intent?): IBinder? {
// This is only required for bound services.
return null
}
override fun onCreate() {
super.onCreate()
notificationManager = NotificationManagerCompat.from(this)
builder = NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER_PROGRESS).apply {
setContentTitle("Anime Download Progress")
setSmallIcon(R.drawable.ic_round_download_24)
priority = NotificationCompat.PRIORITY_DEFAULT
setOnlyAlertOnce(true)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(
NOTIFICATION_ID,
builder.build(),
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
)
} else {
startForeground(NOTIFICATION_ID, builder.build())
}
ContextCompat.registerReceiver(
this,
cancelReceiver,
IntentFilter(ACTION_CANCEL_DOWNLOAD),
ContextCompat.RECEIVER_EXPORTED
)
}
override fun onDestroy() {
super.onDestroy()
AnimeServiceDataSingleton.downloadQueue.clear()
downloadJobs.clear()
AnimeServiceDataSingleton.isServiceRunning = false
unregisterReceiver(cancelReceiver)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
snackString("Download started")
val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
serviceScope.launch {
mutex.withLock {
if (!isCurrentlyProcessing) {
isCurrentlyProcessing = true
processQueue()
isCurrentlyProcessing = false
}
}
}
return START_NOT_STICKY
}
private fun processQueue() {
CoroutineScope(Dispatchers.Default).launch {
while (AnimeServiceDataSingleton.downloadQueue.isNotEmpty()) {
val task = AnimeServiceDataSingleton.downloadQueue.poll()
if (task != null) {
val job = launch { download(task) }
mutex.withLock {
downloadJobs[task.getTaskName()] = job
}
job.join() // Wait for the job to complete before continuing to the next task
mutex.withLock {
downloadJobs.remove(task.getTaskName())
}
updateNotification() // Update the notification after each task is completed
}
if (AnimeServiceDataSingleton.downloadQueue.isEmpty()) {
withContext(Dispatchers.Main) {
stopSelf() // Stop the service when the queue is empty
}
}
}
}
}
@UnstableApi
fun cancelDownload(taskName: String) {
CoroutineScope(Dispatchers.Default).launch {
mutex.withLock {
val url = AnimeServiceDataSingleton.downloadQueue.find { it.getTaskName() == taskName }?.video?.file?.url ?: ""
DownloadService.sendRemoveDownload(
this@AnimeDownloaderService,
ExoplayerDownloadService::class.java,
url,
false
)
downloadJobs[taskName]?.cancel()
downloadJobs.remove(taskName)
AnimeServiceDataSingleton.downloadQueue.removeAll { it.getTaskName() == taskName }
updateNotification() // Update the notification after cancellation
}
}
}
private fun updateNotification() {
// Update the notification to reflect the current state of the queue
val pendingDownloads = AnimeServiceDataSingleton.downloadQueue.size
val text = if (pendingDownloads > 0) {
"Pending downloads: $pendingDownloads"
} else {
"All downloads completed"
}
builder.setContentText(text)
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
return
}
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
@androidx.annotation.OptIn(UnstableApi::class) suspend fun download(task: DownloadTask) {
try {
val downloadManager = Helper.downloadManager(this@AnimeDownloaderService)
withContext(Dispatchers.Main) {
val notifi = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.checkSelfPermission(
this@AnimeDownloaderService,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
} else {
true
}
builder.setContentText("Downloading ${task.title} - ${task.episode}")
if (notifi) {
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
broadcastDownloadStarted(task.getTaskName())
currActivity()?.let {
Helper.downloadVideo(
it,
task.video,
task.subtitle)
}
saveMediaInfo(task)
val downloadStarted = hasDownloadStarted(downloadManager, task, 30000) // 30 seconds timeout
if (!downloadStarted) {
logger("Download failed to start")
builder.setContentText("${task.title} - ${task.episode} Download failed to start")
notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download failed to start")
broadcastDownloadFailed(task.getTaskName())
return@withContext
}
// periodically check if the download is complete
while (downloadManager.downloadIndex.getDownload(task.video.file.url) != null) {
val download = downloadManager.downloadIndex.getDownload(task.video.file.url)
if (download != null) {
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_FAILED) {
logger("Download failed")
builder.setContentText("${task.title} - ${task.episode} Download failed")
notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download failed")
logger("Download failed: ${download.failureReason}")
FirebaseCrashlytics.getInstance().recordException(Exception("Anime Download failed:" +
" ${download.failureReason}" +
" url: ${task.video.file.url}" +
" title: ${task.title}" +
" episode: ${task.episode}"))
broadcastDownloadFailed(task.getTaskName())
break
}
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_COMPLETED) {
logger("Download completed")
builder.setContentText("${task.title} - ${task.episode} Download completed")
notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download completed")
getSharedPreferences(getString(R.string.anime_downloads), Context.MODE_PRIVATE).edit().putString(
task.getTaskName(),
task.video.file.url
).apply()
downloadsManager.addDownload(
DownloadedType(
task.title,
task.episode,
DownloadedType.Type.ANIME,
)
)
broadcastDownloadFinished(task.getTaskName())
break
}
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_STOPPED) {
logger("Download stopped")
builder.setContentText("${task.title} - ${task.episode} Download stopped")
notificationManager.notify(NOTIFICATION_ID, builder.build())
snackString("${task.title} - ${task.episode} Download stopped")
break
}
broadcastDownloadProgress(task.getTaskName(), download.percentDownloaded.toInt())
if (notifi) {
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
}
kotlinx.coroutines.delay(2000)
}
}
} catch (e: Exception) {
logger("Exception while downloading file: ${e.message}")
snackString("Exception while downloading file: ${e.message}")
FirebaseCrashlytics.getInstance().recordException(e)
broadcastDownloadFailed(task.getTaskName())
}
}
@androidx.annotation.OptIn(UnstableApi::class) suspend fun hasDownloadStarted(downloadManager: DownloadManager, task: DownloadTask, timeout: Long): Boolean {
val startTime = System.currentTimeMillis()
while (System.currentTimeMillis() - startTime < timeout) {
val download = downloadManager.downloadIndex.getDownload(task.video.file.url)
if (download != null) {
return true
}
// Delay between each poll
kotlinx.coroutines.delay(500)
}
return false
}
@OptIn(DelicateCoroutinesApi::class)
private fun saveMediaInfo(task: DownloadTask) {
GlobalScope.launch(Dispatchers.IO) {
val directory = File(
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"${DownloadsManager.animeLocation}/${task.title}"
)
val episodeDirectory = File(directory, task.episode)
if (!directory.exists()) directory.mkdirs()
if (!episodeDirectory.exists()) episodeDirectory.mkdirs()
val file = File(directory, "media.json")
val gson = GsonBuilder()
.registerTypeAdapter(SChapter::class.java, InstanceCreator<SChapter> {
SChapterImpl() // Provide an instance of SChapterImpl
})
.registerTypeAdapter(SAnime::class.java, InstanceCreator<SAnime> {
SAnimeImpl() // Provide an instance of SAnimeImpl
})
.registerTypeAdapter(SEpisode::class.java, InstanceCreator<SEpisode> {
SEpisodeImpl() // Provide an instance of SEpisodeImpl
})
.create()
val mediaJson = gson.toJson(task.sourceMedia)
val media = gson.fromJson(mediaJson, Media::class.java)
if (media != null) {
media.cover = media.cover?.let { downloadImage(it, directory, "cover.jpg") }
media.banner = media.banner?.let { downloadImage(it, directory, "banner.jpg") }
if (task.episodeImage != null) {
media.anime?.episodes?.get(task.episode)?.let { episode ->
episode.thumb = downloadImage(task.episodeImage, episodeDirectory, "episodeImage.jpg")?.let {
FileUrl(
it
)
}
}
downloadImage(task.episodeImage, episodeDirectory, "episodeImage.jpg")
}
val jsonString = gson.toJson(media)
withContext(Dispatchers.Main) {
file.writeText(jsonString)
}
}
}
}
private suspend fun downloadImage(url: String, directory: File, name: String): String? =
withContext(Dispatchers.IO) {
var connection: HttpURLConnection? = null
println("Downloading url $url")
try {
connection = URL(url).openConnection() as HttpURLConnection
connection.connect()
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
throw Exception("Server returned HTTP ${connection.responseCode} ${connection.responseMessage}")
}
val file = File(directory, name)
FileOutputStream(file).use { output ->
connection.inputStream.use { input ->
input.copyTo(output)
}
}
return@withContext file.absolutePath
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(
this@AnimeDownloaderService,
"Exception while saving ${name}: ${e.message}",
Toast.LENGTH_LONG
).show()
}
null
} finally {
connection?.disconnect()
}
}
private fun broadcastDownloadStarted(chapterNumber: String) {
val intent = Intent(AnimeWatchFragment.ACTION_DOWNLOAD_STARTED).apply {
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, chapterNumber)
}
sendBroadcast(intent)
}
private fun broadcastDownloadFinished(chapterNumber: String) {
val intent = Intent(AnimeWatchFragment.ACTION_DOWNLOAD_FINISHED).apply {
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, chapterNumber)
}
sendBroadcast(intent)
}
private fun broadcastDownloadFailed(chapterNumber: String) {
val intent = Intent(AnimeWatchFragment.ACTION_DOWNLOAD_FAILED).apply {
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, chapterNumber)
}
sendBroadcast(intent)
}
private fun broadcastDownloadProgress(chapterNumber: String, progress: Int) {
val intent = Intent(AnimeWatchFragment.ACTION_DOWNLOAD_PROGRESS).apply {
putExtra(AnimeWatchFragment.EXTRA_EPISODE_NUMBER, chapterNumber)
putExtra("progress", progress)
}
sendBroadcast(intent)
}
private val cancelReceiver = object : BroadcastReceiver() {
@androidx.annotation.OptIn(UnstableApi::class) override fun onReceive(context: Context, intent: Intent) {
if (intent.action == ACTION_CANCEL_DOWNLOAD) {
val taskName = intent.getStringExtra(EXTRA_TASK_NAME)
taskName?.let {
cancelDownload(it)
}
}
}
}
data class DownloadTask(
val title: String,
val episode: String,
val video: Video,
val subtitle: Subtitle? = null,
val sourceMedia: Media? = null,
val episodeImage: String? = null,
val retries: Int = 2,
val simultaneousDownloads: Int = 2,
) {
fun getTaskName(): String {
return "$title - $episode"
}
}
companion object {
private const val NOTIFICATION_ID = 1103
const val ACTION_CANCEL_DOWNLOAD = "action_cancel_download"
const val EXTRA_TASK_NAME = "extra_task_name"
}
}
object AnimeServiceDataSingleton {
var video: Video? = null
var sourceMedia: Media? = null
var downloadQueue: Queue<AnimeDownloaderService.DownloadTask> = ConcurrentLinkedQueue()
@Volatile
var isServiceRunning: Boolean = false
}

View File

@@ -18,7 +18,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import ani.dantotsu.R
import ani.dantotsu.download.Download
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger
import ani.dantotsu.media.Media
@@ -246,10 +246,10 @@ class MangaDownloaderService : Service() {
saveMediaInfo(task)
downloadsManager.addDownload(
Download(
DownloadedType(
task.title,
task.chapter,
Download.Type.MANGA
DownloadedType.Type.MANGA
)
)
broadcastDownloadFinished(task.chapter)

View File

@@ -1,11 +1,13 @@
package ani.dantotsu.download.manga
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.cardview.widget.CardView
import ani.dantotsu.R
@@ -19,6 +21,7 @@ class OfflineMangaAdapter(
private val inflater: LayoutInflater =
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
private var originalItems: List<OfflineMangaModel> = items
private var style = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0)
override fun getCount(): Int {
return items.size
@@ -32,10 +35,13 @@ class OfflineMangaAdapter(
return position.toLong()
}
@SuppressLint("SetTextI18n")
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
var view = convertView
if (view == null) {
view = inflater.inflate(R.layout.item_media_compact, parent, false)
val view: View = convertView ?: when(style) {
0 -> inflater.inflate(R.layout.item_media_large, parent, false) // large view
1 -> inflater.inflate(R.layout.item_media_compact, parent, false) // compact view
else -> inflater.inflate(R.layout.item_media_compact, parent, false) // compact view
}
val item = getItem(position) as OfflineMangaModel
@@ -44,11 +50,31 @@ class OfflineMangaAdapter(
val itemScore = view.findViewById<TextView>(R.id.itemCompactScore)
val itemScoreBG = view.findViewById<View>(R.id.itemCompactScoreBG)
val ongoing = view.findViewById<CardView>(R.id.itemCompactOngoing)
val totalchapter = view.findViewById<TextView>(R.id.itemCompactTotal)
val type = view.findViewById<TextView>(R.id.itemCompactRelation)
val typeView = view.findViewById<LinearLayout>(R.id.itemCompactType)
if (style == 0){
val bannerView = view.findViewById<ImageView>(R.id.itemCompactBanner) // for large view
val chapters = view.findViewById<TextView>(R.id.itemTotal)
chapters.text = " Chapters"
bannerView.setImageURI(item.banner)
totalchapter.text = item.totalchapter
}
else if (style == 1){
val readchapter = view.findViewById<TextView>(R.id.itemCompactUserProgress) // for compact view
readchapter.text = item.readchapter
totalchapter.text = " | " + item.totalchapter
}
// Bind item data to the views
// For example:
type.text = item.type
typeView.visibility = View.VISIBLE
imageView.setImageURI(item.image)
titleTextView.text = item.title
itemScore.text = item.score
if (item.isOngoing) {
ongoing.visibility = View.VISIBLE
} else {
@@ -74,4 +100,9 @@ class OfflineMangaAdapter(
this.originalItems = items
notifyDataSetChanged()
}
fun notifyNewGrid(){
style = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0)
notifyDataSetChanged()
}
}

View File

@@ -13,16 +13,22 @@ import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AlphaAnimation
import android.view.animation.LayoutAnimationController
import android.view.animation.OvershootInterpolator
import android.widget.AbsListView
import android.widget.AutoCompleteTextView
import android.widget.GridView
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
import androidx.fragment.app.Fragment
import ani.dantotsu.R
import ani.dantotsu.currActivity
import ani.dantotsu.currContext
import ani.dantotsu.download.Download
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.initActivity
import ani.dantotsu.logger
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
@@ -45,6 +51,7 @@ import kotlin.math.max
import kotlin.math.min
class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
private val downloadManager = Injekt.get<DownloadsManager>()
private var downloads: List<OfflineMangaModel> = listOf()
private lateinit var gridView: GridView
@@ -91,24 +98,75 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int, ) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
onSearchQuery(s.toString())
}
})
var style = context?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getInt("offline_view", 0)
val layoutList = view.findViewById<ImageView>(R.id.downloadedList)
val layoutcompact = view.findViewById<ImageView>(R.id.downloadedGrid)
var selected = when (style) {
0 -> layoutList
1 -> layoutcompact
else -> layoutList
}
selected.alpha = 1f
gridView = view.findViewById(R.id.gridView)
fun selected(it: ImageView) {
selected.alpha = 0.33f
selected = it
selected.alpha = 1f
}
layoutList.setOnClickListener {
selected(it as ImageView)
style = 0
context?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
?.putInt("offline_view", style!!)?.apply()
gridView.visibility = View.GONE
gridView = view.findViewById(R.id.gridView)
gridView.adapter = adapter
gridView.scheduleLayoutAnimation()
gridView.visibility = View.VISIBLE
adapter.notifyNewGrid()
}
layoutcompact.setOnClickListener {
selected(it as ImageView)
style = 1
context?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
?.putInt("offline_view", style!!)?.apply()
gridView.visibility = View.GONE
gridView = view.findViewById(R.id.gridView1)
gridView.adapter = adapter
gridView.scheduleLayoutAnimation()
gridView.visibility = View.VISIBLE
adapter.notifyNewGrid()
}
gridView = if(style == 0) view.findViewById(R.id.gridView) else view.findViewById(R.id.gridView1)
gridView.visibility = View.VISIBLE
getDownloads()
val fadeIn = AlphaAnimation(0f, 1f)
fadeIn.duration = 200 // animations pog
val animation = LayoutAnimationController(fadeIn)
gridView.layoutAnimation = animation
adapter = OfflineMangaAdapter(requireContext(), downloads, this)
gridView.adapter = adapter
gridView.scheduleLayoutAnimation()
gridView.setOnItemClickListener { parent, view, position, id ->
// Get the OfflineMangaModel that was clicked
val item = adapter.getItem(position) as OfflineMangaModel
val media =
downloadManager.mangaDownloads.firstOrNull { it.title == item.title }
?: downloadManager.novelDownloads.firstOrNull { it.title == item.title }
downloadManager.mangaDownloadedTypes.firstOrNull { it.title == item.title }
?: downloadManager.novelDownloadedTypes.firstOrNull { it.title == item.title }
media?.let {
startActivity(
Intent(requireContext(), MediaDetailsActivity::class.java)
@@ -123,10 +181,10 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
gridView.setOnItemLongClickListener { parent, view, position, id ->
// Get the OfflineMangaModel that was clicked
val item = adapter.getItem(position) as OfflineMangaModel
val type: Download.Type = if (downloadManager.mangaDownloads.any { it.title == item.title }) {
Download.Type.MANGA
val type: DownloadedType.Type = if (downloadManager.mangaDownloadedTypes.any { it.title == item.title }) {
DownloadedType.Type.MANGA
} else {
Download.Type.NOVEL
DownloadedType.Type.NOVEL
}
// Alert dialog to confirm deletion
val builder = androidx.appcompat.app.AlertDialog.Builder(requireContext(), R.style.MyPopup)
@@ -154,6 +212,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initActivity(requireActivity())
var height = statusBarHeight
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val displayCutout = activity?.window?.decorView?.rootWindowInsets?.displayCutout
@@ -187,9 +246,25 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
}
scrollTop.setOnClickListener {
//TODO: scroll to top
gridView.smoothScrollToPosition(0)
}
// Assuming 'scrollTop' is a view that you want to hide/show
scrollTop.visibility = View.GONE
gridView.setOnScrollListener(object : AbsListView.OnScrollListener {
override fun onScrollStateChanged(view: AbsListView, scrollState: Int) {
// Implement behavior for different scroll states if needed
}
override fun onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) {
val first = view.getChildAt(0)
val visibility = first != null && first.top < -height
scrollTop.visibility = if (visibility) View.VISIBLE else View.GONE
}
})
}
override fun onResume() {
@@ -215,19 +290,19 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
private fun getDownloads() {
downloads = listOf()
val mangaTitles = downloadManager.mangaDownloads.map { it.title }.distinct()
val mangaTitles = downloadManager.mangaDownloadedTypes.map { it.title }.distinct()
val newMangaDownloads = mutableListOf<OfflineMangaModel>()
for (title in mangaTitles) {
val _downloads = downloadManager.mangaDownloads.filter { it.title == title }
val _downloads = downloadManager.mangaDownloadedTypes.filter { it.title == title }
val download = _downloads.first()
val offlineMangaModel = loadOfflineMangaModel(download)
newMangaDownloads += offlineMangaModel
}
downloads = newMangaDownloads
val novelTitles = downloadManager.novelDownloads.map { it.title }.distinct()
val novelTitles = downloadManager.novelDownloadedTypes.map { it.title }.distinct()
val newNovelDownloads = mutableListOf<OfflineMangaModel>()
for (title in novelTitles) {
val _downloads = downloadManager.novelDownloads.filter { it.title == title }
val _downloads = downloadManager.novelDownloadedTypes.filter { it.title == title }
val download = _downloads.first()
val offlineMangaModel = loadOfflineMangaModel(download)
newNovelDownloads += offlineMangaModel
@@ -236,17 +311,17 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
}
private fun getMedia(download: Download): Media? {
val type = if (download.type == Download.Type.MANGA) {
private fun getMedia(downloadedType: DownloadedType): Media? {
val type = if (downloadedType.type == DownloadedType.Type.MANGA) {
"Manga"
} else if (download.type == Download.Type.ANIME) {
} else if (downloadedType.type == DownloadedType.Type.ANIME) {
"Anime"
} else {
"Novel"
}
val directory = File(
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/$type/${download.title}"
"Dantotsu/$type/${downloadedType.title}"
)
//load media.json and convert to media class with gson
return try {
@@ -266,40 +341,45 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
}
}
private fun loadOfflineMangaModel(download: Download): OfflineMangaModel {
val type = if (download.type == Download.Type.MANGA) {
private fun loadOfflineMangaModel(downloadedType: DownloadedType): OfflineMangaModel {
val type = if (downloadedType.type == DownloadedType.Type.MANGA) {
"Manga"
} else if (download.type == Download.Type.ANIME) {
} else if (downloadedType.type == DownloadedType.Type.ANIME) {
"Anime"
} else {
"Novel"
}
val directory = File(
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/$type/${download.title}"
"Dantotsu/$type/${downloadedType.title}"
)
//load media.json and convert to media class with gson
try {
val media = File(directory, "media.json")
val mediaJson = media.readText()
val mediaModel = getMedia(download)!!
val mediaModel = getMedia(downloadedType)!!
val cover = File(directory, "cover.jpg")
val coverUri: Uri? = if (cover.exists()) {
Uri.fromFile(cover)
} else {
null
}
} else null
val banner = File(directory, "banner.jpg")
val bannerUri: Uri? = if (banner.exists()) {
Uri.fromFile(banner)
} else null
val title = mediaModel.nameMAL ?: mediaModel.nameRomaji
val score = ((if (mediaModel.userScore == 0) (mediaModel.meanScore
?: 0) else mediaModel.userScore) / 10.0).toString()
val isOngoing = false
val isOngoing = mediaModel.status == currActivity()!!.getString(R.string.status_releasing)
val isUserScored = mediaModel.userScore != 0
return OfflineMangaModel(title, score, isOngoing, isUserScored, coverUri)
val readchapter = (mediaModel.userProgress ?: "~").toString()
val totalchapter = "${mediaModel.manga?.totalChapters ?: "??"}"
val chapters = " Chapters"
return OfflineMangaModel(title, score, totalchapter, readchapter, type, chapters, isOngoing, isUserScored, coverUri , bannerUri )
} catch (e: Exception) {
logger("Error loading media.json: ${e.message}")
logger(e.printStackTrace())
FirebaseCrashlytics.getInstance().recordException(e)
return OfflineMangaModel("unknown", "0", false, false, null)
return OfflineMangaModel("unknown", "0", "??", "??","movie" ,"hmm", false, false, null , null)
}
}
}

View File

@@ -5,7 +5,12 @@ import android.net.Uri
data class OfflineMangaModel(
val title: String,
val score: String,
val totalchapter: String,
val readchapter : String,
val type: String,
val chapters: String,
val isOngoing: Boolean,
val isUserScored: Boolean,
val image: Uri?
val image: Uri?,
val banner: Uri?
)

View File

@@ -17,7 +17,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import ani.dantotsu.R
import ani.dantotsu.download.Download
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger
import ani.dantotsu.media.Media
@@ -330,10 +330,10 @@ class NovelDownloaderService : Service() {
saveMediaInfo(task)
downloadsManager.addDownload(
Download(
DownloadedType(
task.title,
task.chapter,
Download.Type.NOVEL
DownloadedType.Type.NOVEL
)
)
broadcastDownloadFinished(task.originalLink)

View File

@@ -11,7 +11,7 @@ import androidx.media3.exoplayer.scheduler.Scheduler
import ani.dantotsu.R
@UnstableApi
class MyDownloadService : DownloadService(1, 1, "download_service", R.string.downloads, 0) {
class ExoplayerDownloadService : DownloadService(1, 2000, "download_service", R.string.downloads, 0) {
companion object {
private const val JOB_ID = 1
private const val FOREGROUND_NOTIFICATION_ID = 1

View File

@@ -1,8 +1,19 @@
package ani.dantotsu.download.video
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.util.Log
import androidx.annotation.OptIn
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getString
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.MimeTypes
@@ -15,6 +26,7 @@ import androidx.media3.datasource.cache.NoOpCacheEvictor
import androidx.media3.datasource.cache.SimpleCache
import androidx.media3.datasource.okhttp.OkHttpDataSource
import androidx.media3.exoplayer.DefaultRenderersFactory
import androidx.media3.exoplayer.offline.Download
import androidx.media3.exoplayer.offline.DownloadHelper
import androidx.media3.exoplayer.offline.DownloadManager
import androidx.media3.exoplayer.offline.DownloadService
@@ -22,7 +34,12 @@ import androidx.media3.exoplayer.scheduler.Requirements
import androidx.media3.ui.TrackSelectionDialogBuilder
import ani.dantotsu.R
import ani.dantotsu.defaultHeaders
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.anime.AnimeDownloaderService
import ani.dantotsu.download.anime.AnimeServiceDataSingleton
import ani.dantotsu.logError
import ani.dantotsu.media.Media
import ani.dantotsu.okHttpClient
import ani.dantotsu.parsers.Subtitle
import ani.dantotsu.parsers.SubtitleType
@@ -37,6 +54,8 @@ import java.util.concurrent.*
object Helper {
private var simpleCache: SimpleCache? = null
@SuppressLint("UnsafeOptInUsageError")
fun downloadVideo(context: Context, video: Video, subtitle: Subtitle?) {
val dataSourceFactory = DataSource.Factory {
@@ -82,18 +101,18 @@ object Helper {
)
downloadHelper.prepare(object : DownloadHelper.Callback {
override fun onPrepared(helper: DownloadHelper) {
TrackSelectionDialogBuilder(
context, "Select thingy", helper.getTracks(0).groups
/*TrackSelectionDialogBuilder( TODO: use this for subtitles
context, "Select Source", helper.getTracks(0).groups
) { _, overrides ->
val params = TrackSelectionParameters.Builder(context)
overrides.forEach {
params.addOverride(it.value)
}
helper.addTrackSelection(0, params.build())
MyDownloadService
ExoplayerDownloadService
DownloadService.sendAddDownload(
context,
MyDownloadService::class.java,
ExoplayerDownloadService::class.java,
helper.getDownloadRequest(null),
false
)
@@ -103,6 +122,14 @@ object Helper {
if (it.frameRate > 0f) it.height.toString() + "p" else it.height.toString() + "p (fps : N/A)"
}
build().show()
}*/
helper.getDownloadRequest(null).let {
DownloadService.sendAddDownload(
context,
ExoplayerDownloadService::class.java,
it,
false
)
}
}
@@ -114,13 +141,13 @@ object Helper {
private var download: DownloadManager? = null
private const val DOWNLOAD_CONTENT_DIRECTORY = "downloads"
private const val DOWNLOAD_CONTENT_DIRECTORY = "Anime_Downloads"
@Synchronized
@UnstableApi
fun downloadManager(context: Context): DownloadManager {
return download ?: let {
val database = StandaloneDatabaseProvider(context)
val database = Injekt.get<StandaloneDatabaseProvider>()
val downloadDirectory = File(getDownloadDirectory(context), DOWNLOAD_CONTENT_DIRECTORY)
val dataSourceFactory = DataSource.Factory {
//val dataSource: HttpDataSource = OkHttpDataSource.Factory(okHttpClient).createDataSource()
@@ -133,17 +160,42 @@ object Helper {
}
dataSource
}
DownloadManager(
val threadPoolSize = Runtime.getRuntime().availableProcessors()
val executorService = Executors.newFixedThreadPool(threadPoolSize)
val downloadManager = DownloadManager(
context,
database,
SimpleCache(downloadDirectory, NoOpCacheEvictor(), database),
getSimpleCache(context),
dataSourceFactory,
Executor(Runnable::run)
executorService
).apply {
requirements =
Requirements(Requirements.NETWORK or Requirements.DEVICE_STORAGE_NOT_LOW)
maxParallelDownloads = 3
}
downloadManager.addListener( //for testing
object : DownloadManager.Listener {
override fun onDownloadChanged(
downloadManager: DownloadManager,
download: Download,
finalException: Exception?
) {
if (download.state == Download.STATE_COMPLETED) {
Log.e("Downloader", "Download Completed")
} else if (download.state == Download.STATE_FAILED) {
Log.e("Downloader", "Download Failed")
} else if (download.state == Download.STATE_STOPPED) {
Log.e("Downloader", "Download Stopped")
} else if (download.state == Download.STATE_QUEUED) {
Log.e("Downloader", "Download Queued")
} else if (download.state == Download.STATE_DOWNLOADING) {
Log.e("Downloader", "Download Downloading")
}
}
}
)
downloadManager
}
}
@@ -159,4 +211,108 @@ object Helper {
}
return downloadDirectory!!
}
@OptIn(UnstableApi::class)
fun startAnimeDownloadService(
context: Context,
title: String,
episode: String,
video: Video,
subtitle: Subtitle? = null,
sourceMedia: Media? = null,
episodeImage: String? = null
) {
if (!isNotificationPermissionGranted(context)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.requestPermissions(
context as Activity,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
1
)
}
}
val downloadTask = AnimeDownloaderService.DownloadTask(
title,
episode,
video,
subtitle,
sourceMedia,
episodeImage
)
val downloadsManger = Injekt.get<DownloadsManager>()
val downloadCheck = downloadsManger
.queryDownload(title, episode, DownloadedType.Type.ANIME)
if (downloadCheck) {
AlertDialog.Builder(context , R.style.MyPopup)
.setTitle("Download Exists")
.setMessage("A download for this episode already exists. Do you want to overwrite it?")
.setPositiveButton("Yes") { _, _ ->
DownloadService.sendRemoveDownload(
context,
ExoplayerDownloadService::class.java,
context.getSharedPreferences(
getString(context, R.string.anime_downloads),
Context.MODE_PRIVATE
).getString(
downloadTask.getTaskName(),
""
) ?: "",
false
)
context.getSharedPreferences(
getString(context, R.string.anime_downloads),
Context.MODE_PRIVATE
).edit()
.remove(downloadTask.getTaskName())
.apply()
downloadsManger.removeDownload(
DownloadedType(
title,
episode,
DownloadedType.Type.ANIME
)
)
AnimeServiceDataSingleton.downloadQueue.offer(downloadTask)
if (!AnimeServiceDataSingleton.isServiceRunning) {
val intent = Intent(context, AnimeDownloaderService::class.java)
ContextCompat.startForegroundService(context, intent)
AnimeServiceDataSingleton.isServiceRunning = true
}
}
.setNegativeButton("No") { _, _ -> }
.show()
} else {
AnimeServiceDataSingleton.downloadQueue.offer(downloadTask)
if (!AnimeServiceDataSingleton.isServiceRunning) {
val intent = Intent(context, AnimeDownloaderService::class.java)
ContextCompat.startForegroundService(context, intent)
AnimeServiceDataSingleton.isServiceRunning = true
}
}
}
@OptIn(UnstableApi::class)
fun getSimpleCache(context: Context): SimpleCache {
return if (simpleCache == null) {
val downloadDirectory = File(getDownloadDirectory(context), DOWNLOAD_CONTENT_DIRECTORY)
val database = Injekt.get<StandaloneDatabaseProvider>()
simpleCache = SimpleCache(downloadDirectory, NoOpCacheEvictor(), database)
simpleCache!!
} else {
simpleCache!!
}
}
private fun isNotificationPermissionGranted(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return ActivityCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
}
return true
}
}

View File

@@ -2,6 +2,7 @@ package ani.dantotsu.home
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
@@ -26,6 +27,7 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistAnimeViewModel
import ani.dantotsu.connections.anilist.SearchResults
import ani.dantotsu.connections.anilist.getUserId
import ani.dantotsu.currContext
import ani.dantotsu.databinding.FragmentAnimeBinding
import ani.dantotsu.loadData
import ani.dantotsu.media.MediaAdaptor
@@ -47,16 +49,17 @@ import kotlin.math.min
class AnimeFragment : Fragment() {
private var _binding: FragmentAnimeBinding? = null
private val binding get() = _binding!!
private lateinit var animePageAdapter: AnimePageAdapter
private var uiSettings: UserInterfaceSettings =
loadData("ui_settings") ?: UserInterfaceSettings()
loadData("ui_settings") ?: UserInterfaceSettings()
val model: AnilistAnimeViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentAnimeBinding.inflate(inflater, container, false)
return binding.root
@@ -78,11 +81,11 @@ class AnimeFragment : Fragment() {
if (displayCutout != null) {
if (displayCutout.boundingRects.size > 0) {
height = max(
statusBarHeight,
min(
displayCutout.boundingRects[0].width(),
displayCutout.boundingRects[0].height()
)
statusBarHeight,
min(
displayCutout.boundingRects[0].width(),
displayCutout.boundingRects[0].height()
)
)
}
}
@@ -95,18 +98,18 @@ class AnimeFragment : Fragment() {
binding.animePageRecyclerView.updatePaddingRelative(bottom = navBarHeight + 160f.px)
val animePageAdapter = AnimePageAdapter()
animePageAdapter = AnimePageAdapter()
var loading = true
if (model.notSet) {
model.notSet = false
model.searchResults = SearchResults(
"ANIME",
isAdult = false,
onList = false,
results = mutableListOf(),
hasNextPage = true,
sort = Anilist.sortBy[1]
"ANIME",
isAdult = false,
onList = false,
results = mutableListOf(),
hasNextPage = true,
sort = Anilist.sortBy[1]
)
}
val popularAdaptor = MediaAdaptor(1, model.searchResults.results, requireActivity())
@@ -174,7 +177,7 @@ class AnimeFragment : Fragment() {
}
binding.animePageRecyclerView.addOnScrollListener(object :
RecyclerView.OnScrollListener() {
RecyclerView.OnScrollListener() {
override fun onScrolled(v: RecyclerView, dx: Int, dy: Int) {
if (!v.canScrollVertically(1)) {
if (model.searchResults.hasNextPage && model.searchResults.results.isNotEmpty() && !loading) {
@@ -214,19 +217,19 @@ class AnimeFragment : Fragment() {
model.getTrending().observe(viewLifecycleOwner) {
if (it != null) {
animePageAdapter.updateTrending(
MediaAdaptor(
if (uiSettings.smallView) 3 else 2,
it,
requireActivity(),
viewPager = animePageAdapter.trendingViewPager
)
MediaAdaptor(
if (uiSettings.smallView) 3 else 2,
it,
requireActivity(),
viewPager = animePageAdapter.trendingViewPager
)
)
animePageAdapter.updateAvatar()
}
}
}
binding.animePageScrollTop.translationY =
-(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
-(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
}
}
@@ -244,13 +247,13 @@ class AnimeFragment : Fragment() {
animePageAdapter.onSeasonLongClick = { i ->
val (season, year) = Anilist.currentSeasons[i]
ContextCompat.startActivity(
requireContext(),
Intent(requireContext(), SearchActivity::class.java)
.putExtra("type", "ANIME")
.putExtra("season", season)
.putExtra("seasonYear", year.toString())
.putExtra("search", true),
null
requireContext(),
Intent(requireContext(), SearchActivity::class.java)
.putExtra("type", "ANIME")
.putExtra("season", season)
.putExtra("seasonYear", year.toString())
.putExtra("search", true),
null
)
true
}
@@ -277,6 +280,12 @@ class AnimeFragment : Fragment() {
override fun onResume() {
if (!model.loaded) Refresh.activity[this.hashCode()]!!.postValue(true)
if (animePageAdapter.trendingViewPager != null) {
animePageAdapter.setIncognito()
binding.root.requestApplyInsets()
binding.root.requestLayout()
}
super.onResume()
}
}

View File

@@ -75,12 +75,6 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt()
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
}
val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getBoolean("incognito", false) ?: false
if(incognito) {
binding.incognitoTextView.visibility = View.VISIBLE
binding.incognitoView.visibility = View.VISIBLE
}
binding.animeTitleContainer.updatePadding(top = statusBarHeight)
@@ -155,6 +149,19 @@ class AnimePageAdapter : RecyclerView.Adapter<AnimePageAdapter.AnimePageViewHold
trendingViewPager!!.updateLayoutParams { height += statusBarHeight }
}
fun setIncognito() {
val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getBoolean("incognito", false) ?: false
if(incognito) {
binding.incognitoTextView.visibility = View.VISIBLE
if (!uiSettings.immersiveMode) {
binding.root.fitsSystemWindows = true
}
} else {
binding.incognitoTextView.visibility = View.GONE
}
}
fun updateTrending(adaptor: MediaAdaptor) {
binding.animeTrendingProgressBar.visibility = View.GONE
binding.animeTrendingViewPager.adapter = adaptor

View File

@@ -10,6 +10,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.animation.LayoutAnimationController
import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
@@ -115,12 +116,6 @@ class HomeFragment : Fragment() {
snackString(currContext()?.getString(R.string.please_reload))
}
}
val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getBoolean("incognito", false) ?: false
if(incognito) {
binding.incognitoTextView.visibility = View.VISIBLE
binding.incognitoView.visibility = View.VISIBLE
}
binding.homeUserAvatarContainer.setSafeOnClickListener {
val dialogFragment =
SettingsDialogFragment.newInstance(SettingsDialogFragment.Companion.PageType.HOME)
@@ -369,6 +364,23 @@ class HomeFragment : Fragment() {
override fun onResume() {
if (!model.loaded) Refresh.activity[1]!!.postValue(true)
val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getBoolean("incognito", false) ?: false
if(incognito) {
val uiSettings = loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
binding.incognitoTextView.visibility = View.VISIBLE
if (!uiSettings.immersiveMode) {
binding.root.fitsSystemWindows = true
//holy deprecation
binding.root.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
binding.root.requestApplyInsets()
binding.root.requestLayout()
}
} else {
binding.incognitoTextView.visibility = View.GONE
}
super.onResume()
}
}

View File

@@ -2,6 +2,7 @@ package ani.dantotsu.home
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
@@ -24,6 +25,7 @@ import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.connections.anilist.AnilistMangaViewModel
import ani.dantotsu.connections.anilist.SearchResults
import ani.dantotsu.connections.anilist.getUserId
import ani.dantotsu.currContext
import ani.dantotsu.databinding.FragmentMangaBinding
import ani.dantotsu.loadData
import ani.dantotsu.media.MediaAdaptor
@@ -43,16 +45,17 @@ import kotlin.math.min
class MangaFragment : Fragment() {
private var _binding: FragmentMangaBinding? = null
private val binding get() = _binding!!
private lateinit var mangaPageAdapter: MangaPageAdapter
private var uiSettings: UserInterfaceSettings =
loadData("ui_settings") ?: UserInterfaceSettings()
loadData("ui_settings") ?: UserInterfaceSettings()
val model: AnilistMangaViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentMangaBinding.inflate(inflater, container, false)
return binding.root
@@ -73,11 +76,11 @@ class MangaFragment : Fragment() {
if (displayCutout != null) {
if (displayCutout.boundingRects.size > 0) {
height = max(
statusBarHeight,
min(
displayCutout.boundingRects[0].width(),
displayCutout.boundingRects[0].height()
)
statusBarHeight,
min(
displayCutout.boundingRects[0].width(),
displayCutout.boundingRects[0].height()
)
)
}
}
@@ -90,23 +93,23 @@ class MangaFragment : Fragment() {
binding.mangaPageRecyclerView.updatePaddingRelative(bottom = navBarHeight + 160f.px)
val mangaPageAdapter = MangaPageAdapter()
mangaPageAdapter = MangaPageAdapter()
var loading = true
if (model.notSet) {
model.notSet = false
model.searchResults = SearchResults(
"MANGA",
isAdult = false,
onList = false,
results = arrayListOf(),
hasNextPage = true,
sort = Anilist.sortBy[1]
"MANGA",
isAdult = false,
onList = false,
results = arrayListOf(),
hasNextPage = true,
sort = Anilist.sortBy[1]
)
}
val popularAdaptor = MediaAdaptor(1, model.searchResults.results, requireActivity())
val progressAdaptor = ProgressAdapter(searched = model.searched)
binding.mangaPageRecyclerView.adapter =
ConcatAdapter(mangaPageAdapter, popularAdaptor, progressAdaptor)
ConcatAdapter(mangaPageAdapter, popularAdaptor, progressAdaptor)
val layout = LinearLayoutManager(requireContext())
binding.mangaPageRecyclerView.layoutManager = layout
@@ -132,7 +135,7 @@ class MangaFragment : Fragment() {
}
binding.mangaPageRecyclerView.addOnScrollListener(object :
RecyclerView.OnScrollListener() {
RecyclerView.OnScrollListener() {
override fun onScrolled(v: RecyclerView, dx: Int, dy: Int) {
if (!v.canScrollVertically(1)) {
if (model.searchResults.hasNextPage && model.searchResults.results.isNotEmpty() && !loading) {
@@ -172,19 +175,19 @@ class MangaFragment : Fragment() {
model.getTrending().observe(viewLifecycleOwner) {
if (it != null) {
mangaPageAdapter.updateTrending(
MediaAdaptor(
if (uiSettings.smallView) 3 else 2,
it,
requireActivity(),
viewPager = mangaPageAdapter.trendingViewPager
)
MediaAdaptor(
if (uiSettings.smallView) 3 else 2,
it,
requireActivity(),
viewPager = mangaPageAdapter.trendingViewPager
)
)
mangaPageAdapter.updateAvatar()
}
}
}
binding.mangaPageScrollTop.translationY =
-(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
-(navBarHeight + bottomBar.height + bottomBar.marginBottom).toFloat()
}
}
@@ -251,6 +254,12 @@ class MangaFragment : Fragment() {
override fun onResume() {
if (!model.loaded) Refresh.activity[this.hashCode()]!!.postValue(true)
//make sure mangaPageAdapter is initialized
if (mangaPageAdapter.trendingViewPager != null) {
mangaPageAdapter.setIncognito()
binding.root.requestApplyInsets()
binding.root.requestLayout()
}
super.onResume()
}

View File

@@ -75,13 +75,6 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt())
}
val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getBoolean("incognito", false) ?: false
if(incognito) {
binding.incognitoTextView.visibility = View.VISIBLE
binding.incognitoView.visibility = View.VISIBLE
}
binding.mangaTitleContainer.updatePadding(top = statusBarHeight)
if (uiSettings.smallView) binding.mangaTrendingContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
@@ -148,6 +141,19 @@ class MangaPageAdapter : RecyclerView.Adapter<MangaPageAdapter.MangaPageViewHold
trendingViewPager!!.updateLayoutParams { height += statusBarHeight }
}
fun setIncognito() {
val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getBoolean("incognito", false) ?: false
if(incognito) {
binding.incognitoTextView.visibility = View.VISIBLE
if (!uiSettings.immersiveMode) {
binding.root.fitsSystemWindows = true
}
} else {
binding.incognitoTextView.visibility = View.GONE
}
}
fun updateTrending(adaptor: MediaAdaptor) {
binding.mangaTrendingProgressBar.visibility = View.GONE
binding.mangaTrendingViewPager.adapter = adaptor

View File

@@ -136,7 +136,7 @@ class MediaAdaptor(
val media = mediaList?.get(position)
if (media != null) {
b.itemCompactImage.loadImage(media.cover)
b.itemCompactBanner.loadImage(media.banner ?: media.cover, 400)
b.itemCompactBanner.loadImage(media.banner ?: media.cover)
b.itemCompactOngoing.visibility =
if (media.status == currActivity()!!.getString(R.string.status_releasing)) View.VISIBLE else View.GONE
b.itemCompactTitle.text = media.userPreferredName

View File

@@ -78,7 +78,7 @@ class SearchActivity : AppCompatActivity() {
mediaAdaptor = MediaAdaptor(style, model.searchResults.results, this, matchParent = true)
val headerAdaptor = SearchAdapter(this)
val gridSize = (screenWidth / 124f).toInt()
val gridSize = (screenWidth / 120f).toInt()
val gridLayoutManager = GridLayoutManager(this, gridSize)
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {

View File

@@ -16,5 +16,17 @@ class AnimeNameAdapter {
null
}
}
fun findEpisodeNumber(text: String): Float? {
val episodeRegex = "(episode|ep|e)[\\s:.\\-]*([\\d]+\\.?[\\d]*)"
val episodePattern: Pattern = Pattern.compile(episodeRegex, Pattern.CASE_INSENSITIVE)
val episodeMatcher: Matcher = episodePattern.matcher(text)
return if (episodeMatcher.find()) {
episodeMatcher.group(2)?.toFloat()
} else {
null
}
}
}
}

View File

@@ -1,23 +1,28 @@
package ani.dantotsu.media.anime
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.*
import ani.dantotsu.databinding.ItemAnimeWatchBinding
import ani.dantotsu.databinding.ItemChipBinding
import ani.dantotsu.databinding.DialogLayoutBinding
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.SourceSearchDialogFragment
import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.DynamicAnimeParser
import ani.dantotsu.parsers.WatchSources
@@ -27,6 +32,7 @@ import com.google.android.material.chip.Chip
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
class AnimeWatchAdapter(
private val media: Media,
private val fragment: AnimeWatchFragment,
@@ -40,6 +46,8 @@ class AnimeWatchAdapter(
val bind = ItemAnimeWatchBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(bind)
}
private var nestedDialog: AlertDialog? = null
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
@@ -147,8 +155,9 @@ class AnimeWatchAdapter(
}
}
//Icons
//Subscription
//subscribe
subscribe = MediaDetailsActivity.PopImageButton(
fragment.lifecycleScope,
binding.animeSourceSubscribe,
@@ -167,44 +176,76 @@ class AnimeWatchAdapter(
openSettings(fragment.requireContext(), getChannelId(true, media.id))
}
//Icons
var reversed = media.selected!!.recyclerReversed
var style = media.selected!!.recyclerStyle ?: fragment.uiSettings.animeDefaultView
binding.animeSourceTop.rotation = if (reversed) -90f else 90f
binding.animeSourceTop.setOnClickListener {
reversed = !reversed
binding.animeSourceTop.rotation = if (reversed) -90f else 90f
fragment.onIconPressed(style, reversed)
}
var selected = when (style) {
0 -> binding.animeSourceList
1 -> binding.animeSourceGrid
2 -> binding.animeSourceCompact
else -> binding.animeSourceList
}
selected.alpha = 1f
fun selected(it: ImageView) {
selected.alpha = 0.33f
selected = it
//Nested Button
binding.animeNestedButton.setOnClickListener {
val dialogView =
LayoutInflater.from(fragment.requireContext()).inflate(R.layout.dialog_layout, null)
val dialogBinding = DialogLayoutBinding.bind(dialogView)
var run = false
var reversed = media.selected!!.recyclerReversed
var style = media.selected!!.recyclerStyle ?: fragment.uiSettings.animeDefaultView
dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down"
dialogBinding.animeSourceTop.setOnClickListener {
reversed = !reversed
dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down"
run = true
}
//Grids
var selected = when (style) {
0 -> dialogBinding.animeSourceList
1 -> dialogBinding.animeSourceGrid
2 -> dialogBinding.animeSourceCompact
else -> dialogBinding.animeSourceList
}
when (style) {
0 -> dialogBinding.layoutText.text = "List"
1 -> dialogBinding.layoutText.text = "Grid"
2 -> dialogBinding.layoutText.text = "Compact"
else -> dialogBinding.animeSourceList
}
selected.alpha = 1f
fun selected(it: ImageButton) {
selected.alpha = 0.33f
selected = it
selected.alpha = 1f
}
dialogBinding.animeSourceList.setOnClickListener {
selected(it as ImageButton)
style = 0
dialogBinding.layoutText.text = "List"
run = true
}
dialogBinding.animeSourceGrid.setOnClickListener {
selected(it as ImageButton)
style = 1
dialogBinding.layoutText.text = "Grid"
run = true
}
dialogBinding.animeSourceCompact.setOnClickListener {
selected(it as ImageButton)
style = 2
dialogBinding.layoutText.text = "Compact"
run = true
}
//hidden
dialogBinding.animeScanlatorContainer.visibility = View.GONE
dialogBinding.animeDownloadContainer.visibility = View.GONE
nestedDialog = AlertDialog.Builder(fragment.requireContext() , R.style.MyPopup)
.setTitle("Options")
.setView(dialogView)
.setPositiveButton("OK") { _, _ ->
if (run) fragment.onIconPressed(style, reversed)
}
.setNegativeButton("Cancel") { _, _ ->
}
.create()
nestedDialog?.show()
}
binding.animeSourceList.setOnClickListener {
selected(it as ImageView)
style = 0
fragment.onIconPressed(style, reversed)
}
binding.animeSourceGrid.setOnClickListener {
selected(it as ImageView)
style = 1
fragment.onIconPressed(style, reversed)
}
binding.animeSourceCompact.setOnClickListener {
selected(it as ImageView)
style = 2
fragment.onIconPressed(style, reversed)
}
binding.animeScanlatorTop.visibility = View.GONE
binding.animeDownloadTop.visibility = View.GONE
//Episode Handling
handleEpisodes()
}
@@ -351,12 +392,15 @@ class AnimeWatchAdapter(
parser.extension.sources.firstOrNull()?.lang ?: "Unknown"
)
}
binding?.animeSourceLanguage?.setAdapter(
ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
parser.extension.sources.map { it.lang })
val adapter = ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
parser.extension.sources.sortedBy { it.lang }.map { LanguageMapper.mapLanguageCodeToName(it.lang) }
)
val items = adapter.count
if (items > 1) binding?.animeSourceLanguageContainer?.visibility = View.VISIBLE else binding?.animeSourceLanguageContainer?.visibility = View.GONE
binding?.animeSourceLanguage?.setAdapter(adapter)
}
}

View File

@@ -23,6 +23,7 @@ import ani.dantotsu.databinding.FragmentAnimeWatchBinding
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.parsers.AnimeParser
import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.HAnimeSources
@@ -314,19 +315,19 @@ class AnimeWatchFragment : Fragment() {
if (show) View.GONE else View.VISIBLE
}
}
var itemSelected = false
val allSettings = pkg.sources.filterIsInstance<ConfigurableAnimeSource>()
if (allSettings.isNotEmpty()) {
var selectedSetting = allSettings[0]
if (allSettings.size > 1) {
val names = allSettings.map { it.lang }.toTypedArray()
val names = allSettings.sortedBy { it.lang }.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
var selectedIndex = 0
val dialog = AlertDialog.Builder(requireContext())
val dialog = AlertDialog.Builder(requireContext() , R.style.MyPopup)
.setTitle("Select a Source")
.setSingleChoiceItems(names, selectedIndex) { _, which ->
.setSingleChoiceItems(names, selectedIndex) { dialog, which ->
selectedIndex = which
}
.setPositiveButton("OK") { dialog, _ ->
selectedSetting = allSettings[selectedIndex]
itemSelected = true
dialog.dismiss()
// Move the fragment transaction here
@@ -343,10 +344,10 @@ class AnimeWatchFragment : Fragment() {
.commit()
}
}
.setNegativeButton("Cancel") { dialog, _ ->
dialog.cancel()
changeUIVisibility(true)
return@setNegativeButton
.setOnDismissListener {
if (!itemSelected) {
changeUIVisibility(true)
}
}
.show()
dialog.window?.setDimAmount(0.8f)
@@ -424,4 +425,12 @@ class AnimeWatchFragment : Fragment() {
state = binding.animeSourceRecycler.layoutManager?.onSaveInstanceState()
}
companion object {
const val ACTION_DOWNLOAD_STARTED = "ani.dantotsu.ACTION_DOWNLOAD_STARTED"
const val ACTION_DOWNLOAD_FINISHED = "ani.dantotsu.ACTION_DOWNLOAD_FINISHED"
const val ACTION_DOWNLOAD_FAILED = "ani.dantotsu.ACTION_DOWNLOAD_FAILED"
const val ACTION_DOWNLOAD_PROGRESS = "ani.dantotsu.ACTION_DOWNLOAD_PROGRESS"
const val EXTRA_EPISODE_NUMBER = "extra_episode_number"
}
}

View File

@@ -0,0 +1,43 @@
package ani.dantotsu.media.anime
import android.content.Context
import android.os.Bundle
import androidx.mediarouter.app.MediaRouteActionProvider
import androidx.mediarouter.app.MediaRouteChooserDialog
import androidx.mediarouter.app.MediaRouteChooserDialogFragment
import androidx.mediarouter.app.MediaRouteControllerDialog
import androidx.mediarouter.app.MediaRouteControllerDialogFragment
import androidx.mediarouter.app.MediaRouteDialogFactory
import ani.dantotsu.R
class CustomCastProvider(context: Context) : MediaRouteActionProvider(context) {
init {
dialogFactory = CustomCastThemeFactory()
}
}
class CustomCastThemeFactory : MediaRouteDialogFactory() {
override fun onCreateChooserDialogFragment(): MediaRouteChooserDialogFragment {
return CustomMediaRouterChooserDialogFragment()
}
override fun onCreateControllerDialogFragment(): MediaRouteControllerDialogFragment {
return CustomMediaRouteControllerDialogFragment()
}
}
class CustomMediaRouterChooserDialogFragment: MediaRouteChooserDialogFragment() {
override fun onCreateChooserDialog(
context: Context,
savedInstanceState: Bundle?
): MediaRouteChooserDialog =
MediaRouteChooserDialog(context, R.style.MyPopup)
}
class CustomMediaRouteControllerDialogFragment: MediaRouteControllerDialogFragment() {
override fun onCreateControllerDialog(
context: Context,
savedInstanceState: Bundle?
): MediaRouteControllerDialog =
MediaRouteControllerDialog(context, R.style.MyPopup)
}

View File

@@ -14,7 +14,7 @@ data class Episode(
var selectedExtractor: String? = null,
var selectedVideo: Int = 0,
var selectedSubtitle: Int? = -1,
var extractors: MutableList<VideoExtractor>? = null,
@Transient var extractors: MutableList<VideoExtractor>? = null,
@Transient var extractorCallback: ((VideoExtractor) -> Unit)? = null,
var allStreams: Boolean = false,
var watched: Long? = null,

View File

@@ -4,6 +4,7 @@ import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.app.Dialog
import android.app.DownloadManager
import android.app.PictureInPictureParams
import android.app.PictureInPictureUiState
import android.content.ActivityNotFoundException
@@ -14,7 +15,6 @@ import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Color
import android.graphics.drawable.Animatable
import android.hardware.Sensor
import android.hardware.SensorManager
import android.media.AudioManager
import android.media.AudioManager.*
@@ -96,11 +96,17 @@ import java.util.concurrent.*
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
import androidx.media3.cast.SessionAvailabilityListener
import androidx.media3.cast.CastPlayer
import androidx.media3.exoplayer.offline.Download
import androidx.mediarouter.app.MediaRouteButton
import ani.dantotsu.download.video.Helper
import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.gms.cast.framework.CastContext
@UnstableApi
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
class ExoplayerView : AppCompatActivity(), Player.Listener {
class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityListener {
private val resumeWindow = "resumeWindow"
private val resumePosition = "resumePosition"
@@ -108,6 +114,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
private val playerOnPlay = "playerOnPlay"
private lateinit var exoPlayer: ExoPlayer
private lateinit var castPlayer: CastPlayer
private lateinit var castContext: CastContext
private lateinit var trackSelector: DefaultTrackSelector
private lateinit var cacheFactory: CacheDataSource.Factory
private lateinit var playbackParameters: PlaybackParameters
@@ -145,6 +153,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
private var orientationListener: OrientationEventListener? = null
private var downloadId: String? = null
companion object {
var initialized = false
lateinit var media: Media
@@ -328,6 +338,11 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
setContentView(binding.root)
//Initialize
castContext = CastContext.getSharedInstance(this)
castPlayer = CastPlayer(castContext)
castPlayer.setSessionAvailabilityListener(this)
WindowCompat.setDecorFitsSystemWindows(window, false)
hideSystemBars()
@@ -387,7 +402,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
orientationListener =
object : OrientationEventListener(this, SensorManager.SENSOR_DELAY_UI) {
override fun onOrientationChanged(orientation: Int) {
println(orientation)
if (orientation in 45..135) {
if (rotation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) exoRotate.visibility =
View.VISIBLE
@@ -466,12 +480,18 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
if (isInitialized) {
isPlayerPlaying = exoPlayer.isPlaying
(exoPlay.drawable as Animatable?)?.start()
if (isPlayerPlaying) {
if (isPlayerPlaying || castPlayer.isPlaying) {
Glide.with(this).load(R.drawable.anim_play_to_pause).into(exoPlay)
exoPlayer.pause()
castPlayer.pause()
} else {
Glide.with(this).load(R.drawable.anim_pause_to_play).into(exoPlay)
exoPlayer.play()
if (!castPlayer.isPlaying && castPlayer.currentMediaItem != null) {
Glide.with(this).load(R.drawable.anim_pause_to_play).into(exoPlay)
castPlayer.play()
} else if (!isPlayerPlaying) {
Glide.with(this).load(R.drawable.anim_pause_to_play).into(exoPlay)
exoPlayer.play()
}
}
}
}
@@ -1074,11 +1094,10 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
//Cast
if (settings.cast) {
playerView.findViewById<ImageButton>(R.id.exo_cast).apply {
playerView.findViewById<MediaRouteButton>(R.id.exo_cast).apply {
visibility = View.VISIBLE
setSafeOnClickListener {
cast()
}
CastButtonFactory.setUpMediaRouteButton(context, this)
dialogFactory = CustomCastThemeFactory()
}
}
@@ -1101,7 +1120,21 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
if (settings.cursedSpeeds)
arrayOf(1f, 1.25f, 1.5f, 1.75f, 2f, 2.5f, 3f, 4f, 5f, 10f, 25f, 50f)
else
arrayOf(0.25f, 0.33f, 0.5f, 0.66f, 0.75f, 1f, 1.15f, 1.25f, 1.33f, 1.5f, 1.66f, 1.75f, 2f)
arrayOf(
0.25f,
0.33f,
0.5f,
0.66f,
0.75f,
1f,
1.15f,
1.25f,
1.33f,
1.5f,
1.66f,
1.75f,
2f
)
val speedsName = speeds.map { "${it}x" }.toTypedArray()
var curSpeed = loadData("${media.id}_speed", this) ?: settings.defaultSpeed
@@ -1278,7 +1311,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
if (video?.format == VideoType.CONTAINER || (loadData<Int>("settings_download_manager")
?: 0) != 0
) {
but.visibility = View.VISIBLE
//but.visibility = View.VISIBLE TODO: not sure if this is needed
but.setOnClickListener {
download(this, episode, animeTitle.text.toString())
}
@@ -1303,8 +1336,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
dataSource
}
cacheFactory = CacheDataSource.Factory().apply {
setCache(simpleCache)
setCache(Helper.getSimpleCache(this@ExoplayerView))
setUpstreamDataSourceFactory(dataSourceFactory)
setCacheWriteDataSinkFactory(null)
}
val mimeType = when (video?.format) {
@@ -1313,15 +1347,33 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
else -> MimeTypes.APPLICATION_MP4
}
val builder = MediaItem.Builder().setUri(video!!.file.url).setMimeType(mimeType)
logger("url: ${video!!.file.url}")
logger("mimeType: $mimeType")
val downloadedMediaItem = if (ext.server.offline) {
val key = ext.server.name
downloadId = getSharedPreferences(getString(R.string.anime_downloads), MODE_PRIVATE)
.getString(key, null)
if (downloadId != null) {
Helper.downloadManager(this)
.downloadIndex.getDownload(downloadId!!)?.request?.toMediaItem()
} else {
snackString("Download not found")
null
}
} else null
if (sub != null) {
val listofnotnullsubs = immutableListOf(sub).filterNotNull()
builder.setSubtitleConfigurations(listofnotnullsubs)
mediaItem = if (downloadedMediaItem == null) {
val builder = MediaItem.Builder().setUri(video!!.file.url).setMimeType(mimeType)
logger("url: ${video!!.file.url}")
logger("mimeType: $mimeType")
if (sub != null) {
val listofnotnullsubs = immutableListOf(sub).filterNotNull()
builder.setSubtitleConfigurations(listofnotnullsubs)
}
builder.build()
} else {
downloadedMediaItem
}
mediaItem = builder.build()
//Source
exoSource.setOnClickListener {
@@ -1443,7 +1495,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
exoPlayer.release()
VideoCache.release()
mediaSession?.release()
if(DiscordServiceRunningSingleton.running) {
if (DiscordServiceRunningSingleton.running) {
val stopIntent = Intent(this, DiscordService::class.java)
DiscordServiceRunningSingleton.running = false
stopService(stopIntent)
@@ -1483,7 +1535,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
super.onPause()
orientationListener?.disable()
if (isInitialized) {
playerView.player?.pause()
if (!castPlayer.isPlaying) {
playerView.player?.pause()
}
saveData(
"${media.id}_${media.anime!!.selectedEpisode}",
exoPlayer.currentPosition,
@@ -1504,7 +1558,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
}
override fun onStop() {
playerView.player?.pause()
if (!castPlayer.isPlaying) {
playerView.player?.pause()
}
super.onStop()
}
@@ -1576,7 +1632,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
if (isInitialized) {
if (exoPlayer.currentPosition.toFloat() / exoPlayer.duration > settings.watchPercentage) {
preloading = true
nextEpisode(false) { i ->
nextEpisode(false) { i -> //TODO: make sure this works for offline episodes
val ep = episodes[episodeArr[currentEpisodeIndex + i]] ?: return@nextEpisode
val selected = media.selected ?: return@nextEpisode
lifecycleScope.launch(Dispatchers.IO) {
@@ -1797,7 +1853,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
// Enter PiP Mode
@Suppress("DEPRECATION")
@RequiresApi(Build.VERSION_CODES.N)
private fun enterPipMode() {
wasPlaying = isPlayerPlaying
if (!pipEnabled) return
@@ -1870,6 +1925,47 @@ class ExoplayerView : AppCompatActivity(), Player.Listener {
}
}
private fun startCastPlayer() {
castPlayer.setMediaItem(mediaItem)
castPlayer.prepare()
playerView.player = castPlayer
exoPlayer.stop()
castPlayer.addListener(object : Player.Listener {
//if the player is paused changed, we want to update the UI
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
super.onPlayWhenReadyChanged(playWhenReady, reason)
if (playWhenReady) {
(exoPlay.drawable as Animatable?)?.start()
Glide.with(this@ExoplayerView)
.load(R.drawable.anim_play_to_pause)
.into(exoPlay)
} else {
(exoPlay.drawable as Animatable?)?.start()
Glide.with(this@ExoplayerView)
.load(R.drawable.anim_pause_to_play)
.into(exoPlay)
}
}
})
}
private fun startExoPlayer() {
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
playerView.player = exoPlayer
castPlayer.stop()
}
override fun onCastSessionAvailable() {
startCastPlayer()
}
override fun onCastSessionUnavailable() {
startExoPlayer()
}
@SuppressLint("ViewConstructor")
class ExtendedTimeBar(
context: Context,

View File

@@ -20,6 +20,7 @@ import ani.dantotsu.*
import ani.dantotsu.databinding.BottomSheetSelectorBinding
import ani.dantotsu.databinding.ItemStreamBinding
import ani.dantotsu.databinding.ItemUrlBinding
import ani.dantotsu.download.video.Helper
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.others.Download.download
@@ -214,7 +215,8 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
override fun onBindViewHolder(holder: StreamViewHolder, position: Int) {
val extractor = links[position]
holder.binding.streamName.text = extractor.server.name
holder.binding.streamName.text = ""//extractor.server.name
holder.binding.streamName.visibility = View.GONE
holder.binding.streamRecyclerView.layoutManager = LinearLayoutManager(requireContext())
holder.binding.streamRecyclerView.adapter = VideoAdapter(extractor)
@@ -256,10 +258,10 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
override fun onBindViewHolder(holder: UrlViewHolder, position: Int) {
val binding = holder.binding
val video = extractor.videos[position]
binding.urlQuality.text =
if (video.quality != null) "${video.quality}p" else "Default Quality"
binding.urlNote.text = video.extraNote ?: ""
binding.urlNote.visibility = if (video.extraNote != null) View.VISIBLE else View.GONE
//binding.urlQuality.text =
// if (video.quality != null) "${video.quality}p" else "Default Quality"
//binding.urlNote.text = video.extraNote ?: ""
//binding.urlNote.visibility = if (video.extraNote != null) View.VISIBLE else View.GONE
binding.urlDownload.visibility = View.VISIBLE
binding.urlDownload.setSafeOnClickListener {
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedExtractor =
@@ -267,11 +269,24 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!.selectedVideo =
position
binding.urlDownload.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
download(
requireActivity(),
media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!,
media!!.userPreferredName
)
//download(
// requireActivity(),
// media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!,
// media!!.userPreferredName
//)
val episode = media!!.anime!!.episodes!![media!!.anime!!.selectedEpisode!!]!!
val video = if (extractor.videos.size > episode.selectedVideo) extractor.videos[episode.selectedVideo] else null
if (video != null) {
Helper.startAnimeDownloadService(
requireActivity(),
media!!.userPreferredName,
episode.number,
video,
null,
media,
episode.thumb?.url?: media!!.banner?: media!!.cover
)
}
dismiss()
}
if (video.format == VideoType.CONTAINER) {
@@ -282,11 +297,13 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
"#.##"
).format(video.size ?: 0).toString() + " MB"))
} else {
binding.urlQuality.text = "Multi Quality"
if ((loadData<Int>("settings_download_manager") ?: 0) == 0) {
binding.urlDownload.visibility = View.GONE
////binding.urlDownload.visibility = View.GONE
}
}
binding.urlNote.visibility = View.VISIBLE
binding.urlNote.text = video.format.name
binding.urlQuality.text = extractor.server.name
}
override fun getItemCount(): Int = extractor.videos.size

View File

@@ -2,12 +2,14 @@ package ani.dantotsu.media.manga
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ArrayAdapter
import android.widget.CheckBox
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.NumberPicker
@@ -15,12 +17,14 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.*
import ani.dantotsu.databinding.DialogLayoutBinding
import ani.dantotsu.databinding.ItemAnimeWatchBinding
import ani.dantotsu.databinding.ItemChipBinding
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.SourceSearchDialogFragment
import ani.dantotsu.media.anime.handleProgress
import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.parsers.DynamicMangaParser
import ani.dantotsu.parsers.MangaReadSources
import ani.dantotsu.parsers.MangaSources
@@ -30,6 +34,7 @@ import com.google.android.material.chip.Chip
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
class MangaReadAdapter(
private val media: Media,
private val fragment: MangaReadFragment,
@@ -46,7 +51,7 @@ class MangaReadAdapter(
val bind = ItemAnimeWatchBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(bind)
}
private var nestedDialog: AlertDialog? = null
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val binding = holder.binding
@@ -117,7 +122,7 @@ class MangaReadAdapter(
}
}
//Subscription
//Grids
subscribe = MediaDetailsActivity.PopImageButton(
fragment.lifecycleScope,
binding.animeSourceSubscribe,
@@ -136,98 +141,130 @@ class MangaReadAdapter(
openSettings(fragment.requireContext(), getChannelId(true, media.id))
}
//Icons
binding.animeSourceGrid.visibility = View.GONE
var reversed = media.selected!!.recyclerReversed
var style = media.selected!!.recyclerStyle ?: fragment.uiSettings.mangaDefaultView
binding.animeSourceTop.rotation = if (reversed) -90f else 90f
binding.animeSourceTop.setOnClickListener {
reversed = !reversed
binding.animeSourceTop.rotation = if (reversed) -90f else 90f
fragment.onIconPressed(style, reversed)
}
binding.animeNestedButton.setOnClickListener {
binding.animeScanlatorTop.setOnClickListener {
val dialogView =
LayoutInflater.from(currContext()).inflate(R.layout.custom_dialog_layout, null)
val checkboxContainer = dialogView.findViewById<LinearLayout>(R.id.checkboxContainer)
LayoutInflater.from(fragment.requireContext()).inflate(R.layout.dialog_layout, null)
val dialogBinding = DialogLayoutBinding.bind(dialogView)
// Dynamically add checkboxes
options.forEach { option ->
val checkBox = CheckBox(currContext()).apply {
text = option
}
//set checked if it's already selected
if (media.selected!!.scanlators != null) {
checkBox.isChecked = media.selected!!.scanlators?.contains(option) != true
scanlatorSelectionListener?.onScanlatorsSelected()
} else {
checkBox.isChecked = true
}
checkboxContainer.addView(checkBox)
var run = false
var reversed = media.selected!!.recyclerReversed
var style = media.selected!!.recyclerStyle ?: fragment.uiSettings.animeDefaultView
dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down"
dialogBinding.animeSourceTop.setOnClickListener {
reversed = !reversed
dialogBinding.animeSourceTop.rotation = if (reversed) -90f else 90f
dialogBinding.sortText.text = if (reversed) "Down to Up" else "Up to Down"
run = true
}
// Create AlertDialog
val dialog = AlertDialog.Builder(currContext(), R.style.MyPopup)
.setView(dialogView)
.setPositiveButton("OK") { dialog, which ->
//add unchecked to hidden
hiddenScanlators.clear()
for (i in 0 until checkboxContainer.childCount) {
val checkBox = checkboxContainer.getChildAt(i) as CheckBox
if (!checkBox.isChecked) {
hiddenScanlators.add(checkBox.text.toString())
}
}
fragment.onScanlatorChange(hiddenScanlators)
scanlatorSelectionListener?.onScanlatorsSelected()
}
.setNegativeButton("Cancel", null)
.show()
dialog.window?.setDimAmount(0.8f)
}
binding.animeDownloadTop.setOnClickListener {
//Alert dialog asking for the number of chapters to download
val alertDialog = AlertDialog.Builder(currContext(), R.style.MyPopup)
alertDialog.setTitle("Multi Chapter Downloader")
alertDialog.setMessage("Enter the number of chapters to download")
val input = NumberPicker(currContext())
input.minValue = 1
input.maxValue = 20
input.value = 1
alertDialog.setView(input)
alertDialog.setPositiveButton("OK") { dialog, which ->
fragment.multiDownload(input.value)
//Grids
dialogBinding.animeSourceGrid.visibility = View.GONE
var selected = when (style) {
0 -> dialogBinding.animeSourceList
1 -> dialogBinding.animeSourceCompact
else -> dialogBinding.animeSourceList
}
when (style) {
0 -> dialogBinding.layoutText.text = "List"
1 -> dialogBinding.layoutText.text = "Compact"
else -> dialogBinding.animeSourceList
}
alertDialog.setNegativeButton("Cancel") { dialog, _ -> dialog.cancel() }
val dialog = alertDialog.show()
dialog.window?.setDimAmount(0.8f)
}
var selected = when (style) {
0 -> binding.animeSourceList
1 -> binding.animeSourceCompact
else -> binding.animeSourceList
}
selected.alpha = 1f
fun selected(it: ImageView) {
selected.alpha = 0.33f
selected = it
selected.alpha = 1f
}
binding.animeSourceList.setOnClickListener {
selected(it as ImageView)
style = 0
fragment.onIconPressed(style, reversed)
}
binding.animeSourceCompact.setOnClickListener {
selected(it as ImageView)
style = 1
fragment.onIconPressed(style, reversed)
}
fun selected(it: ImageButton) {
selected.alpha = 0.33f
selected = it
selected.alpha = 1f
}
dialogBinding.animeSourceList.setOnClickListener {
selected(it as ImageButton)
style = 0
dialogBinding.layoutText.text = "List"
run = true
}
dialogBinding.animeSourceCompact.setOnClickListener {
selected(it as ImageButton)
style = 1
dialogBinding.layoutText.text = "Compact"
run = true
}
//Multi download
dialogBinding.downloadNo.text = "0"
dialogBinding.animeDownloadTop.setOnClickListener {
//Alert dialog asking for the number of chapters to download
val alertDialog = AlertDialog.Builder(currContext(), R.style.MyPopup)
alertDialog.setTitle("Multi Chapter Downloader")
alertDialog.setMessage("Enter the number of chapters to download")
val input = NumberPicker(currContext())
input.minValue = 1
input.maxValue = 20
input.value = 1
alertDialog.setView(input)
alertDialog.setPositiveButton("OK") { dialog, which ->
dialogBinding.downloadNo.text = "${input.value}"
}
alertDialog.setNegativeButton("Cancel") { dialog, _ -> dialog.cancel() }
val dialog = alertDialog.show()
dialog.window?.setDimAmount(0.8f)
}
//Scanlator
dialogBinding.animeScanlatorTop.setOnClickListener {
val dialogView2 =
LayoutInflater.from(currContext()).inflate(R.layout.custom_dialog_layout, null)
val checkboxContainer = dialogView2.findViewById<LinearLayout>(R.id.checkboxContainer)
// Dynamically add checkboxes
options.forEach { option ->
val checkBox = CheckBox(currContext()).apply {
text = option
}
//set checked if it's already selected
if (media.selected!!.scanlators != null) {
checkBox.isChecked = media.selected!!.scanlators?.contains(option) != true
scanlatorSelectionListener?.onScanlatorsSelected()
} else {
checkBox.isChecked = true
}
checkboxContainer.addView(checkBox)
}
// Create AlertDialog
val dialog = AlertDialog.Builder(currContext(), R.style.MyPopup)
.setView(dialogView2)
.setPositiveButton("OK") { dialog, which ->
//add unchecked to hidden
hiddenScanlators.clear()
for (i in 0 until checkboxContainer.childCount) {
val checkBox = checkboxContainer.getChildAt(i) as CheckBox
if (!checkBox.isChecked) {
hiddenScanlators.add(checkBox.text.toString())
}
}
fragment.onScanlatorChange(hiddenScanlators)
scanlatorSelectionListener?.onScanlatorsSelected()
}
.setNegativeButton("Cancel", null)
.show()
dialog.window?.setDimAmount(0.8f)
}
nestedDialog = AlertDialog.Builder(fragment.requireContext() , R.style.MyPopup)
.setTitle("Options")
.setView(dialogView)
.setPositiveButton("OK") { _, _ ->
if(run) fragment.onIconPressed(style, reversed)
if (dialogBinding.downloadNo.text != "0"){
fragment.multiDownload(dialogBinding.downloadNo.text.toString().toInt())
}
}
.setNegativeButton("Cancel") { _, _ ->
}
.create()
nestedDialog?.show()
}
//Chapter Handling
handleChapters()
}
@@ -385,12 +422,15 @@ class MangaReadAdapter(
parser.extension.sources.firstOrNull()?.lang ?: "Unknown"
)
}
binding?.animeSourceLanguage?.setAdapter(
ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
parser.extension.sources.map { it.lang })
val adapter = ArrayAdapter(
fragment.requireContext(),
R.layout.item_dropdown,
parser.extension.sources.sortedBy { it.lang }.map { LanguageMapper.mapLanguageCodeToName(it.lang) }
)
val items = adapter.count
if (items > 1) binding?.animeSourceLanguageContainer?.visibility = View.VISIBLE else binding?.animeSourceLanguageContainer?.visibility = View.GONE
binding?.animeSourceLanguage?.setAdapter(adapter)
}
}

View File

@@ -29,7 +29,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.*
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
import ani.dantotsu.download.Download
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.manga.MangaDownloaderService
import ani.dantotsu.download.manga.MangaServiceDataSingleton
@@ -37,6 +37,7 @@ import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.manga.mangareader.ChapterLoaderDialog
import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.parsers.DynamicMangaParser
import ani.dantotsu.parsers.HMangaSources
import ani.dantotsu.parsers.MangaParser
@@ -166,7 +167,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
chapterAdapter =
MangaChapterAdapter(style ?: uiSettings.mangaDefaultView, media, this)
for (download in downloadManager.mangaDownloads) {
for (download in downloadManager.mangaDownloadedTypes) {
chapterAdapter.stopDownload(download.chapter)
}
@@ -196,26 +197,28 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
override fun onScanlatorsSelected() {
updateChapters()
}
fun multiDownload(n: Int) {
//get last viewed chapter
val selected = media.userProgress
val chapters = media.manga?.chapters?.values?.toList()
//filter by selected language
val progressChapterIndex = chapters?.indexOfFirst { MangaNameAdapter.findChapterNumber(it.number)?.toInt() == selected }?:0
if (progressChapterIndex < 0 || n < 1) return
val chaptersToDownload = chapters?.subList(
progressChapterIndex + 1,
progressChapterIndex + n + 1
)
if (chaptersToDownload != null) {
for (chapter in chaptersToDownload) {
onMangaChapterDownloadClick(chapter.title!!)
}
}
val progressChapterIndex = (chapters?.indexOfFirst { MangaNameAdapter.findChapterNumber(it.number)?.toInt() == selected } ?: 0) + 1
if (progressChapterIndex < 0 || n < 1 || chapters == null) return
// Calculate the end index
val endIndex = minOf(progressChapterIndex + n, chapters.size)
//make sure there are enough chapters
val chaptersToDownload = chapters.subList(progressChapterIndex, endIndex)
for (chapter in chaptersToDownload) {
onMangaChapterDownloadClick(chapter.title!!)
}
}
private fun updateChapters() {
val loadedChapters = model.getMangaChapters().value
if (loadedChapters != null) {
@@ -355,19 +358,19 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
if (show) View.GONE else View.VISIBLE
}
}
var itemSelected = false
val allSettings = pkg.sources.filterIsInstance<ConfigurableSource>()
if (allSettings.isNotEmpty()) {
var selectedSetting = allSettings[0]
if (allSettings.size > 1) {
val names = allSettings.map { it.lang }.toTypedArray()
val names = allSettings.sortedBy { it.lang }.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
var selectedIndex = 0
val dialog = AlertDialog.Builder(requireContext())
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
.setTitle("Select a Source")
.setSingleChoiceItems(names, selectedIndex) { _, which ->
.setSingleChoiceItems(names, selectedIndex) { dialog, which ->
selectedIndex = which
}
.setPositiveButton("OK") { dialog, _ ->
selectedSetting = allSettings[selectedIndex]
itemSelected = true
dialog.dismiss()
// Move the fragment transaction here
@@ -382,10 +385,10 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
.addToBackStack(null)
.commit()
}
.setNegativeButton("Cancel") { dialog, _ ->
dialog.cancel()
changeUIVisibility(true)
return@setNegativeButton
.setOnDismissListener {
if (!itemSelected) {
changeUIVisibility(true)
}
}
.show()
dialog.window?.setDimAmount(0.8f)
@@ -481,10 +484,10 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
fun onMangaChapterRemoveDownloadClick(i: String) {
downloadManager.removeDownload(
Download(
DownloadedType(
media.nameMAL ?: media.nameRomaji,
i,
Download.Type.MANGA
DownloadedType.Type.MANGA
)
)
chapterAdapter.deleteDownload(i)
@@ -499,10 +502,10 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
// Remove the download from the manager and update the UI
downloadManager.removeDownload(
Download(
DownloadedType(
media.nameMAL ?: media.nameRomaji,
i,
Download.Type.MANGA
DownloadedType.Type.MANGA
)
)
chapterAdapter.purgeDownload(i)

View File

@@ -89,7 +89,7 @@ abstract class BaseImageAdapter(
}
} else {
val detector = GestureDetectorCompat(view.context, object : GesturesListener() {
override fun onSingleClick(event: MotionEvent) = activity.handleController()
override fun onSingleClick(event: MotionEvent) = activity.handleController(event = event)
})
view.findViewById<View>(R.id.imgProgCover).apply {
setOnTouchListener { _, event ->
@@ -112,6 +112,9 @@ abstract class BaseImageAdapter(
activity.lifecycleScope.launch { loadImage(holder.bindingAdapterPosition, view) }
}
abstract fun isZoomed(): Boolean
abstract fun setZoom(zoom: Float)
abstract suspend fun loadImage(position: Int, parent: View): Boolean
companion object {

View File

@@ -91,4 +91,14 @@ open class ImageAdapter(
}
override fun getItemCount(): Int = images.size
override fun isZoomed(): Boolean {
val imageView = activity.findViewById<SubsamplingScaleImageView>(R.id.imgProgImageNoGestures)
return imageView.scale > imageView.minScale
}
override fun setZoom(zoom: Float) {
val imageView = activity.findViewById<SubsamplingScaleImageView>(R.id.imgProgImageNoGestures)
imageView.setScaleAndCenter(zoom, imageView.center)
}
}

View File

@@ -6,6 +6,7 @@ import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Bitmap
import android.os.Build
import android.os.Bundle
@@ -702,8 +703,60 @@ class MangaReaderActivity : AppCompatActivity() {
goneTimer.schedule(timerTask, controllerDuration)
}
fun handleController(shouldShow: Boolean? = null) {
enum class pressPos {
LEFT, RIGHT, CENTER
}
fun handleController(shouldShow: Boolean? = null, event: MotionEvent? = null) {
var pressLocation = pressPos.CENTER
if (!sliding) {
if (event != null && settings.default.layout == PAGED) {
if (event.action != MotionEvent.ACTION_UP) return
val x = event.rawX.toInt()
val y = event.rawY.toInt()
val screenWidth = Resources.getSystem().displayMetrics.widthPixels
//if in the 1st 1/5th of the screen width, left and lower than 1/5th of the screen height, left
if (screenWidth / 5 in (x + 1)..<y) {
pressLocation = if (settings.default.direction == RIGHT_TO_LEFT) {
pressPos.RIGHT
} else {
pressPos.LEFT
}
}
//if in the last 1/5th of the screen width, right and lower than 1/5th of the screen height, right
else if (x > screenWidth - screenWidth / 5 && y > screenWidth / 5) {
pressLocation = if (settings.default.direction == RIGHT_TO_LEFT) {
pressPos.LEFT
} else {
pressPos.RIGHT
}
}
}
// if pressLocation is left or right go to previous or next page (paged mode only)
if (pressLocation == pressPos.LEFT) {
if (binding.mangaReaderPager.currentItem > 0) {
//if the current images zoomed in, go back to normal before going to previous page
if (imageAdapter?.isZoomed() == true) {
imageAdapter?.setZoom(1f)
}
binding.mangaReaderPager.currentItem -= 1
return
}
} else if (pressLocation == pressPos.RIGHT) {
if (binding.mangaReaderPager.currentItem < maxChapterPage - 1) {
//if the current images zoomed in, go back to normal before going to next page
if (imageAdapter?.isZoomed() == true) {
imageAdapter?.setZoom(1f)
}
//if right to left, go to previous page
binding.mangaReaderPager.currentItem += 1
return
}
}
if (!settings.showSystemBars) {
hideBars()
checkNotch()
@@ -796,7 +849,7 @@ class MangaReaderActivity : AppCompatActivity() {
private fun progress(runnable: Runnable) {
if (maxChapterPage - currentChapterPage <= 1 && Anilist.userid != null) {
if (showProgressDialog) {
if (showProgressDialog) {
val dialogView = layoutInflater.inflate(R.layout.item_custom_dialog, null)
val checkbox = dialogView.findViewById<CheckBox>(R.id.dialog_checkbox)
@@ -805,8 +858,9 @@ class MangaReaderActivity : AppCompatActivity() {
saveData("${media.id}_progressDialog", isChecked)
showProgressDialog = !isChecked
}
val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getBoolean("incognito", false) ?: false
val incognito =
currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
?.getBoolean("incognito", false) ?: false
AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.title_update_progress))
.apply {
@@ -818,7 +872,7 @@ class MangaReaderActivity : AppCompatActivity() {
.setCancelable(false)
.setPositiveButton(getString(R.string.yes)) { dialog, _ ->
saveData("${media.id}_save_progress", true)
updateProgress(
updateProgress(
media,
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
.toString()

View File

@@ -22,7 +22,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
import ani.dantotsu.download.Download
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.novel.NovelDownloaderService
import ani.dantotsu.download.novel.NovelServiceDataSingleton
@@ -92,10 +92,10 @@ class NovelReadFragment : Fragment(),
override fun downloadedCheckWithStart(novel: ShowResponse): Boolean {
val downloadsManager = Injekt.get<DownloadsManager>()
if (downloadsManager.queryDownload(
Download(
DownloadedType(
media.nameMAL ?: media.nameRomaji,
novel.name,
Download.Type.NOVEL
DownloadedType.Type.NOVEL
)
)
) {
@@ -124,10 +124,10 @@ class NovelReadFragment : Fragment(),
override fun downloadedCheck(novel: ShowResponse): Boolean {
val downloadsManager = Injekt.get<DownloadsManager>()
return downloadsManager.queryDownload(
Download(
DownloadedType(
media.nameMAL ?: media.nameRomaji,
novel.name,
Download.Type.NOVEL
DownloadedType.Type.NOVEL
)
)
}
@@ -135,10 +135,10 @@ class NovelReadFragment : Fragment(),
override fun deleteDownload(novel: ShowResponse) {
val downloadsManager = Injekt.get<DownloadsManager>()
downloadsManager.removeDownload(
Download(
DownloadedType(
media.nameMAL ?: media.nameRomaji,
novel.name,
Download.Type.NOVEL
DownloadedType.Type.NOVEL
)
)
}

View File

@@ -36,7 +36,7 @@ import ani.dantotsu.saveData
import ani.dantotsu.setSafeOnClickListener
import ani.dantotsu.settings.CurrentNovelReaderSettings
import ani.dantotsu.settings.CurrentReaderSettings
import ani.dantotsu.settings.NovelReaderSettings
import ani.dantotsu.settings.ReaderSettings
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.snackString
import ani.dantotsu.themes.ThemeManager
@@ -62,7 +62,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
private lateinit var binding: ActivityNovelReaderBinding
private val scope = lifecycleScope
lateinit var settings: NovelReaderSettings
lateinit var settings: ReaderSettings
private lateinit var uiSettings: UserInterfaceSettings
private var notchHeight: Int? = null
@@ -159,9 +159,8 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
ThemeManager(this).applyTheme()
binding = ActivityNovelReaderBinding.inflate(layoutInflater)
setContentView(binding.root)
settings = loadData("novel_reader_settings", this)
?: NovelReaderSettings().apply { saveData("novel_reader_settings", this) }
settings = loadData("reader_settings", this)
?: ReaderSettings().apply { saveData("reader_settings", this) }
uiSettings = loadData("ui_settings", this)
?: UserInterfaceSettings().also { saveData("ui_settings", it) }
@@ -271,7 +270,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
binding.bookReader.getAppearance {
currentTheme = it
themes.add(0, it)
settings.default = loadData("${sanitizedBookId}_current_settings") ?: settings.default
settings.defaultLN = loadData("${sanitizedBookId}_current_settings") ?: settings.defaultLN
applySettings()
}
@@ -323,7 +322,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
return when (event.keyCode) {
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_PAGE_UP -> {
if (event.keyCode == KeyEvent.KEYCODE_VOLUME_UP)
if (!settings.default.volumeButtons)
if (!settings.defaultLN.volumeButtons)
return false
if (event.action == KeyEvent.ACTION_DOWN) {
onVolumeUp?.invoke()
@@ -333,7 +332,7 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_PAGE_DOWN -> {
if (event.keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
if (!settings.default.volumeButtons)
if (!settings.defaultLN.volumeButtons)
return false
if (event.action == KeyEvent.ACTION_DOWN) {
onVolumeDown?.invoke()
@@ -349,13 +348,18 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
fun applySettings() {
saveData("${sanitizedBookId}_current_settings", settings.default)
saveData("${sanitizedBookId}_current_settings", settings.defaultLN)
hideBars()
if(settings.defaultLN.useOledTheme) {
themes.forEach { theme ->
theme.darkBg = Color.parseColor("#000000")
}
}
currentTheme =
themes.first { it.name.equals(settings.default.currentThemeName, ignoreCase = true) }
themes.first { it.name.equals(settings.defaultLN.currentThemeName, ignoreCase = true) }
when (settings.default.layout) {
when (settings.defaultLN.layout) {
CurrentNovelReaderSettings.Layouts.PAGED -> {
currentTheme?.flow = ReaderFlow.PAGINATED
}
@@ -366,22 +370,22 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
}
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
when (settings.default.dualPageMode) {
when (settings.defaultLN.dualPageMode) {
CurrentReaderSettings.DualPageModes.No -> currentTheme?.maxColumnCount = 1
CurrentReaderSettings.DualPageModes.Automatic -> currentTheme?.maxColumnCount = 2
CurrentReaderSettings.DualPageModes.Force -> requestedOrientation =
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
}
currentTheme?.lineHeight = settings.default.lineHeight
currentTheme?.gap = settings.default.margin
currentTheme?.maxInlineSize = settings.default.maxInlineSize
currentTheme?.maxBlockSize = settings.default.maxBlockSize
currentTheme?.useDark = settings.default.useDarkTheme
currentTheme?.lineHeight = settings.defaultLN.lineHeight
currentTheme?.gap = settings.defaultLN.margin
currentTheme?.maxInlineSize = settings.defaultLN.maxInlineSize
currentTheme?.maxBlockSize = settings.defaultLN.maxBlockSize
currentTheme?.useDark = settings.defaultLN.useDarkTheme
currentTheme?.let { binding.bookReader.setAppearance(it) }
if (settings.default.keepScreenOn) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
if (settings.defaultLN.keepScreenOn) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}

View File

@@ -30,8 +30,7 @@ class NovelReaderSettingsDialogFragment : BottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val activity = requireActivity() as NovelReaderActivity
val settings = activity.settings.default
val settings = activity.settings.defaultLN
val themeLabels = activity.themes.map { it.name }
binding.themeSelect.adapter =
NoPaddingArrayAdapter(activity, R.layout.item_dropdown, themeLabels)
@@ -49,7 +48,11 @@ class NovelReaderSettingsDialogFragment : BottomSheetDialogFragment() {
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
binding.useOledTheme.isChecked = settings.useOledTheme
binding.useOledTheme.setOnCheckedChangeListener { _, isChecked ->
settings.useOledTheme = isChecked
activity.applySettings()
}
val layoutList = listOf(
binding.paged,
binding.continuous
@@ -173,6 +176,20 @@ class NovelReaderSettingsDialogFragment : BottomSheetDialogFragment() {
binding.maxBlockSize.setText(value.toString())
activity.applySettings()
}
}
binding.incrementMaxBlockSize.setOnClickListener {
val value = binding.maxBlockSize.text.toString().toIntOrNull() ?: 720
settings.maxBlockSize = value + 10
binding.maxBlockSize.setText(settings.maxBlockSize.toString())
activity.applySettings()
}
binding.decrementMaxBlockSize.setOnClickListener {
val value = binding.maxBlockSize.text.toString().toIntOrNull() ?: 720
settings.maxBlockSize = value - 10
binding.maxBlockSize.setText(settings.maxBlockSize.toString())
activity.applySettings()
}
binding.useDarkTheme.isChecked = settings.useDarkTheme

View File

@@ -1,6 +1,7 @@
package ani.dantotsu.media.user
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.util.TypedValue
import android.view.View
@@ -14,9 +15,11 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import ani.dantotsu.R
import ani.dantotsu.Refresh
import ani.dantotsu.currContext
import ani.dantotsu.databinding.ActivityListBinding
import ani.dantotsu.loadData
import ani.dantotsu.others.LangSet
import ani.dantotsu.saveData
import ani.dantotsu.settings.UserInterfaceSettings
import ani.dantotsu.themes.ThemeManager
import com.google.android.material.tabs.TabLayout
@@ -62,7 +65,7 @@ class ListActivity : AppCompatActivity() {
binding.listTabLayout.setTabTextColors(secondaryTextColor, primaryTextColor)
binding.listTabLayout.setSelectedTabIndicatorColor(primaryTextColor)
val uiSettings = loadData<UserInterfaceSettings>("ui_settings") ?: UserInterfaceSettings()
if (!uiSettings.immersiveModeList) {
if (!uiSettings.immersiveMode) {
this.window.statusBarColor =
ContextCompat.getColor(this, R.color.nav_bg_inv)
binding.root.fitsSystemWindows = true
@@ -78,8 +81,7 @@ class ListActivity : AppCompatActivity() {
setContentView(binding.root)
val anime = intent.getBooleanExtra("anime", true)
binding.listTitle.text =
intent.getStringExtra("username") + "'s " + (if (anime) "Anime" else "Manga") + " List"
binding.listTitle.text = (if (anime) "Anime" else "Manga") + " List"
binding.listTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
@@ -145,7 +147,8 @@ class ListActivity : AppCompatActivity() {
R.id.release -> "release"
else -> null
}
currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)?.edit()
?.putString("sort_order", sort)?.apply()
binding.listProgressBar.visibility = View.VISIBLE
binding.listViewPager.adapter = null
scope.launch {

View File

@@ -46,7 +46,7 @@ class ListFragment : Fragment() {
binding.listRecyclerView.layoutManager =
GridLayoutManager(
requireContext(),
if (grid!!) (screenWidth / 124f).toInt() else 1
if (grid!!) (screenWidth / 120f).toInt() else 1
)
binding.listRecyclerView.adapter = adapter
}

View File

@@ -6,25 +6,116 @@ class LanguageMapper {
fun mapLanguageCodeToName(code: String): String {
return when (code) {
"all" -> "Multi"
"af" -> "Afrikaans"
"am" -> "Amharic"
"ar" -> "Arabic"
"as" -> "Assamese"
"az" -> "Azerbaijani"
"be" -> "Belarusian"
"bg" -> "Bulgarian"
"bn" -> "Bengali"
"bs" -> "Bosnian"
"ca" -> "Catalan"
"ceb" -> "Cebuano"
"cs" -> "Czech"
"da" -> "Danish"
"de" -> "German"
"el" -> "Greek"
"en" -> "English"
"en-Us" -> "English (United States)"
"eo" -> "Esperanto"
"es" -> "Spanish"
"es-419" -> "Spanish (Latin America)"
"et" -> "Estonian"
"eu" -> "Basque"
"fa" -> "Persian"
"fi" -> "Finnish"
"fil" -> "Filipino"
"fo" -> "Faroese"
"fr" -> "French"
"ga" -> "Irish"
"gn" -> "Guarani"
"gu" -> "Gujarati"
"ha" -> "Hausa"
"he" -> "Hebrew"
"hi" -> "Hindi"
"hr" -> "Croatian"
"ht" -> "Haitian Creole"
"hu" -> "Hungarian"
"hy" -> "Armenian"
"id" -> "Indonesian"
"ig" -> "Igbo"
"is" -> "Icelandic"
"it" -> "Italian"
"ja" -> "Japanese"
"jv" -> "Javanese"
"ka" -> "Georgian"
"kk" -> "Kazakh"
"km" -> "Khmer"
"kn" -> "Kannada"
"ko" -> "Korean"
"ku" -> "Kurdish"
"ky" -> "Kyrgyz"
"la" -> "Latin"
"lb" -> "Luxembourgish"
"lo" -> "Lao"
"lt" -> "Lithuanian"
"lv" -> "Latvian"
"mg" -> "Malagasy"
"mi" -> "Maori"
"mk" -> "Macedonian"
"ml" -> "Malayalam"
"mn" -> "Mongolian"
"mo" -> "Moldovan"
"mr" -> "Marathi"
"ms" -> "Malay"
"mt" -> "Maltese"
"my" -> "Burmese"
"ne" -> "Nepali"
"nl" -> "Dutch"
"no" -> "Norwegian"
"ny" -> "Chichewa"
"pl" -> "Polish"
"pt" -> "Portuguese"
"pt-BR" -> "Portuguese (Brazil)"
"pt-PT" -> "Portuguese (Portugal)"
"ps" -> "Pashto"
"ro" -> "Romanian"
"rm" -> "Romansh"
"ru" -> "Russian"
"sd" -> "Sindhi"
"sh" -> "Serbo-Croatian"
"si" -> "Sinhala"
"sk" -> "Slovak"
"sl" -> "Slovenian"
"sm" -> "Samoan"
"sn" -> "Shona"
"so" -> "Somali"
"sq" -> "Albanian"
"sr" -> "Serbian"
"st" -> "Southern Sotho"
"sv" -> "Swedish"
"sw" -> "Swahili"
"ta" -> "Tamil"
"te" -> "Telugu"
"tg" -> "Tajik"
"th" -> "Thai"
"ti" -> "Tigrinya"
"tk" -> "Turkmen"
"tl" -> "Tagalog"
"to" -> "Tongan"
"tr" -> "Turkish"
"uk" -> "Ukrainian"
"ur" -> "Urdu"
"uz" -> "Uzbek"
"vi" -> "Vietnamese"
"yo" -> "Yoruba"
"zh" -> "Chinese"
"zh-Hans" -> "Chinese (Simplified)"
else -> ""
"zh-Hant" -> "Chinese (Traditional)"
"zh-Habt" -> "Chinese (Hakka)"
"zu" -> "Zulu"
else -> code
}
}

View File

@@ -14,6 +14,7 @@ import ani.dantotsu.App.Companion.context
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.databinding.ActivityImageSearchBinding
import ani.dantotsu.initActivity
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.others.LangSet
import ani.dantotsu.themes.ThemeManager
@@ -49,6 +50,7 @@ class ImageSearchActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LangSet.setLocale(this)
initActivity(this)
ThemeManager(this).applyTheme()
binding = ActivityImageSearchBinding.inflate(layoutInflater)
setContentView(binding.root)

View File

@@ -12,11 +12,17 @@ object AnimeSources : WatchSources() {
suspend fun init(fromExtensions: StateFlow<List<AnimeExtension.Installed>>) {
// Initialize with the first value from StateFlow
val initialExtensions = fromExtensions.first()
list = createParsersFromExtensions(initialExtensions)
list = createParsersFromExtensions(initialExtensions) + Lazier(
{ OfflineAnimeParser() },
"Downloaded"
)
// Update as StateFlow emits new values
fromExtensions.collect { extensions ->
list = createParsersFromExtensions(extensions)
list = createParsersFromExtensions(extensions) + Lazier(
{ OfflineAnimeParser() },
"Downloaded"
)
}
}

View File

@@ -26,6 +26,7 @@ import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.interceptor.CloudflareBypassException
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
@@ -41,6 +42,7 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import okhttp3.Request
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
@@ -112,7 +114,14 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
seasonGroups.keys.sorted().flatMap { season ->
seasonGroups[season]?.sortedBy { it.episode_number }?.map { episode ->
if (episode.episode_number != 0f) { // Skip renumbering for episode number 0
episode.episode_number = episodeCounter++
val potentialNumber =
AnimeNameAdapter.findEpisodeNumber(episode.name)
if (potentialNumber != null) {
episode.episode_number = potentialNumber
} else {
episode.episode_number = episodeCounter
}
episodeCounter++
}
episode
} ?: emptyList()
@@ -202,7 +211,11 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
}
return Episode(
if (episodeNumberInt.toInt() != -1) {
episodeNumberInt.toString()
if (sEpisode.episode_number % 1 == 0f) {
episodeNumberInt.toInt().toString()
} else {
sEpisode.episode_number.toString()
}
} else {
sEpisode.name
},
@@ -603,13 +616,18 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
val fileName = queryPairs.find { it.first == "file" }?.second ?: ""
format = getVideoType(fileName)
// this solves a problem no one has, so I'm commenting it out for now
//if (format == null) {
// val networkHelper = Injekt.get<NetworkHelper>()
// format = headRequest(videoUrl, networkHelper)
//}
}
// If the format is still undetermined, log an error or handle it appropriately
// If the format is still undetermined, log an error
if (format == null) {
logger("Unknown video format: $videoUrl")
FirebaseCrashlytics.getInstance()
.recordException(Exception("Unknown video format: $videoUrl"))
//FirebaseCrashlytics.getInstance()
// .recordException(Exception("Unknown video format: $videoUrl"))
format = VideoType.CONTAINER
}
val headersMap: Map<String, String> =
@@ -620,12 +638,12 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
number,
format,
FileUrl(videoUrl, headersMap),
aniVideo.totalContentLength.toDouble()
if (aniVideo.totalContentLength == 0L) null else aniVideo.bytesDownloaded.toDouble()
)
}
private fun getVideoType(fileName: String): VideoType? {
return when {
val type = when {
fileName.endsWith(".mp4", ignoreCase = true) || fileName.endsWith(
".mkv",
ignoreCase = true
@@ -635,6 +653,47 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
fileName.endsWith(".mpd", ignoreCase = true) -> VideoType.DASH
else -> null
}
return type
}
private fun headRequest(fileName: String, networkHelper: NetworkHelper): VideoType? {
return try {
logger("attempting head request for $fileName")
val request = Request.Builder()
.url(fileName)
.head()
.build()
networkHelper.client.newCall(request).execute().use { response ->
val contentType = response.header("Content-Type")
val contentDisposition = response.header("Content-Disposition")
if (contentType != null) {
when {
contentType.contains("mpegurl", ignoreCase = true) -> VideoType.M3U8
contentType.contains("dash", ignoreCase = true) -> VideoType.DASH
contentType.contains("mp4", ignoreCase = true) -> VideoType.CONTAINER
else -> null
}
} else if (contentDisposition != null) {
when {
contentDisposition.contains("mpegurl", ignoreCase = true) -> VideoType.M3U8
contentDisposition.contains("dash", ignoreCase = true) -> VideoType.DASH
contentDisposition.contains("mp4", ignoreCase = true) -> VideoType.CONTAINER
else -> null
}
} else {
logger("failed head request for $fileName")
null
}
}
} catch (e: Exception) {
logger("Exception in headRequest: $e")
null
}
}
private fun TrackToSubtitle(track: Track): Subtitle {

View File

@@ -156,9 +156,9 @@ abstract class BaseParser {
}
fun checkIfVariablesAreEmpty() {
if (hostUrl.isEmpty()) throw UninitializedPropertyAccessException("Please provide a `hostUrl` for the Parser")
if (name.isEmpty()) throw UninitializedPropertyAccessException("Please provide a `name` for the Parser")
if (saveName.isEmpty()) throw UninitializedPropertyAccessException("Please provide a `saveName` for the Parser")
if (hostUrl.isEmpty()) throw UninitializedPropertyAccessException("Cannot find any installed extensions")
if (name.isEmpty()) throw UninitializedPropertyAccessException("Cannot find any installed extensions")
if (saveName.isEmpty()) throw UninitializedPropertyAccessException("Cannot find any installed extensions")
}
open var showUserText = ""

View File

@@ -46,6 +46,19 @@ abstract class WatchSources : BaseSources() {
sEpisode = it.sEpisode
)
}
} else if (parser is OfflineAnimeParser) {
parser.loadEpisodes(showLink, extra, SAnime.create()).forEach {
map[it.number] = Episode(
it.number,
it.link,
it.title,
it.description,
it.thumbnail,
it.isFiller,
extra = it.extra,
sEpisode = it.sEpisode
)
}
}
}
return map

View File

@@ -7,9 +7,6 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
object MangaSources : MangaReadSources() {
// Instantiate the static parser
private val offlineMangaParser by lazy { OfflineMangaParser() }
override var list: List<Lazier<BaseParser>> = emptyList()
suspend fun init(fromExtensions: StateFlow<List<MangaExtension.Installed>>) {

View File

@@ -0,0 +1,106 @@
package ani.dantotsu.parsers
import android.os.Environment
import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger
import ani.dantotsu.media.anime.AnimeNameAdapter
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.SEpisodeImpl
import me.xdrop.fuzzywuzzy.FuzzySearch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
class OfflineAnimeParser : AnimeParser() {
private val downloadManager = Injekt.get<DownloadsManager>()
override val name = "Offline"
override val saveName = "Offline"
override val hostUrl = "Offline"
override val isDubAvailableSeparately = false
override val isNSFW = false
override suspend fun loadEpisodes(
animeLink: String,
extra: Map<String, String>?,
sAnime: SAnime
): List<Episode> {
val directory = File(
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"${DownloadsManager.animeLocation}/$animeLink"
)
//get all of the folder names and add them to the list
val episodes = mutableListOf<Episode>()
if (directory.exists()) {
directory.listFiles()?.forEach {
if (it.isDirectory) {
val episode = Episode(
it.name,
"$animeLink - ${it.name}",
it.name,
null,
null,
sEpisode = SEpisodeImpl()
)
episodes.add(episode)
}
}
episodes.sortBy { AnimeNameAdapter.findEpisodeNumber(it.number) }
return episodes
}
return emptyList()
}
override suspend fun loadVideoServers(
episodeLink: String,
extra: Map<String, String>?,
sEpisode: SEpisode
): List<VideoServer> {
return listOf(
VideoServer(
episodeLink,
offline = true
)
)
}
override suspend fun search(query: String): List<ShowResponse> {
val titles = downloadManager.animeDownloadedTypes.map { it.title }.distinct()
val returnTitles: MutableList<String> = mutableListOf()
for (title in titles) {
if (FuzzySearch.ratio(title.lowercase(), query.lowercase()) > 80) {
returnTitles.add(title)
}
}
val returnList: MutableList<ShowResponse> = mutableListOf()
for (title in returnTitles) {
returnList.add(ShowResponse(title, title, title))
}
return returnList
}
override suspend fun getVideoExtractor(server: VideoServer): VideoExtractor {
return OfflineVideoExtractor(server)
}
}
class OfflineVideoExtractor(val videoServer: VideoServer) : VideoExtractor() {
override val server: VideoServer
get() = videoServer
override suspend fun extract(): VideoContainer {
val sublist = emptyList<Subtitle>()
//we need to return a "fake" video so that the app doesn't crash
val video = Video(
null,
VideoType.CONTAINER,
"",
)
return VideoContainer(listOf(video), sublist)
}
}

View File

@@ -76,7 +76,7 @@ class OfflineMangaParser : MangaParser() {
}
override suspend fun search(query: String): List<ShowResponse> {
val titles = downloadManager.mangaDownloads.map { it.title }.distinct()
val titles = downloadManager.mangaDownloadedTypes.map { it.title }.distinct()
val returnTitles: MutableList<String> = mutableListOf()
for (title in titles) {
if (FuzzySearch.ratio(title.lowercase(), query.lowercase()) > 80) {

View File

@@ -3,10 +3,7 @@ package ani.dantotsu.parsers
import android.os.Environment
import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger
import ani.dantotsu.media.manga.MangaNameAdapter
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import me.xdrop.fuzzywuzzy.FuzzySearch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -53,7 +50,7 @@ class OfflineNovelParser: NovelParser() {
}
override suspend fun search(query: String): List<ShowResponse> {
val titles = downloadManager.novelDownloads.map { it.title }.distinct()
val titles = downloadManager.novelDownloadedTypes.map { it.title }.distinct()
val returnTitles: MutableList<String> = mutableListOf()
for (title in titles) {
if (FuzzySearch.ratio(title.lowercase(), query.lowercase()) > 80) {

View File

@@ -57,11 +57,15 @@ data class VideoServer(
val name: String,
val embed: FileUrl,
val extraData: Map<String, String>? = null,
val video: eu.kanade.tachiyomi.animesource.model.Video? = null
val video: eu.kanade.tachiyomi.animesource.model.Video? = null,
val offline: Boolean = false
) : Serializable {
constructor(name: String, embedUrl: String, extraData: Map<String, String>? = null)
: this(name, FileUrl(embedUrl), extraData)
constructor(name: String, offline: Boolean)
: this(name, FileUrl(""), null, null, offline)
constructor(
name: String,
embedUrl: String,

View File

@@ -11,6 +11,7 @@ data class CurrentNovelReaderSettings(
var justify: Boolean = true,
var hyphenation: Boolean = true,
var useDarkTheme: Boolean = false,
var useOledTheme: Boolean = false,
var invert: Boolean = false,
var maxInlineSize: Int = 720,
var maxBlockSize: Int = 1440,

View File

@@ -61,11 +61,6 @@ class FAQActivity : AppCompatActivity() {
currContext()!!.getString(R.string.question_7),
currContext()!!.getString(R.string.answer_7)
),
Triple(
R.drawable.ic_round_menu_book_24,
currContext()!!.getString(R.string.question_8),
currContext()!!.getString(R.string.answer_8)
),
Triple(
R.drawable.ic_round_lock_open_24,
currContext()!!.getString(R.string.question_9),

View File

@@ -1,5 +1,6 @@
package ani.dantotsu.settings
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.app.NotificationManager
import android.content.Context
@@ -49,74 +50,66 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
private val animeExtensionManager: AnimeExtensionManager = Injekt.get()
private val extensionsAdapter = AnimeExtensionsAdapter(
{ pkg ->
val name= pkg.name
val changeUIVisibility: (Boolean) -> Unit = { show ->
val activity = requireActivity() as ExtensionsActivity
val visibility = if (show) View.VISIBLE else View.GONE
activity.findViewById<ViewPager2>(R.id.viewPager).visibility = visibility
activity.findViewById<TabLayout>(R.id.tabLayout).visibility = visibility
activity.findViewById<TextInputLayout>(R.id.searchView).visibility = visibility
activity.findViewById<ImageView>(R.id.languageselect).visibility = visibility
activity.findViewById<TextView>(R.id.extensions).text = if (show) getString(R.string.extensions) else name
activity.findViewById<FrameLayout>(R.id.fragmentExtensionsContainer).visibility =
if (show) View.GONE else View.VISIBLE
}
var itemSelected = false
val allSettings = pkg.sources.filterIsInstance<ConfigurableAnimeSource>()
if (allSettings.isNotEmpty()) {
var selectedSetting = allSettings[0]
if (allSettings.size > 1) {
val names = allSettings.map { it.lang }.toTypedArray()
val names = allSettings.sortedBy { it.lang }.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
var selectedIndex = 0
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
.setTitle("Select a Source")
.setSingleChoiceItems(names, selectedIndex) { dialog, which ->
itemSelected = true
selectedIndex = which
selectedSetting = allSettings[selectedIndex]
dialog.dismiss()
// Move the fragment transaction here
val eActivity = requireActivity() as ExtensionsActivity
eActivity.runOnUiThread {
val fragment =
AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) {
eActivity.findViewById<ViewPager2>(R.id.viewPager).visibility =
View.VISIBLE
eActivity.findViewById<TabLayout>(R.id.tabLayout).visibility =
View.VISIBLE
eActivity.findViewById<TextInputLayout>(R.id.searchView).visibility =
View.VISIBLE
eActivity.findViewById<FrameLayout>(R.id.fragmentExtensionsContainer).visibility =
View.GONE
}
parentFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.slide_up, R.anim.slide_down)
.replace(R.id.fragmentExtensionsContainer, fragment)
.addToBackStack(null)
.commit()
val fragment =
AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) {
changeUIVisibility(true)
}
parentFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.slide_up, R.anim.slide_down)
.replace(R.id.fragmentExtensionsContainer, fragment)
.addToBackStack(null)
.commit()
}
.setOnDismissListener {
if (!itemSelected) {
changeUIVisibility(true)
}
}
.show()
dialog.window?.setDimAmount(0.8f)
} else {
// If there's only one setting, proceed with the fragment transaction
val eActivity = requireActivity() as ExtensionsActivity
eActivity.runOnUiThread {
val fragment =
AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) {
val fragment =
AnimeSourcePreferencesFragment().getInstance(selectedSetting.id) {
changeUIVisibility(true)
}
parentFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.slide_up, R.anim.slide_down)
.replace(R.id.fragmentExtensionsContainer, fragment)
.addToBackStack(null)
.commit()
eActivity.findViewById<ViewPager2>(R.id.viewPager).visibility =
View.VISIBLE
eActivity.findViewById<TabLayout>(R.id.tabLayout).visibility =
View.VISIBLE
eActivity.findViewById<TextInputLayout>(R.id.searchView).visibility =
View.VISIBLE
eActivity.findViewById<FrameLayout>(R.id.fragmentExtensionsContainer).visibility =
View.GONE
}
parentFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.slide_up, R.anim.slide_down)
.replace(R.id.fragmentExtensionsContainer, fragment)
.addToBackStack(null)
.commit()
}
}
// Hide ViewPager2 and TabLayout
val activity = requireActivity() as ExtensionsActivity
activity.findViewById<ViewPager2>(R.id.viewPager).visibility = View.GONE
activity.findViewById<TabLayout>(R.id.tabLayout).visibility = View.GONE
activity.findViewById<TextInputLayout>(R.id.searchView).visibility = View.GONE
activity.findViewById<FrameLayout>(R.id.fragmentExtensionsContainer).visibility =
View.VISIBLE
changeUIVisibility(false)
} else {
Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT)
.show()
@@ -225,6 +218,7 @@ class InstalledAnimeExtensionsFragment : Fragment(), SearchQueryHandler {
return ViewHolder(view)
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = getItem(position) // Use getItem() from ListAdapter
val nsfw = if (extension.isNsfw) "(18+)" else ""

View File

@@ -47,24 +47,29 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
val skipIcons = loadData("skip_extension_icons") ?: false
private val mangaExtensionManager: MangaExtensionManager = Injekt.get()
private val extensionsAdapter = MangaExtensionsAdapter({ pkg ->
val name= pkg.name
val changeUIVisibility: (Boolean) -> Unit = { show ->
val activity = requireActivity() as ExtensionsActivity
val visibility = if (show) View.VISIBLE else View.GONE
activity.findViewById<ViewPager2>(R.id.viewPager).visibility = visibility
activity.findViewById<TabLayout>(R.id.tabLayout).visibility = visibility
activity.findViewById<TextInputLayout>(R.id.searchView).visibility = visibility
activity.findViewById<ImageView>(R.id.languageselect).visibility = visibility
activity.findViewById<TextView>(R.id.extensions).text = if (show) getString(R.string.extensions) else name
activity.findViewById<FrameLayout>(R.id.fragmentExtensionsContainer).visibility =
if (show) View.GONE else View.VISIBLE
}
var itemSelected = false
val allSettings = pkg.sources.filterIsInstance<ConfigurableSource>()
if (allSettings.isNotEmpty()) {
var selectedSetting = allSettings[0]
if (allSettings.size > 1) {
val names = allSettings.map { it.lang }.toTypedArray()
val names = allSettings.sortedBy { it.lang }.map { LanguageMapper.mapLanguageCodeToName(it.lang) }.toTypedArray()
var selectedIndex = 0
val dialog = AlertDialog.Builder(requireContext(), R.style.MyPopup)
.setTitle("Select a Source")
.setSingleChoiceItems(names, selectedIndex) { dialog, which ->
itemSelected = true
selectedIndex = which
selectedSetting = allSettings[selectedIndex]
dialog.dismiss()
@@ -80,6 +85,11 @@ class InstalledMangaExtensionsFragment : Fragment(), SearchQueryHandler {
.addToBackStack(null)
.commit()
}
.setOnDismissListener {
if (!itemSelected) {
changeUIVisibility(true)
}
}
.show()
dialog.window?.setDimAmount(0.8f)
} else {

View File

@@ -1,10 +0,0 @@
package ani.dantotsu.settings
import java.io.Serializable
data class NovelReaderSettings(
var showSource: Boolean = true,
var showSystemBars: Boolean = false,
var default: CurrentNovelReaderSettings = CurrentNovelReaderSettings(),
var askIndividual: Boolean = true,
) : Serializable

View File

@@ -45,6 +45,6 @@ data class PlayerSettings(
var skipTime: Int = 85,
//Other
var cast: Boolean = false,
var cast: Boolean = true,
var pip: Boolean = true
) : Serializable

View File

@@ -8,6 +8,7 @@ data class ReaderSettings(
var autoDetectWebtoon: Boolean = true,
var default: CurrentReaderSettings = CurrentReaderSettings(),
var defaultLN: CurrentNovelReaderSettings = CurrentNovelReaderSettings(),
var askIndividual: Boolean = true,
var updateForH: Boolean = false

View File

@@ -1,13 +1,17 @@
package ani.dantotsu.settings
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.updateLayoutParams
import ani.dantotsu.NoPaddingArrayAdapter
import ani.dantotsu.R
import ani.dantotsu.databinding.ActivityReaderSettingsBinding
import ani.dantotsu.initActivity
import ani.dantotsu.loadData
import ani.dantotsu.media.novel.novelreader.NovelReaderActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.others.LangSet
import ani.dantotsu.saveData
@@ -42,7 +46,7 @@ class ReaderSettingsActivity : AppCompatActivity() {
onBackPressedDispatcher.onBackPressed()
}
//General
//Manga Settings
binding.readerSettingsSourceName.isChecked = settings.showSource
binding.readerSettingsSourceName.setOnCheckedChangeListener { _, isChecked ->
settings.showSource = isChecked
@@ -54,14 +58,14 @@ class ReaderSettingsActivity : AppCompatActivity() {
settings.showSystemBars = isChecked
saveData(reader, settings)
}
//Default Manga
binding.readerSettingsAutoWebToon.isChecked = settings.autoDetectWebtoon
binding.readerSettingsAutoWebToon.setOnCheckedChangeListener { _, isChecked ->
settings.autoDetectWebtoon = isChecked
saveData(reader, settings)
}
//Default
val layoutList = listOf(
binding.readerSettingsPaged,
binding.readerSettingsContinuousPaged,
@@ -185,6 +189,169 @@ class ReaderSettingsActivity : AppCompatActivity() {
saveData(reader, settings)
}
//LN settings
val layoutListLN = listOf(
binding.LNpaged,
binding.LNcontinuous
)
binding.LNlayoutText.text = settings.defaultLN.layout.string
var selectedLN = layoutListLN[settings.defaultLN.layout.ordinal]
selectedLN.alpha = 1f
layoutListLN.forEachIndexed { index, imageButton ->
imageButton.setOnClickListener {
selectedLN.alpha = 0.33f
selectedLN = imageButton
selectedLN.alpha = 1f
settings.defaultLN.layout = CurrentNovelReaderSettings.Layouts[index]
?: CurrentNovelReaderSettings.Layouts.PAGED
binding.LNlayoutText.text = settings.defaultLN.layout.string
saveData(reader, settings)
}
}
val dualListLN = listOf(
binding.LNdualNo,
binding.LNdualAuto,
binding.LNdualForce
)
binding.LNdualPageText.text = settings.defaultLN.dualPageMode.toString()
var selectedDualLN = dualListLN[settings.defaultLN.dualPageMode.ordinal]
selectedDualLN.alpha = 1f
dualListLN.forEachIndexed { index, imageButton ->
imageButton.setOnClickListener {
selectedDualLN.alpha = 0.33f
selectedDualLN = imageButton
selectedDualLN.alpha = 1f
settings.defaultLN.dualPageMode = CurrentReaderSettings.DualPageModes[index]
?: CurrentReaderSettings.DualPageModes.Automatic
binding.LNdualPageText.text = settings.defaultLN.dualPageMode.toString()
saveData(reader, settings)
}
}
binding.LNlineHeight.setText(settings.defaultLN.lineHeight.toString())
binding.LNlineHeight.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
val value = binding.LNlineHeight.text.toString().toFloatOrNull() ?: 1.4f
settings.defaultLN.lineHeight = value
binding.LNlineHeight.setText(value.toString())
saveData(reader, settings)
}
}
binding.LNincrementLineHeight.setOnClickListener {
val value = binding.LNlineHeight.text.toString().toFloatOrNull() ?: 1.4f
settings.defaultLN.lineHeight = value + 0.1f
binding.LNlineHeight.setText(settings.defaultLN.lineHeight.toString())
saveData(reader, settings)
}
binding.LNdecrementLineHeight.setOnClickListener {
val value = binding.LNlineHeight.text.toString().toFloatOrNull() ?: 1.4f
settings.defaultLN.lineHeight = value - 0.1f
binding.LNlineHeight.setText(settings.defaultLN.lineHeight.toString())
saveData(reader, settings)
}
binding.LNmargin.setText(settings.defaultLN.margin.toString())
binding.LNmargin.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
val value = binding.LNmargin.text.toString().toFloatOrNull() ?: 0.06f
settings.defaultLN.margin = value
binding.LNmargin.setText(value.toString())
saveData(reader, settings)
}
}
binding.LNincrementMargin.setOnClickListener {
val value = binding.LNmargin.text.toString().toFloatOrNull() ?: 0.06f
settings.defaultLN.margin = value + 0.01f
binding.LNmargin.setText(settings.defaultLN.margin.toString())
saveData(reader, settings)
}
binding.LNdecrementMargin.setOnClickListener {
val value = binding.LNmargin.text.toString().toFloatOrNull() ?: 0.06f
settings.defaultLN.margin = value - 0.01f
binding.LNmargin.setText(settings.defaultLN.margin.toString())
saveData(reader, settings)
}
binding.LNmaxInlineSize.setText(settings.defaultLN.maxInlineSize.toString())
binding.LNmaxInlineSize.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
val value = binding.LNmaxInlineSize.text.toString().toIntOrNull() ?: 720
settings.defaultLN.maxInlineSize = value
binding.LNmaxInlineSize.setText(value.toString())
saveData(reader, settings)
}
}
binding.LNincrementMaxInlineSize.setOnClickListener {
val value = binding.LNmaxInlineSize.text.toString().toIntOrNull() ?: 720
settings.defaultLN.maxInlineSize = value + 10
binding.LNmaxInlineSize.setText(settings.defaultLN.maxInlineSize.toString())
saveData(reader, settings)
}
binding.LNdecrementMaxInlineSize.setOnClickListener {
val value = binding.LNmaxInlineSize.text.toString().toIntOrNull() ?: 720
settings.defaultLN.maxInlineSize = value - 10
binding.LNmaxInlineSize.setText(settings.defaultLN.maxInlineSize.toString())
saveData(reader, settings)
}
binding.LNmaxBlockSize.setText(settings.defaultLN.maxBlockSize.toString())
binding.LNmaxBlockSize.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
val value = binding.LNmaxBlockSize.text.toString().toIntOrNull() ?: 720
settings.defaultLN.maxBlockSize = value
binding.LNmaxBlockSize.setText(value.toString())
saveData(reader, settings)
}
}
binding.LNincrementMaxBlockSize.setOnClickListener {
val value = binding.LNmaxBlockSize.text.toString().toIntOrNull() ?: 720
settings.defaultLN.maxInlineSize = value + 10
binding.LNmaxBlockSize.setText(settings.defaultLN.maxInlineSize.toString())
saveData(reader, settings)
}
binding.LNdecrementMaxBlockSize.setOnClickListener {
val value = binding.LNmaxBlockSize.text.toString().toIntOrNull() ?: 720
settings.defaultLN.maxBlockSize = value - 10
binding.LNmaxBlockSize.setText(settings.defaultLN.maxBlockSize.toString())
saveData(reader, settings)
}
binding.LNuseDarkTheme.isChecked = settings.defaultLN.useDarkTheme
binding.LNuseDarkTheme.setOnCheckedChangeListener { _, isChecked ->
settings.defaultLN.useDarkTheme = isChecked
saveData(reader, settings)
}
binding.LNuseOledTheme.isChecked = settings.defaultLN.useOledTheme
binding.LNuseOledTheme.setOnCheckedChangeListener { _, isChecked ->
settings.defaultLN.useOledTheme = isChecked
saveData(reader, settings)
}
binding.LNkeepScreenOn.isChecked = settings.defaultLN.keepScreenOn
binding.LNkeepScreenOn.setOnCheckedChangeListener { _, isChecked ->
settings.defaultLN.keepScreenOn = isChecked
saveData(reader, settings)
}
binding.LNvolumeButton.isChecked = settings.defaultLN.volumeButtons
binding.LNvolumeButton.setOnCheckedChangeListener { _, isChecked ->
settings.defaultLN.volumeButtons = isChecked
saveData(reader, settings)
}
//Update Progress
binding.readerSettingsAskUpdateProgress.isChecked = settings.askIndividual
binding.readerSettingsAskUpdateProgress.setOnCheckedChangeListener { _, isChecked ->

View File

@@ -178,13 +178,18 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListen
binding.customTheme.setOnClickListener {
val originalColor = getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt(
val originalColor = getSharedPreferences("Dantotsu", MODE_PRIVATE).getInt(
"custom_theme_int",
Color.parseColor("#6200EE")
)
class CustomColorDialog : SimpleColorDialog() { //idk where to put it
override fun onPositiveButtonClick() {
restartApp()
super.onPositiveButtonClick()
}
}
val tag = "colorPicker"
SimpleColorDialog.build()
.title("Custom Theme")
CustomColorDialog().title("Custom Theme")
.colorPreset(originalColor)
.colors(this, SimpleColorDialog.BEIGE_COLOR_PALLET)
.allowCustom(true)
@@ -251,9 +256,9 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListen
binding.skipExtensionIcons.setOnCheckedChangeListener { _, isChecked ->
saveData("skip_extension_icons", isChecked)
}
binding.NSFWExtension.isChecked = loadData("NFSWExtension") ?: true
binding.NSFWExtension.isChecked = loadData("NSFWExtension") ?: true
binding.NSFWExtension.setOnCheckedChangeListener { _, isChecked ->
saveData("NFSWExtension", isChecked)
saveData("NSFWExtension", isChecked)
}
@@ -400,6 +405,7 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListen
uiTheme(true, it)
}
var previousStart: View = when (uiSettings.defaultStartUpTab) {
0 -> binding.uiSettingsAnime
1 -> binding.uiSettingsHome
@@ -416,6 +422,7 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListen
initActivity(this)
}
binding.uiSettingsAnime.setOnClickListener {
uiTheme(0, it)
}
@@ -782,4 +789,4 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListen
show()
}
}
}
}

View File

@@ -10,7 +10,6 @@ data class UserInterfaceSettings(
//App
var immersiveMode: Boolean = false,
var immersiveModeList: Boolean = false,
var smallView: Boolean = true,
var defaultStartUpTab: Int = 1,
var homeLayoutShow: MutableList<Boolean> = mutableListOf(

View File

@@ -68,13 +68,6 @@ class UserInterfaceSettingsActivity : AppCompatActivity() {
saveData(ui, settings)
restartApp()
}
binding.uiSettingsImmersiveList.isChecked = settings.immersiveModeList
binding.uiSettingsImmersiveList.setOnCheckedChangeListener { _, isChecked ->
settings.immersiveModeList = isChecked
saveData(ui, settings)
restartApp()
}
binding.uiSettingsBannerAnimation.isChecked = settings.bannerAnimations
binding.uiSettingsBannerAnimation.setOnCheckedChangeListener { _, isChecked ->
settings.bannerAnimations = isChecked

View File

@@ -1,5 +1,6 @@
package ani.dantotsu.settings.paging
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
@@ -91,18 +92,14 @@ class AnimeExtensionPagingSource(
val availableExtensions =
availableExtensionsFlow.filterNot { it.pkgName in installedExtensions }
val query = searchQuery
val isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true
val isNsfwEnabled: Boolean = loadData("NSFWExtension") ?: true
val filteredExtensions = if (query.isEmpty()) {
availableExtensions
} else {
availableExtensions.filter { it.name.contains(query, ignoreCase = true) }
}
val filternfsw = if (isNsfwEnabled) {
filteredExtensions
} else {
filteredExtensions.filterNot { it.isNsfw }
}
val filternfsw = if (isNsfwEnabled) filteredExtensions else filteredExtensions.filterNot { it.isNsfw }
return try {
val sublist = filternfsw.subList(
fromIndex = position,
@@ -198,6 +195,7 @@ class AnimeExtensionAdapter(private val clickListener: OnAnimeInstallClickListen
val extensionIconImageView: ImageView = binding.extensionIconImageView
@SuppressLint("SetTextI18n")
fun bind(extension: AnimeExtension.Available) {
val nsfw = if (extension.isNsfw) "(18+)" else ""
val lang = LanguageMapper.mapLanguageCodeToName(extension.lang)

View File

@@ -1,5 +1,6 @@
package ani.dantotsu.settings.paging
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
@@ -91,17 +92,13 @@ class MangaExtensionPagingSource(
val availableExtensions =
availableExtensionsFlow.filterNot { it.pkgName in installedExtensions }
val query = searchQuery
val isNsfwEnabled: Boolean = loadData("NFSWExtension") ?: true
val isNsfwEnabled: Boolean = loadData("NSFWExtension") ?: true
val filteredExtensions = if (query.isEmpty()) {
availableExtensions
} else {
availableExtensions.filter { it.name.contains(query, ignoreCase = true) }
}
val filternfsw = if (isNsfwEnabled) {
filteredExtensions
} else {
filteredExtensions.filterNot { it.isNsfw }
}
val filternfsw = if (isNsfwEnabled) filteredExtensions else filteredExtensions.filterNot { it.isNsfw }
return try {
val sublist = filternfsw.subList(
fromIndex = position,
@@ -194,6 +191,7 @@ class MangaExtensionAdapter(private val clickListener: OnMangaInstallClickListen
}
val extensionIconImageView: ImageView = binding.extensionIconImageView
@SuppressLint("SetTextI18n")
fun bind(extension: MangaExtension.Available) {
val nsfw = if (extension.isNsfw) "(18+)" else ""
val lang = LanguageMapper.mapLanguageCodeToName(extension.lang)

View File

@@ -87,9 +87,7 @@ class Subscription {
progress(index[it.first]!!, parser.name, media.name)
val ep: MangaChapter? =
SubscriptionHelper.getChapter(context, parser, media.id, media.isAdult)
if (ep != null) currActivity()!!.getString(R.string.chapter) + "${ep.number}${
if (ep.title != null) " : ${ep.title}" else ""
} " + currActivity()!!.getString(R.string.just_released) to null
if (ep != null) ep.number + " " + currActivity()!!.getString(R.string.just_released) to null
else null
} ?: return@map
createNotification(context.applicationContext, media, text.first, text.second)

View File

@@ -42,14 +42,15 @@ class ThemeManager(private val context: Context) {
.getString("theme", "PURPLE")!!
val themeToApply = when (theme) {
"PURPLE" -> if (useOLED) R.style.Theme_Dantotsu_PurpleOLED else R.style.Theme_Dantotsu_Purple
"BLUE" -> if (useOLED) R.style.Theme_Dantotsu_BlueOLED else R.style.Theme_Dantotsu_Blue
"GREEN" -> if (useOLED) R.style.Theme_Dantotsu_GreenOLED else R.style.Theme_Dantotsu_Green
"PURPLE" -> if (useOLED) R.style.Theme_Dantotsu_PurpleOLED else R.style.Theme_Dantotsu_Purple
"PINK" -> if (useOLED) R.style.Theme_Dantotsu_PinkOLED else R.style.Theme_Dantotsu_Pink
"SAIKOU" -> if (useOLED) R.style.Theme_Dantotsu_SaikouOLED else R.style.Theme_Dantotsu_Saikou
"RED" -> if (useOLED) R.style.Theme_Dantotsu_RedOLED else R.style.Theme_Dantotsu_Red
"LAVENDER" -> if (useOLED) R.style.Theme_Dantotsu_LavenderOLED else R.style.Theme_Dantotsu_Lavender
"EMERALD" -> if (useOLED) R.style.Theme_Dantotsu_EmeraldOLED else R.style.Theme_Dantotsu_Emerald
"MONOCHROME (BETA)" -> if (useOLED) R.style.Theme_Dantotsu_MonochromeOLED else R.style.Theme_Dantotsu_Monochrome
"SAIKOU" -> if (useOLED) R.style.Theme_Dantotsu_SaikouOLED else R.style.Theme_Dantotsu_Saikou
else -> if (useOLED) R.style.Theme_Dantotsu_PurpleOLED else R.style.Theme_Dantotsu_Purple
}
@@ -109,14 +110,15 @@ class ThemeManager(private val context: Context) {
companion object {
enum class Theme(val theme: String) {
PURPLE("PURPLE"),
BLUE("BLUE"),
GREEN("GREEN"),
PURPLE("PURPLE"),
PINK("PINK"),
SAIKOU("SAIKOU"),
RED("RED"),
LAVENDER("LAVENDER"),
MONOCHROME("MONOCHROME (BETA)"),
SAIKOU("SAIKOU");
EMERALD("EMERALD"),
MONOCHROME("MONOCHROME (BETA)");
companion object {
fun fromString(value: String): Theme {

View File

@@ -1,67 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="320dp"
android:height="180dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group android:scaleX="0.6666667"
android:scaleY="0.6666667"
android:translateX="18"
android:translateY="18">
<group android:scaleX="0.5625"
android:translateX="-5.535">
<path
android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:fillType="evenOdd">
<aapt:attr name="android:fillColor">
<gradient
android:startY="49.59793"
android:startX="42.9492"
android:endY="92.4963"
android:endX="85.84757"
android:type="linear">
<item
android:color="#44000000"
android:offset="0"/>
<item
android:color="#00000000"
android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:fillColor="#3DDC84"
android:fillType="evenOdd"
android:strokeWidth="1"
android:strokeColor="#00000000"/>
</group>
<group android:scaleX="0.492"
android:scaleY="0.24750866"
android:translateX="277.2"
android:translateY="73.782364">
<group android:translateY="145.33594">
<path android:pathData="M12.796875,-0L12.796875,-101.109375L34.171875,-101.109375Q42.828125,-101.109375,50.234375,-100.234375Q57.65625,-99.359375,63.84375,-97.171875Q70.03125,-95,74.703125,-91.375Q79.390625,-87.75,82.765625,-82.125Q86.0625,-76.5,87.75,-68.765625Q89.4375,-61.03125,89.4375,-50.5625Q89.4375,-40.078125,87.75,-32.34375Q86.0625,-24.609375,82.765625,-18.984375Q79.390625,-13.359375,74.703125,-9.734375Q70.03125,-6.125,63.84375,-3.9375Q57.65625,-1.765625,50.234375,-0.875Q42.828125,-0,34.171875,-0L12.796875,-0ZM36.359375,-10.828125Q47.109375,-10.828125,54.59375,-12.96875Q62.09375,-15.125,66.875,-19.96875Q71.578125,-24.828125,73.71875,-32.3125Q75.875,-39.796875,75.875,-50.5625Q75.875,-61.3125,73.71875,-68.796875Q71.578125,-76.296875,66.875,-81.140625Q62.09375,-86,54.59375,-88.140625Q47.109375,-90.28125,36.359375,-90.28125L26.015625,-90.28125L26.015625,-10.828125L36.359375,-10.828125Z"
android:fillColor="#000000"/>
<path android:pathData="M113.109375,-60.1875L110.015625,-70.03125Q121.828125,-74.75,134.76562,-74.75Q146.07812,-74.75,152.0625,-70.953125Q158.875,-66.59375,158.875,-56.109375L158.875,-27.359375Q158.875,-21.59375,159.01562,-15.546875Q159.09375,-11.953125,159.71875,-8.09375Q160.28125,-4.4375,161.20312,-1.125L149.67188,1.40625Q148.04688,-3.3125,147.625,-9.140625L146.21875,-9.28125Q137.5,1.6875,124,1.6875Q114.15625,1.6875,108.171875,-3.765625Q102.203125,-9.21875,102.203125,-19.125Q102.203125,-26.515625,105.46875,-31.21875Q108.75,-35.9375,114.71875,-39.171875Q124.140625,-44.296875,141.09375,-44.296875Q143.34375,-44.296875,147.14062,-44.15625L147.14062,-54.84375Q147.14062,-59.90625,144.04688,-62.15625Q140.95312,-64.40625,134.34375,-64.40625Q124.703125,-64.40625,113.109375,-60.1875ZM147.14062,-20.53125L147.14062,-35.296875Q145.23438,-35.4375,141.57812,-35.4375Q135.39062,-35.4375,130.1875,-34.390625Q122.453125,-32.84375,118.203125,-29.5Q113.953125,-26.15625,113.953125,-19.90625Q113.953125,-8.65625,126.1875,-8.65625Q132.51562,-8.65625,138,-12.03125Q142.5,-14.84375,147.14062,-20.53125Z"
android:fillColor="#000000"/>
<path android:pathData="M179.5,-71.859375L191.17188,-74.46875Q192.5,-70.25,193.0625,-66.3125Q193.34375,-64.40625,193.76562,-58.78125L195.25,-58.78125Q199.10938,-65.890625,204.8125,-70.109375Q211.0625,-74.75,218.03125,-74.75Q228.5,-74.75,233.98438,-69.6875Q238.70312,-65.390625,240.53125,-56.53125Q241.71875,-50.484375,241.71875,-38.53125L241.71875,0L229.625,0L229.625,-41.90625Q229.625,-52.390625,227.59375,-56.890625Q224.5625,-63.5625,215.42188,-63.5625Q208.95312,-63.5625,202.76562,-57.375Q198.625,-53.234375,193.90625,-45.21875L193.90625,0L181.8125,0L181.8125,-45Q181.8125,-54.5,181.32812,-61.390625Q181.04688,-65.671875,179.5,-71.859375Z"
android:fillColor="#000000"/>
<path android:pathData="M269.28125,-89.859375L281.375,-89.859375L281.375,-73.125L305.98438,-73.125L305.98438,-62.9375L281.375,-62.9375L281.375,-20.953125Q281.375,-16.734375,281.625,-14.515625Q281.875,-12.3125,282.85938,-11.046875Q284.6875,-8.515625,290.23438,-8.515625Q293.32812,-8.515625,296.21875,-9.140625Q299.17188,-9.78125,301.625,-10.546875Q303.59375,-6.265625,305.21875,-1.125Q296.92188,1.6875,288.48438,1.6875Q282.35938,1.6875,278.67188,0.3125Q274.98438,-1.0625,272.875,-4.15625Q270.6875,-7.390625,269.98438,-11.890625Q269.28125,-16.390625,269.28125,-24.328125L269.28125,-62.9375L258.73438,-62.9375L258.73438,-73.125L269.28125,-73.125L269.28125,-89.859375Z"
android:fillColor="#000000"/>
<path android:pathData="M351.03125,-74.75Q354.46875,-74.75,358.1875,-74.21875Q361.92188,-73.6875,365.51562,-72.21875Q369.09375,-70.671875,372.29688,-68.03125Q375.5,-65.390625,377.95312,-61.171875Q380.42188,-56.890625,381.82812,-50.875Q383.23438,-44.859375,383.23438,-36.5625Q383.23438,-28.265625,381.82812,-22.25Q380.42188,-16.25,377.95312,-12.03125Q375.5,-7.734375,372.29688,-5.0625Q369.09375,-2.390625,365.51562,-0.921875Q361.92188,0.640625,358.1875,1.15625Q354.46875,1.6875,351.03125,1.6875Q347.65625,1.6875,343.95312,1.15625Q340.26562,0.640625,336.6875,-0.921875Q333.09375,-2.390625,329.92188,-5.0625Q326.76562,-7.734375,324.3125,-12.03125Q321.84375,-16.25,320.4375,-22.25Q319.03125,-28.265625,319.03125,-36.5625Q319.03125,-44.859375,320.4375,-50.875Q321.84375,-56.890625,324.3125,-61.171875Q326.76562,-65.390625,329.92188,-68.03125Q333.09375,-70.671875,336.6875,-72.21875Q340.26562,-73.6875,343.92188,-74.21875Q347.57812,-74.75,351.03125,-74.75ZM351.03125,-64.828125Q347.01562,-64.828125,343.46875,-63.5625Q339.92188,-62.296875,337.25,-59.140625Q334.5,-55.90625,332.875,-50.484375Q331.26562,-45.078125,331.26562,-36.5625Q331.26562,-28.125,332.875,-22.671875Q334.5,-17.234375,337.25,-14.0625Q339.92188,-10.828125,343.46875,-9.59375Q347.01562,-8.375,351.03125,-8.375Q355.10938,-8.375,358.71875,-9.59375Q362.34375,-10.828125,365.09375,-14.0625Q367.82812,-17.234375,369.40625,-22.671875Q371,-28.125,371,-36.5625Q371,-45.078125,369.40625,-50.484375Q367.82812,-55.90625,365.09375,-59.140625Q362.34375,-62.296875,358.71875,-63.5625Q355.10938,-64.828125,351.03125,-64.828125Z"
android:fillColor="#000000"/>
<path android:pathData="M408.28125,-89.859375L420.375,-89.859375L420.375,-73.125L444.98438,-73.125L444.98438,-62.9375L420.375,-62.9375L420.375,-20.953125Q420.375,-16.734375,420.625,-14.515625Q420.875,-12.3125,421.85938,-11.046875Q423.6875,-8.515625,429.23438,-8.515625Q432.32812,-8.515625,435.21875,-9.140625Q438.17188,-9.78125,440.625,-10.546875Q442.59375,-6.265625,444.21875,-1.125Q435.92188,1.6875,427.48438,1.6875Q421.35938,1.6875,417.67188,0.3125Q413.98438,-1.0625,411.875,-4.15625Q409.6875,-7.390625,408.98438,-11.890625Q408.28125,-16.390625,408.28125,-24.328125L408.28125,-62.9375L397.73438,-62.9375L397.73438,-73.125L408.28125,-73.125L408.28125,-89.859375Z"
android:fillColor="#000000"/>
<path android:pathData="M484.54688,-74.75Q490.3125,-74.75,496.10938,-73.546875Q501.90625,-72.359375,506.90625,-70.25Q505.92188,-68,504.85938,-65.8125Q503.8125,-63.640625,502.54688,-61.53125Q500.78125,-62.234375,498.64062,-62.9375Q496.5,-63.640625,494.14062,-64.125Q491.78125,-64.625,489.28125,-64.9375Q486.79688,-65.25,484.40625,-65.25Q481.79688,-65.25,479.375,-64.71875Q476.95312,-64.203125,475.07812,-63Q473.21875,-61.8125,472.09375,-59.875Q470.96875,-57.9375,470.96875,-55.0625Q470.96875,-52.109375,472.23438,-50.09375Q473.5,-48.09375,475.67188,-46.75Q477.85938,-45.421875,480.78125,-44.5Q483.70312,-43.59375,487,-42.828125Q492.14062,-41.5625,496.5625,-39.96875Q501,-38.390625,504.23438,-35.859375Q507.46875,-33.328125,509.29688,-29.59375Q511.125,-25.875,511.125,-20.390625Q511.125,-14.625,508.82812,-10.46875Q506.54688,-6.328125,502.64062,-3.625Q498.75,-0.921875,493.54688,0.375Q488.34375,1.6875,482.4375,1.6875Q479.34375,1.6875,476.0625,1.296875Q472.79688,0.921875,469.59375,0.171875Q466.40625,-0.5625,463.3125,-1.609375Q460.21875,-2.671875,457.46875,-3.9375Q458.45312,-6.1875,459.54688,-8.328125Q460.64062,-10.484375,461.82812,-12.59375Q464.15625,-11.609375,466.75,-10.71875Q469.35938,-9.84375,472.09375,-9.203125Q474.84375,-8.578125,477.51562,-8.1875Q480.1875,-7.8125,482.64062,-7.8125Q485.73438,-7.8125,488.65625,-8.515625Q491.57812,-9.21875,493.85938,-10.71875Q496.14062,-12.234375,497.51562,-14.625Q498.89062,-17.015625,498.89062,-20.390625Q498.89062,-23.5625,497.57812,-25.59375Q496.28125,-27.640625,494.09375,-29Q491.92188,-30.375,489,-31.25Q486.09375,-32.140625,482.85938,-32.90625Q477.9375,-34.109375,473.53125,-35.65625Q469.14062,-37.203125,465.875,-39.6875Q462.60938,-42.1875,460.67188,-45.875Q458.73438,-49.578125,458.73438,-55.0625Q458.73438,-60.1875,460.84375,-63.90625Q462.95312,-67.640625,466.5,-70.03125Q470.0625,-72.421875,474.73438,-73.578125Q479.40625,-74.75,484.54688,-74.75Z"
android:fillColor="#000000"/>
<path android:pathData="M590.28125,-1.265625L578.6094,1.34375Q577.28125,-2.890625,576.71875,-6.828125Q576.4375,-8.65625,576.0156,-14.28125L574.53125,-14.28125Q570.6719,-7.171875,564.96875,-2.953125Q558.71875,1.6875,551.75,1.6875Q541.28125,1.6875,535.7969,-3.375Q531.0781,-7.671875,529.25,-16.53125Q528.0625,-22.578125,528.0625,-34.53125L528.0625,-73.125L540.15625,-73.125L540.15625,-31.15625Q540.15625,-20.671875,542.1875,-16.171875Q545.21875,-9.5,554.3594,-9.5Q560.8281,-9.5,567.0156,-15.6875Q571.15625,-19.828125,575.875,-27.84375L575.875,-73.125L587.96875,-73.125L587.96875,-27.984375Q587.96875,-18.640625,588.4531,-11.75Q588.7344,-7.453125,590.28125,-1.265625Z"
android:fillColor="#000000"/>
<path android:pathData="M644.78125,0L644.78125,-95.984375L683.03125,-95.984375L683.03125,0L644.78125,0ZM649.5625,-4.78125L678.25,-4.78125L678.25,-91.203125L649.5625,-91.203125L649.5625,-4.78125Z"
android:fillColor="#000000"/>
</group>
</group>
</group>
</vector>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?attr/colorOnPrimaryContainer"/>
<solid android:color="@color/grey_nav"/>
<corners android:radius="40dp"/>
</shape>

View File

@@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000000" android:fillType="evenOdd" android:pathData="M3,7C3,6.448 3.448,6 4,6H20C20.552,6 21,6.448 21,7C21,7.552 20.552,8 20,8H4C3.448,8 3,7.552 3,7ZM6,12C6,11.448 6.448,11 7,11H17C17.552,11 18,11.448 18,12C18,12.552 17.552,13 17,13H7C6.448,13 6,12.552 6,12ZM9,17C9,16.448 9.448,16 10,16H14C14.552,16 15,16.448 15,17C15,17.552 14.552,18 14,18H10C9.448,18 9,17.552 9,17Z"/>
</vector>

View File

@@ -1,16 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="480"
android:viewportHeight="480"
android:width="32dp"
android:height="32dp"
android:viewportWidth="700"
android:viewportHeight="768"
android:tint="?attr/colorControlNormal">
<clip-path
android:fillColor="#e9e6f4"
android:pathData="M125.71,125.71h516.58v516.58h-516.58z"/>
<path
android:fillColor="#e9e6f4"
android:pathData="M298.5,303.98c0,10.32 -6.43,17.41 -7.83,18.82l-140,140C62.36,427.38 0,340.98 0,240c0,-132.55 107.45,-240 240,-240L240.5,0L136.18,104.28c-8.92,8.95 -29.67,33.86 -29.68,71.71 0,10.22 2.27,45.73 31.56,73.57a101,101 0,0 0,45.95 24.93v9.4a27.92,27.92 0,0 0,42 24.23l51.64,-29.81a27.23,27.23 0,0 1,14.21 8.11A27.72,27.72 0,0 1,298.5 303.98Z" />
android:pathData="M44.26,128C44.26,173.48 80.53,210.4 125.71,211.63L125.71,128.01L642.29,128.01L642.29,639.97L768,639.97L768,128L44.26,128zM642.29,639.97L125.71,639.97L125.71,639.99L642.29,639.99L642.29,639.97zM125.71,639.97L125.71,556.38C80.54,557.6 44.28,594.5 44.26,639.97L125.71,639.97zM125.71,556.38C126.48,556.35 127.23,556.26 128,556.26L384,556.26C479.14,556.26 556.26,479.13 556.26,384C556.26,288.86 479.13,211.74 384,211.74L128,211.74C127.23,211.74 126.48,211.65 125.71,211.63L125.71,286.18L384,286.18C438.02,286.18 481.82,329.98 481.82,384C481.82,438.03 438.02,481.82 384,481.82L125.71,481.82L125.71,556.38zM125.71,481.82L125.71,286.18L0,286.18L0,481.82L125.71,481.82z"/>
<path
android:fillColor="#e9e6f4"
android:pathData="M329.34,17.18 L189.26,157.26A27.91,27.91 0,0 0,181.5 176.01a29,29 0,0 0,3.43 12.88,28 28,0 0,1 41,-17l53.73,31c15.36,1.41 44.91,7.64 68.06,33.57S373.5,292.38 373.5,304.02c0,37.74 -20.73,62.68 -29.63,71.65l-0.1,0.1L239.55,480h0.45c132.55,0 240,-107.45 240,-240C480,139.02 417.64,52.62 329.34,17.18Z" />
<path
android:fillColor="#e9e6f4"
android:pathData="M299.99,240a11.85,11.85 0,0 1,-6 10.37l-47,27.13 -29,16.76a12,12 0,0 1,-18 -10.37L199.99,196.11a11.95,11.95 0,0 1,18 -10.37l29,16.76 47,27.13A11.85,11.85 0,0 1,299.99 240Z" />
android:pathData="m442,366.7l-76.02,-43.89c-13.32,-7.69 -29.96,1.92 -29.96,17.3v87.78c0,15.38 16.65,24.99 29.96,17.3l76.02,-43.89c13.32,-7.69 13.32,-26.91 0,-34.6Z"/>
</vector>

View File

@@ -81,23 +81,6 @@
app:tabPaddingStart="16dp"
app:tabTextAppearance="@style/NavBarText"
app:tabGravity="fill">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/installed_anime"/>
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/available_anime"/>
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/installed_manga"/>
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Available Manga"
tools:ignore="HardcodedText" />
</com.google.android.material.tabs.TabLayout>
<com.google.android.material.textfield.TextInputLayout

View File

@@ -2,8 +2,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:fitsSystemWindows="true">
android:layout_height="match_parent"
android:paddingTop="32dp">
<LinearLayout
android:layout_width="match_parent"

View File

@@ -242,7 +242,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="0dp"
android:layout_gravity="bottom"
android:layout_gravity="center_horizontal|bottom"
android:background="?attr/colorSurface"
android:translationZ="1dp"
app:itemActiveIndicatorStyle="@style/BottomNavBar"

View File

@@ -1210,7 +1210,7 @@
android:fontFamily="@font/poppins_family"
android:paddingStart="32dp"
android:paddingEnd="32dp"
android:text="@string/show_cast_button_info"
android:text=""
android:textSize="14sp" />

File diff suppressed because it is too large Load Diff

View File

@@ -121,28 +121,6 @@
</com.google.android.material.materialswitch.MaterialSwitch>
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/uiSettingsImmersiveList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:checked="true"
android:drawableStart="@drawable/ic_round_fullscreen_24"
android:drawablePadding="16dp"
app:drawableTint="?attr/colorPrimary"
android:elegantTextHeight="true"
android:fontFamily="@font/poppins_bold"
android:minHeight="64dp"
android:paddingStart="32dp"
android:paddingEnd="32dp"
android:text="@string/immersive_modelist"
android:textAlignment="viewStart"
android:textColor="?attr/colorOnBackground"
app:cornerRadius="0dp"
app:showText="false"
app:thumbTint="@color/button_switch_track">
</com.google.android.material.switchmaterial.SwitchMaterial>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
@@ -286,4 +264,4 @@
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.core.widget.NestedScrollView>

View File

@@ -512,6 +512,25 @@
app:thumbTint="@color/button_switch_track"
tools:ignore="VisualLintButtonSize" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/useOledTheme"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="false"
android:drawableStart="@drawable/ic_round_brightness_4_24"
android:drawablePadding="16dp"
android:elegantTextHeight="true"
android:fontFamily="@font/poppins_bold"
android:minHeight="64dp"
android:text="Use OLED Theme"
android:textAlignment="viewStart"
android:textColor="?attr/colorOnBackground"
app:cornerRadius="0dp"
app:drawableTint="?attr/colorPrimary"
app:showText="false"
app:thumbTint="@color/button_switch_track"
tools:ignore="VisualLintButtonSize" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/keepScreenOn"
android:layout_width="match_parent"

View File

@@ -0,0 +1,225 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="170dp"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.58"
android:fontFamily="@font/poppins_bold"
android:text="@string/layout" />
<TextView
android:id="@+id/layoutText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_bold"
android:textColor="?attr/colorSecondary"
tools:ignore="TextContrastCheck"
tools:text="Continuous" />
</LinearLayout>
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/nav_bg_inv"
app:cardCornerRadius="16dp"
app:cardElevation="0dp">
<ImageButton
android:id="@+id/animeSourceList"
android:layout_width="48dp"
android:layout_height="48dp"
android:alpha="0.33"
android:background="?android:attr/selectableItemBackground"
android:scaleX="-1"
app:srcCompat="@drawable/ic_round_view_list_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,SpeakableTextPresentCheck,ImageContrastCheck" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/nav_bg_inv"
app:cardCornerRadius="16dp"
app:cardElevation="0dp">
<ImageButton
android:id="@+id/animeSourceGrid"
android:layout_width="48dp"
android:layout_height="48dp"
android:alpha="0.33"
android:background="?android:attr/selectableItemBackground"
app:srcCompat="@drawable/ic_round_grid_view_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,SpeakableTextPresentCheck,ImageContrastCheck" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/nav_bg_inv"
app:cardCornerRadius="16dp"
app:cardElevation="0dp">
<ImageButton
android:id="@+id/animeSourceCompact"
android:layout_width="48dp"
android:layout_height="48dp"
android:alpha="0.33"
android:background="?android:attr/selectableItemBackground"
app:srcCompat="@drawable/ic_round_view_comfy_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,SpeakableTextPresentCheck,ImageContrastCheck" />
</androidx.cardview.widget.CardView>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="265dp"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="152dp"
android:layout_height="wrap_content"
android:alpha="0.58"
android:fontFamily="@font/poppins_bold"
android:text="Sort" />
<TextView
android:id="@+id/sortText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_bold"
android:textColor="?attr/colorSecondary"
tools:ignore="TextContrastCheck"
tools:text="Up to Down" />
</LinearLayout>
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/nav_bg_inv"
app:cardCornerRadius="16dp"
app:cardElevation="0dp">
<ImageButton
android:id="@+id/animeSourceTop"
android:layout_width="48dp"
android:layout_height="48dp"
android:rotation="90"
android:background="?android:attr/selectableItemBackground"
app:srcCompat="@drawable/ic_round_arrow_back_ios_new_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
</androidx.cardview.widget.CardView>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:id="@+id/animeDownloadContainer"
android:orientation="horizontal">
<LinearLayout
android:layout_width="265dp"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_bold"
android:alpha="0.58"
android:text="Download" />
<TextView
android:id="@+id/downloadNo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_bold"
android:textColor="?attr/colorSecondary"
tools:ignore="TextContrastCheck"
tools:text="number" />
</LinearLayout>
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/nav_bg_inv"
app:cardCornerRadius="16dp"
app:cardElevation="0dp">
<ImageButton
android:id="@+id/animeDownloadTop"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground"
app:srcCompat="@drawable/ic_round_download_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
</androidx.cardview.widget.CardView>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:id="@+id/animeScanlatorContainer">
<TextView
android:layout_width="265dp"
android:layout_height="match_parent"
android:fontFamily="@font/poppins_bold"
android:alpha="0.58"
android:text="Scanlator"
android:gravity="center_vertical"/>
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/nav_bg_inv"
app:cardCornerRadius="16dp"
app:cardElevation="0dp">
<ImageButton
android:id="@+id/animeScanlatorTop"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground"
app:srcCompat="@drawable/ic_round_edit_note_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
</androidx.cardview.widget.CardView>
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@@ -174,12 +174,11 @@
app:srcCompat="@drawable/ic_round_screen_rotation_alt_24"
tools:ignore="ContentDescription,SpeakableTextPresentCheck" />
<ImageButton
<androidx.mediarouter.app.MediaRouteButton
android:id="@+id/exo_cast"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:backgroundTint="#00FFFFFF"
android:src="@drawable/ic_round_cast_24"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View File

@@ -17,26 +17,17 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/incognitoView"
<TextView
android:id="@+id/incognitoTextView"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:orientation="horizontal"
android:text="Incognito Mode"
android:fontFamily="@font/poppins_bold"
android:textSize="15sp"
android:textColor="?attr/colorPrimary"
android:layout_gravity="bottom|center_horizontal"
android:padding="8dp"
android:visibility="gone"/>
<TextView
android:id="@+id/incognitoTextView"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Incognito Mode"
android:fontFamily="@font/poppins_bold"
android:textSize="11sp"
android:textColor="#4f2dbd"
android:layout_gravity="bottom|center_horizontal"
android:background="#00FFFFFF"
android:padding="8dp"
android:visibility="gone"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

View File

@@ -35,6 +35,7 @@
android:hint="@string/manga"
android:textColorHint="@color/bg_opp"
android:transitionName="@string/search"
android:fontFamily="@font/poppins_bold"
app:boxBackgroundColor="?attr/colorPrimaryContainer"
app:boxCornerRadiusBottomEnd="28dp"
app:boxCornerRadiusBottomStart="28dp"
@@ -80,7 +81,43 @@
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/sourceTitle"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="@font/poppins_bold"
android:gravity="center"
android:text="Downloaded Manga and Novels"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<ImageView
android:id="@+id/downloadedList"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.33"
android:padding="8dp"
app:srcCompat="@drawable/ic_round_view_list_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView
android:id="@+id/downloadedGrid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.33"
android:padding="8dp"
app:srcCompat="@drawable/ic_round_grid_view_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
</LinearLayout>
<!-- This TextView might overlap with GridView if GridView has items -->
<TextView
android:id="@+id/noMangaOffline"
@@ -91,18 +128,35 @@
android:textColor="?attr/colorOnSurface"
android:textSize="18sp"
android:visibility="gone" />
<!-- for large view -->
<GridView
android:id="@+id/gridView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:numColumns="auto_fit"
android:columnWidth="128dp"
android:verticalSpacing="10dp"
android:numColumns="1"
android:verticalSpacing="20dp"
android:horizontalSpacing="10dp"
android:padding="10dp"
android:gravity="center" />
android:paddingStart="25dp"
android:paddingEnd="25dp"
android:gravity="center"
android:scrollbars="none"
android:visibility="gone"/>
<!-- for compact view -->
<GridView
android:id="@+id/gridView1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:columnWidth="138dp"
android:gravity="center"
android:horizontalSpacing="-30dp"
android:numColumns="auto_fit"
android:paddingStart="-10dp"
android:paddingEnd="-10dp"
android:scrollbars="none"
android:verticalSpacing="20dp"
android:visibility="gone"/>
</LinearLayout>
</FrameLayout>

View File

@@ -8,13 +8,6 @@
android:layout_marginStart="-16dp"
android:layout_marginEnd="-16dp"
android:orientation="vertical">
<LinearLayout
android:id="@+id/incognitoView"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:orientation="horizontal"
android:background="?attr/colorPrimary"
android:visibility="gone"/>
<TextView
android:id="@+id/incognitoTextView"
@@ -22,8 +15,8 @@
android:layout_width="wrap_content"
android:text="Incognito Mode"
android:fontFamily="@font/poppins_bold"
android:textSize="11sp"
android:textColor="#4f2dbd"
android:textSize="15sp"
android:textColor="?attr/colorPrimary"
android:layout_gravity="bottom|center_horizontal"
android:background="#00FFFFFF"
android:padding="8dp"

View File

@@ -25,6 +25,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:backgroundTint="@color/yt_red"
android:visibility="gone"
android:insetTop="0dp"
android:insetBottom="0dp"
android:text="@string/play_yt"
@@ -89,12 +90,12 @@
<ImageView
android:id="@+id/animeSourceSubscribe"
android:id="@+id/animeSourceSettings"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:padding="8dp"
android:layout_gravity="center_vertical"
app:srcCompat="@drawable/ic_round_notifications_none_24"
app:srcCompat="@drawable/ic_round_settings_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
</LinearLayout>
@@ -106,7 +107,8 @@
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:id="@+id/animeSourceLanguageContainer"
android:visibility="gone"
android:layout_height="56dp"
android:layout_weight="1"
android:hint="Language"
@@ -134,15 +136,6 @@
tools:ignore="LabelFor,TextContrastCheck,DuplicateSpeakableTextCheck" />
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/animeSourceSettings"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:padding="8dp"
android:layout_gravity="center_vertical"
app:srcCompat="@drawable/ic_round_settings_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
</LinearLayout>
@@ -185,19 +178,26 @@
</LinearLayout>
<TextView
android:id="@+id/animeSourceSearch"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end"
android:clickable="true"
android:focusable="true"
android:fontFamily="@font/poppins_bold"
android:padding="12dp"
android:text="@string/wrong"
android:textAlignment="textEnd"
android:textColor="?attr/colorSecondary"
tools:ignore="TextContrastCheck" />
android:orientation="horizontal">
<TextView
android:id="@+id/animeSourceSearch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:clickable="true"
android:focusable="true"
android:fontFamily="@font/poppins_bold"
android:padding="12dp"
android:text="@string/wrong"
android:textAlignment="textEnd"
android:textColor="?attr/colorSecondary"
tools:ignore="TextContrastCheck" />
</LinearLayout>
</FrameLayout>
<LinearLayout
@@ -214,65 +214,25 @@
android:fontFamily="@font/poppins_bold"
android:text="@string/eps"
android:textSize="16sp" />
<ImageView
android:id="@+id/animeSourceList"
android:id="@+id/animeSourceSubscribe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.33"
android:padding="8dp"
app:srcCompat="@drawable/ic_round_view_list_24"
android:layout_gravity="center_vertical"
app:srcCompat="@drawable/ic_round_notifications_none_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView
android:id="@+id/animeSourceGrid"
android:id="@+id/animeNestedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.33"
android:layout_height="match_parent"
android:layout_gravity="end"
android:padding="8dp"
app:srcCompat="@drawable/ic_round_grid_view_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView
android:id="@+id/animeSourceCompact"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.33"
android:padding="8dp"
app:srcCompat="@drawable/ic_round_view_comfy_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView
android:id="@+id/animeDownloadTop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1"
android:padding="8dp"
app:srcCompat="@drawable/ic_round_download_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView
android:id="@+id/animeScanlatorTop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1"
android:padding="8dp"
app:srcCompat="@drawable/ic_round_edit_note_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView
android:id="@+id/animeSourceTop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1"
android:padding="8dp"
android:rotation="90"
app:srcCompat="@drawable/ic_round_arrow_back_ios_new_24"
app:srcCompat="@drawable/ic_round_filter_24"
app:tint="?attr/colorOnBackground"
tools:ignore="ContentDescription,ImageContrastCheck" />
</LinearLayout>

View File

@@ -7,7 +7,7 @@
app:gest_disableGestures="true"
app:gest_rotationEnabled="true"
app:gest_restrictRotation="true"
app:gest_doubleTapZoom="3"
app:gest_doubleTapZoom="1.5"
app:gest_maxZoom="6">
<FrameLayout

View File

@@ -8,13 +8,6 @@
android:layout_marginStart="-16dp"
android:layout_marginEnd="-16dp"
android:orientation="vertical">
<LinearLayout
android:id="@+id/incognitoView"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:orientation="horizontal"
android:background="?attr/colorPrimary"
android:visibility="gone"/>
<TextView
android:id="@+id/incognitoTextView"
@@ -22,8 +15,8 @@
android:layout_width="wrap_content"
android:text="Incognito Mode"
android:fontFamily="@font/poppins_bold"
android:textSize="11sp"
android:textColor="#4f2dbd"
android:textSize="15sp"
android:textColor="?attr/colorPrimary"
android:layout_gravity="bottom|center_horizontal"
android:background="#00FFFFFF"
android:padding="8dp"

View File

@@ -150,7 +150,43 @@
android:text="@string/eps" />
</LinearLayout>
<LinearLayout
android:id="@+id/itemCompactType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginBottom="4dp"
android:orientation="horizontal"
android:visibility="gone"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/itemCompactTypeImage"
android:layout_width="18dp"
android:layout_height="18dp"
android:alpha="0.58"
app:srcCompat="@drawable/ic_round_import_contacts_24"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/itemCompactRelation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.58"
android:ellipsize="end"
android:maxLines="1"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:singleLine="true"
android:textAlignment="textEnd"
android:textAllCaps="true"
android:textSize="12sp"
android:textStyle="italic"
android:transitionName="mediaTitle"
tools:ignore="TextContrastCheck"
tools:text="Relation " />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -15,9 +15,8 @@
android:layout_marginTop="16dp"
android:layout_marginBottom="32dp"
android:background="@drawable/bottom_nav"
android:elevation="4dp"
android:padding="8dp"
android:translationZ="12dp"
android:elevation="8dp"
android:padding="6dp"
android:visibility="gone"
app:abb_animationDuration="300"
app:abb_animationInterpolator="@anim/over_shoot"

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_banner_background"/>
<foreground android:drawable="@drawable/ic_banner_foreground"/>
<foreground android:drawable="@mipmap/ic_banner_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -1,647 +0,0 @@
<resources xmlns:tools="http://schemas.android.com/tools"
tools:locale="en-rUS">
<string name="repo">rebelonion/Dantotsu</string>
<string name="preference_file_key" translatable="false">dantotsuprefs</string>
<string name="app_name">Dantotsu</string>
<string name="slogan">The NEW Best Anime &amp; Manga app for Android.</string>
<string name="login">Login</string>
<string name="logout">Logout</string>
<string name="discord">https://discord.gg/4HPZ5nAWwM</string>
<string name="github">https://github.com/rebelonion/Dantotsu</string>
<string name="home">Home</string>
<string name="anime">Anime</string>
<string name="browse_anime">Browse Anime</string>
<string name="manga">Manga</string>
<string name="browse_manga">Browse Manga</string>
<string name="info">Info</string>
<string name="watch">Watch</string>
<string name="read">Read</string>
<string name="anime_list">Anime List</string>
<string name="manga_list">Manga List</string>
<string name="sad">X(</string>
<string name="oh">:o</string>
<string name="colon">" : "</string>
<string name="no_internet">No Internet Connection</string>
<string name="refresh">Refresh</string>
<string name="search">Search</string>
<string name="search_results">Search Results</string>
<string name="sort_by">Sort By</string>
<string name="genre">Genre</string>
<string name="top_score">Top Score</string>
<string name="updated">Recently Updated</string>
<string name="trending_anime">Trending Anime</string>
<string name="popular_anime">Popular Anime</string>
<string name="trending_manga">Trending Manga</string>
<string name="trending_novel">Trending Novel</string>
<string name="popular_manga">Popular Manga</string>
<string name="username">Username</string>
<string name="chapters_read">"Chapters Read "</string>
<string name="episodes_watched">"Episodes Watched "</string>
<string name="continue_reading">Continue Reading</string>
<string name="continue_watching">Continue Watching</string>
<string name="recommended">Recommended</string>
<string name="get_recommendations">Watch/Read some Anime or Manga to get Recommendations</string>
<string name="empty">All caught up, when New?</string>
<string name="action_settings">Settings</string>
<string name="add">Add to List</string>
<string name="list_editor">List Editor</string>
<string name="add_fav">Add to Favourites</string>
<string name="notifications">Notifications</string>
<string-array name="keys">
<item>Reading</item>
<item>Watching</item>
<item>Completed</item>
<item>Paused</item>
<item>Dropped</item>
<item>Planning</item>
<item>Favourites</item>
<item>Rewatching</item>
<item>Rereading</item>
<item>All</item>
</string-array>
<string name="status">STATUS</string>
<string-array name="status" translatable="false">
<item>PLANNING</item>
<item>CURRENT</item>
<item>COMPLETED</item>
<item>REPEATING</item>
<item>PAUSED</item>
<item>DROPPED</item>
</string-array>
<string-array name="status_anime">
<item>PLANNING</item>
<item>WATCHING</item>
<item>COMPLETED</item>
<item>RE-WATCHING</item>
<item>PAUSED</item>
<item>DROPPED</item>
</string-array>
<string-array name="status_manga">
<item>PLANNING</item>
<item>READING</item>
<item>COMPLETED</item>
<item>RE-READING</item>
<item>PAUSED</item>
<item>DROPPED</item>
</string-array>
<string name="progress">PROGRESS</string>
<string name="score">SCORE</string>
<string name="score_suffix">" / 10"</string>
<string name="started_at">STARTED AT</string>
<string name="completed_at">COMPLETED AT</string>
<string name="save">Save</string>
<string name="delete">Delete</string>
<string name="remove">Remove</string>
<string name="name">Name</string>
<string name="name_romaji">Name Romaji</string>
<string name="mean_score">Mean Score</string>
<string name="format">Format</string>
<string name="status_title">Status</string>
<string name="total_eps">Total Episodes</string>
<string name="total_chaps">Total Chapters</string>
<string name="ep_duration">Average Duration</string>
<string name="min">" min"</string>
<string name="season">Season</string>
<string name="start_date">Start Date</string>
<string name="end_date">End Date</string>
<string name="source">Source</string>
<string name="main_studio">Studio</string>
<string name="genres">Genres</string>
<string name="description">Synopsis</string>
<string name="characters">Characters</string>
<string name="relations">Relations</string>
<string name="roles">Roles</string>
<string name="details">Details</string>
<string name="play_yt">Play on Youtube</string>
<string name="eps">Episodes</string>
<string name="ep">Episode</string>
<string name="chaps">Chapters</string>
<string name="chap">Chapter</string>
<string name="wrong"><u>Wrong Title?</u></string>
<string name="source_not_found">
Couldn\'t find anything X( \n
Try another source.
</string>
<string name="not_supported">%1$s is not supported!</string>
<string name="server_selector">Select Server</string>
<string name="auto_select_server">Auto Selecting Server</string>
<string name="make_default">Make Default</string>
<string name="filler">Filler</string>
<string name="adult">Adult</string>
<string name="list_only">List Only</string>
<string name="tag">Tag</string>
<string name="tags">Tags</string>
<string name="synonyms">Synonyms</string>
<string name="trailer">Trailer</string>
<string name="opening">Opening</string>
<string name="ending">Ending</string>
<string name="prequel">Prequel</string>
<string name="sequel">Sequel</string>
<string name="anilist_settings">Anilist Settings</string>
<string name="extension_settings">Extensions</string>
<string name="downloads">Downloads</string>
<string name="settings">Settings</string>
<string name="extensions">Extensions</string>
<string name="player_settings">Player Settings</string>
<string name="recentlyListOnly">Only show My Shows in Recently Updated</string>
<string name="download_manager_select">Download Manager</string>
<string name="downloadInSd">Download in SD card</string>
<string name="noSdFound">No SD card was Found.</string>
<string name="reader_settings">Reader Settings</string>
<string name="default_source">Default Source</string>
<string name="show_yt">Show Youtube Link</string>
<string name="default_ep_view">Default Episode Layout</string>
<string name="default_chp_view">Default Chapter Layout</string>
<string name="ui">User Interface</string>
<string name="common">Common</string>
<string name="theme">Theme</string>
<string name="ui_settings">UI Settings</string>
<string name="about">About</string>
<string name="desc">" Dantotsu is crafted from the ashes of Saikou and based on simplistic yet state-of-the-art elegance. It is an Anilist only client, which also lets you stream-download Anime through extensions &amp; Manga.\nDantotsu literally means the \"best of the best\" in japanese. Well, we would like to say this is the best open source app for anime and manga on Android, what would you say?"</string>
<string name="devs">Developers</string>
<string name="disclaimer">Disclaimer</string>
<string name="full_disclaimer">
- Dantotsu by itself only provides an anime and manga tracker and does not provide any anime or manga streaming or downloading capabilities.
\n\n - Dantotsu or any of its developer/staff don\'t host any of the content found inside Dantotsu. Any and all images and anime/manga information found in the app are taken from various public APIs (AniList, MyAnimeList, Kitsu).
\n\n - Furthermore, all of the anime/manga links found in Dantotsu are taken from various 3rd party plugins and have no affiliation with Dantotsu or its staff.
\n\n - Dantotsu or it\'s owners aren\'t liable for any misuse of any of the contents found inside or outside of the app and cannot be held accountable for the distribution of any of the contents found inside the app.
\n\n - By using Dantotsu, you comply to the fact that the developer of the app is not responsible for any of the contents found in the app. You also agree to the fact that you may not use Dantotsu to download or stream any copyrighted content.
\n\n - If the internet infringement issues are involved, please contact the source website. The developer does not assume any legal responsibility.
</string>
<string name="version_current">Version %1$s</string>
<string-array name="tips">
<item>You can Long Click an episode/chapter to Mark it as Read.</item>
<item>Long Clicking Shows can directly open List Editor.</item>
<item>There are few more easter eggs hidden in the App.</item>
<item>Challenge: Go to the very bottom of the Popular Anime &amp; Manga</item>
<item>Try Long Clicking the Show\'s Title.</item>
<item>Damn, why are you wasting your Time?</item>
<item>You can Long Click to copy this Message.</item>
<item>OMG LOOK! YOU FOUND AN EASTER EGG!?</item>
<item>You know who else likes this Animation?</item>
<item>MAL support? bruh.</item>
<item>Novels? more like NO vels.</item>
<item>Long Click the logo to check for App Update</item>
</string-array>
<string-array name="themes">
<item>Default</item>
<item>Theme 1</item>
<item>Theme 2</item>
<item>Theme 3</item>
<item> Theme 4</item>
</string-array>
<string name="video">Video</string>
<string name="video_info">Show Video Info</string>
<string name="source_info">Show Source Name</string>
<string name="video_info_info">Shows what the Resolution of the current video playing, useful for <b>\"Multi Quality\"</b> servers.</string>
<string name="default_quality">Auto Quality Selection</string>
<string name="height">Height</string>
<string name="width">Width</string>
<string name="default_quality_info">Automatically uses the closest quality provided by default, ONLY applied for \"Multi Quality\" Servers. Auto changes upon playing a video.</string>
<string name="default_playback_speed">Default Playback Speed : %1$s</string>
<string name="cursed_speeds">Cursed Speeds</string>
<string name="resize_mode_button">Default Resize Mode</string>
<string name="subtitles">Subtitles</string>
<string name="subtitle_toggle">Subtitles</string>
<string name="primary_sub_color_select">Subtitle Color</string>
<string name="secondary_sub_color_select">Subtitle Outline Color</string>
<string name="secondary_sub_outline_type_select">Subtitle Outline Type</string>
<string name="sub_background_color_select">Subtitle Background Color</string>
<string name="sub_window_color_select">Subtitle Window Color</string>
<string name="sub_window_color_info">"The subtitle window is the part left and right from them. (where the background isn\'t)"</string>
<string name="sub_color_info"><b>Note:</b> Changing Subtitle formatting only works with Soft-Subbed Sources such as Zoro!</string>
<string name="sub_font_select">Subtitle Font</string>
<string name="subtitle_font_size">Subtitle Size</string>
<string name="auto">Auto</string>
<string name="auto_play_next_episode">Autoplay Next Episode</string>
<string name="auto_play_next_episode_info">Automatically disables if there is no interaction with player after 1 hour.</string>
<string name="auto_skip_fillers">Auto Skip Fillers</string>
<string name="auto_skip_fillers_info">Skips filler episodes when going to next episode.</string>
<string name="update_progress">Update Progress</string>
<string name="ask_update_progress_anime">Ask for each Anime \"Individually\"</string>
<string name="ask_update_progress_manga">Ask for each Manga \"Individually\"</string>
<string name="ask_update_progress_info_ep">Turning off will always automatically update progress when the episode is watched.</string>
<string name="ask_update_progress_info_chap">Turning off will always automatically update progress when the chapter is read.</string>
<string name="ask_update_progress_hentai">Update Progress for Hentai</string>
<string name="ask_update_progress_doujin">Update Progress for Doujins</string>
<string name="very_bold">very bold of you sar</string>
<string name="watch_complete_percentage">Watch Update Percentage</string>
<string name="watch_complete_percentage_info">The percentage at which your Anilist progress should be updated after watching an episode. \nThis also sets the \% for when to preload links for the next episode.</string>
<string name="behaviour">Behaviour</string>
<string name="always_continue">Always Continue from where you left off</string>
<string name="pause_video_focus">Pause when not in Focus</string>
<string name="gestures">Volume &amp; Brightness Gestures</string>
<string name="double_tap">Double tap to Seek</string>
<string name="fast_forward">Fast Forward</string>
<string name="double_tap_info">Turning off will show fast forward &amp; rewind buttons</string>
<string name="seek_time">Seek Time</string>
<string name="seek_time_info">Amount of time in seconds for fast forward &amp; rewind.</string>
<string name="skip_time">Skip Time</string>
<string name="skip_time_info">Setting to 0, hides the Skip Button.</string>
<string name="show_cast_button">Show Cast Button</string>
<string name="show_cast_button_info">Requires \"Web Video Caster\" app to cast.</string>
<string name="picture_in_picture">Picture in Picture</string>
<string name="always_minimize">Always Minimize</string>
<string name="always_minimize_info">Requires PiP to be enabled, makes the player behave like Youtube Player but better.\nAlso hides the PiP button.</string>
<string name="app">App</string>
<string name="immersive_mode">Hide Status Bar</string>
<string name="immersive_mode_info">Requires App restart to fully apply.</string>
<string name="home_layout_show">Show/Hide Layouts on Home</string>
<string-array name="home_layouts">
<item>Continue Watching</item>
<item>Favourite Anime</item>
<item>Planned Anime</item>
<item>Continue Reading</item>
<item>Favourite Manga</item>
<item>Planned Manga</item>
<item>Recommended</item>
</string-array>
<string name="startUpTab">Default Start Up Tab</string>
<string name="small_view">Small View in Trending Shows</string>
<string name="animations">Animations</string>
<string name="banner_animations">Banner Animations</string>
<string name="layout_animations">Layout Animations</string>
<string name="animation_speed">Overall Speed</string>
<string name="empty_fav">Looks like you don\'t like anything,\nTry liking a show to keep it here.</string>
<string name="fav_anime">Favourite Anime</string>
<string name="fav_manga">Favourite Manga</string>
<string name="restart_app">Restart the app?</string>
<string name="next">Next</string>
<string name="previous">Previous</string>
<string name="current_page">Current Page</string>
<string name="dubbed">Dubbed</string>
<string name="subbed">Subbed</string>
<string name="prefer_dub">Prefer Dubbed Anime</string>
<string name="none">None</string>
<string name="selected_dns">Selected DNS</string>
<string name="dns_info">Change if your ISP blocks any source</string>
<string name="keep_screen_on">Keep Screen On</string>
<string name="layout">Layout</string>
<string name="spaced_pages">Spaced Pages</string>
<string name="direction">Direction</string>
<string name="general">General</string>
<string name="show_system_bars">Show Status &amp; Navigation Bars</string>
<string name="auto_detect_webtoon">Auto Detect Webtoon</string>
<string name="auto_detect_webtoon_info">If the Manga is not from Japan, the reader will default to Webtoon Reader Settings</string>
<string name="default_settings">Default Settings</string>
<string name="horizontal_scroll_bar">Horizontal Scroll Bar</string>
<string name="dual_page">Dual Page</string>
<string name="dual_page_info">Shows 2 Images in 1 page, will look weird if the images aren\'t the same size</string>
<string name="true_colors">True Colors</string>
<string name="true_colors_info">(32-bit color Mode) Reduces Banding on the images, may affect performance.</string>
<string name="image_rotation">Image Rotation</string>
<string name="hide_page_numbers">Hide Page Numbers</string>
<string name="sort_by_title">Sort by Title</string>
<string name="sort_by_last_updated">Sort by Last Updated</string>
<string name="sort_by_score">Sort by Score</string>
<string name="over_scroll">Over Scroll to Next/Previous Chapter</string>
<string name="volume_buttons">Change pages with Volume Buttons</string>
<string name="list_private">Private</string>
<string name="wrap_images">Wrap Images</string>
<string name="wrap_images_info">Mostly useful for larger devices, removes space between images, if they exist.</string>
<string name="reload">Reload</string>
<string name="share">Share</string>
<string name="skip">Skip</string>
<string name="show_skip_time_stamp_button">Show Skip Time Stamp Button</string>
<string name="always_load_time_stamps">Always Load Time Stamps</string>
<string name="timestamps">Time Stamps</string>
<string name="other">Other</string>
<string name="auto_skip_op_ed">Auto Skip OP / ED</string>
<string name="requires_time_stamps_to_be_enabled">Requires Time Stamps to be Enabled</string>
<string name="total_repeats">TOTAL REPEATS</string>
<string name="custom_lists">Custom Lists</string>
<string name="donate_desc">Want to support Dantotsu\'s Maintainer?\nConsider Donating</string>
<string name="donate_goal">No donation goal atm</string>
<string name="filter">Filter</string>
<string name="year">Year</string>
<string name="apply">Apply</string>
<string name="cancel">Cancel</string>
<string name="this_season">This Season</string>
<string name="next_season">Next Season</string>
<string name="previous_season">Previous Season</string>
<string name="include_media_in_list">Include List</string>
<string name="release_calendar">Calendar</string>
<string name="planned_anime">Planned Anime</string>
<string name="planned_manga">Planned Manga</string>
<string name="image_long_clicking">Open image by Long Clicking</string>
<string name="always_continue_shows">Always continue Shows</string>
<string name="timestamp_proxy_desc">Useful if you are getting Handshake Fails</string>
<string name="timestamp_proxy">Use Proxy for Timestamps</string>
<string name="check_app_updates">Always check for App Updates</string>
<string name="author">Author</string>
<string name="forks">Versions</string>
<string name="faq">FAQ</string>
<string name="accounts">Accounts</string>
<string name="myanimelist">MyAnimeList</string>
<string name="login_with_anilist">Login with Anilist!</string>
<string name="anilist">Anilist</string>
<string name="account_help">How does this work\?</string>
<string name="full_account_help">
Dantotsu is an Anilist Based App, so for syncing with your MAL Account, It needs an Anilist account to be logged in.
\nOnce logged in with both Anilist and MAL accounts, the app will automatically update your MAL account whenever:
\n- Add a new Media
\n- Edit a Media
\n- Delete a Media
\n\nNote: The app will not sync old Media\'s from Anilist to MAL, It\'s recommended to sync them.
\n- Check __FAQs__ for _Easy Method_
\n\nAnd for _Intermediates_ :
\n- [How to sync Anilist data with MAL](https://anilist.co/forum/thread/2654)
\n- [How to sync MAL data with Anilist](https://anilist.co/forum/thread/3393)
\n\n_It is not required to sync both MAL and Anilist accounts._
</string>
<string name="notification_for_checking_subscriptions">Show notification for Checking Subscriptions</string>
<string name="checking_subscriptions">Notification for Checking Subscriptions</string>
<string name="subscriptions_checking_time_s">Subscriptions Update Frequency : %1$s</string>
<string name="subscriptions_checking_time">Subscriptions Update Frequency</string>
<string name="subscriptions_info">Amount of time for Dantotsu to periodically check for new Episodes/Chapters\n(Less time will cause more battery consumption)</string>
<string name="do_not_update">Don\'t Update</string>
<string name="loading_next_chap">Loading Next Chapter</string>
<string name="grid">Grid</string>
<string name="sort_by_release_date">Sort by Release Date</string>
<string name="crop_borders">Crop Borders</string>
<string name="note">NOTE</string>
<string name="jobless_message">DAMN! YOU TRULY ARE JOBLESS\nYOU REACHED THE END</string>
<string name="file_manager_not_found">Couldn\'t find any File Manager to open SD card</string>
<string name="error_loading_data">Error loading data %1$s</string>
<string name="long_click_to_check_update">You Long Click the button to check for App Update</string>
<string name="saved_to_path">Saved to:\n%s</string>
<string name="setting_progress">Setting progress to %1$d</string>
<string name="login_anilist_account">Please Login into anilist account!</string>
<string name="congrats_vro">Congrats Vro</string>
<string name="please_reload">Please Reload.</string>
<string name="copied_text">Copied "%1$s"</string>
<string name="back_to_exit">Please perform BACK again to Exit</string>
<string name="no_internet_connection">No Internet Connection</string>
<string name="anilist_not_found">Seems like that wasn\'t found on Anilist.</string>
<string name="disabled_auto_skip">Disabled Auto Skipping OP &amp; ED</string>
<string name="auto_skip">Auto Skipping OP &amp; ED</string>
<string name="copied_to_clipboard">Copied to Clipboard</string>
<string name="first_episode">This is the 1st Episode!</string>
<string name="reset_auto_update">You can long click List Editor button to Reset Auto Update</string>
<string name="autoplay_cancelled">Autoplay cancelled, no Interaction for more than 1 Hour.</string>
<string name="auto_select_server_error">Couldn\'t auto select the server, Please try again!</string>
<string name="logging_in_mal">Logging in MAL</string>
<string name="getting_user_data">Getting User Data</string>
<string name="no_next_episode">No next Episode Found!</string>
<string name="enable_banner_animations">Try Enabling Banner Animations from Settings</string>
<string name="please_login_anilist">Please Login with Anilist!</string>
<string name="auto_update_reset">Auto Update Progress has now been Reset-ed</string>
<string name="cant_wait">Can\'t Wait, huh? fine X(</string>
<string name="downloading">Downloading…</string>
<string name="next_chapter_not_found">Next Chapter Not Found</string>
<string name="first_chapter">This is the 1st Chapter!</string>
<string name="adult_stuff">Adult Stuff?&#40; &#865;&#176; &#860;&#662; &#865;&#176; &#41;</string>
<string name="what_did_you_open">What did you even open?</string>
<string name="error_getting_data">Error getting Data from Anilist.</string>
<string name="empty_response">Empty Response, Does your internet perhaps suck?</string>
<string name="error_loading_mal_user_data">Error loading MAL User Data</string>
<string name="error_loading_mal_data">Failed to load data from MAL</string>
<string name="error_loading_anilist_user_data">Error loading Anilist User Data</string>
<string name="episode_not_found">Couldn\'t find episode : %1$s</string>
<string name="list_updated">List Updated</string>
<string name="deleted_from_list">Deleted from List</string>
<string name="no_list_id">No List ID found, reloading…</string>
<string name="checking_for_update">Checking for Update</string>
<string name="dont_show_again">Don\'t show again for version %1$s</string>
<string name="no_update_found">No Update Found</string>
<string name="downloading_update">Downloading Update %1$s</string>
<string name="permission_required">Please give permission to access Files &amp; Folders from Settings, &amp; Try again.</string>
<string name="started_downloading">Started Downloading\n%1$s</string>
<string name="install_1dm">Please install 1DM</string>
<string name="install_adm">Please install ADM</string>
<string name="error_getting_image_data">Error getting Image Data</string>
<string name="loading_image_failed">Loading Image Failed</string>
<string name="copied_device_info">Copied device info</string>
<string name="anilist_down">Seems like Anilist is down, maybe try using a VPN or you can wait for it to come back.</string>
<string name="failed_to_load_data">Failed to load saved data of %1$d</string>
<string name="access_not_available">Wasn\'t able to get access</string>
<string name="mal_login_uri_not_found">Mal Login : Uri not Found</string>
<string name="mal_login_code_challenge_not_found">Mal Login : codeChallenge not found</string>
<string name="mal_login_code_not_present">Mal Login : Code not present in Redirected URI</string>
<string name="refresh_token_load_failed">Refresh Token : Failed to load Saved Token</string>
<string name="refreshing_token_failed">Refreshing Token Failed</string>
<string name="episode_release_countdown">Episode %1$d will be released in</string>
<string name="time_format">%1$d days %2$d hrs %3$d mins %4$d secs</string>
<string-array name="sort_by">
<item>Score</item>
<item>Popular</item>
<item>Trending</item>
<item>A-Z</item>
<item>Z-A</item>
<item>What?</item>
</string-array>
<string name="main_role">MAIN</string>
<string name="supporting_role">SUPPORTING</string>
<string name="status_finished">FINISHED</string>
<string name="status_releasing">RELEASING</string>
<string name="status_not_yet_released">NOT YET RELEASED</string>
<string name="status_cancelled">CANCELLED</string>
<string name="status_hiatus">HIATUS</string>
<string name="type_adaptation">ADAPTATION</string>
<string name="type_parent">PARENT</string>
<string name="type_character">CHARACTER</string>
<string name="type_summary">SUMMARY</string>
<string name="type_alternative">ALTERNATIVE</string>
<string name="type_other">OTHER</string>
<string name="type_source">SOURCE</string>
<string name="type_contains">CONTAINS</string>
<string name="read_on_dantotsu">Read on Dantotsu</string>
<string name="watch_on_dantotsu">Watch on Dantotsu</string>
<string name="continue_episode">"Continue : Episode "</string>
<string name="continue_chapter">"Continue : "</string>
<string name="episode">"Episode "</string>
<string name="episode_num">"Episode %1$s"</string>
<string name="chapter">"Chapter "</string>
<string name="chapter_num">"Chapter %1$s"</string>
<string name="just_released">- just got released!</string>
<string name="checking_subscriptions_title">Checking Subscriptions</string>
<string name="speed">Speed</string>
<string name="auto_update">Auto Update progress for %1$s?</string>
<string name="continue_from">Continue from %1$s?</string>
<string name="title_update_progress">Update progress on anilist?</string>
<string name="incognito_will_not_update">Incognito mode will still ignore progress.</string>
<string name="dont_ask_again">"Don\'t ask again for %1$s"</string>
<string name="default_speed">Default Speed</string>
<string name="default_resize_mode">Default Resize Mode</string>
<string name="primary_sub_color">Primary Sub Color</string>
<string name="outline_sub_color">Outline Sub Color</string>
<string name="outline_type">Outline Type</string>
<string name="subtitle_font">Subtitle Font</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="close">Close</string>
<string name= "no_chapter">No Chapter</string>
<string name= "content_18">Turn on 18+ Content from your Anilist Settings</string>
<string name= "available">Available</string>
<string name="lets_go">Let\'s Go</string>
<string name="cope">Cope</string>
<string name="watched_num">"Watched "</string>
<string name="read_num">"Read "</string>
<string name="episodes_out_of">" out of "</string>
<string name="chapters_out_of">" out of "</string>
<string name="episodes_total_of">"Total of "</string>
<string name="chapters_total_of">"Total of "</string>
<string name="no_description_available">"No Description Available"</string>
<string-array name="manga_directions">
<item>Top to Bottom</item>
<item>Right to Left</item>
<item>Bottom to Top</item>
<item>Left to Right</item>
</string-array>
<string-array name="manga_layouts">
<item>Paged</item>
<item>Continuous Paged</item>
<item>Continuous</item>
</string-array>
<string name="selected">Selected</string>
<string name="found">Found</string>
<string name="age">"__Age:__ "</string>
<string name="birthday">\n"__Birthday:__ "</string>
<string name="gender">\n"__Gender:__ "</string>"
<string name="male">Male</string>
<string name="female">Female</string>
<string name="question_1">What is Dantotsu?\n Why should you use Dantotsu?</string>
<string name="answer_1">Dantotsu is crafted from the ashes of Saikou and based on simplistic yet state-of-the-art elegance. It is an Anilist only client, which also lets you stream &amp; download Anime / Manga through extensions. \n>Dantotsu (断トツ; Dan-totsu) literally means the best of the best in Japanese. Well, we would like to say this is the best open source app for anime and manga on Android, but hey, Try it out yourself &amp; judge!</string>
<string name="question_2">What are some features of Dantotsu?</string>
<string name="answer_2">Some mentionable features of Dantotsu are\n\n- Easy and functional way to both, watch anime and read manga and light novels, Ad Free.\n- A completely open source app with a nice UI &amp; Animations\n- 3rd party plugin support \n- Synchronize anime and manga real-time with AniList. Easily categorize anime and manga based on your current status. (Powered by AniList)\n- Find all shows using thoroughly and frequently updated list of all trending, popular and ongoing anime based on scores.\n- View extensive details about anime shows, movies and manga titles. It also features ability to countdown to the next episode of airing anime. (Powered by AniList &amp; MyAnimeList)</string>
<string name="question_3">What are Artifacts?</string>
<string name="answer_3">Whenever a developer commits or pull requests a feature or fix, GitHub automatically makes an APK file for you to use. This APK is called an Artifact. Artifacts through pull requests may or may not be added to the main release of the app. Artifacts have a higher chance of having bugs and glitches. To know if new artifacts are available, star the Dantotsu repository and turn on notifications\n\nTo download an Artifact:\n1) Sign In/Up in GitHub\n2) Go to Dantotsu\n3) Go to actions\n4) Press on the workflow run you want to download the artifact of.\n5) Press on artifact\n6) Extract the file using a zip extractor\n7) Install and enjoy.</string>
<string name="question_4">Is Dantotsu available for PC?</string>
<string name="answer_4">Currently no (for both Windows and Linux). There isn\'t any estimation when it will be available. But you can download any Android emulator and run Dantotsu on it. For Windows 11 users, they can use the Windows Subsystem for Android (WSA) to run Dantotsu in Windows.</string>
<string name="question_5">Is Dantotsu available for iOS?</string>
<string name="answer_5">No, and currently no plans to support iOS</string>
<string name="question_6">Why are my stats not updating?</string>
<string name="answer_6">This is because it updates every 48 hours automatically (by Anilist). If you really need to update your stats, you can force update your stats after going to this [link](https://anilist.co/settings/lists).</string>
<string name="question_7">How to download Episodes?</string>
<string name="answer_7">1. Download 1DM or ADM from Google Play Store.
\n2. Enter the app, give storage access and set your preferences (downloading speed, downloading path etc(optional))
\n3. Now go to Dantotsu > Settings > Common > Download Managers and choose the download manager you just set up.
\n4. Go to your desired episode and press on the download icon of any server. There may be a popup to set your preferences again, just press on "Download" and it will be saved in the directed path.
\n\nNote: Direct downloads are also possible without a manager but it\'s not recommended.</string>
<string name="question_8">How to download Manga Chapters?</string>
<string name="answer_8">It is yet not possible to download chapters in Dantotsu but this feature will be implemented soon.</string>
<string name="question_9">How to enable NSFW content?</string>
<string name="answer_9">You can enable nsfw content by enabling 18+ contents from this [link](https://anilist.co/settings/media).</string>
<string name="question_10">How to import my MAL/Kitsu list to Anilist?</string>
<string name="answer_10">Here is how you do it,\n\nExport:\n\n1. Go to this [link](https://malscraper.azurewebsites.net).\n2. Give your Kitsu/MAL username and download both anime and manga list. (They will be in XML format)\nNote: You have to write the username of the tracker you selected\n\nImport:\n\n1. After exporting your anime and manga list from Kitsu/MAL, now go [here](https://anilist.co/settings/import) \n2. Select/drop the anime XML file on the box above.\n|→Select/drop the manga XML file on the box below.</string>
<string name="question_11">How to import my Anilist/Kitsu list to MAL?</string>
<string name="answer_11">Here is how you do it,\n\nExport:\n\n1. Go to this [link](https://malscraper.azurewebsites.net/). \n2. Give your Anilist username/Kitsu ID in the \'Username/Kitsu User ID\' box. \n3. Select list type and enable \'update_on_import\'. \n4. Download the file; it will be in .xml format. Be sure to download both Anime and Manga lists.\n\nImport:\n1. To import it in your MAL account, go to this [link](https://myanimelist.net/import.php) and choose \'MyAnimeList Import\' as import type. \n2. Press on \'Choose File\'and select the downloaded anime/manga list XML file. \n3. Press on \'Import Data\'. \nCongratulations, you just imported the selected list to your MAL account.</string>
<string name="question_12">Why can\'t I find a specific anime/manga title?</string>
<string name="answer_12">Let\'s say you are looking for Castlevania in Dantotsu. But Anilist doesn\'t have it, so Dantotsu doesn\'t either.\nA solution to the above problem is as follows:\n1) Go to any anime that\'s not in your list.\n2) Go to the watch section.\n3) Select any source and press on the \'Wrong Title?\'.\n4) Now search for Castlevania (The anime you were looking for) and select it.\n5) ENJOY!\n\nIf you can\'t find the anime even through these steps, then that\'s bad luck for you, bud. Even that source doesn\'t have it. Try a different source.</string>
<string name="question_13">How to fix sources selecting a completely wrong title?</string>
<string name="answer_13">Dantotsu itself doesn\'t host anything but relies on other sources. When showing the episodes, it chooses the 1st result given by the source after searching for the title. Dantotsu has no way of detecting if that\'s legit or not. So, for this, we have the \'Wrong Title?\' just below the source name(above layouts). You can choose the correct result/title by pressing on it and enjoy its episodes.</string>
<string name="question_14">How to read coloured mangas?</string>
<string name="answer_14">Are you in search of coloured manga? Sorry to break it to you but an extremely small amount of mangas have coloured version. Those which has a coloured version is also available in Dantotsu. Let\'s say you want to read the coloured version of chainsaw man. Then follow the below steps ↓\n\n1) Go to Chainsaw Man\n2) Press on \'Read\'\n3) Select any working source\n4) Press on \'Wrong Title\'\n5) Select the colored version chainsaw man\n6) Enjoy\n\nNote: Many sources don\'t have the coloured version available even if it\'s available somewhere on the internet. So try a different source. If none of the sources have it, then a coloured version of your desired manga simply doesn\'t exist. If you can find it on any manga site on the internet, you can suggest that site through the Discord server.</string>
<string name="question_15">Handshake fails? Why are no timestamps not loading?</string>
<string name="answer_15">You can fix this issue by enabling \'Proxy\' from \n\`Settings > Anime > Player Settings > Timestamps > Proxy\`.\nIf the timestamps are still not loading but the handshake failed popup is fixed, then the episode you are watching just doesn\'t have timestamps yet for it.</string>
<string name="question_16">Having trouble with a source?</string>
<string name="answer_16">Some basic fixes would be :\n\n• Restart the app. \n• Use a different DNS from your settings, preferably, CloudFlare. \n• VPN might work as well. \n\nIf you refuse to try the above steps then just use a different source.\n\nNote: Allanime fixes itself most of the time.</string>
<string name="question_17">Some useful tips and tricks</string>
<string name="answer_17">The following presents some tips and tricks you may or may not know about - \n \n \n - By hold pressing the Dantotsu logo in settings, you can check if there are any new updates manually. \n \n - Hold pressing an error message/tag/synonym or title will copy it. \n \n - You can open an episode with other apps by hold pressing any server for that episode. This helps in streaming the episode using other video players or download the episode using download managers. \n \n - You can set up custom lists using this [link](https://anilist.co/settings/lists). (you need to be signed in) \n \n - If your episode/chapter is not being progressed automatically after you finish watching/reading it, then hold press the status bar(planning/repeating/watching button) of that anime/manga. The next time you start a chapter/finish an episode, you will stumble upon a popup. Press yes there. </string>
<string name="subscribed_notification">Subscribed! Receiving notifications, when new episodes are released on %1$s.</string>
<string name="unsubscribed_notification">Unsubscribed, you will not receive any notifications.</string>
<string name="episode_plural">Episodes</string>
<string name="episode_singular">Episode</string>
<string name="chapter_singular">Chapter</string>
<string name="chapter_plural">Chapters</string>
<string name="filter_format">"Format : %1$s"</string>
<string name="filter_sort">"Sort : %1$s"</string>
<string name="filter_exclude">"Not %1$s"</string>
<string name="search_by_image">Search by Image</string>
<string name="upload_image">Upload Image</string>
<string name="similarity_text">Similarity: %1$s %%</string>
<string name="time_range">From %1$s to %2$s</string>
<string name="invalid_url">Invalid URL</string>
<string name="no_anilist_id_found">No Anilist ID found</string>
<string name="error_loading_image">Error loading image</string>
<string name="discord_logout_success">Successfully Logged Out</string>
<string name="discord_try_again">Try logging-in again</string>
<string name="error_loading_discord_user_data">Error loading Discord User Data</string>
<string name="discord_warning"><![CDATA[By logging in, your discord will now show what you are watching & reading on Dantotsu\n\nIf you are on invisible mode, logging in will make you online, when you open Dantotsu.\n\nThis does break the Discord TOS. \nAlthough Discord has never banned anyone for using Custom Rich Presence(what Dantotsu uses), You have still been warned.\n\nDantotsu is not responsible for anything that happens to your account.]]></string>
<string name="warning">Warning</string>
<string name="view_anime">View Anime</string>
<string name="view_manga">View Manga</string>
<string name="force_legacy_installer">Force Legacy Installer</string>
<string name="extensions_settings">Extensions</string>
<string name="NSFWExtention">NSFW Extensions</string>
<string name="skip_loading_extension_icons">Skip loading extension icons</string>
<string name="use_material_you">Material You</string>
<string name="extension_specific_dns">Extension-specific DNS</string>
<string name="theme_">Theme:</string>
<string name="user_agent">User Agent</string>
<string name="custom_theme">Custom Theme</string>
<string name="use_custom_theme">Custom theme</string>
<string name="use_unique_theme_for_each_item">Color same as Anime/Manga cover</string>
<string name="oled_theme_variant">OLED theme variant</string>
<string name="installed_anime">Installed Anime</string>
<string name="available_anime">Available Anime</string>
<string name="installed_manga">Installed Manga</string>
<string name="color_picker">Color Picker</string>
<string name="random_selection">Random Selection</string>
<string name="incognito_mode">Incognito Mode</string>
</resources>

View File

@@ -65,32 +65,32 @@
</style>
<style name="Theme.Dantotsu.Purple" parent="Theme.Base">
<item name="colorPrimary">@color/mdmd_theme_dark_3_primary</item>
<item name="colorOnPrimary">@color/mdmd_theme_dark_3_onPrimary</item>
<item name="colorPrimaryContainer">@color/mdmd_theme_dark_3_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/mdmd_theme_dark_3_onPrimaryContainer</item>
<item name="colorSecondary">@color/mdmd_theme_dark_3_secondary</item>
<item name="colorOnSecondary">@color/mdmd_theme_dark_3_onSecondary</item>
<item name="colorSecondaryContainer">@color/mdmd_theme_dark_3_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/mdmd_theme_dark_3_onSecondaryContainer</item>
<item name="colorTertiary">@color/mdmd_theme_dark_3_tertiary</item>
<item name="colorOnTertiary">@color/mdmd_theme_dark_3_onTertiary</item>
<item name="colorTertiaryContainer">@color/mdmd_theme_dark_3_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/mdmd_theme_dark_3_onTertiaryContainer</item>
<item name="colorError">@color/mdmd_theme_dark_3_error</item>
<item name="colorErrorContainer">@color/mdmd_theme_dark_3_errorContainer</item>
<item name="colorOnError">@color/mdmd_theme_dark_3_onError</item>
<item name="colorOnErrorContainer">@color/mdmd_theme_dark_3_onErrorContainer</item>
<item name="android:colorBackground">@color/mdmd_theme_dark_3_background</item>
<item name="colorOnBackground">@color/mdmd_theme_dark_3_onBackground</item>
<item name="colorSurface">@color/mdmd_theme_dark_3_surface</item>
<item name="colorOnSurface">@color/mdmd_theme_dark_3_onSurface</item>
<item name="colorSurfaceVariant">@color/mdmd_theme_dark_3_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/mdmd_theme_dark_3_onSurfaceVariant</item>
<item name="colorOutline">@color/mdmd_theme_dark_3_outline</item>
<item name="colorOnSurfaceInverse">@color/mdmd_theme_dark_3_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/mdmd_theme_dark_3_inverseSurface</item>
<item name="colorPrimaryInverse">@color/mdmd_theme_dark_3_inversePrimary</item>
<item name="colorPrimary">@color/md_theme_dark_3_primary</item>
<item name="colorOnPrimary">@color/md_theme_dark_3_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_dark_3_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_dark_3_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_dark_3_secondary</item>
<item name="colorOnSecondary">@color/md_theme_dark_3_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_dark_3_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_dark_3_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_dark_3_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_dark_3_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_dark_3_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_dark_3_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_dark_3_error</item>
<item name="colorErrorContainer">@color/md_theme_dark_3_errorContainer</item>
<item name="colorOnError">@color/md_theme_dark_3_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_dark_3_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_dark_3_background</item>
<item name="colorOnBackground">@color/md_theme_dark_3_onBackground</item>
<item name="colorSurface">@color/md_theme_dark_3_surface</item>
<item name="colorOnSurface">@color/md_theme_dark_3_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_dark_3_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_dark_3_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_dark_3_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_dark_3_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_dark_3_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_dark_3_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Pink" parent="Theme.Base">
@@ -122,6 +122,122 @@
<item name="colorPrimaryInverse">@color/md_theme_dark_4_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Saikou" parent="Theme.Base">
<item name="colorPrimary">@color/md_theme_dark_5_primary</item>
<item name="colorOnPrimary">@color/md_theme_dark_5_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_dark_5_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_dark_5_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_dark_5_secondary</item>
<item name="colorOnSecondary">@color/md_theme_dark_5_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_dark_5_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_dark_5_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_dark_5_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_dark_5_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_dark_5_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_dark_5_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_dark_5_error</item>
<item name="colorErrorContainer">@color/md_theme_dark_5_errorContainer</item>
<item name="colorOnError">@color/md_theme_dark_5_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_dark_5_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_dark_5_background</item>
<item name="colorOnBackground">@color/md_theme_dark_5_onBackground</item>
<item name="colorSurface">@color/md_theme_dark_5_surface</item>
<item name="colorOnSurface">@color/md_theme_dark_5_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_dark_5_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_dark_5_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_dark_5_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_dark_5_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_dark_5_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_dark_5_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Red" parent="Theme.Base">
<item name="colorPrimary">@color/md_theme_dark_6_primary</item>
<item name="colorOnPrimary">@color/md_theme_dark_6_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_dark_6_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_dark_6_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_dark_6_secondary</item>
<item name="colorOnSecondary">@color/md_theme_dark_6_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_dark_6_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_dark_6_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_dark_6_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_dark_6_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_dark_6_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_dark_6_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_dark_6_error</item>
<item name="colorErrorContainer">@color/md_theme_dark_6_errorContainer</item>
<item name="colorOnError">@color/md_theme_dark_6_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_dark_6_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_dark_6_background</item>
<item name="colorOnBackground">@color/md_theme_dark_6_onBackground</item>
<item name="colorSurface">@color/md_theme_dark_6_surface</item>
<item name="colorOnSurface">@color/md_theme_dark_6_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_dark_6_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_dark_6_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_dark_6_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_dark_6_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_dark_6_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_dark_6_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Lavender" parent="Theme.Base">
<item name="colorPrimary">@color/md_theme_dark_7_primary</item>
<item name="colorOnPrimary">@color/md_theme_dark_7_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_dark_7_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_dark_7_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_dark_7_secondary</item>
<item name="colorOnSecondary">@color/md_theme_dark_7_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_dark_7_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_dark_7_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_dark_7_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_dark_7_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_dark_7_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_dark_7_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_dark_7_error</item>
<item name="colorErrorContainer">@color/md_theme_dark_7_errorContainer</item>
<item name="colorOnError">@color/md_theme_dark_7_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_dark_7_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_dark_7_background</item>
<item name="colorOnBackground">@color/md_theme_dark_7_onBackground</item>
<item name="colorSurface">@color/md_theme_dark_7_surface</item>
<item name="colorOnSurface">@color/md_theme_dark_7_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_dark_7_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_dark_7_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_dark_7_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_dark_7_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_dark_7_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_dark_7_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Emerald" parent="Theme.Base">
<item name="colorPrimary">@color/md_theme_dark_8_primary</item>
<item name="colorOnPrimary">@color/md_theme_dark_8_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_dark_8_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_dark_8_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_dark_8_secondary</item>
<item name="colorOnSecondary">@color/md_theme_dark_8_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_dark_8_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_dark_8_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_dark_8_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_dark_8_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_dark_8_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_dark_8_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_dark_8_error</item>
<item name="colorErrorContainer">@color/md_theme_dark_8_errorContainer</item>
<item name="colorOnError">@color/md_theme_dark_8_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_dark_8_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_dark_8_background</item>
<item name="colorOnBackground">@color/md_theme_dark_8_onBackground</item>
<item name="colorSurface">@color/md_theme_dark_8_surface</item>
<item name="colorOnSurface">@color/md_theme_dark_8_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_dark_8_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_dark_8_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_dark_8_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_dark_8_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_dark_8_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_dark_8_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Monochrome" parent="Theme.Base">
<item name="colorPrimary">@color/bg_white</item>
<item name="colorOnPrimary">@color/bg_black</item>
@@ -151,93 +267,6 @@
<item name="colorPrimaryInverse">@color/bg_black</item>
</style>
<style name="Theme.Dantotsu.Red" parent="Theme.Base">
<item name="colorPrimary">@color/md_0_theme_dark_primary</item>
<item name="colorOnPrimary">@color/md_0_theme_dark_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_0_theme_dark_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_0_theme_dark_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_0_theme_dark_secondary</item>
<item name="colorOnSecondary">@color/md_0_theme_dark_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_0_theme_dark_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_0_theme_dark_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_0_theme_dark_tertiary</item>
<item name="colorOnTertiary">@color/md_0_theme_dark_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_0_theme_dark_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_0_theme_dark_onTertiaryContainer</item>
<item name="colorError">@color/md_0_theme_dark_error</item>
<item name="colorErrorContainer">@color/md_0_theme_dark_errorContainer</item>
<item name="colorOnError">@color/md_0_theme_dark_onError</item>
<item name="colorOnErrorContainer">@color/md_0_theme_dark_onErrorContainer</item>
<item name="android:colorBackground">@color/md_0_theme_dark_background</item>
<item name="colorOnBackground">@color/md_0_theme_dark_onBackground</item>
<item name="colorSurface">@color/md_0_theme_dark_surface</item>
<item name="colorOnSurface">@color/md_0_theme_dark_onSurface</item>
<item name="colorSurfaceVariant">@color/md_0_theme_dark_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_0_theme_dark_onSurfaceVariant</item>
<item name="colorOutline">@color/md_0_theme_dark_outline</item>
<item name="colorOnSurfaceInverse">@color/md_0_theme_dark_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_0_theme_dark_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_0_theme_dark_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Lavender" parent="Theme.Base">
<item name="colorPrimary">@color/md_1_theme_dark_primary</item>
<item name="colorOnPrimary">@color/md_1_theme_dark_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_1_theme_dark_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_1_theme_dark_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_1_theme_dark_secondary</item>
<item name="colorOnSecondary">@color/md_1_theme_dark_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_1_theme_dark_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_1_theme_dark_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_1_theme_dark_tertiary</item>
<item name="colorOnTertiary">@color/md_1_theme_dark_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_1_theme_dark_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_1_theme_dark_onTertiaryContainer</item>
<item name="colorError">@color/md_1_theme_dark_error</item>
<item name="colorErrorContainer">@color/md_1_theme_dark_errorContainer</item>
<item name="colorOnError">@color/md_1_theme_dark_onError</item>
<item name="colorOnErrorContainer">@color/md_1_theme_dark_onErrorContainer</item>
<item name="android:colorBackground">@color/md_1_theme_dark_background</item>
<item name="colorOnBackground">@color/md_1_theme_dark_onBackground</item>
<item name="colorSurface">@color/md_1_theme_dark_surface</item>
<item name="colorOnSurface">@color/md_1_theme_dark_onSurface</item>
<item name="colorSurfaceVariant">@color/md_1_theme_dark_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_1_theme_dark_onSurfaceVariant</item>
<item name="colorOutline">@color/md_1_theme_dark_outline</item>
<item name="colorOnSurfaceInverse">@color/md_1_theme_dark_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_1_theme_dark_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_1_theme_dark_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Saikou" parent="Theme.Base">
<item name="colorPrimary">@color/md_theme_dark_5_primary</item>
<item name="colorOnPrimary">@color/md_theme_dark_5_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_dark_5_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_dark_5_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_dark_5_secondary</item>
<item name="colorOnSecondary">@color/md_theme_dark_5_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_dark_5_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_dark_5_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_dark_5_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_dark_5_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_dark_5_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_dark_5_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_dark_5_error</item>
<item name="colorErrorContainer">@color/md_theme_dark_5_errorContainer</item>
<item name="colorOnError">@color/md_theme_dark_5_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_dark_5_onErrorContainer</item>
<item name="android:colorBackground">@color/md_1_theme_dark_background</item>
<item name="colorOnBackground">@color/md_theme_dark_5_onBackground</item>
<item name="colorSurface">@color/md_theme_dark_5_surface</item>
<item name="colorOnSurface">@color/md_theme_dark_5_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_dark_5_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_dark_5_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_dark_5_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_dark_5_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_dark_5_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_dark_5_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.BlueOLED" parent="Theme.Dantotsu.Blue">
<item name="android:colorBackground">@color/darkest_Black</item>
<item name="colorSurface">@color/darkest_Black</item>
@@ -277,5 +306,9 @@
<item name="android:colorBackground">@color/darkest_Black</item>
<item name="colorSurface">@color/darkest_Black</item>
</style>
<style name="Theme.Dantotsu.EmeraldOLED" parent="Theme.Dantotsu.Emerald">
<item name="android:colorBackground">@color/darkest_Black</item>
<item name="colorSurface">@color/darkest_Black</item>
</style>
</resources>

View File

@@ -24,6 +24,14 @@
<color name="yt_red">#CD201F</color>
<color name="chip">#a3a2a2</color>
<color name="grey_nav">#E8EDEDED</color>
<color name="CustomColor1">#93DB00</color>
<color name="CustomColor2">#68AF86</color>
<color name="CustomColor3">#0096AE</color>
<color name="CustomColor4">#000000</color>
<color name="light_blue_50">#FFE1F5FE</color>
<color name="light_blue_200">#FF81D4FA</color>
<color name="light_blue_600">#FF039BE5</color>
<color name="light_blue_900">#FF01579B</color>
<!-- theme 1 -->
<color name="seed_1">#00658e</color>
@@ -88,6 +96,7 @@
<color name="md_theme_dark_1_outlineVariant">#41484D</color>
<color name="md_theme_dark_1_scrim">#000000</color>
<!-- theme 2 -->
<color name="seed_2">#426916</color>
<color name="md_theme_light_2_primary">#426916</color>
<color name="md_theme_light_2_onPrimary">#FFFFFF</color>
@@ -150,6 +159,7 @@
<color name="md_theme_dark_2_outlineVariant">#44483D</color>
<color name="md_theme_dark_2_scrim">#000000</color>
<!-- theme 3 -->
<color name="seed_3">#7c4997</color>
<color name="md_theme_light_3_primary">#7C4997</color>
<color name="md_theme_light_3_onPrimary">#FFFFFF</color>
@@ -181,37 +191,38 @@
<color name="md_theme_light_3_surfaceTint">#7C4997</color>
<color name="md_theme_light_3_outlineVariant">#CEC3CE</color>
<color name="md_theme_light_3_scrim">#000000</color>
<color name="mdmd_theme_dark_3_primary">#E7B3FF</color>
<color name="mdmd_theme_dark_3_onPrimary">#4A1765</color>
<color name="mdmd_theme_dark_3_primaryContainer">#62307D</color>
<color name="mdmd_theme_dark_3_onPrimaryContainer">#F6D9FF</color>
<color name="mdmd_theme_dark_3_secondary">#D3C0D8</color>
<color name="mdmd_theme_dark_3_onSecondary">#382C3E</color>
<color name="mdmd_theme_dark_3_secondaryContainer">#504255</color>
<color name="mdmd_theme_dark_3_onSecondaryContainer">#F0DCF4</color>
<color name="mdmd_theme_dark_3_tertiary">#F5B7B7</color>
<color name="mdmd_theme_dark_3_onTertiary">#4C2526</color>
<color name="mdmd_theme_dark_3_tertiaryContainer">#663B3B</color>
<color name="mdmd_theme_dark_3_onTertiaryContainer">#FFDAD9</color>
<color name="mdmd_theme_dark_3_error">#FFB4AB</color>
<color name="mdmd_theme_dark_3_errorContainer">#93000A</color>
<color name="mdmd_theme_dark_3_onError">#690005</color>
<color name="mdmd_theme_dark_3_onErrorContainer">#FFDAD6</color>
<color name="mdmd_theme_dark_3_background">#1D1B1E</color>
<color name="mdmd_theme_dark_3_onBackground">#E8E0E5</color>
<color name="mdmd_theme_dark_3_surface">#1D1B1E</color>
<color name="mdmd_theme_dark_3_onSurface">#E8E0E5</color>
<color name="mdmd_theme_dark_3_surfaceVariant">#4B444D</color>
<color name="mdmd_theme_dark_3_onSurfaceVariant">#CEC3CE</color>
<color name="mdmd_theme_dark_3_outline">#978E98</color>
<color name="mdmd_theme_dark_3_inverseOnSurface">#1D1B1E</color>
<color name="mdmd_theme_dark_3_inverseSurface">#E8E0E5</color>
<color name="mdmd_theme_dark_3_inversePrimary">#7C4997</color>
<color name="mdmd_theme_dark_3_shadow">#000000</color>
<color name="mdmd_theme_dark_3_surfaceTint">#E7B3FF</color>
<color name="mdmd_theme_dark_3_outlineVariant">#4B444D</color>
<color name="mdmd_theme_dark_3_scrim">#000000</color>
<color name="md_theme_dark_3_primary">#E7B3FF</color>
<color name="md_theme_dark_3_onPrimary">#4A1765</color>
<color name="md_theme_dark_3_primaryContainer">#62307D</color>
<color name="md_theme_dark_3_onPrimaryContainer">#F6D9FF</color>
<color name="md_theme_dark_3_secondary">#D3C0D8</color>
<color name="md_theme_dark_3_onSecondary">#382C3E</color>
<color name="md_theme_dark_3_secondaryContainer">#504255</color>
<color name="md_theme_dark_3_onSecondaryContainer">#F0DCF4</color>
<color name="md_theme_dark_3_tertiary">#F5B7B7</color>
<color name="md_theme_dark_3_onTertiary">#4C2526</color>
<color name="md_theme_dark_3_tertiaryContainer">#663B3B</color>
<color name="md_theme_dark_3_onTertiaryContainer">#FFDAD9</color>
<color name="md_theme_dark_3_error">#FFB4AB</color>
<color name="md_theme_dark_3_errorContainer">#93000A</color>
<color name="md_theme_dark_3_onError">#690005</color>
<color name="md_theme_dark_3_onErrorContainer">#FFDAD6</color>
<color name="md_theme_dark_3_background">#1D1B1E</color>
<color name="md_theme_dark_3_onBackground">#E8E0E5</color>
<color name="md_theme_dark_3_surface">#1D1B1E</color>
<color name="md_theme_dark_3_onSurface">#E8E0E5</color>
<color name="md_theme_dark_3_surfaceVariant">#4B444D</color>
<color name="md_theme_dark_3_onSurfaceVariant">#CEC3CE</color>
<color name="md_theme_dark_3_outline">#978E98</color>
<color name="md_theme_dark_3_inverseOnSurface">#1D1B1E</color>
<color name="md_theme_dark_3_inverseSurface">#E8E0E5</color>
<color name="md_theme_dark_3_inversePrimary">#7C4997</color>
<color name="md_theme_dark_3_shadow">#000000</color>
<color name="md_theme_dark_3_surfaceTint">#E7B3FF</color>
<color name="md_theme_dark_3_outlineVariant">#4B444D</color>
<color name="md_theme_dark_3_scrim">#000000</color>
<!-- theme 4 -->
<color name="seed_4">#e800ac</color>
<color name="md_theme_light_4_primary">#B30084</color>
<color name="md_theme_light_4_onPrimary">#FFFFFF</color>
@@ -274,7 +285,8 @@
<color name="md_theme_dark_4_outlineVariant">#504349</color>
<color name="md_theme_dark_4_scrim">#000000</color>
<color name="seed_7">#FF007F
<!-- theme 5 -->
<color name="seed_5">#FF007F
</color> <!-- if there are any issues with Saikou theme message @Wai What to get it fixed, unallocated colors are also set to "#00FF00" as they are not yet being used-->
<color name="md_theme_light_5_primary">#FF007F</color>
<color name="md_theme_light_5_onPrimary">#EEEEEE
@@ -330,7 +342,7 @@
<color name="md_theme_dark_5_errorContainer">#00FF00</color>
<color name="md_theme_dark_5_onError">#00FF00</color>
<color name="md_theme_dark_5_onErrorContainer">#00FF00</color>
<color name="md_theme_dark_5_background">#1C1B20</color>
<color name="md_theme_dark_5_background">#1C1B1E</color>
<color name="md_theme_dark_5_onBackground">#EEEEEE</color>
<color name="md_theme_dark_5_surface">#1C1B20</color>
<color name="md_theme_dark_5_onSurface">#EEEEEE</color>
@@ -345,135 +357,195 @@
<color name="md_theme_dark_5_outlineVariant">#00FF00</color>
<color name="md_theme_dark_5_scrim">#00FF00</color>
<color name="seed_5">#c9000b</color>
<color name="md_0_theme_light_primary">#C0000A</color>
<color name="md_0_theme_light_onPrimary">#FFFFFF</color>
<color name="md_0_theme_light_primaryContainer">#FFDAD5</color>
<color name="md_0_theme_light_onPrimaryContainer">#410001</color>
<color name="md_0_theme_light_secondary">#775652</color>
<color name="md_0_theme_light_onSecondary">#FFFFFF</color>
<color name="md_0_theme_light_secondaryContainer">#FFDAD5</color>
<color name="md_0_theme_light_onSecondaryContainer">#2C1512</color>
<color name="md_0_theme_light_tertiary">#705C2E</color>
<color name="md_0_theme_light_onTertiary">#FFFFFF</color>
<color name="md_0_theme_light_tertiaryContainer">#FCDFA6</color>
<color name="md_0_theme_light_onTertiaryContainer">#261A00</color>
<color name="md_0_theme_light_error">#BA1A1A</color>
<color name="md_0_theme_light_errorContainer">#FFDAD6</color>
<color name="md_0_theme_light_onError">#FFFFFF</color>
<color name="md_0_theme_light_onErrorContainer">#410002</color>
<color name="md_0_theme_light_background">#FFFBFF</color>
<color name="md_0_theme_light_onBackground">#201A19</color>
<color name="md_0_theme_light_surface">#FFFBFF</color>
<color name="md_0_theme_light_onSurface">#201A19</color>
<color name="md_0_theme_light_surfaceVariant">#F5DDDA</color>
<color name="md_0_theme_light_onSurfaceVariant">#534341</color>
<color name="md_0_theme_light_outline">#857370</color>
<color name="md_0_theme_light_inverseOnSurface">#FBEEEC</color>
<color name="md_0_theme_light_inverseSurface">#362F2E</color>
<color name="md_0_theme_light_inversePrimary">#FFB4AA</color>
<color name="md_0_theme_light_shadow">#000000</color>
<color name="md_0_theme_light_surfaceTint">#C0000A</color>
<color name="md_0_theme_light_outlineVariant">#D8C2BE</color>
<color name="md_0_theme_light_scrim">#000000</color>
<color name="md_0_theme_dark_primary">#FFB4AA</color>
<color name="md_0_theme_dark_onPrimary">#690003</color>
<color name="md_0_theme_dark_primaryContainer">#930005</color>
<color name="md_0_theme_dark_onPrimaryContainer">#FFDAD5</color>
<color name="md_0_theme_dark_secondary">#E7BDB7</color>
<color name="md_0_theme_dark_onSecondary">#442926</color>
<color name="md_0_theme_dark_secondaryContainer">#5D3F3B</color>
<color name="md_0_theme_dark_onSecondaryContainer">#FFDAD5</color>
<color name="md_0_theme_dark_tertiary">#DFC38C</color>
<color name="md_0_theme_dark_onTertiary">#3E2E04</color>
<color name="md_0_theme_dark_tertiaryContainer">#574419</color>
<color name="md_0_theme_dark_onTertiaryContainer">#FCDFA6</color>
<color name="md_0_theme_dark_error">#FFB4AB</color>
<color name="md_0_theme_dark_errorContainer">#93000A</color>
<color name="md_0_theme_dark_onError">#690005</color>
<color name="md_0_theme_dark_onErrorContainer">#FFDAD6</color>
<color name="md_0_theme_dark_background">#201A19</color>
<color name="md_0_theme_dark_onBackground">#EDE0DE</color>
<color name="md_0_theme_dark_surface">#201A19</color>
<color name="md_0_theme_dark_onSurface">#EDE0DE</color>
<color name="md_0_theme_dark_surfaceVariant">#534341</color>
<color name="md_0_theme_dark_onSurfaceVariant">#D8C2BE</color>
<color name="md_0_theme_dark_outline">#A08C89</color>
<color name="md_0_theme_dark_inverseOnSurface">#201A19</color>
<color name="md_0_theme_dark_inverseSurface">#EDE0DE</color>
<color name="md_0_theme_dark_inversePrimary">#C0000A</color>
<color name="md_0_theme_dark_shadow">#000000</color>
<color name="md_0_theme_dark_surfaceTint">#FFB4AA</color>
<color name="md_0_theme_dark_outlineVariant">#534341</color>
<color name="md_0_theme_dark_scrim">#000000</color>
<!-- theme 6 -->
<color name="seed_6">#c9000b</color>
<color name="md_theme_light_6_primary">#C0000A</color>
<color name="md_theme_light_6_onPrimary">#FFFFFF</color>
<color name="md_theme_light_6_primaryContainer">#FFDAD5</color>
<color name="md_theme_light_6_onPrimaryContainer">#410001</color>
<color name="md_theme_light_6_secondary">#775652</color>
<color name="md_theme_light_6_onSecondary">#FFFFFF</color>
<color name="md_theme_light_6_secondaryContainer">#FFDAD5</color>
<color name="md_theme_light_6_onSecondaryContainer">#2C1512</color>
<color name="md_theme_light_6_tertiary">#705C2E</color>
<color name="md_theme_light_6_onTertiary">#FFFFFF</color>
<color name="md_theme_light_6_tertiaryContainer">#FCDFA6</color>
<color name="md_theme_light_6_onTertiaryContainer">#261A00</color>
<color name="md_theme_light_6_error">#BA1A1A</color>
<color name="md_theme_light_6_errorContainer">#FFDAD6</color>
<color name="md_theme_light_6_onError">#FFFFFF</color>
<color name="md_theme_light_6_onErrorContainer">#410002</color>
<color name="md_theme_light_6_background">#FFFBFF</color>
<color name="md_theme_light_6_onBackground">#201A19</color>
<color name="md_theme_light_6_surface">#FFFBFF</color>
<color name="md_theme_light_6_onSurface">#201A19</color>
<color name="md_theme_light_6_surfaceVariant">#F5DDDA</color>
<color name="md_theme_light_6_onSurfaceVariant">#534341</color>
<color name="md_theme_light_6_outline">#857370</color>
<color name="md_theme_light_6_inverseOnSurface">#FBEEEC</color>
<color name="md_theme_light_6_inverseSurface">#362F2E</color>
<color name="md_theme_light_6_inversePrimary">#FFB4AA</color>
<color name="md_theme_light_6_shadow">#000000</color>
<color name="md_theme_light_6_surfaceTint">#C0000A</color>
<color name="md_theme_light_6_outlineVariant">#D8C2BE</color>
<color name="md_theme_light_6_scrim">#000000</color>
<color name="md_theme_dark_6_primary">#FFB4AA</color>
<color name="md_theme_dark_6_onPrimary">#690003</color>
<color name="md_theme_dark_6_primaryContainer">#930005</color>
<color name="md_theme_dark_6_onPrimaryContainer">#FFDAD5</color>
<color name="md_theme_dark_6_secondary">#E7BDB7</color>
<color name="md_theme_dark_6_onSecondary">#442926</color>
<color name="md_theme_dark_6_secondaryContainer">#5D3F3B</color>
<color name="md_theme_dark_6_onSecondaryContainer">#FFDAD5</color>
<color name="md_theme_dark_6_tertiary">#DFC38C</color>
<color name="md_theme_dark_6_onTertiary">#3E2E04</color>
<color name="md_theme_dark_6_tertiaryContainer">#574419</color>
<color name="md_theme_dark_6_onTertiaryContainer">#FCDFA6</color>
<color name="md_theme_dark_6_error">#FFB4AB</color>
<color name="md_theme_dark_6_errorContainer">#93000A</color>
<color name="md_theme_dark_6_onError">#690005</color>
<color name="md_theme_dark_6_onErrorContainer">#FFDAD6</color>
<color name="md_theme_dark_6_background">#201A19</color>
<color name="md_theme_dark_6_onBackground">#EDE0DE</color>
<color name="md_theme_dark_6_surface">#201A19</color>
<color name="md_theme_dark_6_onSurface">#EDE0DE</color>
<color name="md_theme_dark_6_surfaceVariant">#534341</color>
<color name="md_theme_dark_6_onSurfaceVariant">#D8C2BE</color>
<color name="md_theme_dark_6_outline">#A08C89</color>
<color name="md_theme_dark_6_inverseOnSurface">#201A19</color>
<color name="md_theme_dark_6_inverseSurface">#EDE0DE</color>
<color name="md_theme_dark_6_inversePrimary">#C0000A</color>
<color name="md_theme_dark_6_shadow">#000000</color>
<color name="md_theme_dark_6_surfaceTint">#FFB4AA</color>
<color name="md_theme_dark_6_outlineVariant">#534341</color>
<color name="md_theme_dark_6_scrim">#000000</color>
<!-- theme 7 -->
<color name="seed_7">#6750A4</color>
<color name="md_theme_light_7_primary">#6750A4</color>
<color name="md_theme_light_7_onPrimary">#FFFFFF</color>
<color name="md_theme_light_7_primaryContainer">#E9DDFF</color>
<color name="md_theme_light_7_onPrimaryContainer">#22005D</color>
<color name="md_theme_light_7_secondary">#625B71</color>
<color name="md_theme_light_7_onSecondary">#FFFFFF</color>
<color name="md_theme_light_7_secondaryContainer">#E8DEF8</color>
<color name="md_theme_light_7_onSecondaryContainer">#1E192B</color>
<color name="md_theme_light_7_tertiary">#7E5260</color>
<color name="md_theme_light_7_onTertiary">#FFFFFF</color>
<color name="md_theme_light_7_tertiaryContainer">#FFD9E3</color>
<color name="md_theme_light_7_onTertiaryContainer">#31101D</color>
<color name="md_theme_light_7_error">#BA1A1A</color>
<color name="md_theme_light_7_errorContainer">#FFDAD6</color>
<color name="md_theme_light_7_onError">#FFFFFF</color>
<color name="md_theme_light_7_onErrorContainer">#410002</color>
<color name="md_theme_light_7_background">#FFFBFF</color>
<color name="md_theme_light_7_onBackground">#1C1B1E</color>
<color name="md_theme_light_7_surface">#FFFBFF</color>
<color name="md_theme_light_7_onSurface">#1C1B1E</color>
<color name="md_theme_light_7_surfaceVariant">#E7E0EB</color>
<color name="md_theme_light_7_onSurfaceVariant">#49454E</color>
<color name="md_theme_light_7_outline">#7A757F</color>
<color name="md_theme_light_7_inverseOnSurface">#F4EFF4</color>
<color name="md_theme_light_7_inverseSurface">#313033</color>
<color name="md_theme_light_7_inversePrimary">#CFBCFF</color>
<color name="md_theme_light_7_shadow">#000000</color>
<color name="md_theme_light_7_surfaceTint">#6750A4</color>
<color name="md_theme_light_7_outlineVariant">#CAC4CF</color>
<color name="md_theme_light_7_scrim">#000000</color>
<color name="md_theme_dark_7_primary">#CFBCFF</color>
<color name="md_theme_dark_7_onPrimary">#381E72</color>
<color name="md_theme_dark_7_primaryContainer">#4F378A</color>
<color name="md_theme_dark_7_onPrimaryContainer">#E9DDFF</color>
<color name="md_theme_dark_7_secondary">#CBC2DB</color>
<color name="md_theme_dark_7_onSecondary">#332D41</color>
<color name="md_theme_dark_7_secondaryContainer">#4A4458</color>
<color name="md_theme_dark_7_onSecondaryContainer">#E8DEF8</color>
<color name="md_theme_dark_7_tertiary">#EFB8C8</color>
<color name="md_theme_dark_7_onTertiary">#4A2532</color>
<color name="md_theme_dark_7_tertiaryContainer">#633B48</color>
<color name="md_theme_dark_7_onTertiaryContainer">#FFD9E3</color>
<color name="md_theme_dark_7_error">#FFB4AB</color>
<color name="md_theme_dark_7_errorContainer">#93000A</color>
<color name="md_theme_dark_7_onError">#690005</color>
<color name="md_theme_dark_7_onErrorContainer">#FFDAD6</color>
<color name="md_theme_dark_7_background">#1C1B1E</color>
<color name="md_theme_dark_7_onBackground">#E6E1E6</color>
<color name="md_theme_dark_7_surface">#1C1B1E</color>
<color name="md_theme_dark_7_onSurface">#E6E1E6</color>
<color name="md_theme_dark_7_surfaceVariant">#49454E</color>
<color name="md_theme_dark_7_onSurfaceVariant">#CAC4CF</color>
<color name="md_theme_dark_7_outline">#948F99</color>
<color name="md_theme_dark_7_inverseOnSurface">#1C1B1E</color>
<color name="md_theme_dark_7_inverseSurface">#E6E1E6</color>
<color name="md_theme_dark_7_inversePrimary">#6750A4</color>
<color name="md_theme_dark_7_shadow">#000000</color>
<color name="md_theme_dark_7_surfaceTint">#CFBCFF</color>
<color name="md_theme_dark_7_outlineVariant">#49454E</color>
<color name="md_theme_dark_7_scrim">#000000</color>
<!-- theme 8 -->
<color name="seed_8">#14AEA7</color>
<color name="md_theme_light_8_primary">#006A65</color>
<color name="md_theme_light_8_onPrimary">#FFFFFF</color>
<color name="md_theme_light_8_primaryContainer">#70F7EF</color>
<color name="md_theme_light_8_onPrimaryContainer">#00201E</color>
<color name="md_theme_light_8_secondary">#4A6361</color>
<color name="md_theme_light_8_onSecondary">#FFFFFF</color>
<color name="md_theme_light_8_secondaryContainer">#CCE8E5</color>
<color name="md_theme_light_8_onSecondaryContainer">#051F1E</color>
<color name="md_theme_light_8_tertiary">#48607B</color>
<color name="md_theme_light_8_onTertiary">#FFFFFF</color>
<color name="md_theme_light_8_tertiaryContainer">#D0E4FF</color>
<color name="md_theme_light_8_onTertiaryContainer">#001D34</color>
<color name="md_theme_light_8_error">#BA1A1A</color>
<color name="md_theme_light_8_errorContainer">#FFDAD6</color>
<color name="md_theme_light_8_onError">#FFFFFF</color>
<color name="md_theme_light_8_onErrorContainer">#410002</color>
<color name="md_theme_light_8_background">#FAFDFB</color>
<color name="md_theme_light_8_onBackground">#191C1C</color>
<color name="md_theme_light_8_surface">#FAFDFB</color>
<color name="md_theme_light_8_onSurface">#191C1C</color>
<color name="md_theme_light_8_surfaceVariant">#DAE5E3</color>
<color name="md_theme_light_8_onSurfaceVariant">#3F4948</color>
<color name="md_theme_light_8_outline">#6F7978</color>
<color name="md_theme_light_8_inverseOnSurface">#EFF1F0</color>
<color name="md_theme_light_8_inverseSurface">#2D3131</color>
<color name="md_theme_light_8_inversePrimary">#4FDAD2</color>
<color name="md_theme_light_8_shadow">#000000</color>
<color name="md_theme_light_8_surfaceTint">#006A65</color>
<color name="md_theme_light_8_outlineVariant">#BEC9C7</color>
<color name="md_theme_light_8_scrim">#000000</color>
<color name="md_theme_dark_8_primary">#4FDAD2</color>
<color name="md_theme_dark_8_onPrimary">#003734</color>
<color name="md_theme_dark_8_primaryContainer">#00504C</color>
<color name="md_theme_dark_8_onPrimaryContainer">#70F7EF</color>
<color name="md_theme_dark_8_secondary">#B0CCC9</color>
<color name="md_theme_dark_8_onSecondary">#1B3533</color>
<color name="md_theme_dark_8_secondaryContainer">#324B49</color>
<color name="md_theme_dark_8_onSecondaryContainer">#CCE8E5</color>
<color name="md_theme_dark_8_tertiary">#B0C9E7</color>
<color name="md_theme_dark_8_onTertiary">#19324A</color>
<color name="md_theme_dark_8_tertiaryContainer">#314962</color>
<color name="md_theme_dark_8_onTertiaryContainer">#D0E4FF</color>
<color name="md_theme_dark_8_error">#FFB4AB</color>
<color name="md_theme_dark_8_errorContainer">#93000A</color>
<color name="md_theme_dark_8_onError">#690005</color>
<color name="md_theme_dark_8_onErrorContainer">#FFDAD6</color>
<color name="md_theme_dark_8_background">#191C1C</color>
<color name="md_theme_dark_8_onBackground">#E0E3E2</color>
<color name="md_theme_dark_8_surface">#191C1C</color>
<color name="md_theme_dark_8_onSurface">#E0E3E2</color>
<color name="md_theme_dark_8_surfaceVariant">#3F4948</color>
<color name="md_theme_dark_8_onSurfaceVariant">#BEC9C7</color>
<color name="md_theme_dark_8_outline">#889391</color>
<color name="md_theme_dark_8_inverseOnSurface">#191C1C</color>
<color name="md_theme_dark_8_inverseSurface">#E0E3E2</color>
<color name="md_theme_dark_8_inversePrimary">#006A65</color>
<color name="md_theme_dark_8_shadow">#000000</color>
<color name="md_theme_dark_8_surfaceTint">#4FDAD2</color>
<color name="md_theme_dark_8_outlineVariant">#3F4948</color>
<color name="md_theme_dark_8_scrim">#000000</color>
<color name="seed_6">#6750A4</color>
<color name="md_1_theme_light_primary">#6750A4</color>
<color name="md_1_theme_light_onPrimary">#FFFFFF</color>
<color name="md_1_theme_light_primaryContainer">#E9DDFF</color>
<color name="md_1_theme_light_onPrimaryContainer">#22005D</color>
<color name="md_1_theme_light_secondary">#625B71</color>
<color name="md_1_theme_light_onSecondary">#FFFFFF</color>
<color name="md_1_theme_light_secondaryContainer">#E8DEF8</color>
<color name="md_1_theme_light_onSecondaryContainer">#1E192B</color>
<color name="md_1_theme_light_tertiary">#7E5260</color>
<color name="md_1_theme_light_onTertiary">#FFFFFF</color>
<color name="md_1_theme_light_tertiaryContainer">#FFD9E3</color>
<color name="md_1_theme_light_onTertiaryContainer">#31101D</color>
<color name="md_1_theme_light_error">#BA1A1A</color>
<color name="md_1_theme_light_errorContainer">#FFDAD6</color>
<color name="md_1_theme_light_onError">#FFFFFF</color>
<color name="md_1_theme_light_onErrorContainer">#410002</color>
<color name="md_1_theme_light_background">#FFFBFF</color>
<color name="md_1_theme_light_onBackground">#1C1B1E</color>
<color name="md_1_theme_light_surface">#FFFBFF</color>
<color name="md_1_theme_light_onSurface">#1C1B1E</color>
<color name="md_1_theme_light_surfaceVariant">#E7E0EB</color>
<color name="md_1_theme_light_onSurfaceVariant">#49454E</color>
<color name="md_1_theme_light_outline">#7A757F</color>
<color name="md_1_theme_light_inverseOnSurface">#F4EFF4</color>
<color name="md_1_theme_light_inverseSurface">#313033</color>
<color name="md_1_theme_light_inversePrimary">#CFBCFF</color>
<color name="md_1_theme_light_shadow">#000000</color>
<color name="md_1_theme_light_surfaceTint">#6750A4</color>
<color name="md_1_theme_light_outlineVariant">#CAC4CF</color>
<color name="md_1_theme_light_scrim">#000000</color>
<color name="md_1_theme_dark_primary">#CFBCFF</color>
<color name="md_1_theme_dark_onPrimary">#381E72</color>
<color name="md_1_theme_dark_primaryContainer">#4F378A</color>
<color name="md_1_theme_dark_onPrimaryContainer">#E9DDFF</color>
<color name="md_1_theme_dark_secondary">#CBC2DB</color>
<color name="md_1_theme_dark_onSecondary">#332D41</color>
<color name="md_1_theme_dark_secondaryContainer">#4A4458</color>
<color name="md_1_theme_dark_onSecondaryContainer">#E8DEF8</color>
<color name="md_1_theme_dark_tertiary">#EFB8C8</color>
<color name="md_1_theme_dark_onTertiary">#4A2532</color>
<color name="md_1_theme_dark_tertiaryContainer">#633B48</color>
<color name="md_1_theme_dark_onTertiaryContainer">#FFD9E3</color>
<color name="md_1_theme_dark_error">#FFB4AB</color>
<color name="md_1_theme_dark_errorContainer">#93000A</color>
<color name="md_1_theme_dark_onError">#690005</color>
<color name="md_1_theme_dark_onErrorContainer">#FFDAD6</color>
<color name="md_1_theme_dark_background">#1C1B1E</color>
<color name="md_1_theme_dark_onBackground">#E6E1E6</color>
<color name="md_1_theme_dark_surface">#1C1B1E</color>
<color name="md_1_theme_dark_onSurface">#E6E1E6</color>
<color name="md_1_theme_dark_surfaceVariant">#49454E</color>
<color name="md_1_theme_dark_onSurfaceVariant">#CAC4CF</color>
<color name="md_1_theme_dark_outline">#948F99</color>
<color name="md_1_theme_dark_inverseOnSurface">#1C1B1E</color>
<color name="md_1_theme_dark_inverseSurface">#E6E1E6</color>
<color name="md_1_theme_dark_inversePrimary">#6750A4</color>
<color name="md_1_theme_dark_shadow">#000000</color>
<color name="md_1_theme_dark_surfaceTint">#CFBCFF</color>
<color name="md_1_theme_dark_outlineVariant">#49454E</color>
<color name="md_1_theme_dark_scrim">#000000</color>
<color name="CustomColor1">#93DB00</color>
<color name="CustomColor2">#68AF86</color>
<color name="CustomColor3">#0096AE</color>
<color name="CustomColor4">#000000</color>
<color name="light_blue_50">#FFE1F5FE</color>
<color name="light_blue_200">#FF81D4FA</color>
<color name="light_blue_600">#FF039BE5</color>
<color name="light_blue_900">#FF01579B</color>
</resources>

View File

@@ -227,7 +227,7 @@
<string name="sub_background_color_select">Subtitle Background Color</string>
<string name="sub_window_color_select">Subtitle Window Color</string>
<string name="sub_window_color_info">"The subtitle window is the part left and right from them. (where the background isn\'t)"</string>
<string name="sub_color_info"><b>Note:</b> Changing Subtitle formatting only works with Soft-Subbed Sources such as Zoro!</string>
<string name="sub_color_info"><b>Note:</b> Changing above settings only affects Soft-Subtitles!</string>
<string name="sub_font_select">Subtitle Font</string>
<string name="subtitle_font_size">Subtitle Size</string>
@@ -569,9 +569,6 @@
\n\nNote: Direct downloads are also possible without a manager but it\'s not recommended.</string>
<string name="question_8">How to download Manga Chapters?</string>
<string name="answer_8">It is yet not possible to download chapters in Dantotsu but this feature will be implemented soon.</string>
<string name="question_9">How to enable NSFW content?</string>
<string name="answer_9">You can enable nsfw content by enabling 18+ contents from this [link](https://anilist.co/settings/media).</string>
@@ -648,5 +645,6 @@
<string name="add_widget">Add widget</string>
<string name="app_widget_description">This is an app widget description</string>
<string name="airing_image">Airing Image</string>
<string name="anime_downloads">animeDownloads</string>
</resources>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NavBarText">
<item name="android:textAllCaps">true</item>
<item name="android:fontFamily">@font/poppins_bold</item>
@@ -14,7 +15,6 @@
</style>
<style name="Suffix">
<item name="android:height">54dp</item>
<item name="android:fontFamily">@font/poppins_bold</item>
@@ -37,19 +37,22 @@
<item name="background">@id/bg</item>
<item name="cornerFamily">rounded</item>
</style>
<style name="roundedImageView" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">16dp</item>
</style>
<style name="MySnackbar" parent="Widget.MaterialComponents.Snackbar">
<item name="android:layout_marginLeft">16dp</item>
<item name="android:layout_marginRight">16dp</item>
<item name="android:background">@drawable/round_corner</item>
</style>
<string name="MySnackBarText" parent="Widget.MaterialComponents.Snackbar.TextView" translatable="false">
<item name="android:textColor">@color/bg_opp</item>
</string>
<style name="fontTooltip" parent="Widget.Material3.Tooltip">
<item name="android:padding">8dp</item>
<item name="android:textAppearance">@style/Suffix</item>
@@ -62,15 +65,17 @@
<item name="android:textColor">?attr/colorOnBackground</item>
<item name="android:popupBackground">?attr/colorSurface</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_width">match_parent</item>
<item name="android:windowBackground">@drawable/shape_corner_16dp</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowNoTitle">true</item>
<item name="windowActionBar">false</item>
</style>
</style>
<style name="BottomNavBar" parent="">
<!-- set background color to transparent -->
<item name="android:background">@android:color/transparent</item>
</style>
<style name="ThemeOverlay_Dantotsu_MediaRouter" parent="ThemeOverlay.AppCompat">
</style>
</resources>

View File

@@ -198,93 +198,6 @@
<item name="colorPrimaryInverse">@color/md_theme_light_4_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Monochrome" parent="Theme.Base">
<item name="colorPrimary">@color/bg_black</item>
<item name="colorOnPrimary">@color/bg_white</item>
<item name="colorPrimaryContainer">@color/bg_black</item>
<item name="colorOnPrimaryContainer">@color/bg_white</item>
<item name="colorSecondary">@color/bg_black</item>
<item name="colorOnSecondary">@color/bg_white</item>
<item name="colorSecondaryContainer">@color/bg_black</item>
<item name="colorOnSecondaryContainer">@color/bg_white</item>
<item name="colorTertiary">@color/bg_black</item>
<item name="colorOnTertiary">@color/bg_white</item>
<item name="colorTertiaryContainer">@color/bg_black</item>
<item name="colorOnTertiaryContainer">@color/bg_white</item>
<item name="colorError">@color/bg_black</item>
<item name="colorErrorContainer">@color/bg_black</item>
<item name="colorOnError">@color/bg_white</item>
<item name="colorOnErrorContainer">@color/bg_white</item>
<item name="android:colorBackground">@color/bg_white</item>
<item name="colorOnBackground">@color/bg_black</item>
<item name="colorSurface">@color/bg_white</item>
<item name="colorOnSurface">@color/bg_black</item>
<item name="colorSurfaceVariant">@color/bg_white</item>
<item name="colorOnSurfaceVariant">@color/bg_black</item>
<item name="colorOutline">@color/bg_black</item>
<item name="colorOnSurfaceInverse">@color/bg_black</item>
<item name="colorSurfaceInverse">@color/bg_white</item>
<item name="colorPrimaryInverse">@color/bg_white</item>
</style>
<style name="Theme.Dantotsu.Red" parent="Theme.Base">
<item name="colorPrimary">@color/md_0_theme_light_primary</item>
<item name="colorOnPrimary">@color/md_0_theme_light_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_0_theme_light_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_0_theme_light_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_0_theme_light_secondary</item>
<item name="colorOnSecondary">@color/md_0_theme_light_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_0_theme_light_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_0_theme_light_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_0_theme_light_tertiary</item>
<item name="colorOnTertiary">@color/md_0_theme_light_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_0_theme_light_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_0_theme_light_onTertiaryContainer</item>
<item name="colorError">@color/md_0_theme_light_error</item>
<item name="colorErrorContainer">@color/md_0_theme_light_errorContainer</item>
<item name="colorOnError">@color/md_0_theme_light_onError</item>
<item name="colorOnErrorContainer">@color/md_0_theme_light_onErrorContainer</item>
<item name="android:colorBackground">@color/md_0_theme_light_background</item>
<item name="colorOnBackground">@color/md_0_theme_light_onBackground</item>
<item name="colorSurface">@color/md_0_theme_light_surface</item>
<item name="colorOnSurface">@color/md_0_theme_light_onSurface</item>
<item name="colorSurfaceVariant">@color/md_0_theme_light_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_0_theme_light_onSurfaceVariant</item>
<item name="colorOutline">@color/md_0_theme_light_outline</item>
<item name="colorOnSurfaceInverse">@color/md_0_theme_light_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_0_theme_light_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_0_theme_light_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Lavender" parent="Theme.Base">
<item name="colorPrimary">@color/md_1_theme_light_primary</item>
<item name="colorOnPrimary">@color/md_1_theme_light_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_1_theme_light_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_1_theme_light_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_1_theme_light_secondary</item>
<item name="colorOnSecondary">@color/md_1_theme_light_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_1_theme_light_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_1_theme_light_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_1_theme_light_tertiary</item>
<item name="colorOnTertiary">@color/md_1_theme_light_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_1_theme_light_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_1_theme_light_onTertiaryContainer</item>
<item name="colorError">@color/md_1_theme_light_error</item>
<item name="colorErrorContainer">@color/md_1_theme_light_errorContainer</item>
<item name="colorOnError">@color/md_1_theme_light_onError</item>
<item name="colorOnErrorContainer">@color/md_1_theme_light_onErrorContainer</item>
<item name="android:colorBackground">@color/md_1_theme_light_background</item>
<item name="colorOnBackground">@color/md_1_theme_light_onBackground</item>
<item name="colorSurface">@color/md_1_theme_light_surface</item>
<item name="colorOnSurface">@color/md_1_theme_light_onSurface</item>
<item name="colorSurfaceVariant">@color/md_1_theme_light_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_1_theme_light_onSurfaceVariant</item>
<item name="colorOutline">@color/md_1_theme_light_outline</item>
<item name="colorOnSurfaceInverse">@color/md_1_theme_light_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_1_theme_light_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_1_theme_light_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Saikou" parent="Theme.Base">
<item name="colorPrimary">@color/md_theme_light_5_primary</item>
<item name="colorOnPrimary">@color/md_theme_light_5_onPrimary</item>
@@ -314,6 +227,122 @@
<item name="colorPrimaryInverse">@color/md_theme_light_5_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Red" parent="Theme.Base">
<item name="colorPrimary">@color/md_theme_light_6_primary</item>
<item name="colorOnPrimary">@color/md_theme_light_6_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_light_6_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_light_6_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_light_6_secondary</item>
<item name="colorOnSecondary">@color/md_theme_light_6_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_light_6_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_light_6_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_light_6_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_light_6_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_light_6_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_light_6_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_light_6_error</item>
<item name="colorErrorContainer">@color/md_theme_light_6_errorContainer</item>
<item name="colorOnError">@color/md_theme_light_6_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_light_6_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_light_6_background</item>
<item name="colorOnBackground">@color/md_theme_light_6_onBackground</item>
<item name="colorSurface">@color/md_theme_light_6_surface</item>
<item name="colorOnSurface">@color/md_theme_light_6_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_light_6_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_light_6_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_light_6_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_light_6_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_light_6_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_light_6_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Lavender" parent="Theme.Base">
<item name="colorPrimary">@color/md_theme_light_7_primary</item>
<item name="colorOnPrimary">@color/md_theme_light_7_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_light_7_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_light_7_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_light_7_secondary</item>
<item name="colorOnSecondary">@color/md_theme_light_7_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_light_7_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_light_7_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_light_7_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_light_7_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_light_7_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_light_7_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_light_7_error</item>
<item name="colorErrorContainer">@color/md_theme_light_7_errorContainer</item>
<item name="colorOnError">@color/md_theme_light_7_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_light_7_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_light_7_background</item>
<item name="colorOnBackground">@color/md_theme_light_7_onBackground</item>
<item name="colorSurface">@color/md_theme_light_7_surface</item>
<item name="colorOnSurface">@color/md_theme_light_7_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_light_7_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_light_7_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_light_7_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_light_7_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_light_7_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_light_7_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Emerald" parent="Theme.Base">
<item name="colorPrimary">@color/md_theme_light_8_primary</item>
<item name="colorOnPrimary">@color/md_theme_light_8_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_light_8_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_light_8_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_light_8_secondary</item>
<item name="colorOnSecondary">@color/md_theme_light_8_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_light_8_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_light_8_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_light_8_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_light_8_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_light_8_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_light_8_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_light_8_error</item>
<item name="colorErrorContainer">@color/md_theme_light_8_errorContainer</item>
<item name="colorOnError">@color/md_theme_light_8_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_light_8_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_light_8_background</item>
<item name="colorOnBackground">@color/md_theme_light_8_onBackground</item>
<item name="colorSurface">@color/md_theme_light_8_surface</item>
<item name="colorOnSurface">@color/md_theme_light_8_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_light_8_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_light_8_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_light_8_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_light_8_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_light_8_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_light_8_inversePrimary</item>
</style>
<style name="Theme.Dantotsu.Monochrome" parent="Theme.Base">
<item name="colorPrimary">@color/bg_black</item>
<item name="colorOnPrimary">@color/bg_white</item>
<item name="colorPrimaryContainer">@color/bg_black</item>
<item name="colorOnPrimaryContainer">@color/bg_white</item>
<item name="colorSecondary">@color/bg_black</item>
<item name="colorOnSecondary">@color/bg_white</item>
<item name="colorSecondaryContainer">@color/bg_black</item>
<item name="colorOnSecondaryContainer">@color/bg_white</item>
<item name="colorTertiary">@color/bg_black</item>
<item name="colorOnTertiary">@color/bg_white</item>
<item name="colorTertiaryContainer">@color/bg_black</item>
<item name="colorOnTertiaryContainer">@color/bg_white</item>
<item name="colorError">@color/bg_black</item>
<item name="colorErrorContainer">@color/bg_black</item>
<item name="colorOnError">@color/bg_white</item>
<item name="colorOnErrorContainer">@color/bg_white</item>
<item name="android:colorBackground">@color/bg_white</item>
<item name="colorOnBackground">@color/bg_black</item>
<item name="colorSurface">@color/bg_white</item>
<item name="colorOnSurface">@color/bg_black</item>
<item name="colorSurfaceVariant">@color/bg_white</item>
<item name="colorOnSurfaceVariant">@color/bg_black</item>
<item name="colorOutline">@color/bg_black</item>
<item name="colorOnSurfaceInverse">@color/bg_black</item>
<item name="colorSurfaceInverse">@color/bg_white</item>
<item name="colorPrimaryInverse">@color/bg_white</item>
</style>
<style name="Theme.Dantotsu.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault">
<!-- Radius of the outer bound of widgets to make the rounded corners -->
<item name="appWidgetRadius">16dp</item>