diff --git a/app/build.gradle b/app/build.gradle
index 218155c..cae0d62 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -46,8 +46,8 @@ dependencies {
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
- implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
- implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
+ implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
+ implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
implementation 'androidx.security:security-crypto:1.1.0-alpha03'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 11b032f..794a418 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -13,7 +13,7 @@
android:supportsRtl="true"
android:theme="@style/AppTheme.Dark">
@@ -23,24 +23,24 @@
()
private var csrfToken: String = ""
@@ -323,7 +323,7 @@ object AoDParser {
}
Log.i(javaClass.name, "Loaded playlists successfully")
- // parse additional info from the media page
+ // additional info from the media page
res.select("table.vertical-table").select("tr").forEach { row ->
when (row.select("th").text().toLowerCase(Locale.ROOT)) {
"produktionsjahr" -> media.info.year = row.select("td").text().toInt()
@@ -337,7 +337,21 @@ object AoDParser {
}
}
- // parse additional information for tv shows the episode title (description) is loaded from the "api"
+ // similar titles from media page
+ media.info.similar = res.select("h2:contains(Ähnliche Animes)").next().select("li").mapNotNull {
+ val mediaId = it.select("a.thumbs").attr("href")
+ .substringAfterLast("/").toIntOrNull()
+ val mediaImage = it.select("a.thumbs > img").attr("src")
+ val mediaTitle = it.select("a").text()
+
+ if (mediaId != null) {
+ ItemMedia(mediaId, mediaTitle, mediaImage)
+ } else {
+ null
+ }
+ }
+
+ // additional information for tv shows the episode title (description) is loaded from the "api"
if (media.type == MediaType.TVSHOW) {
res.select("div.three-box-container > div.episodebox").forEach { episodebox ->
// make sure the episode has a streaming link
diff --git a/app/src/main/java/org/mosad/teapod/activity/SplashActivity.kt b/app/src/main/java/org/mosad/teapod/ui/activity/SplashActivity.kt
similarity index 79%
rename from app/src/main/java/org/mosad/teapod/activity/SplashActivity.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/SplashActivity.kt
index 39b6744..c7dfa49 100644
--- a/app/src/main/java/org/mosad/teapod/activity/SplashActivity.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/SplashActivity.kt
@@ -1,9 +1,9 @@
-package org.mosad.teapod.activity
+package org.mosad.teapod.ui.activity
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
-import org.mosad.teapod.activity.main.MainActivity
+import org.mosad.teapod.ui.activity.main.MainActivity
class SplashActivity : AppCompatActivity() {
diff --git a/app/src/main/java/org/mosad/teapod/activity/main/MainActivity.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/MainActivity.kt
similarity index 92%
rename from app/src/main/java/org/mosad/teapod/activity/main/MainActivity.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/main/MainActivity.kt
index c22a68b..19dd297 100644
--- a/app/src/main/java/org/mosad/teapod/activity/main/MainActivity.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/MainActivity.kt
@@ -20,7 +20,7 @@
*
*/
-package org.mosad.teapod.activity.main
+package org.mosad.teapod.ui.activity.main
import android.content.Intent
import android.os.Bundle
@@ -37,20 +37,19 @@ import kotlinx.coroutines.runBlocking
import org.mosad.teapod.R
import org.mosad.teapod.databinding.ActivityMainBinding
import org.mosad.teapod.parser.AoDParser
-import org.mosad.teapod.activity.player.PlayerActivity
+import org.mosad.teapod.ui.activity.player.PlayerActivity
import org.mosad.teapod.preferences.EncryptedPreferences
import org.mosad.teapod.preferences.Preferences
import org.mosad.teapod.ui.components.LoginDialog
-import org.mosad.teapod.activity.main.fragments.AccountFragment
-import org.mosad.teapod.activity.main.fragments.HomeFragment
-import org.mosad.teapod.activity.main.fragments.LibraryFragment
-import org.mosad.teapod.activity.main.fragments.SearchFragment
-import org.mosad.teapod.activity.onboarding.OnboardingActivity
+import org.mosad.teapod.ui.activity.main.fragments.AccountFragment
+import org.mosad.teapod.ui.activity.main.fragments.HomeFragment
+import org.mosad.teapod.ui.activity.main.fragments.LibraryFragment
+import org.mosad.teapod.ui.activity.main.fragments.SearchFragment
+import org.mosad.teapod.ui.activity.onboarding.OnboardingActivity
import org.mosad.teapod.util.DataTypes
import org.mosad.teapod.util.StorageController
import org.mosad.teapod.util.exitAndRemoveTask
import java.net.SocketTimeoutException
-import kotlin.system.exitProcess
import kotlin.system.measureTimeMillis
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
@@ -60,6 +59,11 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
companion object {
var wasInitialized = false
+ lateinit var instance: MainActivity
+ }
+
+ init {
+ instance = this
}
override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/app/src/main/java/org/mosad/teapod/activity/main/fragments/AboutFragment.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/AboutFragment.kt
similarity index 98%
rename from app/src/main/java/org/mosad/teapod/activity/main/fragments/AboutFragment.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/AboutFragment.kt
index ddba535..a30d129 100644
--- a/app/src/main/java/org/mosad/teapod/activity/main/fragments/AboutFragment.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/AboutFragment.kt
@@ -1,4 +1,4 @@
-package org.mosad.teapod.activity.main.fragments
+package org.mosad.teapod.ui.activity.main.fragments
import android.content.Intent
import android.net.Uri
diff --git a/app/src/main/java/org/mosad/teapod/activity/main/fragments/AccountFragment.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/AccountFragment.kt
similarity index 97%
rename from app/src/main/java/org/mosad/teapod/activity/main/fragments/AccountFragment.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/AccountFragment.kt
index 1561b1c..61ca637 100644
--- a/app/src/main/java/org/mosad/teapod/activity/main/fragments/AccountFragment.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/AccountFragment.kt
@@ -1,4 +1,4 @@
-package org.mosad.teapod.activity.main.fragments
+package org.mosad.teapod.ui.activity.main.fragments
import android.os.Bundle
import android.util.Log
@@ -9,7 +9,7 @@ import androidx.fragment.app.Fragment
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsSingleChoice
import org.mosad.teapod.BuildConfig
-import org.mosad.teapod.activity.main.MainActivity
+import org.mosad.teapod.ui.activity.main.MainActivity
import org.mosad.teapod.R
import org.mosad.teapod.databinding.FragmentAccountBinding
import org.mosad.teapod.parser.AoDParser
diff --git a/app/src/main/java/org/mosad/teapod/activity/main/fragments/HomeFragment.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/HomeFragment.kt
similarity index 98%
rename from app/src/main/java/org/mosad/teapod/activity/main/fragments/HomeFragment.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/HomeFragment.kt
index cf0fc0f..6e19d11 100644
--- a/app/src/main/java/org/mosad/teapod/activity/main/fragments/HomeFragment.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/HomeFragment.kt
@@ -1,4 +1,4 @@
-package org.mosad.teapod.activity.main.fragments
+package org.mosad.teapod.ui.activity.main.fragments
import android.os.Bundle
import android.util.Log
@@ -11,7 +11,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.mosad.teapod.R
-import org.mosad.teapod.activity.main.MainActivity
+import org.mosad.teapod.ui.activity.main.MainActivity
import org.mosad.teapod.databinding.FragmentHomeBinding
import org.mosad.teapod.parser.AoDParser
import org.mosad.teapod.util.ItemMedia
diff --git a/app/src/main/java/org/mosad/teapod/activity/main/fragments/LibraryFragment.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/LibraryFragment.kt
similarity index 94%
rename from app/src/main/java/org/mosad/teapod/activity/main/fragments/LibraryFragment.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/LibraryFragment.kt
index 50ba7b9..01ab191 100644
--- a/app/src/main/java/org/mosad/teapod/activity/main/fragments/LibraryFragment.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/LibraryFragment.kt
@@ -1,4 +1,4 @@
-package org.mosad.teapod.activity.main.fragments
+package org.mosad.teapod.ui.activity.main.fragments
import android.os.Bundle
import android.view.LayoutInflater
@@ -9,7 +9,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import org.mosad.teapod.activity.main.MainActivity
import org.mosad.teapod.databinding.FragmentLibraryBinding
import org.mosad.teapod.parser.AoDParser
import org.mosad.teapod.util.adapter.MediaItemAdapter
diff --git a/app/src/main/java/org/mosad/teapod/activity/main/fragments/MediaFragment.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragment.kt
similarity index 54%
rename from app/src/main/java/org/mosad/teapod/activity/main/fragments/MediaFragment.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragment.kt
index ed3f42f..47d9067 100644
--- a/app/src/main/java/org/mosad/teapod/activity/main/fragments/MediaFragment.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragment.kt
@@ -1,4 +1,4 @@
-package org.mosad.teapod.activity.main.fragments
+package org.mosad.teapod.ui.activity.main.fragments
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
@@ -8,26 +8,34 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.activityViewModels
+import androidx.viewpager2.adapter.FragmentStateAdapter
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
+import com.google.android.material.tabs.TabLayoutMediator
import jp.wasabeef.glide.transformations.BlurTransformation
import kotlinx.coroutines.*
import org.mosad.teapod.R
-import org.mosad.teapod.activity.main.MainActivity
+import org.mosad.teapod.ui.activity.main.MainActivity
+import org.mosad.teapod.ui.activity.main.viewmodel.MediaFragmentViewModel
import org.mosad.teapod.databinding.FragmentMediaBinding
-import org.mosad.teapod.parser.AoDParser
import org.mosad.teapod.util.*
import org.mosad.teapod.util.DataTypes.MediaType
-import org.mosad.teapod.util.adapter.EpisodeItemAdapter
+/**
+ * The media detail fragment.
+ * Note: the fragment is created only once, when selecting a similar title etc.
+ * therefore fragments may be not empty and model may be the old one
+ */
class MediaFragment(private val mediaId: Int) : Fragment() {
private lateinit var binding: FragmentMediaBinding
- private lateinit var adapterRecEpisodes: EpisodeItemAdapter
+ private lateinit var pagerAdapter: FragmentStateAdapter
- private lateinit var media: Media
- private lateinit var tmdb: TMDBResponse
- private lateinit var nextEpisode: Episode
+ private val fragments = arrayListOf()
+
+ private val model: MediaFragmentViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentMediaBinding.inflate(inflater, container, false)
@@ -38,10 +46,19 @@ class MediaFragment(private val mediaId: Int) : Fragment() {
super.onViewCreated(view, savedInstanceState)
binding.frameLoading.visibility = View.VISIBLE
+ // tab layout and pager TODO
+ pagerAdapter = ScreenSlidePagerAdapter(requireActivity())
+ binding.pagerEpisodesSimilar.adapter = pagerAdapter
+ TabLayoutMediator(binding.tabEpisodesSimilar, binding.pagerEpisodesSimilar) { tab, position ->
+ tab.text = if (model.media.type == MediaType.TVSHOW && position == 0) {
+ getString(R.string.episodes)
+ } else {
+ getString(R.string.similar_titles)
+ }
+ }.attach()
+
GlobalScope.launch(Dispatchers.Main) {
- // load the streams for the selected media
- media = AoDParser.getMediaById(mediaId)
- tmdb = TMDBApiController().search(media.info.title, media.type)
+ model.load(mediaId) // load the streams and tmdb for the selected media
if (this@MediaFragment.isAdded) {
updateGUI()
@@ -53,20 +70,14 @@ class MediaFragment(private val mediaId: Int) : Fragment() {
override fun onResume() {
super.onResume()
- // only notify adapter, if initialized
- if (this::adapterRecEpisodes.isInitialized) {
- // TODO find a better solution for this
- media.episodes.forEachIndexed { index, episode ->
- adapterRecEpisodes.updateWatchedState(episode.watched, index)
- }
- adapterRecEpisodes.notifyDataSetChanged()
- }
+ // update the next ep text, since it may have changed
+ binding.textTitle.text = model.nextEpisode.title
}
/**
* if tmdb data is present, use it, else use the aod data
*/
- private fun updateGUI() = with(binding) {
+ private fun updateGUI() = with(model) {
// generic gui
val backdropUrl = if (tmdb.backdropUrl.isNotEmpty()) tmdb.backdropUrl else media.info.posterUrl
val posterUrl = if (tmdb.posterUrl.isNotEmpty()) tmdb.posterUrl else media.info.posterUrl
@@ -74,33 +85,27 @@ class MediaFragment(private val mediaId: Int) : Fragment() {
Glide.with(requireContext()).load(backdropUrl)
.apply(RequestOptions.placeholderOf(ColorDrawable(Color.DKGRAY)))
.apply(RequestOptions.bitmapTransform(BlurTransformation(20, 3)))
- .into(imageBackdrop)
+ .into(binding.imageBackdrop)
Glide.with(requireContext()).load(posterUrl)
- .into(imagePoster)
+ .into(binding.imagePoster)
- textTitle.text = media.info.title
- textYear.text = media.info.year.toString()
- textAge.text = media.info.age.toString()
- textOverview.text = media.info.shortDesc
+ binding.textTitle.text = media.info.title
+ binding.textYear.text = media.info.year.toString()
+ binding.textAge.text = media.info.age.toString()
+ binding.textOverview.text = media.info.shortDesc
if (StorageController.myList.contains(media.id)) {
- Glide.with(requireContext()).load(R.drawable.ic_baseline_check_24).into(imageMyListAction)
+ Glide.with(requireContext()).load(R.drawable.ic_baseline_check_24).into(binding.imageMyListAction)
} else {
- Glide.with(requireContext()).load(R.drawable.ic_baseline_add_24).into(imageMyListAction)
+ Glide.with(requireContext()).load(R.drawable.ic_baseline_add_24).into(binding.imageMyListAction)
}
+ // clear fragments, since it lives in onCreate scope (don't do this in onPause/onStop -> FragmentManager transaction)
+ fragments.clear()
+ pagerAdapter.notifyDataSetChanged()
+
// specific gui
if (media.type == MediaType.TVSHOW) {
- adapterRecEpisodes = EpisodeItemAdapter(media.episodes)
- recyclerEpisodes.adapter = adapterRecEpisodes
-
- // episodes count
- binding.textEpisodesOrRuntime.text = resources.getQuantityString(
- R.plurals.text_episodes_count,
- media.info.episodesCount,
- media.info.episodesCount
- )
-
// get next episode
nextEpisode = if (media.episodes.firstOrNull{ !it.watched } != null) {
media.episodes.first{ !it.watched }
@@ -109,28 +114,44 @@ class MediaFragment(private val mediaId: Int) : Fragment() {
}
// title is the next episodes title
- textTitle.text = nextEpisode.title
+ binding.textTitle.text = nextEpisode.title
+
+ // episodes count
+ binding.textEpisodesOrRuntime.text = resources.getQuantityString(
+ R.plurals.text_episodes_count,
+ media.info.episodesCount,
+ media.info.episodesCount
+ )
+
+ // episodes
+ fragments.add(MediaFragmentEpisodes())
+ pagerAdapter.notifyDataSetChanged()
} else if (media.type == MediaType.MOVIE) {
- recyclerEpisodes.visibility = View.GONE
if (tmdb.runtime > 0) {
- textEpisodesOrRuntime.text = resources.getQuantityString(
+ binding.textEpisodesOrRuntime.text = resources.getQuantityString(
R.plurals.text_runtime,
tmdb.runtime,
tmdb.runtime
)
} else {
- textEpisodesOrRuntime.visibility = View.GONE
+ binding.textEpisodesOrRuntime.visibility = View.GONE
}
}
- frameLoading.visibility = View.GONE // hide loading indicator
+ // if has similar titles
+ if (media.info.similar.isNotEmpty()) {
+ fragments.add(MediaFragmentSimilar())
+ pagerAdapter.notifyDataSetChanged()
+ }
+
+ binding.frameLoading.visibility = View.GONE // hide loading indicator
}
- private fun initActions() {
+ private fun initActions() = with(model) {
binding.buttonPlay.setOnClickListener {
when (media.type) {
- MediaType.MOVIE -> playStream(media.episodes.first())
+ MediaType.MOVIE -> playEpisode(media.episodes.first())
MediaType.TVSHOW -> playEpisode(nextEpisode)
else -> Log.e(javaClass.name, "Wrong Type: $media.type")
}
@@ -152,30 +173,26 @@ class MediaFragment(private val mediaId: Int) : Fragment() {
(it as HomeFragment).updateMyListMedia()
}
}
-
- // set onItemClick only in adapter is initialized
- if (this::adapterRecEpisodes.isInitialized) {
- adapterRecEpisodes.onImageClick = { _, position ->
- playEpisode(media.episodes[position])
- }
- }
}
+ /**
+ * play the current episode
+ * TODO this is also used in MediaFragmentEpisode, we should only have on implementation
+ */
private fun playEpisode(ep: Episode) {
- playStream(ep)
+ (activity as MainActivity).startPlayer(model.media.id, ep.id)
+ Log.d(javaClass.name, "Started Player with episodeId: ${ep.id}")
- // update nextEpisode
- nextEpisode = if (media.episodes.firstOrNull{ !it.watched } != null) {
- media.episodes.first{ !it.watched }
- } else {
- media.episodes.first()
- }
- binding.textTitle.text = nextEpisode.title
+ model.updateNextEpisode(ep) // set the correct next episode
}
- private fun playStream(ep: Episode) {
- Log.d(javaClass.name, "Starting Player with mediaId: ${media.id}")
- (activity as MainActivity).startPlayer(media.id, ep.id)
+ /**
+ * A simple pager adapter
+ */
+ private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
+ override fun getItemCount(): Int = fragments.size
+
+ override fun createFragment(position: Int): Fragment = fragments[position]
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragmentEpisodes.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragmentEpisodes.kt
new file mode 100644
index 0000000..78f480f
--- /dev/null
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragmentEpisodes.kt
@@ -0,0 +1,61 @@
+package org.mosad.teapod.ui.activity.main.fragments
+
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import org.mosad.teapod.ui.activity.main.MainActivity
+import org.mosad.teapod.ui.activity.main.viewmodel.MediaFragmentViewModel
+import org.mosad.teapod.databinding.FragmentMediaEpisodesBinding
+import org.mosad.teapod.util.Episode
+import org.mosad.teapod.util.adapter.EpisodeItemAdapter
+
+class MediaFragmentEpisodes : Fragment() {
+
+ private lateinit var binding: FragmentMediaEpisodesBinding
+ private lateinit var adapterRecEpisodes: EpisodeItemAdapter
+
+ private val model: MediaFragmentViewModel by activityViewModels()
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ binding = FragmentMediaEpisodesBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ adapterRecEpisodes = EpisodeItemAdapter(model.media.episodes)
+ binding.recyclerEpisodes.adapter = adapterRecEpisodes
+
+ // set onItemClick only in adapter is initialized
+ if (this::adapterRecEpisodes.isInitialized) {
+ adapterRecEpisodes.onImageClick = { _, position ->
+ playEpisode(model.media.episodes[position])
+ }
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+
+ // if adapterRecEpisodes is initialized, update the watched state for the episodes
+ if (this::adapterRecEpisodes.isInitialized) {
+ model.media.episodes.forEachIndexed { index, episode ->
+ adapterRecEpisodes.updateWatchedState(episode.watched, index)
+ }
+ adapterRecEpisodes.notifyDataSetChanged()
+ }
+ }
+
+ private fun playEpisode(ep: Episode) {
+ (activity as MainActivity).startPlayer(model.media.id, ep.id)
+ Log.d(javaClass.name, "Started Player with episodeId: ${ep.id}")
+
+ model.updateNextEpisode(ep) // set the correct next episode
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragmentSimilar.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragmentSimilar.kt
new file mode 100644
index 0000000..db6d519
--- /dev/null
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/MediaFragmentSimilar.kt
@@ -0,0 +1,41 @@
+package org.mosad.teapod.ui.activity.main.fragments
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import org.mosad.teapod.databinding.FragmentMediaSimilarBinding
+import org.mosad.teapod.ui.activity.main.viewmodel.MediaFragmentViewModel
+import org.mosad.teapod.util.adapter.MediaItemAdapter
+import org.mosad.teapod.util.decoration.MediaItemDecoration
+import org.mosad.teapod.util.showFragment
+
+class MediaFragmentSimilar : Fragment() {
+
+ private lateinit var binding: FragmentMediaSimilarBinding
+ private val model: MediaFragmentViewModel by activityViewModels()
+
+ private lateinit var adapterSimilar: MediaItemAdapter
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ binding = FragmentMediaSimilarBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ adapterSimilar = MediaItemAdapter(model.media.info.similar)
+ binding.recyclerMediaSimilar.adapter = adapterSimilar
+ binding.recyclerMediaSimilar.addItemDecoration(MediaItemDecoration(9))
+
+ // set onItemClick only in adapter is initialized
+ if (this::adapterSimilar.isInitialized) {
+ adapterSimilar.onItemClick = { mediaId, _ ->
+ activity?.showFragment(MediaFragment(mediaId))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/mosad/teapod/activity/main/fragments/SearchFragment.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/SearchFragment.kt
similarity index 95%
rename from app/src/main/java/org/mosad/teapod/activity/main/fragments/SearchFragment.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/SearchFragment.kt
index 69ea9c4..57c43b1 100644
--- a/app/src/main/java/org/mosad/teapod/activity/main/fragments/SearchFragment.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/SearchFragment.kt
@@ -1,4 +1,4 @@
-package org.mosad.teapod.activity.main.fragments
+package org.mosad.teapod.ui.activity.main.fragments
import android.os.Bundle
import android.view.LayoutInflater
@@ -7,7 +7,6 @@ import android.view.ViewGroup
import android.widget.SearchView
import androidx.fragment.app.Fragment
import kotlinx.coroutines.*
-import org.mosad.teapod.activity.main.MainActivity
import org.mosad.teapod.databinding.FragmentSearchBinding
import org.mosad.teapod.parser.AoDParser
import org.mosad.teapod.util.decoration.MediaItemDecoration
diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/main/viewmodel/MediaFragmentViewModel.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/viewmodel/MediaFragmentViewModel.kt
new file mode 100644
index 0000000..c2ba21d
--- /dev/null
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/viewmodel/MediaFragmentViewModel.kt
@@ -0,0 +1,48 @@
+package org.mosad.teapod.ui.activity.main.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import org.mosad.teapod.parser.AoDParser
+import org.mosad.teapod.util.*
+import org.mosad.teapod.util.DataTypes.MediaType
+
+/**
+ * handle media, next ep and tmdb
+ */
+class MediaFragmentViewModel(application: Application) : AndroidViewModel(application) {
+
+ var media = Media(-1, "", MediaType.OTHER)
+ internal set
+ var nextEpisode = Episode()
+ internal set
+ var tmdb = TMDBResponse()
+ internal set
+
+ /**
+ * set media, tmdb and nextEpisode
+ */
+ suspend fun load(mediaId: Int) {
+ media = AoDParser.getMediaById(mediaId)
+ tmdb = TMDBApiController().search(media.info.title, media.type)
+
+ if (media.type == MediaType.TVSHOW) {
+ nextEpisode = if (media.episodes.firstOrNull{ !it.watched } != null) {
+ media.episodes.first{ !it.watched }
+ } else {
+ media.episodes.first()
+ }
+ }
+ }
+
+ /**
+ * get the next episode based on episode number (the true next episode)
+ * if no matching is found, use first episode
+ */
+ fun updateNextEpisode(currentEp: Episode) {
+ if (media.type == MediaType.MOVIE) return // return if movie
+
+ nextEpisode = media.episodes.firstOrNull{ it.number > currentEp.number }
+ ?: media.episodes.first()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/mosad/teapod/activity/onboarding/OnLoginFragment.kt b/app/src/main/java/org/mosad/teapod/ui/activity/onboarding/OnLoginFragment.kt
similarity index 97%
rename from app/src/main/java/org/mosad/teapod/activity/onboarding/OnLoginFragment.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/onboarding/OnLoginFragment.kt
index b60646e..f65ab30 100644
--- a/app/src/main/java/org/mosad/teapod/activity/onboarding/OnLoginFragment.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/onboarding/OnLoginFragment.kt
@@ -1,4 +1,4 @@
-package org.mosad.teapod.activity.onboarding
+package org.mosad.teapod.ui.activity.onboarding
import android.os.Bundle
import android.view.LayoutInflater
diff --git a/app/src/main/java/org/mosad/teapod/activity/onboarding/OnWelcomeFragment.kt b/app/src/main/java/org/mosad/teapod/ui/activity/onboarding/OnWelcomeFragment.kt
similarity index 95%
rename from app/src/main/java/org/mosad/teapod/activity/onboarding/OnWelcomeFragment.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/onboarding/OnWelcomeFragment.kt
index 28e221c..b51e6ed 100644
--- a/app/src/main/java/org/mosad/teapod/activity/onboarding/OnWelcomeFragment.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/onboarding/OnWelcomeFragment.kt
@@ -1,4 +1,4 @@
-package org.mosad.teapod.activity.onboarding
+package org.mosad.teapod.ui.activity.onboarding
import android.os.Bundle
import android.view.LayoutInflater
diff --git a/app/src/main/java/org/mosad/teapod/activity/onboarding/OnboardingActivity.kt b/app/src/main/java/org/mosad/teapod/ui/activity/onboarding/OnboardingActivity.kt
similarity index 92%
rename from app/src/main/java/org/mosad/teapod/activity/onboarding/OnboardingActivity.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/onboarding/OnboardingActivity.kt
index 4493b82..8087e98 100644
--- a/app/src/main/java/org/mosad/teapod/activity/onboarding/OnboardingActivity.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/onboarding/OnboardingActivity.kt
@@ -1,4 +1,4 @@
-package org.mosad.teapod.activity.onboarding
+package org.mosad.teapod.ui.activity.onboarding
import android.content.Intent
import android.os.Bundle
@@ -8,7 +8,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayoutMediator
-import org.mosad.teapod.activity.main.MainActivity
+import org.mosad.teapod.ui.activity.main.MainActivity
import org.mosad.teapod.databinding.ActivityOnboardingBinding
class OnboardingActivity : AppCompatActivity() {
@@ -67,8 +67,7 @@ class OnboardingActivity : AppCompatActivity() {
}
/**
- * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
- * sequence.
+ * A simple pager adapter
*/
private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
override fun getItemCount(): Int = fragments.size
diff --git a/app/src/main/java/org/mosad/teapod/activity/player/PlayerActivity.kt b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerActivity.kt
similarity index 99%
rename from app/src/main/java/org/mosad/teapod/activity/player/PlayerActivity.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerActivity.kt
index d3e66ec..0999106 100644
--- a/app/src/main/java/org/mosad/teapod/activity/player/PlayerActivity.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerActivity.kt
@@ -1,4 +1,4 @@
-package org.mosad.teapod.activity.player
+package org.mosad.teapod.ui.activity.player
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/app/src/main/java/org/mosad/teapod/activity/player/PlayerViewModel.kt b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt
similarity index 99%
rename from app/src/main/java/org/mosad/teapod/activity/player/PlayerViewModel.kt
rename to app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt
index 8a1eb89..4efb7c4 100644
--- a/app/src/main/java/org/mosad/teapod/activity/player/PlayerViewModel.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt
@@ -1,4 +1,4 @@
-package org.mosad.teapod.activity.player
+package org.mosad.teapod.ui.activity.player
import android.app.Application
import android.net.Uri
diff --git a/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt b/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt
index 259305f..cb51deb 100644
--- a/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/components/EpisodesListPlayer.kt
@@ -6,7 +6,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.LinearLayout
import org.mosad.teapod.databinding.PlayerEpisodesListBinding
-import org.mosad.teapod.activity.player.PlayerViewModel
+import org.mosad.teapod.ui.activity.player.PlayerViewModel
import org.mosad.teapod.util.adapter.PlayerEpisodeItemAdapter
class EpisodesListPlayer @JvmOverloads constructor(
diff --git a/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt b/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt
index a1b1d92..404ba7e 100644
--- a/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt
+++ b/app/src/main/java/org/mosad/teapod/ui/components/LanguageSettingsPlayer.kt
@@ -13,7 +13,7 @@ import android.widget.TextView
import androidx.core.view.children
import org.mosad.teapod.R
import org.mosad.teapod.databinding.PlayerLanguageSettingsBinding
-import org.mosad.teapod.activity.player.PlayerViewModel
+import org.mosad.teapod.ui.activity.player.PlayerViewModel
import java.util.*
class LanguageSettingsPlayer @JvmOverloads constructor(
diff --git a/app/src/main/java/org/mosad/teapod/util/DataTypes.kt b/app/src/main/java/org/mosad/teapod/util/DataTypes.kt
index bc27719..cf936bd 100644
--- a/app/src/main/java/org/mosad/teapod/util/DataTypes.kt
+++ b/app/src/main/java/org/mosad/teapod/util/DataTypes.kt
@@ -55,6 +55,7 @@ data class Media(
fun getEpisodeById(id: Int) = episodes.first { it.id == id }
}
+// TODO all val?
data class Info(
var title: String = "",
var posterUrl: String = "",
@@ -62,7 +63,8 @@ data class Info(
var description: String = "",
var year: Int = 0,
var age: Int = 0,
- var episodesCount: Int = 0
+ var episodesCount: Int = 0,
+ var similar: List = listOf()
)
/**
@@ -96,6 +98,7 @@ data class Stream(
/**
* this class is used for tmdb responses
+ * TODO why is runtime var?
*/
data class TMDBResponse(
val id: Int = 0,
diff --git a/app/src/main/res/layout/activity_player.xml b/app/src/main/res/layout/activity_player.xml
index 050d442..5a8f6bd 100644
--- a/app/src/main/res/layout/activity_player.xml
+++ b/app/src/main/res/layout/activity_player.xml
@@ -7,7 +7,7 @@
android:layout_height="match_parent"
android:background="#000000"
android:keepScreenOn="true"
- tools:context=".activity.player.PlayerActivity">
+ tools:context=".ui.activity.player.PlayerActivity">
+ tools:context=".ui.activity.main.fragments.AboutFragment">
+ tools:context=".ui.activity.main.fragments.AccountFragment">
+ tools:context=".ui.activity.main.fragments.HomeFragment">
+ tools:context=".ui.activity.main.fragments.LibraryFragment">
+ tools:context=".ui.activity.main.fragments.MediaFragment">
-
+ app:tabMode="scrollable"
+ app:tabGravity="start"
+ app:tabSelectedTextColor="?textPrimary"
+ app:tabTextColor="?textSecondary" />
+
+
+
diff --git a/app/src/main/res/layout/fragment_media_episodes.xml b/app/src/main/res/layout/fragment_media_episodes.xml
new file mode 100644
index 0000000..89b3e68
--- /dev/null
+++ b/app/src/main/res/layout/fragment_media_episodes.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_media_similar.xml b/app/src/main/res/layout/fragment_media_similar.xml
new file mode 100644
index 0000000..67399e4
--- /dev/null
+++ b/app/src/main/res/layout/fragment_media_similar.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
index 02fd428..6427560 100644
--- a/app/src/main/res/layout/fragment_search.xml
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -5,7 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?themePrimary"
- tools:context=".activity.main.fragments.SearchFragment">
+ tools:context=".ui.activity.main.fragments.SearchFragment">
diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml
index d710961..c958865 100644
--- a/app/src/main/res/values-de-rDE/strings.xml
+++ b/app/src/main/res/values-de-rDE/strings.xml
@@ -27,6 +27,7 @@
- %d Minute
- %d Minuten
+ Ähnliche Titel
Flg. %1$d %2$s
Flg. %1$d %2$s (OmU)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1c6411c..fe2da5e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -34,6 +34,7 @@
- %d Minute
- %d Minutes
+ Similar titles
Ep. %1$d %2$s
Ep. %1$d %2$s (Sub)
episode poster