show AoD highlights on home fragment
This commit is contained in:
		| @ -47,6 +47,7 @@ object AoDParser { | ||||
|  | ||||
|     private val mediaList = arrayListOf<Media>() | ||||
|     val itemMediaList = arrayListOf<ItemMedia>() | ||||
|     val highlightsList = arrayListOf<ItemMedia>() | ||||
|     val newEpisodesList = arrayListOf<ItemMedia>() | ||||
|  | ||||
|     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)) | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -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<Drawable>(48, 48) { | ||||
|                 override fun onLoadCleared(drawable: Drawable?) { | ||||
|                     textView.setCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null) | ||||
|                 } | ||||
|  | ||||
|                 override fun onResourceReady(res: Drawable, transition: Transition<in Drawable>?) { | ||||
|                     textView.setCompoundDrawablesWithIntrinsicBounds(null, res, null, null) | ||||
|                 } | ||||
|  | ||||
|             }) | ||||
|     } | ||||
| } | ||||
| @ -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<ItemMedia>) : RecyclerView.Adapter<MediaItemAdapter.MediaViewHolder>(), Filterable { | ||||
| class MediaItemAdapter(private val initMedia: List<ItemMedia>) : RecyclerView.Adapter<MediaItemAdapter.MediaViewHolder>(), 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<ItemMedia>) : RecyclerView.Adapte | ||||
|         return filter | ||||
|     } | ||||
|  | ||||
|     fun updateMediaList(mediaList: List<ItemMedia>) { | ||||
|         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<ItemMedia>) : RecyclerView.Adapte | ||||
|             val results = FilterResults() | ||||
|  | ||||
|             val filteredList = if (filterTerm.isEmpty()) { | ||||
|                 media | ||||
|                 initMedia | ||||
|             } else { | ||||
|                 media.filter { | ||||
|                 initMedia.filter { | ||||
|                     it.title.toLowerCase(Locale.ROOT).contains(filterTerm) | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @ -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" /> | ||||
|  | ||||
|             <TextView | ||||
|  | ||||
| @ -18,6 +18,97 @@ | ||||
|             android:orientation="vertical"> | ||||
|  | ||||
|             <LinearLayout | ||||
|                 android:id="@+id/linear_highlight" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="match_parent" | ||||
|                 android:orientation="vertical" | ||||
|                 android:paddingBottom="7dp"> | ||||
|  | ||||
|                 <ImageView | ||||
|                     android:id="@+id/image_highlight" | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:contentDescription="@string/highlight_media" | ||||
|                     app:layout_constraintDimensionRatio="H,16:9" | ||||
|                     tools:src="@drawable/ic_launcher_background" /> | ||||
|  | ||||
|                 <TextView | ||||
|                     android:id="@+id/text_highlight_title" | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:layout_marginTop="7dp" | ||||
|                     android:text="@string/text_title_ex" | ||||
|                     android:textAlignment="center" | ||||
|                     android:textSize="16sp" /> | ||||
|  | ||||
|                 <LinearLayout | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_height="match_parent" | ||||
|                     android:layout_marginTop="7dp" | ||||
|                     android:gravity="center" | ||||
|                     android:orientation="horizontal"> | ||||
|  | ||||
|                     <Space | ||||
|                         android:layout_width="0dp" | ||||
|                         android:layout_height="1dp" | ||||
|                         android:layout_weight="1" /> | ||||
|  | ||||
|                     <TextView | ||||
|                         android:id="@+id/text_highlight_my_list" | ||||
|                         android:layout_width="64dp" | ||||
|                         android:layout_height="wrap_content" | ||||
|                         android:gravity="center" | ||||
|                         android:text="@string/my_list" | ||||
|                         android:textColor="?textSecondary" | ||||
|                         android:textSize="12sp" | ||||
|                         app:drawableTint="?buttonBackground" | ||||
|                         app:drawableTopCompat="@drawable/ic_baseline_add_24" /> | ||||
|  | ||||
|                     <Space | ||||
|                         android:layout_width="0dp" | ||||
|                         android:layout_height="1dp" | ||||
|                         android:layout_weight="1" /> | ||||
|  | ||||
|                     <com.google.android.material.button.MaterialButton | ||||
|                         android:id="@+id/button_play_highlight" | ||||
|                         android:layout_width="wrap_content" | ||||
|                         android:layout_height="wrap_content" | ||||
|                         android:gravity="center" | ||||
|                         android:text="@string/button_play" | ||||
|                         android:textAllCaps="false" | ||||
|                         android:textColor="?themePrimary" | ||||
|                         android:textSize="16sp" | ||||
|                         app:backgroundTint="?buttonBackground" | ||||
|                         app:icon="@drawable/ic_baseline_play_arrow_24" | ||||
|                         app:iconGravity="textStart" | ||||
|                         app:iconTint="?themePrimary" /> | ||||
|  | ||||
|                     <Space | ||||
|                         android:layout_width="0dp" | ||||
|                         android:layout_height="1dp" | ||||
|                         android:layout_weight="1" /> | ||||
|  | ||||
|                     <TextView | ||||
|                         android:id="@+id/text_highlight_info" | ||||
|                         android:layout_width="64dp" | ||||
|                         android:layout_height="wrap_content" | ||||
|                         android:gravity="center" | ||||
|                         android:text="@string/info" | ||||
|                         android:textColor="?textSecondary" | ||||
|                         android:textSize="12sp" | ||||
|                         app:drawableTint="?buttonBackground" | ||||
|                         app:drawableTopCompat="@drawable/ic_baseline_info_24" /> | ||||
|  | ||||
|                     <Space | ||||
|                         android:layout_width="0dp" | ||||
|                         android:layout_height="1dp" | ||||
|                         android:layout_weight="1" /> | ||||
|  | ||||
|                 </LinearLayout> | ||||
|             </LinearLayout> | ||||
|  | ||||
|             <LinearLayout | ||||
|                 android:id="@+id/linear_my_list" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="match_parent" | ||||
|                 android:orientation="vertical" | ||||
| @ -45,6 +136,7 @@ | ||||
|             </LinearLayout> | ||||
|  | ||||
|             <LinearLayout | ||||
|                 android:id="@+id/linear_new_episodes" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="match_parent" | ||||
|                 android:orientation="vertical" | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
|     <string name="title_account">Account</string> | ||||
|  | ||||
|     <!-- home fragment --> | ||||
|     <string name="highlight_media">Highlight</string> | ||||
|     <string name="my_list">Meine Liste</string> | ||||
|     <string name="new_episodes">Neue Episoden</string> | ||||
|  | ||||
| @ -24,13 +25,6 @@ | ||||
|     <string name="account_login_desc">Zum bearbeiten tippen</string> | ||||
|     <string name="info">Info</string> | ||||
|     <string name="info_about_desc">Version %1$s (%2$s)</string> | ||||
|     <string name="info_about_dialog"> | ||||
|         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 | ||||
|     </string> | ||||
|     <string name="licenses">Lizenzen</string> | ||||
|     <string name="settings">Einstellungen</string> | ||||
|     <string name="settings_secondary">Bevorzuge alternativen Stream</string> | ||||
|     <string name="settings_secondary_desc">Untertitle-Stream verwenden, sofern vorhanden</string> | ||||
| @ -41,6 +35,12 @@ | ||||
|     <string name="theme_dark">Dunkel</string> | ||||
|  | ||||
|     <!-- about fragment --> | ||||
|     <string name="about_info"> | ||||
|         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 | ||||
|     </string> | ||||
|     <string name="third_party_heading">Lizenzen von Drittanbietern</string> | ||||
|     <string name="third_party_component_desc">© %1$s %2$s unter %3$s</string> | ||||
|  | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
|     <string name="title_account">Account</string> | ||||
|  | ||||
|     <!-- home fragment --> | ||||
|     <string name="highlight_media">Highlight</string> | ||||
|     <string name="my_list">My list</string> | ||||
|     <string name="new_episodes">New episodes</string> | ||||
|  | ||||
| @ -33,12 +34,6 @@ | ||||
|     <string name="info">Info</string> | ||||
|     <string name="info_about" translatable="false">Teapod by @Seil0</string> | ||||
|     <string name="info_about_desc">Version %1$s (%2$s)</string> | ||||
|     <string name="info_about_dialog"> | ||||
|         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</string> | ||||
|     <string name="licenses">Licenses</string> | ||||
|     <string name="settings">Settings</string> | ||||
|     <string name="settings_secondary">Prefer secondary (sub) stream</string> | ||||
|     <string name="settings_secondary_desc">Use the subtitles stream if present</string> | ||||
| @ -49,8 +44,14 @@ | ||||
|     <string name="theme_dark">Dark</string> | ||||
|  | ||||
|     <!-- about fragment --> | ||||
|     <string name="teapod_repo" translatable="false">git.mosad.xyz/Seil0/teapod</string> | ||||
|     <string name="about_info"> | ||||
|         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 | ||||
|     </string> | ||||
|     <string name="tmdb_notice" translatable="false">This product uses the TMDb API but is not endorsed or certified by TMDb.</string> | ||||
|     <string name="teapod_repo" translatable="false">git.mosad.xyz/Seil0/teapod</string> | ||||
|     <string name="third_party_heading">Third Party Licenses</string> | ||||
|     <string name="third_party_component_desc">© %1$s %2$s under %3$s</string> | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user