Add to playlist feature initial commit

This commit is contained in:
digiwizkid 2022-01-08 13:18:41 +05:30
parent 4b1efade40
commit c7893ddd38
8 changed files with 346 additions and 150 deletions

View File

@ -30,28 +30,26 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.mikepenz.iconics.Iconics.Builder
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import net.schueller.peertube.R.color import net.schueller.peertube.R
import net.schueller.peertube.R.string import net.schueller.peertube.R.*
import net.schueller.peertube.activity.AccountActivity import net.schueller.peertube.activity.AccountActivity
import net.schueller.peertube.activity.VideoListActivity import net.schueller.peertube.activity.VideoListActivity
import net.schueller.peertube.activity.VideoPlayActivity import net.schueller.peertube.activity.VideoPlayActivity
import net.schueller.peertube.databinding.*
import net.schueller.peertube.fragment.VideoMetaDataFragment
import net.schueller.peertube.helper.APIUrlHelper import net.schueller.peertube.helper.APIUrlHelper
import net.schueller.peertube.helper.MetaDataHelper.getCreatorAvatar
import net.schueller.peertube.helper.MetaDataHelper.getCreatorString
import net.schueller.peertube.helper.MetaDataHelper.getDuration import net.schueller.peertube.helper.MetaDataHelper.getDuration
import net.schueller.peertube.helper.MetaDataHelper.getMetaString import net.schueller.peertube.helper.MetaDataHelper.getMetaString
import net.schueller.peertube.helper.MetaDataHelper.getOwnerString import net.schueller.peertube.helper.MetaDataHelper.getOwnerString
import com.mikepenz.iconics.Iconics.Builder import net.schueller.peertube.helper.MetaDataHelper.isChannel
import net.schueller.peertube.R
import net.schueller.peertube.R.id
import net.schueller.peertube.R.menu
import net.schueller.peertube.databinding.*
import net.schueller.peertube.fragment.VideoMetaDataFragment
import net.schueller.peertube.helper.MetaDataHelper.getCreatorAvatar
import net.schueller.peertube.helper.MetaDataHelper.getCreatorString
import net.schueller.peertube.helper.MetaDataHelper.getTagsString
import net.schueller.peertube.intents.Intents import net.schueller.peertube.intents.Intents
import net.schueller.peertube.model.* import net.schueller.peertube.model.*
import net.schueller.peertube.model.ui.VideoMetaViewItem import net.schueller.peertube.model.ui.VideoMetaViewItem
import net.schueller.peertube.network.GetUserService
import net.schueller.peertube.network.GetVideoDataService import net.schueller.peertube.network.GetVideoDataService
import net.schueller.peertube.network.RetrofitInstance import net.schueller.peertube.network.RetrofitInstance
import net.schueller.peertube.network.Session import net.schueller.peertube.network.Session
@ -61,8 +59,6 @@ import okhttp3.ResponseBody
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import net.schueller.peertube.helper.MetaDataHelper.isChannel
import net.schueller.peertube.network.GetUserService
sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) { sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {
@ -70,13 +66,13 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
var videoRating: Rating? = null var videoRating: Rating? = null
var isLeaveAppExpected = false var isLeaveAppExpected = false
class CategoryViewHolder(private val binding: ItemCategoryTitleBinding): MultiViewRecyclerViewHolder(binding) { class CategoryViewHolder(private val binding: ItemCategoryTitleBinding) : MultiViewRecyclerViewHolder(binding) {
fun bind(category: Category) { fun bind(category: Category) {
binding.textViewTitle.text = category.label binding.textViewTitle.text = category.label
} }
} }
class VideoCommentsViewHolder(private val binding: ItemVideoCommentsOverviewBinding): MultiViewRecyclerViewHolder(binding) { class VideoCommentsViewHolder(private val binding: ItemVideoCommentsOverviewBinding) : MultiViewRecyclerViewHolder(binding) {
fun bind(commentThread: CommentThread) { fun bind(commentThread: CommentThread) {
binding.videoCommentsTotalCount.text = commentThread.total.toString() binding.videoCommentsTotalCount.text = commentThread.total.toString()
@ -90,8 +86,8 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
val baseUrl = APIUrlHelper.getUrl(binding.videoHighlightedAvatar.context) val baseUrl = APIUrlHelper.getUrl(binding.videoHighlightedAvatar.context)
val avatarPath = avatar.path val avatarPath = avatar.path
Picasso.get() Picasso.get()
.load(baseUrl + avatarPath) .load(baseUrl + avatarPath)
.into(binding.videoHighlightedAvatar) .into(binding.videoHighlightedAvatar)
} }
binding.videoHighlightedComment.text = highlightedComment.text binding.videoHighlightedComment.text = highlightedComment.text
} }
@ -99,7 +95,7 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
} }
class VideoMetaViewHolder(private val binding: ItemVideoMetaBinding, private val videoMetaDataFragment: VideoMetaDataFragment?): MultiViewRecyclerViewHolder(binding) { class VideoMetaViewHolder(private val binding: ItemVideoMetaBinding, private val videoMetaDataFragment: VideoMetaDataFragment?) : MultiViewRecyclerViewHolder(binding) {
fun bind(videoMetaViewItem: VideoMetaViewItem) { fun bind(videoMetaViewItem: VideoMetaViewItem) {
val video = videoMetaViewItem.video val video = videoMetaViewItem.video
@ -109,16 +105,16 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
val context = binding.avatar.context val context = binding.avatar.context
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context) val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
val videoDataService = RetrofitInstance.getRetrofitInstance( val videoDataService = RetrofitInstance.getRetrofitInstance(
apiBaseURL, apiBaseURL,
APIUrlHelper.useInsecureConnection(context) APIUrlHelper.useInsecureConnection(context)
).create( ).create(
GetVideoDataService::class.java GetVideoDataService::class.java
) )
val userService = RetrofitInstance.getRetrofitInstance( val userService = RetrofitInstance.getRetrofitInstance(
apiBaseURL, apiBaseURL,
APIUrlHelper.useInsecureConnection(context) APIUrlHelper.useInsecureConnection(context)
).create( ).create(
GetUserService::class.java GetUserService::class.java
) )
// Title // Title
@ -137,27 +133,25 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
rateVideo(false, video, context, binding) rateVideo(false, video, context, binding)
} }
// Add to playlist
binding.videoAddToPlaylistWrapper.setOnClickListener { binding.videoAddToPlaylistWrapper.setOnClickListener {
Toast.makeText( videoMetaDataFragment.saveToPlaylist(video)
context, Toast.makeText(context, "Saved to playlist", Toast.LENGTH_SHORT).show()
context.getString(string.video_feature_not_yet_implemented),
Toast.LENGTH_SHORT
).show()
} }
binding.videoBlockWrapper.setOnClickListener { binding.videoBlockWrapper.setOnClickListener {
Toast.makeText( Toast.makeText(
context, context,
context.getString(string.video_feature_not_yet_implemented), context.getString(string.video_feature_not_yet_implemented),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
binding.videoFlagWrapper.setOnClickListener { binding.videoFlagWrapper.setOnClickListener {
Toast.makeText( Toast.makeText(
context, context,
context.getString(string.video_feature_not_yet_implemented), context.getString(string.video_feature_not_yet_implemented),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
@ -198,10 +192,10 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
// created at / views // created at / views
binding.videoMeta.text = getMetaString( binding.videoMeta.text = getMetaString(
video.createdAt, video.createdAt,
video.views, video.views,
context, context,
true true
) )
// owner / creator // owner / creator
@ -220,8 +214,8 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
val baseUrl = APIUrlHelper.getUrl(context) val baseUrl = APIUrlHelper.getUrl(context)
val avatarPath = avatar.path val avatarPath = avatar.path
Picasso.get() Picasso.get()
.load(baseUrl + avatarPath) .load(baseUrl + avatarPath)
.into(binding.avatar) .into(binding.avatar)
} }
// videoOwnerSubscribers // videoOwnerSubscribers
@ -243,7 +237,6 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
} }
// get subscription status // get subscription status
var isSubscribed = false var isSubscribed = false
@ -262,6 +255,7 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
} }
} }
} }
override fun onFailure(call: Call<JsonObject>, t: Throwable) { override fun onFailure(call: Call<JsonObject>, t: Throwable) {
// Do nothing. // Do nothing.
} }
@ -278,52 +272,54 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
val call = userService.subscribe(body) val call = userService.subscribe(body)
call.enqueue(object : Callback<ResponseBody?> { call.enqueue(object : Callback<ResponseBody?> {
override fun onResponse( override fun onResponse(
call: Call<ResponseBody?>, call: Call<ResponseBody?>,
response: Response<ResponseBody?> response: Response<ResponseBody?>
) { ) {
if (response.isSuccessful) { if (response.isSuccessful) {
binding.videoOwnerSubscribeButton.setText(string.unsubscribe) binding.videoOwnerSubscribeButton.setText(string.unsubscribe)
isSubscribed = true isSubscribed = true
} }
} }
override fun onFailure(call: Call<ResponseBody?>, t: Throwable) { override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
// Do nothing. // Do nothing.
} }
}) })
} else { } else {
AlertDialog.Builder(context) AlertDialog.Builder(context)
.setTitle(context.getString(string.video_sub_del_alert_title)) .setTitle(context.getString(string.video_sub_del_alert_title))
.setMessage(context.getString(string.video_sub_del_alert_msg)) .setMessage(context.getString(string.video_sub_del_alert_msg))
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
// Yes // Yes
val payload = video.channel.name + "@" + video.channel.host val payload = video.channel.name + "@" + video.channel.host
val call = userService.unsubscribe(payload) val call = userService.unsubscribe(payload)
call.enqueue(object : Callback<ResponseBody?> { call.enqueue(object : Callback<ResponseBody?> {
override fun onResponse( override fun onResponse(
call: Call<ResponseBody?>, call: Call<ResponseBody?>,
response: Response<ResponseBody?> response: Response<ResponseBody?>
) { ) {
if (response.isSuccessful) { if (response.isSuccessful) {
binding.videoOwnerSubscribeButton.setText(string.subscribe) binding.videoOwnerSubscribeButton.setText(string.subscribe)
isSubscribed = false isSubscribed = false
}
} }
}
override fun onFailure(call: Call<ResponseBody?>, t: Throwable) { override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
// Do nothing. // Do nothing.
} }
}) })
} }
.setNegativeButton(android.R.string.cancel) { _: DialogInterface?, _: Int -> .setNegativeButton(android.R.string.cancel) { _: DialogInterface?, _: Int ->
// No // No
} }
.setIcon(android.R.drawable.ic_dialog_alert) .setIcon(android.R.drawable.ic_dialog_alert)
.show() .show()
} }
} else { } else {
Toast.makeText( Toast.makeText(
context, context,
context.getString(string.video_login_required_for_service), context.getString(string.video_login_required_for_service),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
} }
@ -333,7 +329,7 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
} }
} }
class ChannelViewHolder(private val binding: ItemChannelTitleBinding): MultiViewRecyclerViewHolder(binding) { class ChannelViewHolder(private val binding: ItemChannelTitleBinding) : MultiViewRecyclerViewHolder(binding) {
fun bind(channel: Channel) { fun bind(channel: Channel) {
val context = binding.avatar.context val context = binding.avatar.context
@ -344,22 +340,22 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
if (avatar != null) { if (avatar != null) {
val avatarPath = avatar.path val avatarPath = avatar.path
Picasso.get() Picasso.get()
.load(baseUrl + avatarPath) .load(baseUrl + avatarPath)
.placeholder(R.drawable.test_image) .placeholder(R.drawable.test_image)
.into(binding.avatar) .into(binding.avatar)
} }
binding.textViewTitle.text = channel.displayName binding.textViewTitle.text = channel.displayName
} }
} }
class TagViewHolder(private val binding: ItemTagTitleBinding): MultiViewRecyclerViewHolder(binding) { class TagViewHolder(private val binding: ItemTagTitleBinding) : MultiViewRecyclerViewHolder(binding) {
fun bind(tag: TagVideo) { fun bind(tag: TagVideo) {
binding.textViewTitle.text = tag.tag binding.textViewTitle.text = tag.tag
} }
} }
class VideoViewHolder(private val binding: RowVideoListBinding): MultiViewRecyclerViewHolder(binding) { class VideoViewHolder(private val binding: RowVideoListBinding) : MultiViewRecyclerViewHolder(binding) {
fun bind(video: Video) { fun bind(video: Video) {
@ -368,18 +364,18 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
// Temp Loading Image // Temp Loading Image
Picasso.get() Picasso.get()
.load(baseUrl + video.previewPath) .load(baseUrl + video.previewPath)
.placeholder(R.drawable.test_image) .placeholder(R.drawable.test_image)
.error(R.drawable.test_image) .error(R.drawable.test_image)
.into(binding.thumb) .into(binding.thumb)
// Avatar // Avatar
val avatar = getCreatorAvatar(video, context) val avatar = getCreatorAvatar(video, context)
if (avatar != null) { if (avatar != null) {
val avatarPath = avatar.path val avatarPath = avatar.path
Picasso.get() Picasso.get()
.load(baseUrl + avatarPath) .load(baseUrl + avatarPath)
.into(binding.avatar) .into(binding.avatar)
} }
// set Name // set Name
binding.slRowName.text = video.name binding.slRowName.text = video.name
@ -395,9 +391,9 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
// set age and view count // set age and view count
binding.videoMeta.text = getMetaString( binding.videoMeta.text = getMetaString(
video.createdAt, video.createdAt,
video.views, video.views,
context context
) )
// set owner // set owner
@ -431,8 +427,8 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
binding.moreButton.setOnClickListener { v: View? -> binding.moreButton.setOnClickListener { v: View? ->
val popup = PopupMenu( val popup = PopupMenu(
context, context,
v!! v!!
) )
popup.setOnMenuItemClickListener { menuItem: MenuItem -> popup.setOnMenuItemClickListener { menuItem: MenuItem ->
when (menuItem.itemId) { when (menuItem.itemId) {
@ -492,17 +488,17 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context) val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
val videoDataService = RetrofitInstance.getRetrofitInstance( val videoDataService = RetrofitInstance.getRetrofitInstance(
apiBaseURL, APIUrlHelper.useInsecureConnection( apiBaseURL, APIUrlHelper.useInsecureConnection(
context context
) )
).create( ).create(
GetVideoDataService::class.java GetVideoDataService::class.java
) )
val call = videoDataService.rateVideo(video.id, body) val call = videoDataService.rateVideo(video.id, body)
call.enqueue(object : Callback<ResponseBody?> { call.enqueue(object : Callback<ResponseBody?> {
override fun onResponse( override fun onResponse(
call: Call<ResponseBody?>, call: Call<ResponseBody?>,
response: Response<ResponseBody?> response: Response<ResponseBody?>
) { ) {
// if 20x, update likes/dislikes // if 20x, update likes/dislikes
if (response.isSuccessful) { if (response.isSuccessful) {
@ -539,17 +535,17 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
override fun onFailure(call: Call<ResponseBody?>, t: Throwable) { override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
Toast.makeText( Toast.makeText(
context, context,
context.getString(string.video_rating_failed), context.getString(string.video_rating_failed),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })
} else { } else {
Toast.makeText( Toast.makeText(
context, context,
context.getString(string.video_login_required_for_service), context.getString(string.video_login_required_for_service),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
} }

View File

@ -19,7 +19,9 @@ package net.schueller.peertube.database;
import androidx.room.Database; import androidx.room.Database;
import androidx.room.RoomDatabase; import androidx.room.RoomDatabase;
@Database(entities = {Server.class}, version = 1) @Database(entities = {Server.class, Video.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase { public abstract class AppDatabase extends RoomDatabase {
public abstract ServerDao serverDao(); public abstract ServerDao serverDao();
public abstract VideoDao videoDao();
} }

View File

@ -0,0 +1,21 @@
package net.schueller.peertube.database
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
@Parcelize
@Entity(tableName = "watch_later")
data class Video(
@PrimaryKey(autoGenerate = true)
var id: Int = 0,
@ColumnInfo(name = "video_name")
var videoName: String,
@ColumnInfo(name = "video_description")
var videoDescription: String?
) : Parcelable

View File

@ -0,0 +1,39 @@
/*
* 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.database
import androidx.lifecycle.LiveData
import androidx.room.*
@Dao
interface VideoDao {
@Insert
suspend fun insert(video: Video)
@Update
suspend fun update(video: Video)
@Query("DELETE FROM watch_later")
suspend fun deleteAll()
@Delete
suspend fun delete(video: Video)
@get:Query("SELECT * from watch_later ORDER BY video_name DESC")
val allVideos: LiveData<List<Video>>
}

View File

@ -0,0 +1,47 @@
/*
* 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.database
import android.app.Application
import androidx.lifecycle.LiveData
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
internal class VideoRepository(application: Application) {
private val mVideoDao: VideoDao
val allVideos: LiveData<List<Video>>
get() = mVideoDao.allVideos
init {
val db = VideoRoomDatabase.getDatabase(application)
mVideoDao = db.videoDao()
}
suspend fun update(video: Video) = withContext(Dispatchers.IO) {
mVideoDao.update(video)
}
suspend fun insert(video: Video) = withContext(Dispatchers.IO) {
mVideoDao.insert(video)
}
suspend fun delete(video: Video) = withContext(Dispatchers.IO) {
mVideoDao.delete(video)
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Database(entities = {Video.class}, version = 1, exportSchema = false)
public abstract class VideoRoomDatabase extends RoomDatabase {
public abstract VideoDao videoDao();
private static volatile VideoRoomDatabase INSTANCE;
private static final int NUMBER_OF_THREADS = 4;
static final ExecutorService databaseWriteExecutor =
Executors.newFixedThreadPool(NUMBER_OF_THREADS);
public static VideoRoomDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (VideoRoomDatabase.class) {
if (INSTANCE == null) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
VideoRoomDatabase.class, "playlist_database")
.build();
}
}
}
}
return INSTANCE;
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.database
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class VideoViewModel(application: Application) : AndroidViewModel(application) {
private val mRepository: VideoRepository = VideoRepository(application)
val allVideos: LiveData<List<Video>> = mRepository.allVideos
fun insert(video: Video) {
viewModelScope.launch {
mRepository.insert(video)
}
}
fun update(video: Video) {
viewModelScope.launch {
mRepository.update(video)
}
}
fun delete(video: Video) {
viewModelScope.launch {
mRepository.delete(video)
}
}
}

View File

@ -16,54 +16,36 @@
*/ */
package net.schueller.peertube.fragment package net.schueller.peertube.fragment
import android.Manifest
import net.schueller.peertube.helper.MetaDataHelper.getMetaString
import net.schueller.peertube.helper.MetaDataHelper.getOwnerString
import android.content.res.ColorStateList
import android.view.LayoutInflater
import android.view.ViewGroup
import android.os.Bundle
import net.schueller.peertube.R
import net.schueller.peertube.service.VideoPlayerService
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import net.schueller.peertube.helper.APIUrlHelper import android.content.res.ColorStateList
import net.schueller.peertube.network.GetVideoDataService import android.os.Bundle
import net.schueller.peertube.network.RetrofitInstance
import net.schueller.peertube.helper.ErrorHelper
import androidx.core.app.ActivityCompat
import android.content.pm.PackageManager
import android.util.Log import android.util.Log
import android.widget.Toast import android.view.LayoutInflater
import com.squareup.picasso.Picasso
import android.widget.TextView
import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.Button import android.view.ViewGroup
import android.widget.ImageView import android.widget.TextView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.activityViewModels
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.iconics.Iconics import com.mikepenz.iconics.Iconics
import net.schueller.peertube.R
import net.schueller.peertube.adapter.MultiViewRecycleViewAdapter import net.schueller.peertube.adapter.MultiViewRecycleViewAdapter
import net.schueller.peertube.intents.Intents import net.schueller.peertube.database.VideoViewModel
import net.schueller.peertube.helper.APIUrlHelper
import net.schueller.peertube.helper.ErrorHelper
import net.schueller.peertube.model.CommentThread import net.schueller.peertube.model.CommentThread
import net.schueller.peertube.model.Rating import net.schueller.peertube.model.Rating
import net.schueller.peertube.model.Video import net.schueller.peertube.model.Video
import net.schueller.peertube.model.VideoList import net.schueller.peertube.model.VideoList
import net.schueller.peertube.model.ui.VideoMetaViewItem import net.schueller.peertube.model.ui.VideoMetaViewItem
import net.schueller.peertube.network.GetUserService import net.schueller.peertube.network.GetVideoDataService
import net.schueller.peertube.network.Session import net.schueller.peertube.network.RetrofitInstance
import okhttp3.MediaType.Companion.toMediaType import net.schueller.peertube.service.VideoPlayerService
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.ResponseBody
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import java.lang.Exception
class VideoMetaDataFragment : Fragment() { class VideoMetaDataFragment : Fragment() {
private var videoRating: Rating? = null private var videoRating: Rating? = null
@ -73,12 +55,14 @@ class VideoMetaDataFragment : Fragment() {
private lateinit var videoDescriptionFragment: VideoDescriptionFragment private lateinit var videoDescriptionFragment: VideoDescriptionFragment
private val mVideoViewModel: VideoViewModel by activityViewModels()
var isLeaveAppExpected = false var isLeaveAppExpected = false
private set private set
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
// Inflate the layout for this fragment // Inflate the layout for this fragment
@ -94,11 +78,11 @@ class VideoMetaDataFragment : Fragment() {
// show full description fragment // show full description fragment
videoDescriptionFragment = VideoDescriptionFragment.newInstance(video, this) videoDescriptionFragment = VideoDescriptionFragment.newInstance(video, this)
childFragmentManager.beginTransaction() childFragmentManager.beginTransaction()
.add(R.id.video_meta_data_fragment, videoDescriptionFragment, VideoDescriptionFragment.TAG).commit() .add(R.id.video_meta_data_fragment, videoDescriptionFragment, VideoDescriptionFragment.TAG).commit()
} }
fun hideDescriptionFragment() { fun hideDescriptionFragment() {
val fragment: Fragment? = childFragmentManager.findFragmentByTag(VideoDescriptionFragment.TAG) val fragment: Fragment? = childFragmentManager.findFragmentByTag(VideoDescriptionFragment.TAG)
if (fragment != null) { if (fragment != null) {
childFragmentManager.beginTransaction().remove(fragment).commit() childFragmentManager.beginTransaction().remove(fragment).commit()
} }
@ -113,10 +97,10 @@ class VideoMetaDataFragment : Fragment() {
val activity: Activity? = activity val activity: Activity? = activity
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context) val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
val videoDataService = RetrofitInstance.getRetrofitInstance( val videoDataService = RetrofitInstance.getRetrofitInstance(
apiBaseURL, apiBaseURL,
APIUrlHelper.useInsecureConnection(context) APIUrlHelper.useInsecureConnection(context)
).create( ).create(
GetVideoDataService::class.java GetVideoDataService::class.java
) )
// related videos // related videos
@ -149,8 +133,8 @@ class VideoMetaDataFragment : Fragment() {
videoOptions.setOnClickListener { videoOptions.setOnClickListener {
val videoOptionsFragment = VideoOptionsFragment.newInstance(mService, video.files) val videoOptionsFragment = VideoOptionsFragment.newInstance(mService, video.files)
videoOptionsFragment.show( videoOptionsFragment.show(
getActivity()!!.supportFragmentManager, getActivity()!!.supportFragmentManager,
VideoOptionsFragment.TAG VideoOptionsFragment.TAG
) )
} }
} }
@ -166,9 +150,9 @@ class VideoMetaDataFragment : Fragment() {
// We set this to default to null so that on initial start there are videos listed. // We set this to default to null so that on initial start there are videos listed.
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context) val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
val service = val service =
RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create( RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create(
GetVideoDataService::class.java GetVideoDataService::class.java
) )
val call: Call<CommentThread> = service.getCommentThreads(videoId, start, count, sort) val call: Call<CommentThread> = service.getCommentThreads(videoId, start, count, sort)
call.enqueue(object : Callback<CommentThread?> { call.enqueue(object : Callback<CommentThread?> {
@ -176,7 +160,7 @@ class VideoMetaDataFragment : Fragment() {
if (response.body() != null) { if (response.body() != null) {
val commentThread = response.body() val commentThread = response.body()
if (commentThread != null) { if (commentThread != null) {
mMultiViewAdapter!!.setVideoComment(commentThread); mMultiViewAdapter!!.setVideoComment(commentThread)
} }
} }
} }
@ -197,8 +181,8 @@ class VideoMetaDataFragment : Fragment() {
val filter: String? = null val filter: String? = null
val sharedPref = context?.getSharedPreferences( val sharedPref = context?.getSharedPreferences(
context.packageName + "_preferences", context.packageName + "_preferences",
Context.MODE_PRIVATE Context.MODE_PRIVATE
) )
var nsfw = "false" var nsfw = "false"
@ -211,9 +195,9 @@ class VideoMetaDataFragment : Fragment() {
// We set this to default to null so that on initial start there are videos listed. // We set this to default to null so that on initial start there are videos listed.
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context) val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
val service = val service =
RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create( RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create(
GetVideoDataService::class.java GetVideoDataService::class.java
) )
val call: Call<VideoList> = service.getVideosData(start, count, sort, nsfw, filter, languages) val call: Call<VideoList> = service.getVideosData(start, count, sort, nsfw, filter, languages)
/*Log the URL called*/Log.d("URL Called", call.request().url.toString() + "") /*Log the URL called*/Log.d("URL Called", call.request().url.toString() + "")
@ -234,6 +218,12 @@ class VideoMetaDataFragment : Fragment() {
} }
}) })
} }
fun saveToPlaylist(video: Video) {
val playlistVideo: net.schueller.peertube.database.Video = net.schueller.peertube.database.Video(videoName = video.name, videoDescription = video.description)
mVideoViewModel.insert(playlistVideo)
}
companion object { companion object {
const val TAG = "VMDF" const val TAG = "VMDF"
} }