Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 20 additions & 25 deletions app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import com.github.libretube.constants.PreferenceKeys.HOME_TAB_CONTENT
import com.github.libretube.databinding.FragmentHomeBinding
import com.github.libretube.db.DatabaseHelper
import com.github.libretube.db.obj.PlaylistBookmark
import com.github.libretube.helpers.LocaleHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.activities.SettingsActivity
import com.github.libretube.ui.adapters.CarouselPlaylist
Expand Down Expand Up @@ -114,24 +113,9 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
}

binding.trendingRegion.setOnClickListener {
val currentRegionPref = PreferenceHelper.getTrendingRegion(requireContext())

val countries = LocaleHelper.getAvailableCountries()
var selected = countries.indexOfFirst { it.code == currentRegionPref }
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.region)
.setSingleChoiceItems(
countries.map { it.name }.toTypedArray(),
selected
) { _, checked ->
selected = checked
}
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.okay) { _, _ ->
PreferenceHelper.putString(PreferenceKeys.REGION, countries[selected].code)
fetchHomeFeed()
}
.show()
TrendsFragment.showChangeRegionDialog(requireContext()) {
fetchHomeFeed()
}
}

val trendingCategories = MediaServiceRepository.instance.getTrendingCategories()
Expand Down Expand Up @@ -178,8 +162,13 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
override fun onResume() {
super.onResume()

// Avoid re-fetching when re-entering the screen if it was loaded successfully
if (homeViewModel.loadedSuccessfully.value == false) {
// Avoid re-fetching when re-entering the screen if it was loaded successfully, except when
// the value of trending region has changed
val isTrendingRegionChanged = homeViewModel.trending.value?.let {
it.second.region != PreferenceHelper.getTrendingRegion(requireContext())
} == true

if (homeViewModel.loadedSuccessfully.value == false || isTrendingRegionChanged) {
fetchHomeFeed()
}
}
Expand All @@ -202,16 +191,20 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
)
}

private fun showTrending(trends: Pair<TrendingCategory, List<StreamItem>>?) {
private fun showTrending(trends: Pair<TrendingCategory, TrendsViewModel.TrendingStreams>?) {
if (trends == null) return
val (category, streamItems) = trends
val (category, trendingStreams) = trends

// cache the loaded trends in the [TrendsViewModel] so that the trends don't need to be
// reloaded there
trendsViewModel.setStreamsForCategory(category, streamItems)
val region = PreferenceHelper.getTrendingRegion(requireContext())
trendsViewModel.setStreamsForCategory(
category,
TrendsViewModel.TrendingStreams(region, trendingStreams.streams)
)

makeVisible(binding.trendingRV, binding.trendingTV)
trendingAdapter.submitList(streamItems.take(10))
trendingAdapter.submitList(trendingStreams.streams.take(10))
}

private fun showFeed(streamItems: List<StreamItem>?) {
Expand Down Expand Up @@ -270,6 +263,7 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
private fun showLoading() {
binding.progress.isVisible = !binding.refresh.isRefreshing
binding.nothingHere.isVisible = false
binding.scroll.alpha = 0.3f
}

private fun hideLoading() {
Expand All @@ -282,6 +276,7 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
} else {
showNothingHere()
}
binding.scroll.alpha = 1.0f
}

private fun showNothingHere() {
Expand Down
104 changes: 85 additions & 19 deletions app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.github.libretube.ui.fragments

import android.content.Intent
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
import android.view.View
Expand All @@ -18,13 +18,16 @@ import com.github.libretube.R
import com.github.libretube.api.MediaServiceRepository
import com.github.libretube.api.TrendingCategory
import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.FragmentTrendsBinding
import com.github.libretube.databinding.FragmentTrendsContentBinding
import com.github.libretube.extensions.serializable
import com.github.libretube.ui.activities.SettingsActivity
import com.github.libretube.helpers.LocaleHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.adapters.VideoCardsAdapter
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
import com.github.libretube.ui.models.TrendsViewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.launch
Expand All @@ -37,37 +40,81 @@ class TrendsFragment : Fragment(R.layout.fragment_trends) {
_binding = FragmentTrendsBinding.bind(view)

val categories = MediaServiceRepository.instance.getTrendingCategories()
binding.pager.adapter = TrendsAdapter(this, categories)

val adapter = TrendsAdapter(this, categories)
binding.pager.adapter = adapter

if (categories.size <= 1) binding.tabLayout.isGone = true
TabLayoutMediator(binding.tabLayout, binding.pager) { tab, position ->
val category = categories[position]
tab.text = getString(category.titleRes)
}.attach()

binding.trendingRegion.setOnClickListener {
showChangeRegionDialog(requireContext()) {
adapter.getFragmentAt(binding.pager.currentItem)?.also {
it.refreshTrending()
}
}
}
}

companion object {
fun showChangeRegionDialog(context: Context, onPositiveButtonClick: () -> Unit) {
val currentRegionPref = PreferenceHelper.getTrendingRegion(context)

val countries = LocaleHelper.getAvailableCountries()
var selected = countries.indexOfFirst { it.code == currentRegionPref }
MaterialAlertDialogBuilder(context)
.setTitle(R.string.region)
.setSingleChoiceItems(
countries.map { it.name }.toTypedArray(),
selected
) { _, checked ->
selected = checked
}
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.okay) { _, _ ->
PreferenceHelper.putString(PreferenceKeys.REGION, countries[selected].code)
onPositiveButtonClick()
}
.show()
}
}
}

