diff --git a/app/src/main/java/ani/dantotsu/MainActivity.kt b/app/src/main/java/ani/dantotsu/MainActivity.kt index a089d22ea6..79674b72c3 100644 --- a/app/src/main/java/ani/dantotsu/MainActivity.kt +++ b/app/src/main/java/ani/dantotsu/MainActivity.kt @@ -14,6 +14,7 @@ import android.provider.Settings import android.util.Log import android.view.View import android.view.ViewGroup +import android.view.WindowManager import android.view.animation.AnticipateInterpolator import android.widget.TextView import androidx.activity.addCallback diff --git a/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeAdapter.kt b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeAdapter.kt new file mode 100644 index 0000000000..f1ce28f111 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeAdapter.kt @@ -0,0 +1,114 @@ +package ani.dantotsu.download.anime + + +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 +import ani.dantotsu.download.anime.OfflineAnimeModel +import ani.dantotsu.download.anime.OfflineAnimeSearchListener + + +class OfflineAnimeAdapter( + private val context: Context, + private var items: List, + private val searchListener: OfflineAnimeSearchListener +) : BaseAdapter() { + private val inflater: LayoutInflater = + context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + private var originalItems: List = items + private var style = + context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0) + + 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() + } + + @SuppressLint("SetTextI18n") + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + + 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 OfflineAnimeModel + 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) + val totalchapter = view.findViewById(R.id.itemCompactTotal) + val typeimage = view.findViewById(R.id.itemCompactTypeImage) + val type = view.findViewById(R.id.itemCompactRelation) + val typeView = view.findViewById(R.id.itemCompactType) + + if (style == 0) { + val bannerView = view.findViewById(R.id.itemCompactBanner) // for large view + val chapters = view.findViewById(R.id.itemTotal) + chapters.text = " Chapters" + bannerView.setImageURI(item.banner) + totalchapter.text = item.totalEpisode + } else if (style == 1) { + val readchapter = + view.findViewById(R.id.itemCompactUserProgress) // for compact view + readchapter.text = item.watchedEpisode + totalchapter.text = " | " + item.totalEpisode + } + + // Bind item data to the views + typeimage.setImageResource(R.drawable.ic_round_movie_filter_24) + 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 { + ongoing.visibility = View.GONE + } + return view + } + + fun onSearchQuery(query: String) { + // Implement the filtering logic here, for example: + items = if (query.isEmpty()) { + // Return the original list if the query is empty + originalItems + } else { + // Filter the list based on the query + originalItems.filter { it.title.contains(query, ignoreCase = true) } + } + notifyDataSetChanged() // Notify the adapter that the data set has changed + } + + fun setItems(items: List) { + this.items = items + this.originalItems = items + notifyDataSetChanged() + } + + fun notifyNewGrid() { + style = + context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0) + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt new file mode 100644 index 0000000000..b59928b264 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeFragment.kt @@ -0,0 +1,439 @@ +package ani.dantotsu.download.anime + + +import android.animation.ObjectAnimator +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Environment +import android.text.Editable +import android.text.TextWatcher +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.FrameLayout +import android.widget.GridView +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.annotation.OptIn +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.ThemedSpinnerAdapter.Helper +import androidx.cardview.widget.CardView +import androidx.core.view.updatePadding +import androidx.core.view.updatePaddingRelative +import androidx.fragment.app.Fragment +import androidx.media3.common.util.UnstableApi +import ani.dantotsu.R +import ani.dantotsu.currActivity +import ani.dantotsu.currContext +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 +import ani.dantotsu.navBarHeight +import ani.dantotsu.px +import ani.dantotsu.setSafeOnClickListener +import ani.dantotsu.settings.SettingsDialogFragment +import ani.dantotsu.snackString +import ani.dantotsu.statusBarHeight +import com.google.android.material.card.MaterialCardView +import com.google.android.material.imageview.ShapeableImageView +import com.google.android.material.textfield.TextInputLayout +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.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SChapterImpl +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.io.File +import kotlin.math.max +import kotlin.math.min + +class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener { + + private val downloadManager = Injekt.get() + private var downloads: List = listOf() + private lateinit var gridView: GridView + private lateinit var adapter: OfflineAnimeAdapter + + @OptIn(UnstableApi::class) override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val view = inflater.inflate(R.layout.fragment_manga_offline, container, false) + + val textInputLayout = view.findViewById(R.id.offlineMangaSearchBar) + textInputLayout.hint = "Anime" + val currentColor = textInputLayout.boxBackgroundColor + val semiTransparentColor = (currentColor and 0x00FFFFFF) or 0xA8000000.toInt() + textInputLayout.boxBackgroundColor = semiTransparentColor + val materialCardView = view.findViewById(R.id.offlineMangaAvatarContainer) + materialCardView.setCardBackgroundColor(semiTransparentColor) + val typedValue = TypedValue() + requireContext().theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true) + val color = typedValue.data + + val animeTitleContainer = view.findViewById(R.id.animeTitleContainer) + animeTitleContainer.updatePadding(top = statusBarHeight) + + val animeUserAvatar = view.findViewById(R.id.offlineMangaUserAvatar) + animeUserAvatar.setSafeOnClickListener { + val dialogFragment = + SettingsDialogFragment.newInstance2(SettingsDialogFragment.Companion.PageType2.OfflineANIME) + dialogFragment.show((it.context as AppCompatActivity).supportFragmentManager, "dialog") + } + + val colorOverflow = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) + ?.getBoolean("colorOverflow", false) ?: false + if (!colorOverflow) { + textInputLayout.boxBackgroundColor = (color and 0x00FFFFFF) or 0x28000000.toInt() + materialCardView.setCardBackgroundColor((color and 0x00FFFFFF) or 0x28000000.toInt()) + } + + val searchView = view.findViewById(R.id.animeSearchBarText) + searchView.addTextChangedListener(object : TextWatcher { + override fun afterTextChanged(s: Editable?) { + } + + 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(R.id.downloadedList) + val layoutcompact = view.findViewById(R.id.downloadedGrid) + var selected = when (style) { + 0 -> layoutList + 1 -> layoutcompact + else -> layoutList + } + selected.alpha = 1f + + 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 = OfflineAnimeAdapter(requireContext(), downloads, this) + gridView.adapter = adapter + gridView.scheduleLayoutAnimation() + gridView.setOnItemClickListener { parent, view, position, id -> + // Get the OfflineAnimeModel that was clicked + val item = adapter.getItem(position) as OfflineAnimeModel + val media = + downloadManager.animeDownloadedTypes.firstOrNull { it.title == item.title } + media?.let { + startActivity( + Intent(requireContext(), MediaDetailsActivity::class.java) + .putExtra("media", getMedia(it)) + .putExtra("download", true) + ) + } ?: run { + snackString("no media found") + } + } + + val total = view.findViewById(R.id.total) + total.text = + if (gridView.count > 0) "Anime (${gridView.count})" else "Empty List" + gridView.setOnItemLongClickListener { parent, view, position, id -> + // Get the OfflineAnimeModel that was clicked + val item = adapter.getItem(position) as OfflineAnimeModel + val type: DownloadedType.Type = + DownloadedType.Type.ANIME + + // Alert dialog to confirm deletion + val builder = + androidx.appcompat.app.AlertDialog.Builder(requireContext(), R.style.MyPopup) + builder.setTitle("Delete ${item.title}?") + builder.setMessage("Are you sure you want to delete ${item.title}?") + builder.setPositiveButton("Yes") { _, _ -> + downloadManager.removeMedia(item.title, type) + val mediaIds = requireContext().getSharedPreferences(getString(R.string.anime_downloads), Context.MODE_PRIVATE) + ?.all?.filter { it.key.contains(item.title) }?.values ?: emptySet() + for (mediaId in mediaIds) { + ani.dantotsu.download.video.Helper.downloadManager(requireContext()) + .removeDownload(mediaId.toString()) + } + getDownloads() + adapter.setItems(downloads) + } + builder.setNegativeButton("No") { _, _ -> + // Do nothing + } + val dialog = builder.show() + dialog.window?.setDimAmount(0.8f) + true + } + view.rootView.fitsSystemWindows = true + + return view + } + + override fun onSearchQuery(query: String) { + adapter.onSearchQuery(query) + } + + 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 mangaRefresh = view.findViewById(R.id.mangaRefresh) + mangaRefresh.updatePaddingRelative(bottom = navBarHeight + 160f.px) + val scrollTop = view.findViewById(R.id.mangaPageScrollTop) + val 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 { + 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 + } + }) + initActivity(requireActivity()) + + } + + 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() { + downloads = listOf() + val animeTitles = downloadManager.animeDownloadedTypes.map { it.title }.distinct() + val newAnimeDownloads = mutableListOf() + for (title in animeTitles) { + val _downloads = downloadManager.animeDownloadedTypes.filter { it.title == title } + val download = _downloads.first() + val offlineAnimeModel = loadOfflineAnimeModel(download) + newAnimeDownloads += offlineAnimeModel + } + downloads = newAnimeDownloads + } + + private fun getMedia(downloadedType: DownloadedType): Media? { + val type = if (downloadedType.type == DownloadedType.Type.ANIME) { + "Anime" + } else if (downloadedType.type == DownloadedType.Type.MANGA) { + "Manga" + } else { + "Novel" + } + val directory = File( + currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), + "Dantotsu/$type/${downloadedType.title}" + ) + //load media.json and convert to media class with gson + return try { + val gson = GsonBuilder() + .registerTypeAdapter(SChapter::class.java, InstanceCreator { + SChapterImpl() // Provide an instance of SChapterImpl + }) + .registerTypeAdapter(SAnime::class.java, InstanceCreator { + SAnimeImpl() // Provide an instance of SAnimeImpl + }) + .registerTypeAdapter(SEpisode::class.java, InstanceCreator { + SEpisodeImpl() // Provide an instance of SEpisodeImpl + }) + .create() + val media = File(directory, "media.json") + val mediaJson = media.readText() + gson.fromJson(mediaJson, Media::class.java) + } catch (e: Exception) { + logger("Error loading media.json: ${e.message}") + logger(e.printStackTrace()) + FirebaseCrashlytics.getInstance().recordException(e) + null + } + } + + private fun loadOfflineAnimeModel(downloadedType: DownloadedType): OfflineAnimeModel { + val type = if (downloadedType.type == DownloadedType.Type.MANGA) { + "Manga" + } else if (downloadedType.type == DownloadedType.Type.ANIME) { + "Anime" + } else { + "Novel" + } + val directory = File( + currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), + "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(downloadedType)!! + val cover = File(directory, "cover.jpg") + val coverUri: Uri? = if (cover.exists()) { + Uri.fromFile(cover) + } 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 = + mediaModel.status == currActivity()!!.getString(R.string.status_releasing) + val isUserScored = mediaModel.userScore != 0 + val readEpisode = (mediaModel.userProgress ?: "~").toString() + val totalEpisode = "${mediaModel.anime?.totalEpisodes ?: "??"}" + val chapters = " Chapters" + return OfflineAnimeModel( + title, + score, + totalEpisode, + readEpisode, + type, + chapters, + isOngoing, + isUserScored, + coverUri, + bannerUri + ) + } catch (e: Exception) { + logger("Error loading media.json: ${e.message}") + logger(e.printStackTrace()) + FirebaseCrashlytics.getInstance().recordException(e) + return OfflineAnimeModel( + "unknown", + "0", + "??", + "??", + "movie", + "hmm", + false, + false, + null, + null + ) + } + } +} + +interface OfflineAnimeSearchListener { + fun onSearchQuery(query: String) +} diff --git a/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeModel.kt b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeModel.kt new file mode 100644 index 0000000000..a3fe79058c --- /dev/null +++ b/app/src/main/java/ani/dantotsu/download/anime/OfflineAnimeModel.kt @@ -0,0 +1,15 @@ +package ani.dantotsu.download.anime + +import android.net.Uri +data class OfflineAnimeModel ( + val title: String, + val score: String, + val totalEpisode: String, + val watchedEpisode: String, + val type: String, + val episodes: String, + val isOngoing: Boolean, + val isUserScored: Boolean, + val image: Uri?, + val banner: Uri?, +) \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt index 73bc4dc5e4..3bacbcb2af 100644 --- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt +++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaAdapter.kt @@ -61,12 +61,12 @@ class OfflineMangaAdapter( val chapters = view.findViewById(R.id.itemTotal) chapters.text = " Chapters" bannerView.setImageURI(item.banner) - totalchapter.text = item.totalchapter + totalchapter.text = item.totalChapter } else if (style == 1) { val readchapter = view.findViewById(R.id.itemCompactUserProgress) // for compact view - readchapter.text = item.readchapter - totalchapter.text = " | " + item.totalchapter + readchapter.text = item.readChapter + totalchapter.text = " | " + item.totalChapter } // Bind item data to the views 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 3782c6e368..6b39f21a4c 100644 --- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt +++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt @@ -18,11 +18,15 @@ import android.view.animation.LayoutAnimationController import android.view.animation.OvershootInterpolator import android.widget.AbsListView import android.widget.AutoCompleteTextView +import android.widget.FrameLayout import android.widget.GridView import android.widget.ImageView +import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.cardview.widget.CardView +import androidx.core.view.updatePadding +import androidx.core.view.updatePaddingRelative import androidx.fragment.app.Fragment import ani.dantotsu.R import ani.dantotsu.currActivity @@ -33,6 +37,8 @@ import ani.dantotsu.initActivity 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.setSafeOnClickListener import ani.dantotsu.settings.SettingsDialogFragment import ani.dantotsu.snackString @@ -75,6 +81,9 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { requireContext().theme?.resolveAttribute(android.R.attr.windowBackground, typedValue, true) val color = typedValue.data + val animeTitleContainer = view.findViewById(R.id.animeTitleContainer) + animeTitleContainer.updatePadding(top = statusBarHeight) + val animeUserAvatar = view.findViewById(R.id.offlineMangaUserAvatar) animeUserAvatar.setSafeOnClickListener { val dialogFragment = @@ -204,6 +213,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { dialog.window?.setDimAmount(0.8f) true } + view.rootView.fitsSystemWindows = true return view } @@ -230,8 +240,11 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener { } } } + val mangaRefresh = view.findViewById(R.id.mangaRefresh) + mangaRefresh.updatePaddingRelative(bottom = navBarHeight + 160f.px) 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 diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaModel.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaModel.kt index b57ec28e8b..e9693a5b83 100644 --- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaModel.kt +++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaModel.kt @@ -5,8 +5,8 @@ import android.net.Uri data class OfflineMangaModel( val title: String, val score: String, - val totalchapter: String, - val readchapter: String, + val totalChapter: String, + val readChapter: String, val type: String, val chapters: String, val isOngoing: Boolean, diff --git a/app/src/main/java/ani/dantotsu/home/NoInternet.kt b/app/src/main/java/ani/dantotsu/home/NoInternet.kt index db47081fef..658f529766 100644 --- a/app/src/main/java/ani/dantotsu/home/NoInternet.kt +++ b/app/src/main/java/ani/dantotsu/home/NoInternet.kt @@ -1,5 +1,6 @@ package ani.dantotsu.home +import android.app.AlertDialog import android.content.Context import android.graphics.drawable.GradientDrawable import android.os.Build @@ -19,7 +20,9 @@ import androidx.lifecycle.Lifecycle import androidx.viewpager2.adapter.FragmentStateAdapter import ani.dantotsu.R import ani.dantotsu.ZoomOutPageTransformer +import ani.dantotsu.currContext import ani.dantotsu.databinding.ActivityNoInternetBinding +import ani.dantotsu.download.anime.OfflineAnimeFragment import ani.dantotsu.download.manga.OfflineMangaFragment import ani.dantotsu.initActivity import ani.dantotsu.loadData @@ -113,12 +116,11 @@ class NoInternet : AppCompatActivity() { override fun getItemCount(): Int = 3 override fun createFragment(position: Int): Fragment { - when (position) { - 0 -> return OfflineFragment() - 1 -> return OfflineFragment() - 2 -> return OfflineMangaFragment() + return when (position) { + 0 -> OfflineAnimeFragment() + 2 -> OfflineMangaFragment() + else -> OfflineFragment() } - return LoginFragment() } } } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt b/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt index a36f6c2e05..7cf8b2b61b 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt @@ -10,6 +10,7 @@ import android.view.GestureDetector import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.view.WindowManager import android.view.animation.AccelerateDecelerateInterpolator import android.widget.ImageView import androidx.activity.viewModels @@ -77,6 +78,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi ThemeManager(this).applyTheme(MediaSingleton.bitmap) MediaSingleton.bitmap = null super.onCreate(savedInstanceState) + binding = ActivityMediaBinding.inflate(layoutInflater) setContentView(binding.root) screenWidth = resources.displayMetrics.widthPixels.toFloat() @@ -85,8 +87,6 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi initActivity(this) uiSettings = loadData("ui_settings") ?: UserInterfaceSettings() - if (!uiSettings.immersiveMode) this.window.statusBarColor = - ContextCompat.getColor(this, R.color.nav_bg_inv) binding.mediaBanner.updateLayoutParams { height += statusBarHeight } binding.mediaBannerNoKen.updateLayoutParams { height += statusBarHeight } @@ -445,7 +445,6 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi ObjectAnimator.ofFloat(binding.mediaCollapseContainer, "translationX", screenWidth) .setDuration(duration).start() binding.mediaBanner.pause() - if (!uiSettings.immersiveMode) this.window.statusBarColor = color } if (percentage <= percent && isCollapsed) { isCollapsed = false @@ -458,7 +457,6 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi ObjectAnimator.ofFloat(binding.mediaCollapseContainer, "translationX", 0f) .setDuration(duration).start() if (uiSettings.bannerAnimations) binding.mediaBanner.resume() - if (!uiSettings.immersiveMode) this.window.statusBarColor = color } if (percentage == 1 && model.scrolledToTop.value != false) model.scrolledToTop.postValue( false diff --git a/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt b/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt index 05596091fe..896c523e25 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/SelectorDialogFragment.kt @@ -279,7 +279,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() { if (video != null) { Helper.startAnimeDownloadService( requireActivity(), - media!!.userPreferredName, + media!!.nameMAL ?: media!!.nameRomaji, episode.number, video, null, diff --git a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt index ba6057eb9c..40ce4601ea 100644 --- a/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt +++ b/app/src/main/java/ani/dantotsu/parsers/BaseParser.kt @@ -56,7 +56,7 @@ abstract class BaseParser { * **/ open suspend fun autoSearch(mediaObj: Media): ShowResponse? { var response: ShowResponse? = loadSavedShowResponse(mediaObj.id) - if (response != null && this !is OfflineMangaParser) { + if (response != null && this !is OfflineMangaParser && this !is OfflineAnimeParser) { saveShowResponse(mediaObj.id, response, true) } else { setUserText("Searching : ${mediaObj.mainName()}") diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt b/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt index 5f69741217..45ab6d874b 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsDialogFragment.kt @@ -13,14 +13,15 @@ import ani.dantotsu.MainActivity import ani.dantotsu.R import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.databinding.BottomSheetSettingsBinding +import ani.dantotsu.download.anime.OfflineAnimeFragment import ani.dantotsu.download.manga.OfflineMangaFragment import ani.dantotsu.home.AnimeFragment import ani.dantotsu.home.HomeFragment import ani.dantotsu.home.LoginFragment import ani.dantotsu.home.MangaFragment import ani.dantotsu.home.NoInternet -import ani.dantotsu.loadImage import ani.dantotsu.incognitoNotification +import ani.dantotsu.loadImage import ani.dantotsu.offline.OfflineFragment import ani.dantotsu.openLinkInBrowser import ani.dantotsu.others.imagesearch.ImageSearchActivity @@ -145,7 +146,10 @@ class SettingsDialogFragment : BottomSheetDialogFragment() { PageType.ANIME -> { val intent = Intent(activity, NoInternet::class.java) - intent.putExtra("FRAGMENT_CLASS_NAME", OfflineFragment::class.java.name) + intent.putExtra( + "FRAGMENT_CLASS_NAME", + OfflineAnimeFragment::class.java.name + ) startActivity(intent) } diff --git a/app/src/main/java/ani/dantotsu/themes/ThemeManager.kt b/app/src/main/java/ani/dantotsu/themes/ThemeManager.kt index 076277fd56..666786bb14 100644 --- a/app/src/main/java/ani/dantotsu/themes/ThemeManager.kt +++ b/app/src/main/java/ani/dantotsu/themes/ThemeManager.kt @@ -4,12 +4,15 @@ import android.app.Activity import android.content.Context import android.content.res.Configuration import android.graphics.Bitmap +import android.os.Build +import android.view.Window +import android.view.WindowManager import ani.dantotsu.R import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColorsOptions -class ThemeManager(private val context: Context) { +class ThemeManager(private val context: Activity) { fun applyTheme(fromImage: Bitmap? = null) { val useOLED = context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE) .getBoolean("use_oled", false) && isDarkThemeActive(context) @@ -54,9 +57,24 @@ class ThemeManager(private val context: Context) { else -> if (useOLED) R.style.Theme_Dantotsu_PurpleOLED else R.style.Theme_Dantotsu_Purple } + val window = context.window + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) + window.statusBarColor = 0x00000000 context.setTheme(themeToApply) } + fun setWindowFlag(activity: Activity, bits: Int, on: Boolean) { + val win: Window = activity.window + val winParams: WindowManager.LayoutParams = win.attributes + if (on) { + winParams.flags = winParams.flags or bits + } else { + winParams.flags = winParams.flags and bits.inv() + } + win.setAttributes(winParams) + } + private fun applyDynamicColors( useMaterialYou: Boolean, context: Context, diff --git a/app/src/main/res/layout/fragment_manga_offline.xml b/app/src/main/res/layout/fragment_manga_offline.xml index 307a2f49ac..bcd97c8622 100644 --- a/app/src/main/res/layout/fragment_manga_offline.xml +++ b/app/src/main/res/layout/fragment_manga_offline.xml @@ -7,6 +7,7 @@ tools:context=".home.MangaFragment"> #001C1C1C #40ffffff #40ffffff - #54000000 + #00000000 #80000000 #29FF6B08 #b3aead diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 06f9958b82..cebccf97c5 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -12,7 +12,7 @@ #00FFFFFF #40000000 #19000000 - #54EEEEEE + #00000000 #A9FFFFFF #80FFFFFF #ad5edd diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 691ac4aef6..7d23135943 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -4,6 +4,7 @@ @android:color/transparent ?android:colorBackground true + @null false false true @@ -27,7 +28,7 @@