offline anime

This commit is contained in:
rebelonion 2024-01-14 23:59:39 -06:00
parent 52dadf34cf
commit 98cb11e841
17 changed files with 630 additions and 22 deletions

View file

@ -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

View file

@ -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<OfflineAnimeModel>,
private val searchListener: OfflineAnimeSearchListener
) : BaseAdapter() {
private val inflater: LayoutInflater =
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
private var originalItems: List<OfflineAnimeModel> = 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<ImageView>(R.id.itemCompactImage)
val titleTextView = view.findViewById<TextView>(R.id.itemCompactTitle)
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 typeimage = view.findViewById<ImageView>(R.id.itemCompactTypeImage)
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.totalEpisode
} else if (style == 1) {
val readchapter =
view.findViewById<TextView>(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<OfflineAnimeModel>) {
this.items = items
this.originalItems = items
notifyDataSetChanged()
}
fun notifyNewGrid() {
style =
context.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE).getInt("offline_view", 0)
notifyDataSetChanged()
}
}

View file

@ -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<DownloadsManager>()
private var downloads: List<OfflineAnimeModel> = 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<TextInputLayout>(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<MaterialCardView>(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<LinearLayout>(R.id.animeTitleContainer)
animeTitleContainer.updatePadding(top = statusBarHeight)
val animeUserAvatar = view.findViewById<ShapeableImageView>(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<AutoCompleteTextView>(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<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
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<TextView>(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<FrameLayout>(R.id.mangaRefresh)
mangaRefresh.updatePaddingRelative(bottom = navBarHeight + 160f.px)
val scrollTop = view.findViewById<CardView>(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<OfflineAnimeModel>()
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<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 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)
}

View file

@ -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?,
)

View file

@ -61,12 +61,12 @@ class OfflineMangaAdapter(
val chapters = view.findViewById<TextView>(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<TextView>(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

View file

@ -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<LinearLayout>(R.id.animeTitleContainer)
animeTitleContainer.updatePadding(top = statusBarHeight)
val animeUserAvatar = view.findViewById<ShapeableImageView>(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<FrameLayout>(R.id.mangaRefresh)
mangaRefresh.updatePaddingRelative(bottom = navBarHeight + 160f.px)
val scrollTop = view.findViewById<CardView>(R.id.mangaPageScrollTop)
var visible = false
fun animate() {
val start = if (visible) 0f else 1f
val end = if (!visible) 0f else 1f

View file

@ -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,

View file

@ -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()
}
}
}

View file

@ -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<UserInterfaceSettings>("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

View file

@ -279,7 +279,7 @@ class SelectorDialogFragment : BottomSheetDialogFragment() {
if (video != null) {
Helper.startAnimeDownloadService(
requireActivity(),
media!!.userPreferredName,
media!!.nameMAL ?: media!!.nameRomaji,
episode.number,
video,
null,

View file

@ -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()}")

View file

@ -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)
}

View file

@ -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,

View file

@ -7,6 +7,7 @@
tools:context=".home.MangaFragment">
<FrameLayout
android:id="@+id/mangaRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
@ -16,6 +17,8 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
<LinearLayout

View file

@ -7,7 +7,7 @@
<color name="nav_bg_inv">#001C1C1C</color>
<color name="nav_tab">#40ffffff</color>
<color name="nav_tab_disabled">#40ffffff</color>
<color name="status">#54000000</color>
<color name="status">#00000000</color>
<color name="nav_status">#80000000</color>
<color name="filler">#29FF6B08</color>
<color name="chip">#b3aead</color>

View file

@ -12,7 +12,7 @@
<color name="nav_bg_inv">#00FFFFFF</color>
<color name="nav_tab">#40000000</color>
<color name="nav_tab_disabled">#19000000</color>
<color name="status">#54EEEEEE</color>
<color name="status">#00000000</color>
<color name="button_icon">#A9FFFFFF</color>
<color name="nav_status">#80FFFFFF</color>
<color name="fav">#ad5edd</color>

View file

@ -4,6 +4,7 @@
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">?android:colorBackground</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="elevationOverlayEnabled">false</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
@ -27,7 +28,7 @@
</style>
<style name="Theme.Dantotsu" parent="Theme.Base">
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowLightStatusBar">false</item>
</style>
<style name="Theme.Dantotsu.NoActionBar">