Merge branch 'Overview' into 'develop'

feat: Overview

See merge request sschueller/peertube!38
This commit is contained in:
Stefan Schüller 2021-12-26 13:59:46 +00:00
commit a6a7716ac4
34 changed files with 972 additions and 953 deletions

View File

@ -89,7 +89,7 @@ android {
}
buildFeatures{
viewBinding = true
viewBinding true
}
}

View File

@ -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<String> 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();

View File

@ -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,21 +243,89 @@ class VideoListActivity : CommonActivity() {
// is at end of list?
if (!recyclerView.canScrollVertically(RecyclerView.FOCUS_DOWN)) {
if (!isLoading) {
if (overViewActive) {
currentPage++
loadOverview(currentPage)
} else {
currentStart += count
loadVideos(currentStart, count, sort, filter)
}
}
}
}
}
})
swipeRefreshLayout?.setOnRefreshListener {
// Refresh items
if (!isLoading) {
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<Overview>? = service.getOverviewVideosData(page)
call?.enqueue(object : Callback<Overview?> {
override fun onResponse(call: Call<Overview?>, response: Response<Overview?>) {
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<Overview?>, 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<VideoList?> {
override fun onResponse(call: Call<VideoList?>, response: Response<VideoList?>) {
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<BottomNavigationView>(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

View File

@ -86,7 +86,9 @@ public class ChannelAdapter extends RecyclerView.Adapter<ChannelAdapter.AccountV
holder.name.setText(videoList.get(position).getName());
// set duration
holder.videoDuration.setText( MetaDataHelper.getDuration(videoList.get(position).getDuration().longValue()));
// TODO
// holder.videoDuration.setText( MetaDataHelper.getDuration(videoList.get(position).getDuration().toLong()));
// set age and view count
holder.videoMeta.setText(
@ -106,7 +108,6 @@ public class ChannelAdapter extends RecyclerView.Adapter<ChannelAdapter.AccountV
holder.mView.setOnClickListener(v -> {
// Log.v("VideoAdapter", "click: " + videoList.get(position).getName());
Intent intent = new Intent(context,VideoPlayActivity.class);
intent.putExtra(EXTRA_VIDEOID, videoList.get(position).getUuid());

View File

@ -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<MultiViewRecyclerViewHolder>() {
private var items = ArrayList<OverviewRecycleViewItem>()
set(value) {
field = value
notifyDataSetChanged()
}
fun setVideoListData(videoList: VideoList) {
items.addAll(videoList.videos)
notifyDataSetChanged()
}
fun setVideoData(videos: ArrayList<Video>) {
items.addAll(videos)
notifyDataSetChanged()
}
fun setCategoryTitle(category: Category) {
items.add(category)
notifyDataSetChanged()
}
fun setChannelTitle(channel: Channel) {
items.add(channel)
notifyDataSetChanged()
}
fun setTagTitle(tag: TagVideo) {
items.add(tag)
notifyDataSetChanged()
}
fun clearData() {
items.clear()
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MultiViewRecyclerViewHolder {
return when(viewType){
R.layout.item_category_title -> MultiViewRecyclerViewHolder.CategoryViewHolder(
ItemCategoryTitleBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
R.layout.item_channel_title -> MultiViewRecyclerViewHolder.ChannelViewHolder(
ItemChannelTitleBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
R.layout.item_tag_title -> MultiViewRecyclerViewHolder.TagViewHolder(
ItemTagTitleBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
R.layout.row_video_list -> MultiViewRecyclerViewHolder.VideoViewHolder(
RowVideoListBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
else -> throw IllegalArgumentException("Invalid ViewType Provided")
}
}
override fun onBindViewHolder(holder: MultiViewRecyclerViewHolder, position: Int) {
when(holder){
is MultiViewRecyclerViewHolder.VideoViewHolder -> holder.bind(items[position] as Video)
is MultiViewRecyclerViewHolder.CategoryViewHolder -> holder.bind(items[position] as Category)
is MultiViewRecyclerViewHolder.ChannelViewHolder -> holder.bind(items[position] as Channel)
is MultiViewRecyclerViewHolder.TagViewHolder -> holder.bind(items[position] as TagVideo)
}
}
override fun getItemCount() = items.size
override fun getItemViewType(position: Int): Int {
return when(items[position]){
is Video -> R.layout.row_video_list
is Channel -> R.layout.item_channel_title
is Category -> R.layout.item_category_title
is TagVideo -> R.layout.item_tag_title
else -> { return 0}
}
}
}

View File

@ -0,0 +1,181 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.adapter
import android.content.Intent
import android.view.MenuItem
import android.view.View
import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import com.squareup.picasso.Picasso
import net.schueller.peertube.R
import net.schueller.peertube.R.color
import net.schueller.peertube.R.string
import net.schueller.peertube.activity.AccountActivity
import net.schueller.peertube.activity.VideoListActivity
import net.schueller.peertube.activity.VideoListActivity.Companion
import net.schueller.peertube.activity.VideoPlayActivity
import net.schueller.peertube.databinding.ItemCategoryTitleBinding
import net.schueller.peertube.databinding.ItemChannelTitleBinding
import net.schueller.peertube.databinding.RowVideoListBinding
import net.schueller.peertube.helper.APIUrlHelper
import net.schueller.peertube.helper.MetaDataHelper.getDuration
import net.schueller.peertube.helper.MetaDataHelper.getMetaString
import net.schueller.peertube.helper.MetaDataHelper.getOwnerString
import net.schueller.peertube.model.Avatar
import net.schueller.peertube.model.Category
import net.schueller.peertube.model.Channel
import net.schueller.peertube.model.Video
import com.mikepenz.iconics.Iconics.Builder
import net.schueller.peertube.R.id
import net.schueller.peertube.R.menu
import net.schueller.peertube.databinding.ItemTagTitleBinding
import net.schueller.peertube.intents.Intents
import net.schueller.peertube.model.TagVideo
sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {
class CategoryViewHolder(private val binding: ItemCategoryTitleBinding): MultiViewRecyclerViewHolder(binding) {
fun bind(category: Category) {
binding.textViewTitle.text = category.label
}
}
class ChannelViewHolder(private val binding: ItemChannelTitleBinding): MultiViewRecyclerViewHolder(binding) {
fun bind(channel: Channel) {
val context = binding.avatar.context
val baseUrl = APIUrlHelper.getUrl(context)
// Avatar
val avatar: Avatar? = channel.avatar
if (avatar != null) {
val avatarPath = avatar.path
Picasso.get()
.load(baseUrl + avatarPath)
.placeholder(R.drawable.test_image)
.into(binding.avatar)
}
binding.textViewTitle.text = channel.displayName
}
}
class TagViewHolder(private val binding: ItemTagTitleBinding): MultiViewRecyclerViewHolder(binding) {
fun bind(tag: TagVideo) {
binding.textViewTitle.text = tag.tag
}
}
class VideoViewHolder(private val binding: RowVideoListBinding): MultiViewRecyclerViewHolder(binding) {
fun bind(video: Video) {
val context = binding.thumb.context
val baseUrl = APIUrlHelper.getUrl(context)
// Temp Loading Image
Picasso.get()
.load(baseUrl + video.previewPath)
.placeholder(R.drawable.test_image)
.error(R.drawable.test_image)
.into(binding.thumb)
// Avatar
val avatar: Avatar? = video.account.avatar
if (avatar != null) {
val avatarPath = avatar.path
Picasso.get()
.load(baseUrl + avatarPath)
.into(binding.avatar)
}
// set Name
binding.slRowName.text = video.name
// set duration (if not live stream)
if (video.live) {
binding.videoDuration.setText(string.video_list_live_marker)
binding.videoDuration.setBackgroundColor(ContextCompat.getColor(context, color.durationLiveBackgroundColor))
} else {
binding.videoDuration.text = getDuration(video.duration.toLong())
binding.videoDuration.setBackgroundColor(ContextCompat.getColor(context, color.durationBackgroundColor))
}
// set age and view count
binding.videoMeta.text = getMetaString(
video.createdAt,
video.views,
context
)
// set owner
val displayNameAndHost = getOwnerString(
video.account.name,
video.account.host,
context
)
binding.videoOwner.text = displayNameAndHost
// video owner click
binding.videoOwner.setOnClickListener {
val intent = Intent(context, AccountActivity::class.java)
intent.putExtra(VideoListActivity.EXTRA_ACCOUNTDISPLAYNAME, displayNameAndHost)
context.startActivity(intent)
}
// avatar click
binding.avatar.setOnClickListener {
val intent = Intent(context, AccountActivity::class.java)
intent.putExtra(Companion.EXTRA_ACCOUNTDISPLAYNAME, displayNameAndHost)
context.startActivity(intent)
}
// Video Click
binding.root.setOnClickListener {
val intent = Intent(context, VideoPlayActivity::class.java)
intent.putExtra(Companion.EXTRA_VIDEOID, video.uuid)
context.startActivity(intent)
}
// More Button
binding.moreButton.setText(string.video_more_icon)
Builder().on(binding.moreButton).build()
binding.moreButton.setOnClickListener { v: View? ->
val popup = PopupMenu(
context,
v!!
)
popup.setOnMenuItemClickListener { menuItem: MenuItem ->
when (menuItem.itemId) {
id.menu_share -> {
Intents.Share(context, video)
return@setOnMenuItemClickListener true
}
else -> return@setOnMenuItemClickListener false
}
}
popup.inflate(menu.menu_video_row_mode)
popup.show()
}
}
}
}

View File

@ -1,197 +0,0 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.adapter;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.PopupMenu;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.mikepenz.iconics.Iconics;
import com.squareup.picasso.Picasso;
import net.schueller.peertube.R;
import net.schueller.peertube.activity.AccountActivity;
import net.schueller.peertube.activity.VideoPlayActivity;
import net.schueller.peertube.helper.APIUrlHelper;
import net.schueller.peertube.helper.MetaDataHelper;
import net.schueller.peertube.intents.Intents;
import net.schueller.peertube.model.Avatar;
import net.schueller.peertube.model.Video;
import java.util.ArrayList;
import static net.schueller.peertube.activity.VideoListActivity.EXTRA_ACCOUNTDISPLAYNAME;
import static net.schueller.peertube.activity.VideoListActivity.EXTRA_VIDEOID;
public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoViewHolder> {
private ArrayList<Video> videoList;
private Context context;
private String baseUrl;
public VideoAdapter(ArrayList<Video> videoList, Context context) {
this.videoList = videoList;
this.context = context;
}
@NonNull
@Override
public VideoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.row_video_list, parent, false);
baseUrl = APIUrlHelper.getUrl(context);
return new VideoViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull VideoViewHolder holder, int position) {
Picasso.get()
.load(baseUrl + videoList.get(position).getPreviewPath())
.placeholder(R.drawable.test_image)
.error(R.drawable.test_image)
.into(holder.thumb);
Avatar avatar = videoList.get(position).getAccount().getAvatar();
if (avatar != null) {
String avatarPath = avatar.getPath();
Picasso.get()
.load(baseUrl + avatarPath)
.into(holder.avatar);
}
// set Name
holder.name.setText(videoList.get(position).getName());
// set duration (if not live stream)
if (videoList.get(position).getLive()) {
holder.videoDuration.setText(R.string.video_list_live_marker);
holder.videoDuration.setBackgroundColor(context.getResources().getColor(R.color.durationLiveBackgroundColor));
} else {
holder.videoDuration.setText(MetaDataHelper.getDuration(videoList.get(position).getDuration().longValue()));
holder.videoDuration.setBackgroundColor(context.getResources().getColor(R.color.durationBackgroundColor));
}
// set age and view count
holder.videoMeta.setText(
MetaDataHelper.getMetaString(videoList.get(position).getCreatedAt(),
videoList.get(position).getViews(),
context
)
);
// set owner
String displayNameAndHost = MetaDataHelper.getOwnerString(videoList.get(position).getAccount().getName(),
videoList.get(position).getAccount().getHost(),
context
);
holder.videoOwner.setText(displayNameAndHost);
// video owner click
holder.videoOwner.setOnClickListener(v -> {
Intent intent = new Intent(context, AccountActivity.class);
intent.putExtra(EXTRA_ACCOUNTDISPLAYNAME, displayNameAndHost);
context.startActivity(intent);
});
// avatar click
holder.avatar.setOnClickListener(v -> {
Intent intent = new Intent(context, AccountActivity.class);
intent.putExtra(EXTRA_ACCOUNTDISPLAYNAME, displayNameAndHost);
context.startActivity(intent);
});
holder.mView.setOnClickListener(v -> {
// Log.v("VideoAdapter", "click: " + videoList.get(position).getName());
Intent intent = new Intent(context, VideoPlayActivity.class);
intent.putExtra(EXTRA_VIDEOID, videoList.get(position).getUuid());
context.startActivity(intent);
});
holder.moreButton.setText(R.string.video_more_icon);
new Iconics.Builder().on(holder.moreButton).build();
holder.moreButton.setOnClickListener(v -> {
PopupMenu popup = new PopupMenu(context, v);
popup.setOnMenuItemClickListener(menuItem -> {
switch (menuItem.getItemId()) {
case R.id.menu_share:
Intents.Share(context, videoList.get(position));
return true;
default:
return false;
}
});
popup.inflate(R.menu.menu_video_row_mode);
popup.show();
});
}
public void setData(ArrayList<Video> data) {
videoList.addAll(data);
this.notifyDataSetChanged();
}
public void clearData() {
videoList.clear();
this.notifyDataSetChanged();
}
@Override
public int getItemCount() {
return videoList.size();
}
class VideoViewHolder extends RecyclerView.ViewHolder {
TextView name, videoMeta, videoOwner, moreButton, videoDuration;
ImageView thumb, avatar;
View mView;
VideoViewHolder(View itemView) {
super(itemView);
name = itemView.findViewById(R.id.sl_row_name);
thumb = itemView.findViewById(R.id.thumb);
avatar = itemView.findViewById(R.id.avatar);
videoMeta = itemView.findViewById(R.id.videoMeta);
videoOwner = itemView.findViewById(R.id.videoOwner);
moreButton = itemView.findViewById(R.id.moreButton);
videoDuration = itemView.findViewById(R.id.video_duration);
mView = itemView;
}
}
}

View File

@ -1,131 +0,0 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model;
import java.util.Date;
public class Account {
private Integer id;
private String url;
private String uuid;
private String name;
private String host;
private Integer followingCount;
private Integer followersCount;
private Avatar avatar;
private Date createdAt;
private Date updatedAt;
private String displayName;
private String description;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public Integer getFollowingCount() {
return followingCount;
}
public void setFollowingCount(Integer followingCount) {
this.followingCount = followingCount;
}
public Integer getFollowersCount() {
return followersCount;
}
public void setFollowersCount(Integer followersCount) {
this.followersCount = followersCount;
}
public Avatar getAvatar() {
return avatar;
}
public void setAvatar(Avatar avatar) {
this.avatar = avatar;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -14,37 +14,21 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model;
package net.schueller.peertube.model
import java.util.Date;
import java.util.Date
public class Avatar {
private String path;
private Date createdAt;
private Date updatedAt;
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
}
class Account(
var id: Int,
var url: String,
var uuid: String,
var name: String,
var host: String,
var followingCount: Int,
var followersCount: Int,
var avatar: Avatar?,
var createdAt: Date,
var updatedAt: Date,
var displayName: String,
var description: String
)

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model
import java.util.Date
class Avatar(
var path: String,
var createdAt: Date,
var updatedAt: Date
)

View File

@ -14,26 +14,12 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model;
package net.schueller.peertube.model
public class Category {
import net.schueller.peertube.model.ui.OverviewRecycleViewItem
private Integer id;
private String label;
class Category: OverviewRecycleViewItem() {
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
var id: Int? = null
var label: String? = null
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2018 Stefan Schüller <sschueller@techdroid.com>
*
* License: GPL-3.0+
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model
import java.util.ArrayList
class CategoryVideo(
val category: Category,
val videos: ArrayList<Video>
)

View File

@ -1,148 +0,0 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model;
import java.util.Date;
public class Channel {
private Integer id;
private String url;
private String uuid;
private String name;
private String host;
private Integer followingCount;
private Integer followersCount;
private Avatar avatar;
private Date createdAt;
private Date updatedAt;
private String displayName;
private String description;
private String support;
private Boolean isLocal;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public Integer getFollowingCount() {
return followingCount;
}
public void setFollowingCount(Integer followingCount) {
this.followingCount = followingCount;
}
public Integer getFollowersCount() {
return followersCount;
}
public void setFollowersCount(Integer followersCount) {
this.followersCount = followersCount;
}
public Avatar getAvatar() {
return avatar;
}
public void setAvatar(Avatar avatar) {
this.avatar = avatar;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getSupport() {
return support;
}
public void setSupport(String support) {
this.support = support;
}
public Boolean getLocal() {
return isLocal;
}
public void setLocal(Boolean local) {
isLocal = local;
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model
import net.schueller.peertube.model.ui.OverviewRecycleViewItem
import java.util.Date
class Channel(
val id: Int,
val url: String,
val uuid: String,
val name: String,
val host: String,
val followingCount: Int,
val followersCount: Int,
val avatar: Avatar?,
val createdAt: Date,
val updatedAt: Date,
val displayName: String,
val description: String,
val support: String,
val local: Boolean
): OverviewRecycleViewItem()

View File

@ -0,0 +1,41 @@
/*
* Copyright 2018 Stefan Schüller <sschueller@techdroid.com>
*
* License: GPL-3.0+
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model;
import java.util.ArrayList;
public class ChannelVideo {
private Channel channel;
private ArrayList<Video> videos;
public Channel getChannel() {
return channel;
}
public void setChannel(Channel channel) {
this.channel = channel;
}
public ArrayList<Video> getVideos() {
return videos;
}
public void setVideos(ArrayList<Video> videos) {
this.videos = videos;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2018 Stefan Schüller <sschueller@techdroid.com>
*
* License: GPL-3.0+
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model
import java.util.ArrayList
class Overview(
val categories: ArrayList<CategoryVideo>,
val channels: ArrayList<ChannelVideo>,
val tags: ArrayList<TagVideo>
)

View File

@ -0,0 +1,26 @@
/*
* Copyright 2018 Stefan Schüller <sschueller@techdroid.com>
*
* License: GPL-3.0+
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model
import net.schueller.peertube.model.ui.OverviewRecycleViewItem
import java.util.ArrayList
class TagVideo(
var tag: String,
var videos: ArrayList<Video>
): OverviewRecycleViewItem()

View File

@ -1,361 +0,0 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.media.MediaDescriptionCompat;
import com.squareup.picasso.Picasso;
import net.schueller.peertube.R;
import net.schueller.peertube.helper.APIUrlHelper;
import java.util.ArrayList;
import java.util.Date;
import androidx.annotation.DrawableRes;
public class Video {
private Integer id;
private String uuid;
private String name;
private Category category;
private Licence licence;
private Language language;
private Boolean nsfw;
private String description;
private Boolean isLocal;
private Boolean isLive;
private Integer duration;
private Integer views;
private Integer likes;
private Integer dislikes;
private String thumbnailPath;
private String previewPath;
private String embedPath;
private Date createdAt;
private Date updatedAt;
private Privacy privacy;
private String support;
private String descriptionPath;
private Channel channel;
private Account account;
private ArrayList<String> tags;
private Boolean commentsEnabled;
private Boolean downloadEnabled;
private Boolean waitTranscoding;
private State state;
private ArrayList<String> trackerUrls;
private ArrayList<File> files;
private ArrayList<StreamingPlaylist> streamingPlaylists;
public Video() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public Licence getLicence() {
return licence;
}
public void setLicence(Licence licence) {
this.licence = licence;
}
public Language getLanguage() {
return language;
}
public void setLanguage(Language language) {
this.language = language;
}
public Boolean getNsfw() {
return nsfw;
}
public void setNsfw(Boolean nsfw) {
this.nsfw = nsfw;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Boolean getLocal() {
return isLocal;
}
public void setLocal(Boolean local) {
isLocal = local;
}
public Boolean getLive() {
return isLive;
}
public void setLive(final Boolean live) {
isLive = live;
}
public Integer getDuration() {
return duration;
}
public void setDuration(Integer duration) {
this.duration = duration;
}
public Integer getViews() {
return views;
}
public void setViews(Integer views) {
this.views = views;
}
public Integer getLikes() {
return likes;
}
public void setLikes(Integer likes) {
this.likes = likes;
}
public Integer getDislikes() {
return dislikes;
}
public void setDislikes(Integer dislikes) {
this.dislikes = dislikes;
}
public String getThumbnailPath() {
return thumbnailPath;
}
public void setThumbnailPath(String thumbnailPath) {
this.thumbnailPath = thumbnailPath;
}
public String getPreviewPath() {
return previewPath;
}
public void setPreviewPath(String previewPath) {
this.previewPath = previewPath;
}
public String getEmbedPath() {
return embedPath;
}
public void setEmbedPath(String embedPath) {
this.embedPath = embedPath;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public Privacy getPrivacy() {
return privacy;
}
public void setPrivacy(Privacy privacy) {
this.privacy = privacy;
}
public String getSupport() {
return support;
}
public void setSupport(String support) {
this.support = support;
}
public String getDescriptionPath() {
return descriptionPath;
}
public void setDescriptionPath(String descriptionPath) {
this.descriptionPath = descriptionPath;
}
public Channel getChannel() {
return channel;
}
public void setChannel(Channel channel) {
this.channel = channel;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public ArrayList<String> getTags() {
return tags;
}
public void setTags(ArrayList<String> tags) {
this.tags = tags;
}
public Boolean getCommentsEnabled() {
return commentsEnabled;
}
public void setCommentsEnabled(Boolean commentsEnabled) {
this.commentsEnabled = commentsEnabled;
}
public ArrayList<File> getFiles() {
return files;
}
public void setFiles(ArrayList<File> files) {
this.files = files;
}
public Boolean getDownloadEnabled() {
return downloadEnabled;
}
public void setDownloadEnabled(final Boolean downloadEnabled) {
this.downloadEnabled = downloadEnabled;
}
public Boolean getWaitTranscoding() {
return waitTranscoding;
}
public void setWaitTranscoding(final Boolean waitTranscoding) {
this.waitTranscoding = waitTranscoding;
}
public State getState() {
return state;
}
public void setState(final State state) {
this.state = state;
}
public ArrayList<String> getTrackerUrls() {
return trackerUrls;
}
public void setTrackerUrls(final ArrayList<String> trackerUrls) {
this.trackerUrls = trackerUrls;
}
public ArrayList<StreamingPlaylist> getStreamingPlaylists() {
return streamingPlaylists;
}
public void setStreamingPlaylists(final ArrayList<StreamingPlaylist> streamingPlaylists) {
this.streamingPlaylists = streamingPlaylists;
}
public static MediaDescriptionCompat getMediaDescription(Context context, Video video) {
// String apiBaseURL = APIUrlHelper.getUrlWithVersion(context);
// Bundle extras = new Bundle();
// Bitmap bitmap = getBitmap(context, Uri.parse(apiBaseURL + video.thumbnailPath));
// extras.putParcelable(MediaDescriptionCompat.DESCRIPTION_KEY_MEDIA_URI, bitmap);
return new MediaDescriptionCompat.Builder()
.setMediaId(video.getUuid())
// .setIconBitmap(bitmap)
// .setExtras(extras)
.setTitle(video.getName())
.setDescription(video.getDescription())
.build();
}
// TODO: add support for the thumbnail
// public static Bitmap getBitmap(Context context, Uri fullThumbnailUrl) {
//
// return Picasso.with(context).load(fullThumbnailUrl)
// .placeholder(R.drawable.ic_peertube)
// .error(R.drawable.ic_peertube).get();
// }
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model
import android.content.Context
import net.schueller.peertube.model.ui.OverviewRecycleViewItem
import net.schueller.peertube.model.Licence
import net.schueller.peertube.model.Privacy
import net.schueller.peertube.model.StreamingPlaylist
import android.support.v4.media.MediaDescriptionCompat
import android.support.v4.media.MediaDescriptionCompat.Builder
import java.util.ArrayList
import java.util.Date
class Video(
var id: Int,
var uuid: String,
var name: String,
var category: Category,
var licence: Licence,
var language: Language,
var nsfw: Boolean,
var description: String,
var local: Boolean,
var live: Boolean,
var duration: Int,
var views: Int,
var likes: Int,
var dislikes: Int,
var thumbnailPath: String,
var previewPath: String,
var embedPath: String,
var createdAt: Date,
var updatedAt: Date,
var privacy: Privacy,
var support: String,
var descriptionPath: String,
var channel: Channel,
var account: Account,
var tags: ArrayList<String>,
var commentsEnabled: Boolean,
var downloadEnabled: Boolean,
var waitTranscoding: Boolean,
var state: State,
var trackerUrls: ArrayList<String>,
var files: ArrayList<File>,
var streamingPlaylists: ArrayList<StreamingPlaylist>
): OverviewRecycleViewItem() {
companion object {
@JvmStatic
fun getMediaDescription(context: Context?, video: Video): MediaDescriptionCompat {
// String apiBaseURL = APIUrlHelper.getUrlWithVersion(context);
// Bundle extras = new Bundle();
// Bitmap bitmap = getBitmap(context, Uri.parse(apiBaseURL + video.thumbnailPath));
// extras.putParcelable(MediaDescriptionCompat.DESCRIPTION_KEY_MEDIA_URI, bitmap);
return Builder()
.setMediaId(video.uuid) // .setIconBitmap(bitmap)
// .setExtras(extras)
.setTitle(video.name)
.setDescription(video.description)
.build()
} // TODO: add support for the thumbnail
// public static Bitmap getBitmap(Context context, Uri fullThumbnailUrl) {
//
// return Picasso.with(context).load(fullThumbnailUrl)
// .placeholder(R.drawable.ic_peertube)
// .error(R.drawable.ic_peertube).get();
// }
}
}

View File

@ -14,18 +14,12 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.model;
package net.schueller.peertube.model
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
public class VideoList {
import com.google.gson.annotations.SerializedName
import java.util.ArrayList
class VideoList(
@SerializedName("data")
private ArrayList<Video> videoList;
public ArrayList<Video> getVideoArrayList() {
return videoList;
}
}
val videos: ArrayList<Video>
)

View File

@ -0,0 +1,5 @@
package net.schueller.peertube.model.ui
open class OverviewRecycleViewItem {
}

View File

@ -17,6 +17,7 @@
package net.schueller.peertube.network;
import net.schueller.peertube.model.Description;
import net.schueller.peertube.model.Overview;
import net.schueller.peertube.model.Rating;
import net.schueller.peertube.model.Video;
import net.schueller.peertube.model.VideoList;
@ -27,7 +28,6 @@ import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Field;
import retrofit2.http.GET;
import retrofit2.http.PUT;
import retrofit2.http.Path;
@ -86,4 +86,9 @@ public interface GetVideoDataService {
@Query("sort") String sort
);
@GET("overviews/videos")
Call<Overview> getOverviewVideosData(
@Query("page") int page
);
}

View File

@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M2,12L22,12"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M12,2a15.3,15.3 0,0 1,4 10,15.3 15.3,0 0,1 -4,10 15.3,15.3 0,0 1,-4 -10,15.3 15.3,0 0,1 4,-10z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="8.466667dp"
android:height="8.466667dp"
android:viewportWidth="8.466667"
android:viewportHeight="8.466667">
<path
android:pathData="m4.23308,0.0003c-1.90629,0 -3.45664,1.55086 -3.45664,3.45715 0,1.04134 0.84545,2.22034 1.65623,3.20135 0.81078,0.98101 1.61902,1.73633 1.61902,1.73633a0.26461,0.26461 0,0 0,0.36122 0c0,0 0.80824,-0.75531 1.61902,-1.73633 0.81078,-0.98102 1.65829,-2.16001 1.65829,-3.20135 0,-1.90629 -1.55086,-3.45715 -3.45715,-3.45715zM4.23308,0.52999c1.62031,0 2.92799,1.30716 2.92798,2.92747 0,0.72196 -0.74972,1.91492 -1.53531,2.86546 -0.69523,0.8412 -1.24161,1.3482 -1.39268,1.4919C4.08255,7.67165 3.53602,7.1646 2.8404,6.32292 2.05481,5.37237 1.30354,4.17942 1.30354,3.45746 1.30353,1.83715 2.61277,0.52999 4.23308,0.52999ZM4.24186,1.44518a0.26461,0.26461 0,0 0,-0.02636 0.00052,0.26461 0.26461,0 0,0 -0.14056,0.05116L2.44404,2.69988a0.26474,0.26474 0,1 0,0.31471 0.42581l0.05633,-0.04134v1.22473a0.26461,0.26461 0,0 0,0.26407 0.26561h2.30632a0.26461,0.26461 0,0 0,0.26562 -0.26561v-1.2237l0.05478,0.04031A0.26475,0.26475 0,1 0,6.02056 2.69988L5.55134,2.35365a0.26461,0.26461 0,0 0,-0.00775 -0.0057L4.38966,1.49685a0.26461,0.26461 0,0 0,-0.1478 -0.05168zM4.23308,2.03946 L5.12191,2.69523v0.21756,1.13275L4.49714,4.04553v-0.4377a0.26461,0.26461 0,0 0,-0.26768 -0.26768,0.26461 0.26461,0 0,0 -0.262,0.26768v0.4377L3.34476,4.04553v-1.13275,-0.21911z"
android:fillColor="#000000"/>
</vector>

View File

@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M12,8L12,16"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M8,12L16,12"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M11,11m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M21,21L16.65,16.65"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M4,2L20,2A2,2 0,0 1,22 4L22,8A2,2 0,0 1,20 10L4,10A2,2 0,0 1,2 8L2,4A2,2 0,0 1,4 2z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M4,14L20,14A2,2 0,0 1,22 16L22,20A2,2 0,0 1,20 22L4,22A2,2 0,0 1,2 20L2,16A2,2 0,0 1,4 14z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M6,6L6.01,6"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M6,18L6.01,18"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M20,8L4,8L4,6h16v2zM18,2L6,2v2h12L18,2zM22,12v8c0,1.1 -0.9,2 -2,2L4,22c-1.1,0 -2,-0.9 -2,-2v-8c0,-1.1 0.9,-2 2,-2h16c1.1,0 2,0.9 2,2zM16,16l-6,-3.27v6.53L16,16z"
android:fillColor="#000000"
/>
</vector>

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M23,6l-9.5,9.5l-5,-5l-7.5,7.5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M17,6l6,0l0,6"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M20,21v-2a4,4 0,0 0,-4 -4H8a4,4 0,0 0,-4 4v2"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M12,7m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text_view_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatar"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="@+id/text_view_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toRightOf="@+id/avatar"
app:layout_constraintTop_toTopOf="parent"
tools:text="" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text_view_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -9,7 +9,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.2'
classpath 'com.android.tools.build:gradle:7.0.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"