Merge pull request #1895 from Bnyro/master

Improve the new home layout
This commit is contained in:
Bnyro 2022-11-18 16:24:21 +01:00 committed by GitHub
commit 594017f3a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 175 additions and 181 deletions

View File

@ -3,6 +3,8 @@ package com.github.libretube.api
import android.content.Context
import android.util.Log
import com.github.libretube.R
import com.github.libretube.api.obj.StreamItem
import com.github.libretube.api.obj.Subscription
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.db.obj.LocalSubscription
@ -131,4 +133,28 @@ object SubscriptionHelper {
val localSubscriptions = getLocalSubscriptions()
return localSubscriptions.joinToString(",") { it.channelId }
}
suspend fun getSubscriptions(): List<Subscription> {
return if (PreferenceHelper.getToken() != "") {
RetrofitInstance.authApi.subscriptions(
PreferenceHelper.getToken()
)
} else {
RetrofitInstance.authApi.unauthenticatedSubscriptions(
getFormattedLocalSubscriptions()
)
}
}
suspend fun getFeed(): List<StreamItem> {
return if (PreferenceHelper.getToken() != "") {
RetrofitInstance.authApi.getFeed(
PreferenceHelper.getToken()
)
} else {
RetrofitInstance.authApi.getUnauthenticatedFeed(
getFormattedLocalSubscriptions()
)
}
}
}

View File

@ -60,7 +60,7 @@ class VideosAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideosViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return when {
forceMode in listOf(ForceMode.TRENDING, ForceMode.RELATED) -> VideosViewHolder(TrendingRowBinding.inflate(layoutInflater, parent, false))
forceMode in listOf(ForceMode.TRENDING, ForceMode.RELATED, ForceMode.HOME) -> VideosViewHolder(TrendingRowBinding.inflate(layoutInflater, parent, false))
forceMode == ForceMode.CHANNEL -> VideosViewHolder(VideoRowBinding.inflate(layoutInflater, parent, false))
PreferenceHelper.getBoolean(
PreferenceKeys.ALTERNATIVE_VIDEOS_LAYOUT,
@ -83,11 +83,14 @@ class VideosAdapter(
// Trending layout
holder.trendingRowBinding?.apply {
if (forceMode == ForceMode.RELATED) {
val params = root.layoutParams
params.width = (180).toDp(root.context.resources).toInt()
root.layoutParams = params
// set a fixed width for better visuals
val params = root.layoutParams
when (forceMode) {
ForceMode.RELATED -> params.width = (180).toDp(root.context.resources).toInt()
ForceMode.HOME -> params.width = (250).toDp(root.context.resources).toInt()
else -> {}
}
root.layoutParams = params
textViewTitle.text = video.title
textViewChannel.text =
@ -168,7 +171,8 @@ class VideosAdapter(
TRENDING,
ROW,
CHANNEL,
RELATED
RELATED,
HOME
}
fun getLayout(context: Context): LayoutManager {

View File

@ -4,25 +4,27 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.SubscriptionHelper
import com.github.libretube.databinding.FragmentHomeBinding
import com.github.libretube.extensions.toastFromMainThread
import com.github.libretube.ui.adapters.PlaylistsAdapter
import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.models.HomeModel
import com.github.libretube.ui.extensions.withMaxSize
import com.github.libretube.util.LocaleHelper
import com.github.libretube.util.PreferenceHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class HomeFragment : BaseFragment() {
private lateinit var binding: FragmentHomeBinding
private val viewModel: HomeModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
@ -48,52 +50,85 @@ class HomeFragment : BaseFragment() {
findNavController().navigate(R.id.libraryFragment)
}
binding.refresh.setOnRefreshListener {
binding.refresh.isRefreshing = true
lifecycleScope.launch(Dispatchers.IO) {
fetchHome(LocaleHelper.getTrendingRegion(requireContext()))
}
}
lifecycleScope.launch(Dispatchers.IO) {
viewModel.fetchHome(requireContext(), LocaleHelper.getTrendingRegion(requireContext()))
}
viewModel.feed.observe(viewLifecycleOwner) {
binding.featuredTV.visibility = View.VISIBLE
binding.featuredRV.visibility = View.VISIBLE
binding.progress.visibility = View.GONE
binding.featuredRV.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
binding.featuredRV.adapter = VideosAdapter(
it.toMutableList(),
childFragmentManager,
forceMode = VideosAdapter.Companion.ForceMode.RELATED
)
}
viewModel.trending.observe(viewLifecycleOwner) {
if (it.isEmpty()) return@observe
binding.trendingTV.visibility = View.VISIBLE
binding.trendingRV.visibility = View.VISIBLE
binding.progress.visibility = View.GONE
binding.trendingRV.layoutManager = GridLayoutManager(context, 2)
binding.trendingRV.adapter = VideosAdapter(
it.toMutableList(),
childFragmentManager,
forceMode = VideosAdapter.Companion.ForceMode.TRENDING
)
}
viewModel.playlists.observe(viewLifecycleOwner) {
if (it.isEmpty()) return@observe
binding.playlistsRV.visibility = View.VISIBLE
binding.playlistsTV.visibility = View.VISIBLE
binding.progress.visibility = View.GONE
binding.playlistsRV.layoutManager = LinearLayoutManager(context)
binding.playlistsRV.adapter = PlaylistsAdapter(it.toMutableList(), childFragmentManager)
binding.playlistsRV.adapter?.registerAdapterDataObserver(object :
RecyclerView.AdapterDataObserver() {
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
super.onItemRangeRemoved(positionStart, itemCount)
if (itemCount == 0) {
binding.playlistsRV.visibility = View.GONE
binding.playlistsTV.visibility = View.GONE
}
}
})
fetchHome(LocaleHelper.getTrendingRegion(requireContext()))
}
}
private suspend fun fetchHome(trendingRegion: String) {
val token = PreferenceHelper.getToken()
runOrError {
val feed = SubscriptionHelper.getFeed().withMaxSize(20)
if (feed.isEmpty()) return@runOrError
runOnUiThread {
makeVisible(binding.featuredRV, binding.featuredTV)
binding.featuredRV.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
binding.featuredRV.adapter = VideosAdapter(
feed.toMutableList(),
childFragmentManager,
forceMode = VideosAdapter.Companion.ForceMode.HOME
)
}
}
runOrError {
val trending = RetrofitInstance.api.getTrending(trendingRegion).withMaxSize(10)
if (trending.isEmpty()) return@runOrError
runOnUiThread {
makeVisible(binding.trendingRV, binding.trendingTV)
binding.trendingRV.layoutManager = GridLayoutManager(context, 2)
binding.trendingRV.adapter = VideosAdapter(
trending.toMutableList(),
childFragmentManager,
forceMode = VideosAdapter.Companion.ForceMode.TRENDING
)
}
}
runOrError {
val playlists = RetrofitInstance.authApi.getUserPlaylists(token).withMaxSize(20)
if (playlists.isEmpty()) return@runOrError
runOnUiThread {
makeVisible(binding.playlistsRV, binding.playlistsTV)
binding.playlistsRV.layoutManager = LinearLayoutManager(context)
binding.playlistsRV.adapter = PlaylistsAdapter(playlists.toMutableList(), childFragmentManager)
binding.playlistsRV.adapter?.registerAdapterDataObserver(object :
RecyclerView.AdapterDataObserver() {
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
super.onItemRangeRemoved(positionStart, itemCount)
if (itemCount == 0) {
binding.playlistsRV.visibility = View.GONE
binding.playlistsTV.visibility = View.GONE
}
}
})
}
}
}
private fun runOrError(action: suspend () -> Unit) {
lifecycleScope.launch(Dispatchers.IO) {
try {
action.invoke()
} catch (e: Exception) {
e.localizedMessage?.let { context?.toastFromMainThread(it) }
}
}
}
private fun makeVisible(vararg views: View) {
views.forEach {
it.visibility = View.VISIBLE
}
binding.progress.visibility = View.GONE
binding.scroll.visibility = View.VISIBLE
binding.refresh.isRefreshing = false
}
}

View File

@ -1,57 +0,0 @@
package com.github.libretube.ui.models
import android.content.Context
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.Playlists
import com.github.libretube.api.obj.StreamItem
import com.github.libretube.extensions.toastFromMainThread
import com.github.libretube.ui.extensions.withMaxSize
import com.github.libretube.util.PreferenceHelper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class HomeModel : ViewModel() {
val feed = MutableLiveData<List<StreamItem>>()
var trending = MutableLiveData<List<StreamItem>>()
val playlists = MutableLiveData<List<Playlists>>()
suspend fun fetchHome(context: Context, trendingRegion: String) {
val token = PreferenceHelper.getToken()
val appContext = context.applicationContext
runOrError(appContext) {
if (trending.value.isNullOrEmpty()) {
trending.postValue(
RetrofitInstance.api.getTrending(trendingRegion).withMaxSize(10)
)
}
}
runOrError(appContext) {
if (feed.value.isNullOrEmpty()) {
feed.postValue(
RetrofitInstance.authApi.getFeed(token).withMaxSize(20)
)
}
}
runOrError(appContext) {
if (token == "" || !playlists.value.isNullOrEmpty()) return@runOrError
playlists.postValue(
RetrofitInstance.authApi.getUserPlaylists(token).withMaxSize(20)
)
}
}
private fun runOrError(context: Context, action: suspend () -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
try {
action.invoke()
} catch (e: Exception) {
e.localizedMessage?.let { context.toastFromMainThread(it) }
}
}
}
}

View File

@ -3,8 +3,9 @@ package com.github.libretube.ui.models
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.SubscriptionHelper
import com.github.libretube.api.obj.StreamItem
import com.github.libretube.api.obj.Subscription
import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.toID
import com.github.libretube.util.PreferenceHelper
@ -17,26 +18,18 @@ class SubscriptionsViewModel : ViewModel() {
value = false
}
var videoFeed = MutableLiveData<List<com.github.libretube.api.obj.StreamItem>?>().apply {
var videoFeed = MutableLiveData<List<StreamItem>?>().apply {
value = null
}
var subscriptions = MutableLiveData<List<com.github.libretube.api.obj.Subscription>?>().apply {
var subscriptions = MutableLiveData<List<Subscription>?>().apply {
value = null
}
fun fetchFeed() {
CoroutineScope(Dispatchers.IO).launch {
val videoFeed = try {
if (PreferenceHelper.getToken() != "") {
RetrofitInstance.authApi.getFeed(
PreferenceHelper.getToken()
)
} else {
RetrofitInstance.authApi.getUnauthenticatedFeed(
SubscriptionHelper.getFormattedLocalSubscriptions()
)
}
SubscriptionHelper.getFeed()
} catch (e: Exception) {
errorResponse.postValue(true)
Log.e(TAG(), e.toString())
@ -53,15 +46,7 @@ class SubscriptionsViewModel : ViewModel() {
fun fetchSubscriptions() {
CoroutineScope(Dispatchers.IO).launch {
val subscriptions = try {
if (PreferenceHelper.getToken() != "") {
RetrofitInstance.authApi.subscriptions(
PreferenceHelper.getToken()
)
} else {
RetrofitInstance.authApi.unauthenticatedSubscriptions(
SubscriptionHelper.getFormattedLocalSubscriptions()
)
}
SubscriptionHelper.getSubscriptions()
} catch (e: Exception) {
errorResponse.postValue(true)
Log.e(TAG(), e.toString())

View File

@ -10,7 +10,6 @@ import androidx.core.app.NotificationManagerCompat
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.SubscriptionHelper
import com.github.libretube.constants.PUSH_CHANNEL_ID
import com.github.libretube.constants.PreferenceKeys
@ -82,16 +81,9 @@ class NotificationWorker(appContext: Context, parameters: WorkerParameters) :
private fun checkForNewStreams(): Boolean {
var success = true
val token = PreferenceHelper.getToken()
runBlocking {
val task = async {
if (token != "") {
RetrofitInstance.authApi.getFeed(token)
} else {
RetrofitInstance.authApi.getUnauthenticatedFeed(
SubscriptionHelper.getFormattedLocalSubscriptions()
)
}
SubscriptionHelper.getFeed()
}
// fetch the users feed
val videoFeed = try {

View File

@ -9,63 +9,72 @@
android:layout_height="match_parent"
android:layout_gravity="center" />
<ScrollView
<com.github.libretube.ui.views.CustomSwipeToRefresh
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
<ScrollView
android:id="@+id/scroll"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="10dp">
android:layout_height="wrap_content">
<TextView
android:id="@+id/featuredTV"
style="@style/HomeCategoryTitle"
android:text="@string/featured" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/featuredRV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:nestedScrollingEnabled="false"
android:visibility="gone" />
<TextView
android:id="@+id/trendingTV"
style="@style/HomeCategoryTitle"
android:text="@string/trending" />
<RelativeLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
android:orientation="vertical"
android:paddingBottom="10dp">
<TextView
android:id="@+id/featuredTV"
style="@style/HomeCategoryTitle"
android:text="@string/featured" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/trendingRV"
android:layout_width="match_parent"
android:id="@+id/featuredRV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:nestedScrollingEnabled="false"
android:visibility="gone" />
</RelativeLayout>
<TextView
android:id="@+id/trendingTV"
style="@style/HomeCategoryTitle"
android:text="@string/trending" />
<TextView
android:id="@+id/playlistsTV"
style="@style/HomeCategoryTitle"
android:text="@string/playlists" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/playlistsRV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/trendingRV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:nestedScrollingEnabled="false"
android:visibility="gone" />
</LinearLayout>
</RelativeLayout>
</ScrollView>
<TextView
android:id="@+id/playlistsTV"
style="@style/HomeCategoryTitle"
android:text="@string/playlists" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/playlistsRV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:visibility="gone" />
</LinearLayout>
</ScrollView>
</com.github.libretube.ui.views.CustomSwipeToRefresh>
</FrameLayout>