From 6775a4da2e17218cc4900dab9bdc5d38eb348438 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sun, 6 Dec 2020 15:18:15 +0100 Subject: [PATCH] show AoD highlights on home fragment --- .../java/org/mosad/teapod/parser/AoDParser.kt | 20 ++- .../mosad/teapod/ui/fragments/HomeFragment.kt | 129 ++++++++++++++---- .../teapod/util/adapter/MediaItemAdapter.kt | 12 +- app/src/main/res/layout/fragment_about.xml | 2 +- app/src/main/res/layout/fragment_home.xml | 92 +++++++++++++ app/src/main/res/values-de-rDE/strings.xml | 14 +- app/src/main/res/values/strings.xml | 15 +- 7 files changed, 237 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt b/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt index 835c37b..da55dbb 100644 --- a/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt +++ b/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt @@ -47,6 +47,7 @@ object AoDParser { private val mediaList = arrayListOf() val itemMediaList = arrayListOf() + val highlightsList = arrayListOf() val newEpisodesList = arrayListOf() fun login(): Boolean = runBlocking { @@ -95,7 +96,7 @@ object AoDParser { */ fun initialLoading() = runBlocking { val newEPJob = GlobalScope.async { - listNewEpisodes() + loadHome() } val listJob = GlobalScope.async { @@ -182,9 +183,9 @@ object AoDParser { } /** - * load all new episodes from AoD into newEpisodesList + * load new episodes and highlights */ - private fun listNewEpisodes() = runBlocking { + private fun loadHome() = runBlocking { if (sessionCookies.isEmpty()) login() withContext(Dispatchers.Default) { @@ -192,6 +193,7 @@ object AoDParser { .cookies(sessionCookies) .get() + // get all new episodes from AoD newEpisodesList.clear() resHome.select("div.jcarousel-container-new").select("li").forEach { if (it.select("span").hasClass("neweps")) { @@ -204,6 +206,18 @@ object AoDParser { } } + // get highlights from AoD + highlightsList.clear() + resHome.select("#aod-highlights").select("div.news-item").forEach { + val mediaId = it.select("div.news-item-text").select("a.serienlink") + .attr("href").substringAfterLast("/").toInt() + val mediaTitle = it.select("div.news-title").select("h2").text() + val mediaImage = it.select("img").attr("src") + + highlightsList.add(ItemMedia(mediaId, mediaTitle, mediaImage)) + + } + } } diff --git a/app/src/main/java/org/mosad/teapod/ui/fragments/HomeFragment.kt b/app/src/main/java/org/mosad/teapod/ui/fragments/HomeFragment.kt index c9e8088..b3f74ea 100644 --- a/app/src/main/java/org/mosad/teapod/ui/fragments/HomeFragment.kt +++ b/app/src/main/java/org/mosad/teapod/ui/fragments/HomeFragment.kt @@ -1,17 +1,24 @@ package org.mosad.teapod.ui.fragments +import android.graphics.drawable.Drawable import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.TextView import androidx.fragment.app.Fragment +import com.bumptech.glide.Glide +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.mosad.teapod.MainActivity +import org.mosad.teapod.R import org.mosad.teapod.databinding.FragmentHomeBinding import org.mosad.teapod.parser.AoDParser +import org.mosad.teapod.util.ItemMedia import org.mosad.teapod.util.StorageController import org.mosad.teapod.util.adapter.MediaItemAdapter import org.mosad.teapod.util.decoration.MediaItemDecoration @@ -22,6 +29,8 @@ class HomeFragment : Fragment() { private lateinit var adapterMyList: MediaItemAdapter private lateinit var adapterNewEpisodes: MediaItemAdapter + private lateinit var highlightMedia: ItemMedia + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { binding = FragmentHomeBinding.inflate(inflater, container, false) return binding.root @@ -30,24 +39,89 @@ class HomeFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - GlobalScope.launch { - withContext(Dispatchers.Main) { - context?.let { - binding.recyclerMyList.addItemDecoration(MediaItemDecoration(9)) - - updateMyListMedia() - - adapterNewEpisodes = MediaItemAdapter(AoDParser.newEpisodesList) - binding.recyclerNewEpisodes.adapter = adapterNewEpisodes - binding.recyclerNewEpisodes.addItemDecoration(MediaItemDecoration(9)) - - initActions() - } + GlobalScope.launch(Dispatchers.Main) { + context?.let { + initHighlight() + initRecyclerViews() + initActions() } } } - // TODO recreating the adapter on list change is not a good solution + private fun initHighlight() { + highlightMedia = AoDParser.highlightsList[0] + + binding.textHighlightTitle.text = highlightMedia.title + Glide.with(requireContext()).load(highlightMedia.posterUrl) + .into(binding.imageHighlight) + + if (StorageController.myList.contains(highlightMedia.id)) { + loadIntoCompoundDrawable(R.drawable.ic_baseline_check_24, binding.textHighlightMyList) + } else { + loadIntoCompoundDrawable(R.drawable.ic_baseline_add_24, binding.textHighlightMyList) + } + } + + private fun initRecyclerViews() { + binding.recyclerMyList.addItemDecoration(MediaItemDecoration(9)) + binding.recyclerNewEpisodes.addItemDecoration(MediaItemDecoration(9)) + + // my list + val myListMedia = StorageController.myList.map { elementId -> + AoDParser.itemMediaList.first { + elementId == it.id + } + } + adapterMyList = MediaItemAdapter(myListMedia) + adapterMyList.onItemClick = { mediaId, _ -> + (activity as MainActivity).showFragment(MediaFragment(mediaId)) + } + binding.recyclerMyList.adapter = adapterMyList + + // new episodes + adapterNewEpisodes = MediaItemAdapter(AoDParser.newEpisodesList) + binding.recyclerNewEpisodes.adapter = adapterNewEpisodes + } + + private fun initActions() { + binding.buttonPlayHighlight.setOnClickListener { + // TODO get next episode + GlobalScope.launch { + val media = AoDParser.getMediaById(highlightMedia.id) + + Log.d(javaClass.name, "Starting Player with mediaId: ${media.id}") + (activity as MainActivity).startPlayer(media.id, media.episodes.first().id) + } + } + + binding.textHighlightMyList.setOnClickListener { + if (StorageController.myList.contains(highlightMedia.id)) { + StorageController.myList.remove(highlightMedia.id) + loadIntoCompoundDrawable(R.drawable.ic_baseline_add_24, binding.textHighlightMyList) + } else { + StorageController.myList.add(highlightMedia.id) + loadIntoCompoundDrawable(R.drawable.ic_baseline_check_24, binding.textHighlightMyList) + } + StorageController.saveMyList(requireContext()) + + updateMyListMedia() // update my list, since it has changed + } + + binding.textHighlightInfo.setOnClickListener { + (activity as MainActivity).showFragment(MediaFragment(highlightMedia.id)) + } + + adapterNewEpisodes.onItemClick = { mediaId, _ -> + (activity as MainActivity).showFragment(MediaFragment(mediaId)) + } + } + + /** + * update my media list + * TODO + * * auto call when StorageController.myList is changed + * * only update actual change and not all data (performance) + */ fun updateMyListMedia() { val myListMedia = StorageController.myList.map { elementId -> AoDParser.itemMediaList.first { @@ -55,17 +129,22 @@ class HomeFragment : Fragment() { } } - adapterMyList = MediaItemAdapter(myListMedia) - adapterMyList.onItemClick = { mediaId, _ -> - (activity as MainActivity).showFragment(MediaFragment(mediaId)) - } - - binding.recyclerMyList.adapter = adapterMyList + adapterMyList.updateMediaList(myListMedia) + adapterMyList.notifyDataSetChanged() } - private fun initActions() { - adapterNewEpisodes.onItemClick = { mediaId, _ -> - (activity as MainActivity).showFragment(MediaFragment(mediaId)) - } + private fun loadIntoCompoundDrawable(drawable: Int, textView: TextView) { + Glide.with(requireContext()) + .load(drawable) + .into(object : CustomTarget(48, 48) { + override fun onLoadCleared(drawable: Drawable?) { + textView.setCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null) + } + + override fun onResourceReady(res: Drawable, transition: Transition?) { + textView.setCompoundDrawablesWithIntrinsicBounds(null, res, null, null) + } + + }) } } \ No newline at end of file diff --git a/app/src/main/java/org/mosad/teapod/util/adapter/MediaItemAdapter.kt b/app/src/main/java/org/mosad/teapod/util/adapter/MediaItemAdapter.kt index c525d2f..0038afa 100644 --- a/app/src/main/java/org/mosad/teapod/util/adapter/MediaItemAdapter.kt +++ b/app/src/main/java/org/mosad/teapod/util/adapter/MediaItemAdapter.kt @@ -10,11 +10,11 @@ import org.mosad.teapod.databinding.ItemMediaBinding import org.mosad.teapod.util.ItemMedia import java.util.* -class MediaItemAdapter(private val media: List) : RecyclerView.Adapter(), Filterable { +class MediaItemAdapter(private val initMedia: List) : RecyclerView.Adapter(), Filterable { var onItemClick: ((Int, Int) -> Unit)? = null private val filter = MediaFilter() - private var filteredMedia = media.map { it.copy() } + private var filteredMedia = initMedia.map { it.copy() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaItemAdapter.MediaViewHolder { return MediaViewHolder(ItemMediaBinding.inflate(LayoutInflater.from(parent.context), parent, false)) @@ -35,6 +35,10 @@ class MediaItemAdapter(private val media: List) : RecyclerView.Adapte return filter } + fun updateMediaList(mediaList: List) { + filteredMedia = mediaList + } + inner class MediaViewHolder(val binding: ItemMediaBinding) : RecyclerView.ViewHolder(binding.root) { init { binding.root.setOnClickListener { @@ -49,9 +53,9 @@ class MediaItemAdapter(private val media: List) : RecyclerView.Adapte val results = FilterResults() val filteredList = if (filterTerm.isEmpty()) { - media + initMedia } else { - media.filter { + initMedia.filter { it.title.toLowerCase(Locale.ROOT).contains(filterTerm) } } diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml index 5302dc9..12f0b67 100644 --- a/app/src/main/res/layout/fragment_about.xml +++ b/app/src/main/res/layout/fragment_about.xml @@ -55,7 +55,7 @@ android:layout_marginStart="5dp" android:layout_marginTop="7dp" android:layout_marginEnd="5dp" - android:text="@string/info_about_dialog" + android:text="@string/about_info" android:textAlignment="center" /> + + + + + + + + + + + + + + + + + + + + + + + + + Account + Highlight Meine Liste Neue Episoden @@ -24,13 +25,6 @@ Zum bearbeiten tippen Info Version %1$s (%2$s) - - Teapod ist eine inoffizielle App für Anime on Demand. - Sie wird unter den Bedingungen der GNU GPL 3 oder höher zur Verfügung gestellt. - \n\n - © 2020 seil0@mosad.xyz - - Lizenzen Einstellungen Bevorzuge alternativen Stream Untertitle-Stream verwenden, sofern vorhanden @@ -41,6 +35,12 @@ Dunkel + + Teapod ist eine inoffizielle App für Anime on Demand. + Sie wird unter den Bedingungen der GNU GPL 3 oder höher zur Verfügung gestellt. + \n\n + © 2020 seil0@mosad.xyz + Lizenzen von Drittanbietern © %1$s %2$s unter %3$s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a11fb4a..ed21368 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,6 +6,7 @@ Account + Highlight My list New episodes @@ -33,12 +34,6 @@ Info Teapod by @Seil0 Version %1$s (%2$s) - - Teapod is an unofficial app for anime on demand. - It is published under the terms and conditions of the GNU GPL 3 or later. - \n\n - © 2020 seil0@mosad.xyz - Licenses Settings Prefer secondary (sub) stream Use the subtitles stream if present @@ -49,8 +44,14 @@ Dark - git.mosad.xyz/Seil0/teapod + + Teapod is an unofficial app for anime on demand. + It is published under the terms and conditions of the GNU GPL 3 or later. + \n\n + © 2020 seil0@mosad.xyz + This product uses the TMDb API but is not endorsed or certified by TMDb. + git.mosad.xyz/Seil0/teapod Third Party Licenses © %1$s %2$s under %3$s