class TrendsAdapter(fragment: Fragment, private val categories: List<TrendingCategory>) :
FragmentStateAdapter(fragment) {
private val fragments: MutableList<TrendsContentFragment?> =
MutableList(categories.size) { null }

override fun createFragment(position: Int): Fragment {
return TrendsContentFragment().apply {
val trendContentFragment = TrendsContentFragment().apply {
arguments = bundleOf(
IntentData.category to categories[position]
)
}
fragments[position] = trendContentFragment
return trendContentFragment
}

override fun getItemCount(): Int {
return categories.size
}

fun getFragmentAt(position: Int): TrendsContentFragment? {
return fragments[position]
}
}

class TrendsContentFragment : DynamicLayoutManagerFragment(R.layout.fragment_trends_content) {
private var _binding: FragmentTrendsContentBinding? = null
private val binding get() = _binding!!
private val viewModel: TrendsViewModel by activityViewModels()

private var _category: TrendingCategory? = null
private val category get() = _category!!

override fun setLayoutManagers(gridItems: Int) {
_binding?.recview?.layoutManager = GridLayoutManager(context, gridItems)
}
Expand All @@ -76,7 +123,7 @@ class TrendsContentFragment : DynamicLayoutManagerFragment(R.layout.fragment_tre
_binding = FragmentTrendsContentBinding.bind(view)
super.onViewCreated(view, savedInstanceState)

val category = requireArguments()
_category = requireArguments()
.serializable<TrendingCategory>(IntentData.category)!!

val adapter = VideoCardsAdapter()
Expand All @@ -87,24 +134,29 @@ class TrendsContentFragment : DynamicLayoutManagerFragment(R.layout.fragment_tre
val videos = categoryMap[category]
if (videos == null) return@observe

binding.homeRefresh.isRefreshing = false
binding.progressBar.isGone = true

adapter.submitList(videos)

if (videos.isEmpty()) {
Snackbar.make(binding.root, R.string.change_region, Snackbar.LENGTH_LONG)
.setAction(R.string.settings) {
val settingsIntent = Intent(context, SettingsActivity::class.java)
startActivity(settingsIntent)
toggleLoadingIndicator(false)

adapter.submitList(videos.streams)

val trendingRegion = PreferenceHelper.getTrendingRegion(requireContext())
if (videos.streams.isEmpty() && (videos.region == trendingRegion)) {
Snackbar.make(
requireParentFragment().requireView(),
R.string.change_region,
Snackbar.LENGTH_LONG
)
.setAction(R.string.change) {
TrendsFragment.showChangeRegionDialog(requireContext()) {
refreshTrending()
}
}
.show()
}
}

binding.homeRefresh.isEnabled = true
binding.homeRefresh.setOnRefreshListener {
viewModel.fetchTrending(requireContext(), category)
refreshTrending()
}

binding.recview.addOnScrollListener(object : RecyclerView.OnScrollListener() {
Expand All @@ -117,10 +169,13 @@ class TrendsContentFragment : DynamicLayoutManagerFragment(R.layout.fragment_tre
viewModel.fetchTrending(requireContext(), category)
lifecycleScope.launch {
// every time the user navigates to the fragment for the selected category,
// fetch the trends for the selected category if they're not yet cached
// fetch the trends for the selected category if they're not yet cached or if the value
// for trending region has been changed
repeatOnLifecycle(Lifecycle.State.RESUMED) {
if (viewModel.trendingVideos.value.orEmpty()[category].isNullOrEmpty()) {
viewModel.fetchTrending(requireContext(), category)
val trendingRegion = PreferenceHelper.getTrendingRegion(requireContext())
val trendingVideos = viewModel.trendingVideos.value.orEmpty()[category]
if (trendingVideos == null || (trendingVideos.region != trendingRegion)) {
refreshTrending()
}
}
}
Expand All @@ -136,4 +191,15 @@ class TrendsContentFragment : DynamicLayoutManagerFragment(R.layout.fragment_tre
super.onDestroyView()
_binding = null
}

private fun toggleLoadingIndicator(show: Boolean) {
binding.recview.alpha = if (show) 0.3f else 1.0f
binding.progressBar.isGone = !show
if (!show) binding.homeRefresh.isRefreshing = false
}

fun refreshTrending() {
toggleLoadingIndicator(true)
viewModel.fetchTrending(requireContext(), category)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import kotlinx.coroutines.withContext
class HomeViewModel : ViewModel() {
private val hideWatched get() = PreferenceHelper.getBoolean(HIDE_WATCHED_FROM_FEED, false)

val trending: MutableLiveData<Pair<TrendingCategory, List<StreamItem>>> = MutableLiveData(null)
val trending: MutableLiveData<Pair<TrendingCategory, TrendsViewModel.TrendingStreams>> =
MutableLiveData(null)
val feed: MutableLiveData<List<StreamItem>> = MutableLiveData(null)
val bookmarks: MutableLiveData<List<PlaylistBookmark>> = MutableLiveData(null)
val playlists: MutableLiveData<List<Playlists>> = MutableLiveData(null)
Expand Down Expand Up @@ -82,7 +83,12 @@ class HomeViewModel : ViewModel() {

runSafely(
onSuccess = { videos ->
trending.updateIfChanged(Pair(category, videos))
trending.updateIfChanged(
Pair(
category,
TrendsViewModel.TrendingStreams(region, videos)
)
)
},
ioBlock = {
MediaServiceRepository.instance.getTrending(region, category)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class TrendsViewModel : ViewModel() {
val trendingVideos = MutableLiveData<Map<TrendingCategory, List<StreamItem>>>()
data class TrendingStreams(val region: String, val streams: List<StreamItem>)

val trendingVideos = MutableLiveData<Map<TrendingCategory, TrendingStreams>>()
var recyclerViewState: Parcelable? = null

private var currentJob: Job? = null
Expand All @@ -34,15 +36,15 @@ class TrendsViewModel : ViewModel() {
val response = withContext(Dispatchers.IO) {
MediaServiceRepository.instance.getTrending(region, category)
}
setStreamsForCategory(category, response)
setStreamsForCategory(category, TrendingStreams(region, response))
} catch (e: Exception) {
Log.e(TAG(), e.stackTraceToString())
context.toastFromMainDispatcher(e.localizedMessage.orEmpty())
}
}
}

fun setStreamsForCategory(category: TrendingCategory, streams: List<StreamItem>) {
fun setStreamsForCategory(category: TrendingCategory, streams: TrendingStreams) {
val newState = trendingVideos.value.orEmpty()
.toMutableMap()
.apply {
Expand Down
20 changes: 11 additions & 9 deletions app/src/main/res/layout/fragment_home.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.google.android.material.loadingindicator.LoadingIndicator
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
tools:visibility="gone" />

<com.github.libretube.ui.views.CustomSwipeToRefresh
android:id="@+id/refresh"
android:layout_width="match_parent"
Expand Down Expand Up @@ -109,7 +102,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:icon="@drawable/ic_frame" />
app:icon="@drawable/ic_frame"
android:tooltipText="@string/category"/>

<com.google.android.material.button.MaterialButton
android:id="@+id/trending_region"
Expand All @@ -118,7 +112,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
app:icon="@drawable/ic_region" />
app:icon="@drawable/ic_region"
android:tooltipText="@string/region"/>

</LinearLayout>

Expand Down Expand Up @@ -239,4 +234,11 @@
android:text="@string/change_instance" />
</LinearLayout>

<com.google.android.material.loadingindicator.LoadingIndicator
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
tools:visibility="gone" />

</FrameLayout>
Loading
Loading