From 9e6f71feb2a1fef8e57af2f4dbcd251c67218e41 Mon Sep 17 00:00:00 2001 From: rebelonion <87634197+rebelonion@users.noreply.github.com> Date: Thu, 4 Apr 2024 01:02:37 -0500 Subject: [PATCH] feat: novel to new system | load freezing --- .../download/anime/OfflineAnimeFragment.kt | 101 +++++++++------- .../download/manga/OfflineMangaFragment.kt | 108 +++++++++++------- .../download/novel/NovelDownloaderService.kt | 72 ++++++++---- .../dantotsu/media/novel/NovelReadFragment.kt | 33 +++--- .../dantotsu/parsers/OfflineNovelParser.kt | 33 +++--- 5 files changed, 206 insertions(+), 141 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt index 54a1e562..6c21cb86 100644 --- a/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt +++ b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt @@ -4,7 +4,6 @@ package ani.dantotsu.download.anime import android.content.Intent import android.net.Uri import android.os.Bundle -import android.os.Environment import android.text.Editable import android.text.TextWatcher import android.util.TypedValue @@ -25,6 +24,7 @@ import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.core.view.marginBottom import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.media3.common.util.UnstableApi import ani.dantotsu.R import ani.dantotsu.bottomBar @@ -33,6 +33,7 @@ import ani.dantotsu.currActivity import ani.dantotsu.currContext import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadsManager +import ani.dantotsu.download.DownloadsManager.Companion.findValidName import ani.dantotsu.initActivity import ani.dantotsu.media.Media import ani.dantotsu.media.MediaDetailsActivity @@ -56,9 +57,13 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode import eu.kanade.tachiyomi.animesource.model.SEpisodeImpl import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapterImpl +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.io.File class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { @@ -67,6 +72,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { private lateinit var gridView: GridView private lateinit var adapter: OfflineAnimeAdapter private lateinit var total: TextView + private var downloadsJob: Job = Job() override fun onCreateView( inflater: LayoutInflater, @@ -113,10 +119,10 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { }) var style: Int = PrefManager.getVal(PrefName.OfflineView) val layoutList = view.findViewById(R.id.downloadedList) - val layoutcompact = view.findViewById(R.id.downloadedGrid) + val layoutCompact = view.findViewById(R.id.downloadedGrid) var selected = when (style) { 0 -> layoutList - 1 -> layoutcompact + 1 -> layoutCompact else -> layoutList } selected.alpha = 1f @@ -137,7 +143,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { grid() } - layoutcompact.setOnClickListener { + layoutCompact.setOnClickListener { selected(it as ImageView) style = 1 PrefManager.setVal(PrefName.OfflineView, style) @@ -157,11 +163,11 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { @OptIn(UnstableApi::class) private fun grid() { gridView.visibility = View.VISIBLE - getDownloads() val fadeIn = AlphaAnimation(0f, 1f) fadeIn.duration = 300 // animations pog gridView.layoutAnimation = LayoutAnimationController(fadeIn) adapter = OfflineAnimeAdapter(requireContext(), downloads, this) + getDownloads() gridView.adapter = adapter gridView.scheduleLayoutAnimation() total.text = if (gridView.count > 0) "Anime (${gridView.count})" else "Empty List" @@ -169,20 +175,22 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { // Get the OfflineAnimeModel that was clicked val item = adapter.getItem(position) as OfflineAnimeModel val media = - downloadManager.animeDownloadedTypes.firstOrNull { it.title == item.title } + downloadManager.animeDownloadedTypes.firstOrNull { it.title == item.title.findValidName() } media?.let { - val mediaModel = getMedia(it) - if (mediaModel == null) { - snackString("Error loading media.json") - return@let + lifecycleScope.launch { + val mediaModel = getMedia(it) + if (mediaModel == null) { + snackString("Error loading media.json") + return@launch + } + MediaDetailsActivity.mediaSingleton = mediaModel + ContextCompat.startActivity( + requireActivity(), + Intent(requireContext(), MediaDetailsActivity::class.java) + .putExtra("download", true), + null + ) } - MediaDetailsActivity.mediaSingleton = mediaModel - ContextCompat.startActivity( - requireActivity(), - Intent(requireContext(), MediaDetailsActivity::class.java) - .putExtra("download", true), - null - ) } ?: run { snackString("no media found") } @@ -206,8 +214,6 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { snackString("No media found") // if this happens, terrible things have happened } getDownloads() - adapter.setItems(downloads) - total.text = if (gridView.count > 0) "Anime (${gridView.count})" else "Empty List" } builder.setNegativeButton("No") { _, _ -> // Do nothing @@ -235,7 +241,6 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { gridView.setOnScrollListener(object : AbsListView.OnScrollListener { override fun onScrollStateChanged(view: AbsListView, scrollState: Int) { - // Implement behavior for different scroll states if needed } override fun onScroll( @@ -258,7 +263,6 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { override fun onResume() { super.onResume() getDownloads() - adapter.notifyDataSetChanged() } override fun onPause() { @@ -278,20 +282,34 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { private fun getDownloads() { downloads = listOf() - val animeTitles = downloadManager.animeDownloadedTypes.map { it.title }.distinct() - val newAnimeDownloads = mutableListOf() - for (title in animeTitles) { - val tDownloads = downloadManager.animeDownloadedTypes.filter { it.title == title } - val download = tDownloads.first() - val offlineAnimeModel = loadOfflineAnimeModel(download) - newAnimeDownloads += offlineAnimeModel + if (downloadsJob.isActive) { + downloadsJob.cancel() + } + downloadsJob = Job() + CoroutineScope(Dispatchers.IO + downloadsJob).launch { + val animeTitles = downloadManager.animeDownloadedTypes.map { it.title }.distinct() + val newAnimeDownloads = mutableListOf() + for (title in animeTitles) { + val tDownloads = downloadManager.animeDownloadedTypes.filter { it.title == title } + val download = tDownloads.first() + val offlineAnimeModel = loadOfflineAnimeModel(download) + newAnimeDownloads += offlineAnimeModel + } + downloads = newAnimeDownloads + withContext(Dispatchers.Main) { + adapter.setItems(downloads) + total.text = if (gridView.count > 0) "Anime (${gridView.count})" else "Empty List" + adapter.notifyDataSetChanged() + } } - downloads = newAnimeDownloads } - private fun getMedia(downloadedType: DownloadedType): Media? { - val type = downloadedType.type.asText() - //load media.json and convert to media class with gson + /** + * Load media.json file from the directory and convert it to Media class + * @param downloadedType DownloadedType object + * @return Media object + */ + private suspend fun getMedia(downloadedType: DownloadedType): Media? { return try { val directory = DownloadsManager.getSubDirectory( context ?: currContext()!!, downloadedType.type, @@ -310,10 +328,11 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { .create() val media = directory?.findFile("media.json") ?: return null - val mediaJson = media.openInputStream(context?:currContext()!!)?.bufferedReader().use { - it?.readText() - } - ?: return null + val mediaJson = + media.openInputStream(context ?: currContext()!!)?.bufferedReader().use { + it?.readText() + } + ?: return null gson.fromJson(mediaJson, Media::class.java) } catch (e: Exception) { Logger.log("Error loading media.json: ${e.message}") @@ -323,9 +342,13 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { } } - private fun loadOfflineAnimeModel(downloadedType: DownloadedType): OfflineAnimeModel { + /** + * Load OfflineAnimeModel from the directory + * @param downloadedType DownloadedType object + * @return OfflineAnimeModel object + */ + private suspend fun loadOfflineAnimeModel(downloadedType: DownloadedType): OfflineAnimeModel { val type = downloadedType.type.asText() - //load media.json and convert to media class with gson try { val directory = DownloadsManager.getSubDirectory( context ?: currContext()!!, downloadedType.type, diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt index 152cd542..1d912887 100644 --- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt +++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt @@ -3,7 +3,6 @@ package ani.dantotsu.download.manga import android.content.Intent import android.net.Uri import android.os.Bundle -import android.os.Environment import android.text.Editable import android.text.TextWatcher import android.util.TypedValue @@ -23,6 +22,7 @@ import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.core.view.marginBottom import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import ani.dantotsu.R import ani.dantotsu.bottomBar import ani.dantotsu.connections.crashlytics.CrashlyticsInterface @@ -50,9 +50,13 @@ 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 kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.io.File class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { @@ -61,6 +65,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { private lateinit var gridView: GridView private lateinit var adapter: OfflineMangaAdapter private lateinit var total: TextView + private var downloadsJob: Job = Job() override fun onCreateView( inflater: LayoutInflater, @@ -150,11 +155,11 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { private fun grid() { gridView.visibility = View.VISIBLE - getDownloads() val fadeIn = AlphaAnimation(0f, 1f) fadeIn.duration = 300 // animations pog gridView.layoutAnimation = LayoutAnimationController(fadeIn) adapter = OfflineMangaAdapter(requireContext(), downloads, this) + getDownloads() gridView.adapter = adapter gridView.scheduleLayoutAnimation() total.text = @@ -166,14 +171,15 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { downloadManager.mangaDownloadedTypes.firstOrNull { it.title == item.title } ?: downloadManager.novelDownloadedTypes.firstOrNull { it.title == item.title } media?.let { - - ContextCompat.startActivity( - requireActivity(), - Intent(requireContext(), MediaDetailsActivity::class.java) - .putExtra("media", getMedia(it)) - .putExtra("download", true), - null - ) + lifecycleScope.launch { + ContextCompat.startActivity( + requireActivity(), + Intent(requireContext(), MediaDetailsActivity::class.java) + .putExtra("media", getMedia(it)) + .putExtra("download", true), + null + ) + } } ?: run { snackString("no media found") } @@ -196,9 +202,6 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { builder.setPositiveButton("Yes") { _, _ -> downloadManager.removeMedia(item.title, type) getDownloads() - adapter.setItems(downloads) - total.text = - if (gridView.count > 0) "Manga and Novels (${gridView.count})" else "Empty List" } builder.setNegativeButton("No") { _, _ -> // Do nothing @@ -227,7 +230,6 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { gridView.setOnScrollListener(object : AbsListView.OnScrollListener { override fun onScrollStateChanged(view: AbsListView, scrollState: Int) { - // Implement behavior for different scroll states if needed } override fun onScroll( @@ -250,7 +252,6 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { override fun onResume() { super.onResume() getDownloads() - adapter.notifyDataSetChanged() } override fun onPause() { @@ -270,33 +271,51 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { private fun getDownloads() { downloads = listOf() - val mangaTitles = downloadManager.mangaDownloadedTypes.map { it.title }.distinct() - val newMangaDownloads = mutableListOf() - for (title in mangaTitles) { - val tDownloads = downloadManager.mangaDownloadedTypes.filter { it.title == title } - val download = tDownloads.first() - val offlineMangaModel = loadOfflineMangaModel(download) - newMangaDownloads += offlineMangaModel + if (downloadsJob.isActive) { + downloadsJob.cancel() } - downloads = newMangaDownloads - val novelTitles = downloadManager.novelDownloadedTypes.map { it.title }.distinct() - val newNovelDownloads = mutableListOf() - for (title in novelTitles) { - val tDownloads = downloadManager.novelDownloadedTypes.filter { it.title == title } - val download = tDownloads.first() - val offlineMangaModel = loadOfflineMangaModel(download) - newNovelDownloads += offlineMangaModel + downloads = listOf() + downloadsJob = Job() + CoroutineScope(Dispatchers.IO + downloadsJob).launch { + val mangaTitles = downloadManager.mangaDownloadedTypes.map { it.title }.distinct() + val newMangaDownloads = mutableListOf() + for (title in mangaTitles) { + val tDownloads = downloadManager.mangaDownloadedTypes.filter { it.title == title } + val download = tDownloads.first() + val offlineMangaModel = loadOfflineMangaModel(download) + newMangaDownloads += offlineMangaModel + } + downloads = newMangaDownloads + val novelTitles = downloadManager.novelDownloadedTypes.map { it.title }.distinct() + val newNovelDownloads = mutableListOf() + for (title in novelTitles) { + val tDownloads = downloadManager.novelDownloadedTypes.filter { it.title == title } + val download = tDownloads.first() + val offlineMangaModel = loadOfflineMangaModel(download) + newNovelDownloads += offlineMangaModel + } + downloads += newNovelDownloads + withContext(Dispatchers.Main) { + adapter.setItems(downloads) + total.text = + if (gridView.count > 0) "Manga and Novels (${gridView.count})" else "Empty List" + adapter.notifyDataSetChanged() + } } - downloads += newNovelDownloads } - private fun getMedia(downloadedType: DownloadedType): Media? { - val type = downloadedType.type.asText() - //load media.json and convert to media class with gson + /** + * Load media.json file from the directory and convert it to Media class + * @param downloadedType DownloadedType object + * @return Media object + */ + private suspend fun getMedia(downloadedType: DownloadedType): Media? { return try { - val directory = getSubDirectory(context?:currContext()!!, downloadedType.type, - false, downloadedType.title) + val directory = getSubDirectory( + context ?: currContext()!!, downloadedType.type, + false, downloadedType.title + ) val gson = GsonBuilder() .registerTypeAdapter(SChapter::class.java, InstanceCreator { SChapterImpl() // Provide an instance of SChapterImpl @@ -304,9 +323,10 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { .create() val media = directory?.findFile("media.json") ?: return null - val mediaJson = media.openInputStream(context?:currContext()!!)?.bufferedReader().use { - it?.readText() - } + val mediaJson = + media.openInputStream(context ?: currContext()!!)?.bufferedReader().use { + it?.readText() + } gson.fromJson(mediaJson, Media::class.java) } catch (e: Exception) { Logger.log("Error loading media.json: ${e.message}") @@ -316,12 +336,14 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { } } - private fun loadOfflineMangaModel(downloadedType: DownloadedType): OfflineMangaModel { + private suspend fun loadOfflineMangaModel(downloadedType: DownloadedType): OfflineMangaModel { val type = downloadedType.type.asText() //load media.json and convert to media class with gson try { - val directory = getSubDirectory(context?:currContext()!!, downloadedType.type, - false, downloadedType.title) + val directory = getSubDirectory( + context ?: currContext()!!, downloadedType.type, + false, downloadedType.title + ) val mediaModel = getMedia(downloadedType)!! val cover = directory?.findFile("cover.jpg") val coverUri: Uri? = if (cover?.exists() == true) { diff --git a/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt index fc432313..123a54c1 100644 --- a/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt +++ b/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt @@ -16,15 +16,19 @@ import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat +import androidx.documentfile.provider.DocumentFile import ani.dantotsu.R import ani.dantotsu.connections.crashlytics.CrashlyticsInterface import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadsManager +import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory import ani.dantotsu.media.Media import ani.dantotsu.media.MediaType import ani.dantotsu.media.novel.NovelReadFragment import ani.dantotsu.snackString import ani.dantotsu.util.Logger +import com.anggrayudi.storage.file.forceDelete +import com.anggrayudi.storage.file.openOutputStream import com.google.gson.GsonBuilder import com.google.gson.InstanceCreator import eu.kanade.tachiyomi.data.notification.Notifications @@ -250,24 +254,25 @@ class NovelDownloaderService : Service() { if (!response.isSuccessful) { throw IOException("Failed to download file: ${response.message}") } + val directory = getSubDirectory( + this@NovelDownloaderService, + MediaType.NOVEL, + false, + task.title, + task.chapter + ) ?: throw Exception("Directory not found") + directory.findFile("0.epub")?.forceDelete(this@NovelDownloaderService) - val file = File( - this@NovelDownloaderService.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), - "Dantotsu/Novel/${task.title}/${task.chapter}/0.epub" - ) - - // Create directories if they don't exist - file.parentFile?.takeIf { !it.exists() }?.mkdirs() - - // Overwrite existing file - if (file.exists()) file.delete() + val file = directory.createFile("application/epub+zip", "0.epub") + ?: throw Exception("File not created") //download cover task.coverUrl?.let { file.parentFile?.let { it1 -> downloadImage(it, it1, "cover.jpg") } } + val outputStream = this@NovelDownloaderService.contentResolver.openOutputStream(file.uri) ?: throw Exception("Could not open OutputStream") - val sink = file.sink().buffer() + val sink = outputStream.sink().buffer() val responseBody = response.body val totalBytes = responseBody.contentLength() var downloadedBytes = 0L @@ -352,13 +357,16 @@ class NovelDownloaderService : Service() { @OptIn(DelicateCoroutinesApi::class) private fun saveMediaInfo(task: DownloadTask) { launchIO { - val directory = File( - getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), - "Dantotsu/Novel/${task.title}" - ) - if (!directory.exists()) directory.mkdirs() - - val file = File(directory, "media.json") + val directory = + DownloadsManager.getSubDirectory( + this@NovelDownloaderService, + MediaType.NOVEL, + false, + task.title + ) ?: throw Exception("Directory not found") + directory.findFile("media.json")?.forceDelete(this@NovelDownloaderService) + val file = directory.createFile("application/json", "media.json") + ?: throw Exception("File not created") val gson = GsonBuilder() .registerTypeAdapter(SChapter::class.java, InstanceCreator { SChapterImpl() // Provide an instance of SChapterImpl @@ -372,33 +380,47 @@ class NovelDownloaderService : Service() { val jsonString = gson.toJson(media) withContext(Dispatchers.Main) { - file.writeText(jsonString) + try { + file.openOutputStream(this@NovelDownloaderService, false).use { output -> + if (output == null) throw Exception("Output stream is null") + output.write(jsonString.toByteArray()) + } + } catch (e: android.system.ErrnoException) { + e.printStackTrace() + Toast.makeText( + this@NovelDownloaderService, + "Error while saving: ${e.localizedMessage}", + Toast.LENGTH_LONG + ).show() + } } } } } - private suspend fun downloadImage(url: String, directory: File, name: String): String? = + private suspend fun downloadImage(url: String, directory: DocumentFile, name: String): String? = withContext( Dispatchers.IO ) { var connection: HttpURLConnection? = null - println("Downloading url $url") + Logger.log("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 -> + directory.findFile(name)?.forceDelete(this@NovelDownloaderService) + val file = + directory.createFile("image/jpeg", name) ?: throw Exception("File not created") + file.openOutputStream(this@NovelDownloaderService, false).use { output -> + if (output == null) throw Exception("Output stream is null") connection.inputStream.use { input -> input.copyTo(output) } } - return@withContext file.absolutePath + return@withContext file.uri.toString() } catch (e: Exception) { e.printStackTrace() withContext(Dispatchers.Main) { diff --git a/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt b/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt index cf372b40..c57f943f 100644 --- a/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt @@ -20,6 +20,7 @@ import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.LinearLayoutManager +import ani.dantotsu.currContext import ani.dantotsu.databinding.FragmentAnimeWatchBinding import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadsManager @@ -94,23 +95,23 @@ class NovelReadFragment : Fragment(), ) ) ) { - val file = File( - context?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), - "a/${media.mainName()}/${novel.name}/0.epub" //FIXME - ) - if (!file.exists()) return false - val fileUri = FileProvider.getUriForFile( - requireContext(), - "${requireContext().packageName}.provider", - file - ) - val intent = Intent(context, NovelReaderActivity::class.java).apply { - action = Intent.ACTION_VIEW - setDataAndType(fileUri, "application/epub+zip") - flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + try { + val directory = + DownloadsManager.getSubDirectory(context?:currContext()!!, MediaType.NOVEL, false, novel.name) + val file = directory?.findFile(novel.name) + if (file?.exists() == false) return false + val fileUri = file?.uri ?: return false + val intent = Intent(context, NovelReaderActivity::class.java).apply { + action = Intent.ACTION_VIEW + setDataAndType(fileUri, "application/epub+zip") + flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + } + startActivity(intent) + return true + } catch (e: Exception) { + Logger.log(e) + return false } - startActivity(intent) - return true } else { return false } diff --git a/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt b/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt index 11009aed..2ae88200 100644 --- a/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt @@ -1,9 +1,12 @@ package ani.dantotsu.parsers +import android.app.Application import android.os.Environment import ani.dantotsu.currContext import ani.dantotsu.download.DownloadsManager +import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory import ani.dantotsu.media.MediaNameAdapter +import ani.dantotsu.media.MediaType import me.xdrop.fuzzywuzzy.FuzzySearch import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -11,6 +14,7 @@ import java.io.File class OfflineNovelParser : NovelParser() { private val downloadManager = Injekt.get() + private val context = Injekt.get() override val hostUrl: String = "Offline" override val name: String = "Offline" @@ -21,19 +25,16 @@ class OfflineNovelParser : NovelParser() { override suspend fun loadBook(link: String, extra: Map?): Book { //link should be a directory - val directory = File( - currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), - "Dantotsu/Novel/$link" - ) + val directory = getSubDirectory(context, MediaType.NOVEL, false, link) val chapters = mutableListOf() - if (directory.exists()) { - directory.listFiles()?.forEach { + if (directory?.exists() == true) { + directory.listFiles().forEach { if (it.isDirectory) { val chapter = Book( - it.name, - it.absolutePath + "/cover.jpg", + it.name?:"Unknown", + it.uri.toString(), null, - listOf(it.absolutePath + "/0.epub") + listOf(it.uri.toString()) ) chapters.add(chapter) } @@ -60,20 +61,16 @@ class OfflineNovelParser : NovelParser() { val returnList: MutableList = mutableListOf() for (title in returnTitles) { //need to search the subdirectories for the ShowResponses - val directory = File( - currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), - "Dantotsu/Novel/$title" - ) + val directory = getSubDirectory(context, MediaType.NOVEL, false, title) val names = mutableListOf() - if (directory.exists()) { - directory.listFiles()?.forEach { + if (directory?.exists() == true) { + directory.listFiles().forEach { if (it.isDirectory) { - names.add(it.name) + names.add(it.name?: "Unknown") } } } - val cover = - currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath + "/Dantotsu/Novel/$title/cover.jpg" + val cover = directory?.findFile("cover.jpg")?.uri.toString() names.forEach { returnList.add(ShowResponse(it, it, cover)) }