show AoD highlights on home fragment

This commit is contained in:
Jannik 2020-12-06 15:18:15 +01:00
parent a390bc9686
commit 6775a4da2e
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
7 changed files with 237 additions and 47 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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