diff --git a/app/src/main/java/ani/dantotsu/Functions.kt b/app/src/main/java/ani/dantotsu/Functions.kt index 25a415f9..f05405ad 100644 --- a/app/src/main/java/ani/dantotsu/Functions.kt +++ b/app/src/main/java/ani/dantotsu/Functions.kt @@ -207,23 +207,21 @@ open class BottomSheetDialogFragment : BottomSheetDialogFragment() { fun isOnline(context: Context): Boolean { val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager return tryWith { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val cap = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) - return@tryWith if (cap != null) { - when { - cap.hasTransport(TRANSPORT_BLUETOOTH) || - cap.hasTransport(TRANSPORT_CELLULAR) || - cap.hasTransport(TRANSPORT_ETHERNET) || - cap.hasTransport(TRANSPORT_LOWPAN) || - cap.hasTransport(TRANSPORT_USB) || - cap.hasTransport(TRANSPORT_VPN) || - cap.hasTransport(TRANSPORT_WIFI) || - cap.hasTransport(TRANSPORT_WIFI_AWARE) -> true + val cap = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + return@tryWith if (cap != null) { + when { + cap.hasTransport(TRANSPORT_BLUETOOTH) || + cap.hasTransport(TRANSPORT_CELLULAR) || + cap.hasTransport(TRANSPORT_ETHERNET) || + cap.hasTransport(TRANSPORT_LOWPAN) || + cap.hasTransport(TRANSPORT_USB) || + cap.hasTransport(TRANSPORT_VPN) || + cap.hasTransport(TRANSPORT_WIFI) || + cap.hasTransport(TRANSPORT_WIFI_AWARE) -> true - else -> false - } - } else false - } else true + else -> false + } + } else false } ?: false } @@ -732,7 +730,7 @@ fun snackString(s: String?, activity: Activity? = null, clipboard: String? = nul if (s != null) { (activity ?: currActivity())?.apply { runOnUiThread { - val snackBar = Snackbar.make(window.decorView.findViewById(android.R.id.content), s, Snackbar.LENGTH_LONG) + val snackBar = Snackbar.make(window.decorView.findViewById(android.R.id.content), s, Snackbar.LENGTH_SHORT) snackBar.view.apply { updateLayoutParams { gravity = (Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM) diff --git a/app/src/main/java/ani/dantotsu/MainActivity.kt b/app/src/main/java/ani/dantotsu/MainActivity.kt index 33ac52e7..cd0a5184 100644 --- a/app/src/main/java/ani/dantotsu/MainActivity.kt +++ b/app/src/main/java/ani/dantotsu/MainActivity.kt @@ -41,6 +41,7 @@ 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.manga.OfflineMangaFragment import ani.dantotsu.home.AnimeFragment import ani.dantotsu.home.HomeFragment import ani.dantotsu.home.LoginFragment diff --git a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt index 4cdd981b..7266ec53 100644 --- a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt +++ b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt @@ -33,6 +33,7 @@ import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_DOWNLOADER_PR import java.net.HttpURLConnection import java.net.URL import androidx.core.content.ContextCompat +import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_FAILED import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_FINISHED import ani.dantotsu.media.manga.MangaReadFragment.Companion.ACTION_DOWNLOAD_STARTED import ani.dantotsu.media.manga.MangaReadFragment.Companion.EXTRA_CHAPTER_NUMBER @@ -169,6 +170,7 @@ class MangaDownloaderService : Service() { "Please grant notification permission", Toast.LENGTH_SHORT ).show() + broadcastDownloadFailed(task.chapter) return@withContext } @@ -223,14 +225,14 @@ class MangaDownloaderService : Service() { saveMediaInfo(task) downloadsManager.addDownload(Download(task.title, task.chapter, Download.Type.MANGA)) - downloadsManager.exportDownloads(Download(task.title, task.chapter, Download.Type.MANGA)) + //downloadsManager.exportDownloads(Download(task.title, task.chapter, Download.Type.MANGA)) broadcastDownloadFinished(task.chapter) snackString("${task.title} - ${task.chapter} Download finished") } } - fun saveToDisk(fileName: String, bitmap: Bitmap, title: String, chapter: String) { + private fun saveToDisk(fileName: String, bitmap: Bitmap, title: String, chapter: String) { try { // Define the directory within the private external storage space val directory = File( @@ -262,7 +264,7 @@ class MangaDownloaderService : Service() { GlobalScope.launch(Dispatchers.IO) { val directory = File( getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), - "Dantotsu/Manga/${task.title}/${task.chapter}" + "Dantotsu/Manga/${task.title}" ) if (!directory.exists()) directory.mkdirs() @@ -272,7 +274,7 @@ class MangaDownloaderService : Service() { SChapterImpl() // Provide an instance of SChapterImpl }) .create() - val mediaJson = gson.toJson(task.sourceMedia) // Assuming sourceMedia is part of DownloadTask + 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") } @@ -329,6 +331,13 @@ class MangaDownloaderService : Service() { sendBroadcast(intent) } + private fun broadcastDownloadFailed(chapterNumber: String) { + val intent = Intent(ACTION_DOWNLOAD_FAILED).apply { + putExtra(EXTRA_CHAPTER_NUMBER, chapterNumber) + } + sendBroadcast(intent) + } + private val cancelReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action == ACTION_CANCEL_DOWNLOAD) { diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt new file mode 100644 index 00000000..fc4240bf --- /dev/null +++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt @@ -0,0 +1,52 @@ +package ani.dantotsu.download.manga + +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.TextView +import androidx.cardview.widget.CardView +import ani.dantotsu.R + + +class OfflineMangaAdapter(private val context: Context, private val items: List) : BaseAdapter() { + private val inflater: LayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + override fun getCount(): Int { + return items.size + } + + override fun getItem(position: Int): Any? { + return items[position] + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + 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 item = getItem(position) as OfflineMangaModel + val imageView = view!!.findViewById(R.id.itemCompactImage) + val titleTextView = view.findViewById(R.id.itemCompactTitle) + val itemScore = view.findViewById(R.id.itemCompactScore) + val itemScoreBG = view.findViewById(R.id.itemCompactScoreBG) + val ongoing = view.findViewById(R.id.itemCompactOngoing) + // Bind item data to the views + // For example: + imageView.setImageURI(item.image) + titleTextView.text = item.title + itemScore.text = item.score + if (item.isOngoing) { + ongoing.visibility = View.VISIBLE + } else { + ongoing.visibility = View.GONE + } + return view + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt new file mode 100644 index 00000000..a7c0477b --- /dev/null +++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt @@ -0,0 +1,201 @@ +package ani.dantotsu.download.manga + +import android.animation.ObjectAnimator +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Environment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.OvershootInterpolator +import android.widget.GridView +import androidx.cardview.widget.CardView +import androidx.core.view.updatePaddingRelative +import androidx.fragment.app.Fragment +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import ani.dantotsu.R +import ani.dantotsu.Refresh +import ani.dantotsu.currContext +import ani.dantotsu.databinding.FragmentMangaBinding +import ani.dantotsu.download.Download +import ani.dantotsu.download.DownloadsManager +import ani.dantotsu.logger +import ani.dantotsu.media.Media +import ani.dantotsu.media.MediaDetailsActivity +import ani.dantotsu.navBarHeight +import ani.dantotsu.px +import ani.dantotsu.statusBarHeight +import com.google.firebase.crashlytics.FirebaseCrashlytics +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.io.File +import com.google.gson.GsonBuilder +import com.google.gson.InstanceCreator +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SChapterImpl +import kotlin.math.max +import kotlin.math.min + +class OfflineMangaFragment: Fragment() { + private val downloadManager = Injekt.get() + private var downloads: List = listOf() + private lateinit var gridView: GridView + private lateinit var adapter: OfflineMangaAdapter + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.fragment_manga_offline, container, false) + gridView = view.findViewById(R.id.gridView) + getDownloads() + adapter = OfflineMangaAdapter(requireContext(), downloads) + gridView.adapter = adapter + gridView.setOnItemClickListener { parent, view, position, id -> + // Get the OfflineMangaModel that was clicked + val item = adapter.getItem(position) as OfflineMangaModel + val media = downloadManager.mangaDownloads.filter { it.title == item.title }.first() + startActivity( + Intent(requireContext(), MediaDetailsActivity::class.java) + .putExtra("media", getMedia(media)) + ) + } + + return view + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + var height = statusBarHeight + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + val displayCutout = activity?.window?.decorView?.rootWindowInsets?.displayCutout + if (displayCutout != null) { + if (displayCutout.boundingRects.size > 0) { + height = max( + statusBarHeight, + min( + displayCutout.boundingRects[0].width(), + displayCutout.boundingRects[0].height() + ) + ) + } + } + } + val refreshLayout = view.findViewById(R.id.mangaRefresh) + refreshLayout.setSlingshotDistance(height + 128) + refreshLayout.setProgressViewEndTarget(false, height + 128) + refreshLayout.setOnRefreshListener { + Refresh.activity[this.hashCode()]!!.postValue(true) + } + + val scrollTop = view.findViewById(R.id.mangaPageScrollTop) + var visible = false + fun animate() { + val start = if (visible) 0f else 1f + val end = if (!visible) 0f else 1f + ObjectAnimator.ofFloat(scrollTop, "scaleX", start, end).apply { + duration = 300 + interpolator = OvershootInterpolator(2f) + start() + } + ObjectAnimator.ofFloat(scrollTop, "scaleY", start, end).apply { + duration = 300 + interpolator = OvershootInterpolator(2f) + start() + } + } + + scrollTop.setOnClickListener { + //TODO: scroll to top + } + + } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } + override fun onResume() { + super.onResume() + getDownloads() + adapter.notifyDataSetChanged() + } + + override fun onPause() { + super.onPause() + downloads = listOf() + } + + override fun onDestroy() { + super.onDestroy() + downloads = listOf() + } + + override fun onStop() { + super.onStop() + downloads = listOf() + } + private fun getDownloads() { + val titles = downloadManager.mangaDownloads.map { it.title }.distinct() + val newDownloads = mutableListOf() + for (title in titles) { + val _downloads = downloadManager.mangaDownloads.filter { it.title == title } + val download = _downloads.first() + val offlineMangaModel = loadOfflineMangaModel(download) + newDownloads += offlineMangaModel + } + downloads = newDownloads + } + + private fun getMedia(download: Download): Media? { + val directory = File( + currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), + "Dantotsu/Manga/${download.title}" + ) + //load media.json and convert to media class with gson + try { + val gson = GsonBuilder() + .registerTypeAdapter(SChapter::class.java, InstanceCreator { + SChapterImpl() // Provide an instance of SChapterImpl + }) + .create() + val media = File(directory, "media.json") + val mediaJson = media.readText() + return gson.fromJson(mediaJson, Media::class.java) + } + catch (e: Exception){ + logger("Error loading media.json: ${e.message}") + logger(e.printStackTrace()) + FirebaseCrashlytics.getInstance().recordException(e) + return null + } + } + + private fun loadOfflineMangaModel(download: Download): OfflineMangaModel{ + val directory = File( + currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), + "Dantotsu/Manga/${download.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 cover = File(directory, "cover.jpg") + val coverUri: Uri? = if (cover.exists()) { + Uri.fromFile(cover) + } else { + null + } + val title = mediaModel.nameMAL?:"unknown" + val score = if (mediaModel.userScore != 0) mediaModel.userScore.toString() else + if (mediaModel.meanScore == null) "?" else mediaModel.meanScore.toString() + val isOngoing = false + val isUserScored = mediaModel.userScore != 0 + return OfflineMangaModel(title, score, isOngoing, isUserScored, coverUri) + } + catch (e: Exception){ + logger("Error loading media.json: ${e.message}") + logger(e.printStackTrace()) + FirebaseCrashlytics.getInstance().recordException(e) + return OfflineMangaModel("unknown", "0", false, false, null) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaModel.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaModel.kt new file mode 100644 index 00000000..30b97911 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaModel.kt @@ -0,0 +1,6 @@ +package ani.dantotsu.download.manga + +import android.net.Uri + +data class OfflineMangaModel(val title: String, val score: String, val isOngoing: Boolean, val isUserScored: Boolean, val image: Uri?) { +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt index d399c89c..60d77b3a 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaChapterAdapter.kt @@ -80,6 +80,16 @@ class MangaChapterAdapter( } } + fun removeDownload(chapterNumber: String) { + activeDownloads.remove(chapterNumber) + downloadedChapters.remove(chapterNumber) + // Find the position of the chapter and notify only that item + val position = arr.indexOfFirst { it.number == chapterNumber } + if (position != -1) { + notifyItemChanged(position) + } + } + inner class ChapterListViewHolder(val binding: ItemChapterListBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(chapterNumber: String) { if (activeDownloads.contains(chapterNumber)) { diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt index a396abe1..bbb51656 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt @@ -417,6 +417,12 @@ open class MangaReadFragment : Fragment() { val chapterNumber = intent.getStringExtra(EXTRA_CHAPTER_NUMBER) chapterNumber?.let { chapterAdapter.stopDownload(it) } } + ACTION_DOWNLOAD_FAILED -> { + val chapterNumber = intent.getStringExtra(EXTRA_CHAPTER_NUMBER) + chapterNumber?.let { + chapterAdapter.removeDownload(it) + } + } } } } @@ -468,6 +474,7 @@ open class MangaReadFragment : Fragment() { 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 EXTRA_CHAPTER_NUMBER = "extra_chapter_number" } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt index b14d243a..35b7b98b 100644 --- a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt @@ -50,7 +50,7 @@ abstract class BaseParser { * Isn't necessary to override, but recommended, if you want to improve auto search results * **/ open suspend fun autoSearch(mediaObj: Media): ShowResponse? { - var response = loadSavedShowResponse(mediaObj.id) + var response: ShowResponse? = null//loadSavedShowResponse(mediaObj.id) if (response != null) { saveShowResponse(mediaObj.id, response, true) } else { diff --git a/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt b/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt index db55d916..09292577 100644 --- a/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt +++ b/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt @@ -7,6 +7,7 @@ import ani.dantotsu.media.manga.MangaChapter import ani.dantotsu.media.Media import ani.dantotsu.tryWithSuspend import eu.kanade.tachiyomi.animesource.model.SAnime +import eu.kanade.tachiyomi.source.model.SManga abstract class WatchSources : BaseSources() { @@ -23,14 +24,28 @@ abstract class WatchSources : BaseSources() { } ?: mutableMapOf() } - suspend fun loadEpisodes(i: Int, showLink: String, extra: Map?, sAnime: SAnime?): MutableMap { + suspend fun loadEpisodes( + i: Int, + showLink: String, + extra: Map?, + sAnime: SAnime? + ): MutableMap { println("finder333 $showLink") val map = mutableMapOf() val parser = get(i) tryWithSuspend(true) { if (sAnime != null) { - parser.loadEpisodes(showLink,extra, sAnime).forEach { - map[it.number] = Episode(it.number, it.link, it.title, it.description, it.thumbnail, it.isFiller, extra = it.extra, sEpisode = it.sEpisode) + parser.loadEpisodes(showLink, extra, sAnime).forEach { + map[it.number] = Episode( + it.number, + it.link, + it.title, + it.description, + it.thumbnail, + it.isFiller, + extra = it.extra, + sEpisode = it.sEpisode + ) } } } @@ -42,7 +57,7 @@ abstract class WatchSources : BaseSources() { abstract class MangaReadSources : BaseSources() { override operator fun get(i: Int): MangaParser { - return (list.getOrNull(i)?:list.firstOrNull())?.get?.value as? MangaParser + return (list.getOrNull(i) ?: list.firstOrNull())?.get?.value as? MangaParser ?: EmptyMangaParser() } @@ -56,6 +71,7 @@ abstract class MangaReadSources : BaseSources() { suspend fun loadChapters(i: Int, show: ShowResponse): MutableMap { val map = mutableMapOf() val parser = get(i) + show.sManga?.let { sManga -> tryWithSuspend(true) { parser.loadChapters(show.link, show.extra, sManga).forEach { @@ -63,15 +79,28 @@ abstract class MangaReadSources : BaseSources() { } } } - if(show.sManga == null) { + //must be downloaded + if (show.sManga == null) { logger("sManga is null") } + if (parser is OfflineMangaParser && show.sManga == null) { + tryWithSuspend(true) { + // Since we've checked, we can safely cast parser to OfflineMangaParser and call its methods + parser.loadChapters(show.link, show.extra, SManga.create()).forEach { + map[it.number] = MangaChapter(it) + } + } + } else { + logger("Parser is not an instance of OfflineMangaParser") + } + + logger("map size ${map.size}") return map } } -abstract class NovelReadSources : BaseSources(){ +abstract class NovelReadSources : BaseSources() { override operator fun get(i: Int): NovelParser? { return if (list.isNotEmpty()) { (list.getOrNull(i) ?: list[0]).get.value as NovelParser @@ -87,7 +116,7 @@ class EmptyNovelParser : NovelParser() { override val volumeRegex: Regex = Regex("") override suspend fun loadBook(link: String, extra: Map?): Book { - return Book("","", null, emptyList()) // Return an empty Book object or some default value + return Book("", "", null, emptyList()) // Return an empty Book object or some default value } override suspend fun search(query: String): List { diff --git a/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt b/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt index 0afdbefe..511049ce 100644 --- a/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt +++ b/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt @@ -7,16 +7,19 @@ 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> = emptyList() suspend fun init(fromExtensions: StateFlow>) { // Initialize with the first value from StateFlow val initialExtensions = fromExtensions.first() - list = createParsersFromExtensions(initialExtensions) + list = createParsersFromExtensions(initialExtensions) + Lazier({ OfflineMangaParser() }, "Downloaded") // Update as StateFlow emits new values fromExtensions.collect { extensions -> - list = createParsersFromExtensions(extensions) + list = createParsersFromExtensions(extensions) + Lazier({ OfflineMangaParser() }, "Downloaded") } } diff --git a/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt b/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt new file mode 100644 index 00000000..1340160a --- /dev/null +++ b/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt @@ -0,0 +1,75 @@ +package ani.dantotsu.parsers + +import android.os.Environment +import ani.dantotsu.currContext +import ani.dantotsu.download.DownloadsManager +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 +import java.io.File + +class OfflineMangaParser: MangaParser() { + private val downloadManager = Injekt.get() + + override val hostUrl: String = "Offline" + override val name: String = "Offline" + override val saveName: String = "Offline" + override suspend fun loadChapters( + mangaLink: String, + extra: Map?, + sManga: SManga + ): List { + val directory = File( + currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), + "Dantotsu/Manga/$mangaLink" + ) + //get all of the folder names and add them to the list + val chapters = mutableListOf() + if (directory.exists()) { + directory.listFiles()?.forEach { + if (it.isDirectory) { + val chapter = MangaChapter(it.name, "$mangaLink/${it.name}", it.name, null, SChapter.create()) + chapters.add(chapter) + } + } + return chapters + } + return emptyList() + } + + override suspend fun loadImages(chapterLink: String, sChapter: SChapter): List { + val directory = File( + currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), + "Dantotsu/Manga/$chapterLink" + ) + val images = mutableListOf() + if (directory.exists()) { + directory.listFiles()?.forEach { + if (it.isFile) { + val image = MangaImage(it.absolutePath, false, null) + images.add(image) + } + } + return images + } + return emptyList() + } + + override suspend fun search(query: String): List { + val titles = downloadManager.mangaDownloads.map { it.title }.distinct() + val returnTitles: MutableList = mutableListOf() + for (title in titles) { + if (FuzzySearch.ratio(title.lowercase(), query.lowercase()) > 80) { + returnTitles.add(title) + } + } + val returnList: MutableList = mutableListOf() + for (title in returnTitles) { + returnList.add(ShowResponse(title, title, title)) + } + return returnList + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_no_internet.xml b/app/src/main/res/layout/activity_no_internet.xml index 15a4d8f3..53da52b1 100644 --- a/app/src/main/res/layout/activity_no_internet.xml +++ b/app/src/main/res/layout/activity_no_internet.xml @@ -20,6 +20,7 @@ android:gravity="center" android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + +