From 335bbbbd627e67702f01adf11b7f1075417772ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Sch=C3=BCller?= Date: Sun, 26 Dec 2021 13:59:46 +0000 Subject: [PATCH] feat: Overview --- app/build.gradle | 2 +- .../peertube/activity/AccountActivity.java | 17 +- .../peertube/activity/VideoListActivity.kt | 145 +++++-- .../peertube/adapter/ChannelAdapter.java | 5 +- .../adapter/MultiViewRecycleViewAdapter.kt | 110 ++++++ .../adapter/MultiViewRecyclerViewHolder.kt | 181 +++++++++ .../peertube/adapter/VideoAdapter.java | 197 ---------- .../net/schueller/peertube/model/Account.java | 131 ------- .../model/{Avatar.java => Account.kt} | 48 +-- .../net/schueller/peertube/model/Avatar.kt | 25 ++ .../model/{Category.java => Category.kt} | 26 +- .../schueller/peertube/model/CategoryVideo.kt | 25 ++ .../net/schueller/peertube/model/Channel.java | 148 ------- .../net/schueller/peertube/model/Channel.kt | 37 ++ .../peertube/model/ChannelVideo.java | 41 ++ .../net/schueller/peertube/model/Overview.kt | 26 ++ .../net/schueller/peertube/model/TagVideo.kt | 26 ++ .../net/schueller/peertube/model/Video.java | 361 ------------------ .../net/schueller/peertube/model/Video.kt | 90 +++++ .../model/{VideoList.java => VideoList.kt} | 18 +- .../model/ui/OverviewRecycleViewItem.kt | 5 + .../peertube/network/GetVideoDataService.java | 7 +- app/src/main/res/drawable/ic_globe.xml | 27 ++ app/src/main/res/drawable/ic_local.xml | 9 + app/src/main/res/drawable/ic_plus_circle.xml | 27 ++ app/src/main/res/drawable/ic_search.xml | 20 + app/src/main/res/drawable/ic_server.xml | 34 ++ .../main/res/drawable/ic_subscriptions.xml | 10 + app/src/main/res/drawable/ic_trending.xml | 20 + app/src/main/res/drawable/ic_user.xml | 20 + .../main/res/layout/item_category_title.xml | 24 ++ .../main/res/layout/item_channel_title.xml | 37 ++ app/src/main/res/layout/item_tag_title.xml | 24 ++ build.gradle | 2 +- 34 files changed, 972 insertions(+), 953 deletions(-) create mode 100644 app/src/main/java/net/schueller/peertube/adapter/MultiViewRecycleViewAdapter.kt create mode 100644 app/src/main/java/net/schueller/peertube/adapter/MultiViewRecyclerViewHolder.kt delete mode 100644 app/src/main/java/net/schueller/peertube/adapter/VideoAdapter.java delete mode 100644 app/src/main/java/net/schueller/peertube/model/Account.java rename app/src/main/java/net/schueller/peertube/model/{Avatar.java => Account.kt} (55%) create mode 100644 app/src/main/java/net/schueller/peertube/model/Avatar.kt rename app/src/main/java/net/schueller/peertube/model/{Category.java => Category.kt} (66%) create mode 100644 app/src/main/java/net/schueller/peertube/model/CategoryVideo.kt delete mode 100644 app/src/main/java/net/schueller/peertube/model/Channel.java create mode 100644 app/src/main/java/net/schueller/peertube/model/Channel.kt create mode 100644 app/src/main/java/net/schueller/peertube/model/ChannelVideo.java create mode 100644 app/src/main/java/net/schueller/peertube/model/Overview.kt create mode 100644 app/src/main/java/net/schueller/peertube/model/TagVideo.kt delete mode 100644 app/src/main/java/net/schueller/peertube/model/Video.java create mode 100644 app/src/main/java/net/schueller/peertube/model/Video.kt rename app/src/main/java/net/schueller/peertube/model/{VideoList.java => VideoList.kt} (74%) create mode 100644 app/src/main/java/net/schueller/peertube/model/ui/OverviewRecycleViewItem.kt create mode 100644 app/src/main/res/drawable/ic_globe.xml create mode 100644 app/src/main/res/drawable/ic_local.xml create mode 100644 app/src/main/res/drawable/ic_plus_circle.xml create mode 100644 app/src/main/res/drawable/ic_search.xml create mode 100644 app/src/main/res/drawable/ic_server.xml create mode 100644 app/src/main/res/drawable/ic_subscriptions.xml create mode 100644 app/src/main/res/drawable/ic_trending.xml create mode 100644 app/src/main/res/drawable/ic_user.xml create mode 100644 app/src/main/res/layout/item_category_title.xml create mode 100644 app/src/main/res/layout/item_channel_title.xml create mode 100644 app/src/main/res/layout/item_tag_title.xml diff --git a/app/build.gradle b/app/build.gradle index 7ddb367..0addeb1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -89,7 +89,7 @@ android { } buildFeatures{ - viewBinding = true + viewBinding true } } diff --git a/app/src/main/java/net/schueller/peertube/activity/AccountActivity.java b/app/src/main/java/net/schueller/peertube/activity/AccountActivity.java index 6f933cd..58fe075 100644 --- a/app/src/main/java/net/schueller/peertube/activity/AccountActivity.java +++ b/app/src/main/java/net/schueller/peertube/activity/AccountActivity.java @@ -27,13 +27,14 @@ import android.widget.TextView; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.LabelVisibilityMode; +import com.google.android.material.navigation.NavigationBarView; import com.mikepenz.iconics.IconicsDrawable; import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome; import com.squareup.picasso.Picasso; import net.schueller.peertube.R; import net.schueller.peertube.adapter.ChannelAdapter; -import net.schueller.peertube.adapter.VideoAdapter; +import net.schueller.peertube.adapter.MultiViewRecycleViewAdapter; import net.schueller.peertube.helper.APIUrlHelper; import net.schueller.peertube.helper.ErrorHelper; import net.schueller.peertube.helper.MetaDataHelper; @@ -71,7 +72,7 @@ public class AccountActivity extends CommonActivity { private Set videosLanguages; private ChannelAdapter channelAdapter; - private VideoAdapter videoAdapter; + private MultiViewRecycleViewAdapter mMultiViewRecycleViewAdapter; private RecyclerView recyclerViewVideos; private RecyclerView recyclerViewChannels; @@ -110,8 +111,8 @@ public class AccountActivity extends CommonActivity { RecyclerView.LayoutManager layoutManagerVideosChannels = new LinearLayoutManager(AccountActivity.this); recyclerViewChannels.setLayoutManager(layoutManagerVideosChannels); - videoAdapter = new VideoAdapter(new ArrayList<>(), AccountActivity.this); - recyclerViewVideos.setAdapter(videoAdapter); + mMultiViewRecycleViewAdapter = new MultiViewRecycleViewAdapter(); + recyclerViewVideos.setAdapter(mMultiViewRecycleViewAdapter); channelAdapter = new ChannelAdapter(new ArrayList<>(), AccountActivity.this); recyclerViewChannels.setAdapter(channelAdapter); @@ -184,7 +185,7 @@ public class AccountActivity extends CommonActivity { ownerStringView.setText(owner); TextView followers = findViewById(R.id.account_followers); - followers.setText(account.getFollowersCount().toString()); + followers.setText(String.valueOf(account.getFollowersCount())); TextView description = findViewById(R.id.account_description); description.setText(account.getDescription()); @@ -238,11 +239,11 @@ public class AccountActivity extends CommonActivity { if (response.isSuccessful()) { if (videosCurrentStart == 0) { - videoAdapter.clearData(); + mMultiViewRecycleViewAdapter.clearData(); } if (response.body() != null) { - videoAdapter.setData(response.body().getVideoArrayList()); + mMultiViewRecycleViewAdapter.setVideoData(response.body().getVideos()); } } else{ @@ -301,7 +302,7 @@ public class AccountActivity extends CommonActivity { BottomNavigationView navigation = findViewById(R.id.account_navigation); // Always show text label - navigation.setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_LABELED); + navigation.setLabelVisibilityMode(NavigationBarView.LABEL_VISIBILITY_LABELED); // Add Icon font Menu navMenu = navigation.getMenu(); diff --git a/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.kt b/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.kt index 65fce93..e2787e6 100644 --- a/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.kt +++ b/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.kt @@ -43,23 +43,14 @@ import androidx.recyclerview.widget.RecyclerView.LayoutManager import androidx.recyclerview.widget.RecyclerView.OnScrollListener import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.google.android.material.bottomnavigation.BottomNavigationView -import com.google.android.material.bottomnavigation.LabelVisibilityMode -import com.mikepenz.iconics.IconicsDrawable -import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome.Icon.faw_fire -import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome.Icon.faw_folder -import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome.Icon.faw_globe -import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome.Icon.faw_home -import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome.Icon.faw_plus_circle -import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome.Icon.faw_search -import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome.Icon.faw_server -import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome.Icon.faw_user_circle -import com.mikepenz.iconics.utils.actionBar +import com.google.android.material.navigation.NavigationBarView import net.schueller.peertube.R import net.schueller.peertube.R.id import net.schueller.peertube.R.layout -import net.schueller.peertube.adapter.VideoAdapter +import net.schueller.peertube.adapter.MultiViewRecycleViewAdapter import net.schueller.peertube.helper.APIUrlHelper import net.schueller.peertube.helper.ErrorHelper +import net.schueller.peertube.model.Overview import net.schueller.peertube.model.VideoList import net.schueller.peertube.network.GetUserService import net.schueller.peertube.network.GetVideoDataService @@ -70,15 +61,15 @@ import net.schueller.peertube.service.VideoPlayerService import retrofit2.Call import retrofit2.Callback import retrofit2.Response -import java.util.ArrayList private const val TAG = "_VideoListActivity" class VideoListActivity : CommonActivity() { - private var videoAdapter: VideoAdapter? = null + private var mMultiViewAdapter: MultiViewRecycleViewAdapter? = null private var swipeRefreshLayout: SwipeRefreshLayout? = null private var currentStart = 0 + private var currentPage = 1 private val count = 12 private var sort = "-createdAt" private var filter: String? = null @@ -87,6 +78,7 @@ class VideoListActivity : CommonActivity() { private var emptyView: TextView? = null private var recyclerView: RecyclerView? = null private var isLoading = false + private var overViewActive = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(layout.activity_video_list) @@ -107,10 +99,10 @@ class VideoListActivity : CommonActivity() { inflater.inflate(R.menu.menu_top_videolist, menu) // Set an icon in the ActionBar - menu.findItem(id.action_account).icon = IconicsDrawable(this, faw_user_circle).actionBar() - menu.findItem(id.action_server_address_book).icon = IconicsDrawable(this, faw_server).actionBar() + menu.findItem(id.action_account).setIcon(R.drawable.ic_user) + menu.findItem(id.action_server_address_book).setIcon(R.drawable.ic_server) val searchMenuItem = menu.findItem(id.action_search) - searchMenuItem.icon = IconicsDrawable(this, faw_search).actionBar() + searchMenuItem.setIcon(R.drawable.ic_search) // Get the SearchView and set the searchable configuration val searchManager = getSystemService(SEARCH_SERVICE) as SearchManager @@ -236,9 +228,14 @@ class VideoListActivity : CommonActivity() { emptyView = findViewById(id.empty_view) val layoutManager: LayoutManager = LinearLayoutManager(this@VideoListActivity) recyclerView?.layoutManager = layoutManager - videoAdapter = VideoAdapter(ArrayList(), this@VideoListActivity) - recyclerView?.adapter = videoAdapter - loadVideos(currentStart, count, sort, filter) + + mMultiViewAdapter = MultiViewRecycleViewAdapter() + recyclerView?.adapter = mMultiViewAdapter + +// loadVideos(currentStart, count, sort, filter) + overViewActive = true + loadOverview(currentPage) + recyclerView?.addOnScrollListener(object : OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { @@ -246,8 +243,13 @@ class VideoListActivity : CommonActivity() { // is at end of list? if (!recyclerView.canScrollVertically(RecyclerView.FOCUS_DOWN)) { if (!isLoading) { - currentStart += count - loadVideos(currentStart, count, sort, filter) + if (overViewActive) { + currentPage++ + loadOverview(currentPage) + } else { + currentStart += count + loadVideos(currentStart, count, sort, filter) + } } } } @@ -256,12 +258,75 @@ class VideoListActivity : CommonActivity() { swipeRefreshLayout?.setOnRefreshListener { // Refresh items if (!isLoading) { - currentStart = 0 - loadVideos(currentStart, count, sort, filter) + if (overViewActive) { + currentPage = 1 + loadOverview(currentPage) + } else { + currentStart = 0 + loadVideos(currentStart, count, sort, filter) + } } } } + private fun loadOverview(page: Int) { + isLoading = true + + // We set this to default to null so that on initial start there are videos listed. + val apiBaseURL = APIUrlHelper.getUrlWithVersion(this) + val service = + RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(this)).create( + GetVideoDataService::class.java + ) + val call: Call? = service.getOverviewVideosData(page) + + call?.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + if (page == 1) { + mMultiViewAdapter?.clearData() + } + if (response.body() != null) { + val overview = response.body() + if (overview != null) { + if (overview.categories.isNotEmpty()) { + mMultiViewAdapter?.setCategoryTitle(overview.categories[0].category) + mMultiViewAdapter?.setVideoData(overview.categories[0].videos) + } + if (overview.channels.isNotEmpty()) { + mMultiViewAdapter?.setChannelTitle(overview.channels[0].channel) + mMultiViewAdapter?.setVideoData(overview.channels[0].videos) + } + if (overview.tags.isNotEmpty()) { + mMultiViewAdapter?.setTagTitle(overview.tags[0]) + mMultiViewAdapter?.setVideoData(overview.tags[0].videos) + } + + } + } + + // no results show no results message + if (mMultiViewAdapter?.itemCount == 0) { + emptyView!!.visibility = View.VISIBLE + recyclerView!!.visibility = View.GONE + } else { + emptyView!!.visibility = View.GONE + recyclerView!!.visibility = View.VISIBLE + } + isLoading = false + swipeRefreshLayout!!.isRefreshing = false + } + + override fun onFailure(call: Call, t: Throwable) { + Log.wtf("err", t.fillInStackTrace()) + ErrorHelper.showToastFromCommunicationError(this@VideoListActivity, t) + isLoading = false + swipeRefreshLayout!!.isRefreshing = false + } + }) + + + } + private fun loadVideos(start: Int, count: Int, sort: String, filter: String?) { isLoading = true @@ -303,17 +368,17 @@ class VideoListActivity : CommonActivity() { call.enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (currentStart == 0) { - videoAdapter!!.clearData() + mMultiViewAdapter!!.clearData() } if (response.body() != null) { - val videoList = response.body()!!.videoArrayList + val videoList = response.body() if (videoList != null) { - videoAdapter!!.setData(response.body()!!.videoArrayList) + mMultiViewAdapter!!.setVideoListData(videoList) } } // no results show no results message - if (currentStart == 0 && videoAdapter!!.itemCount == 0) { + if (currentStart == 0 && mMultiViewAdapter!!.itemCount == 0) { emptyView!!.visibility = View.VISIBLE recyclerView!!.visibility = View.GONE } else { @@ -383,15 +448,15 @@ class VideoListActivity : CommonActivity() { val navigation = findViewById(id.navigation) // Always show text label - navigation.labelVisibilityMode = LabelVisibilityMode.LABEL_VISIBILITY_LABELED + navigation.labelVisibilityMode = NavigationBarView.LABEL_VISIBILITY_LABELED // Add Icon font val navMenu = navigation.menu - navMenu.findItem(id.navigation_overview).icon = IconicsDrawable(this, faw_globe) - navMenu.findItem(id.navigation_trending).icon = IconicsDrawable(this, faw_fire) - navMenu.findItem(id.navigation_recent).icon = IconicsDrawable(this, faw_plus_circle) - navMenu.findItem(id.navigation_local).icon = IconicsDrawable(this, faw_home) - navMenu.findItem(id.navigation_subscriptions).icon = IconicsDrawable(this, faw_folder) + navMenu.findItem(id.navigation_overview).setIcon(R.drawable.ic_globe) + navMenu.findItem(id.navigation_trending).setIcon(R.drawable.ic_trending) + navMenu.findItem(id.navigation_recent).setIcon(R.drawable.ic_plus_circle) + navMenu.findItem(id.navigation_local).setIcon(R.drawable.ic_local) + navMenu.findItem(id.navigation_subscriptions).setIcon(R.drawable.ic_subscriptions) // navMenu.findItem(R.id.navigation_account).setIcon( // new IconicsDrawable(this, FontAwesome.Icon.faw_user_circle)); @@ -401,17 +466,16 @@ class VideoListActivity : CommonActivity() { id.navigation_overview -> { // TODO if (!isLoading) { - sort = "-createdAt" - currentStart = 0 - filter = null - subscriptions = false - loadVideos(currentStart, count, sort, filter) + currentPage = 1 + loadOverview(currentPage) + overViewActive = true } return@setOnNavigationItemSelectedListener true } id.navigation_trending -> { //Log.v(TAG, "navigation_trending"); if (!isLoading) { + overViewActive = false sort = "-trending" currentStart = 0 filter = null @@ -422,6 +486,7 @@ class VideoListActivity : CommonActivity() { } id.navigation_recent -> { if (!isLoading) { + overViewActive = false sort = "-createdAt" currentStart = 0 filter = null @@ -433,6 +498,7 @@ class VideoListActivity : CommonActivity() { id.navigation_local -> { //Log.v(TAG, "navigation_trending"); if (!isLoading) { + overViewActive = false sort = "-publishedAt" filter = "local" currentStart = 0 @@ -450,6 +516,7 @@ class VideoListActivity : CommonActivity() { return@setOnNavigationItemSelectedListener false } else { if (!isLoading) { + overViewActive = false sort = "-publishedAt" filter = null currentStart = 0 diff --git a/app/src/main/java/net/schueller/peertube/adapter/ChannelAdapter.java b/app/src/main/java/net/schueller/peertube/adapter/ChannelAdapter.java index 55fbb02..ceca929 100644 --- a/app/src/main/java/net/schueller/peertube/adapter/ChannelAdapter.java +++ b/app/src/main/java/net/schueller/peertube/adapter/ChannelAdapter.java @@ -86,7 +86,9 @@ public class ChannelAdapter extends RecyclerView.Adapter { - // Log.v("VideoAdapter", "click: " + videoList.get(position).getName()); Intent intent = new Intent(context,VideoPlayActivity.class); intent.putExtra(EXTRA_VIDEOID, videoList.get(position).getUuid()); diff --git a/app/src/main/java/net/schueller/peertube/adapter/MultiViewRecycleViewAdapter.kt b/app/src/main/java/net/schueller/peertube/adapter/MultiViewRecycleViewAdapter.kt new file mode 100644 index 0000000..c55309a --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/adapter/MultiViewRecycleViewAdapter.kt @@ -0,0 +1,110 @@ +package net.schueller.peertube.adapter; + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import net.schueller.peertube.R +import net.schueller.peertube.databinding.ItemCategoryTitleBinding +import net.schueller.peertube.databinding.ItemChannelTitleBinding +import net.schueller.peertube.databinding.ItemTagTitleBinding +import net.schueller.peertube.databinding.RowVideoListBinding +import net.schueller.peertube.model.Category +import net.schueller.peertube.model.Channel +import net.schueller.peertube.model.TagVideo +import net.schueller.peertube.model.Video +import net.schueller.peertube.model.VideoList +import net.schueller.peertube.model.ui.OverviewRecycleViewItem +import java.util.ArrayList + +class MultiViewRecycleViewAdapter : RecyclerView.Adapter() { + + private var items = ArrayList() + set(value) { + field = value + notifyDataSetChanged() + } + + fun setVideoListData(videoList: VideoList) { + items.addAll(videoList.videos) + notifyDataSetChanged() + } + + fun setVideoData(videos: ArrayList