Merge pull request #656 from Bnyro/master

Watch History
This commit is contained in:
Bnyro 2022-07-01 19:48:47 +02:00 committed by GitHub
commit ddd31aa859
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 437 additions and 31 deletions

View File

@ -79,28 +79,28 @@ class MainActivity : AppCompatActivity() {
// hide the trending page if enabled
val hideTrendingPage = PreferenceHelper.getBoolean(this, "hide_trending_page", false)
if (hideTrendingPage) bottomNavigationView.menu.findItem(R.id.home2).isVisible = false
if (hideTrendingPage) bottomNavigationView.menu.findItem(R.id.homeFragment).isVisible = false
// navigate to the default start tab
when (PreferenceHelper.getString(this, "default_tab", "home")) {
"home" -> navController.navigate(R.id.home2)
"subscriptions" -> navController.navigate(R.id.subscriptions)
"library" -> navController.navigate(R.id.library)
"home" -> navController.navigate(R.id.homeFragment)
"subscriptions" -> navController.navigate(R.id.subscriptionsFragment)
"library" -> navController.navigate(R.id.libraryFragment)
}
binding.bottomNav.setOnItemSelectedListener {
when (it.itemId) {
R.id.home2 -> {
R.id.homeFragment -> {
navController.backQueue.clear()
navController.navigate(R.id.home2)
navController.navigate(R.id.homeFragment)
}
R.id.subscriptions -> {
R.id.subscriptionsFragment -> {
// navController.backQueue.clear()
navController.navigate(R.id.subscriptions)
navController.navigate(R.id.subscriptionsFragment)
}
R.id.library -> {
R.id.libraryFragment -> {
// navController.backQueue.clear()
navController.navigate(R.id.library)
navController.navigate(R.id.libraryFragment)
}
}
false
@ -176,7 +176,7 @@ class MainActivity : AppCompatActivity() {
channel = channel!!.replace("/c/", "")
channel = channel.replace("/user/", "")
val bundle = bundleOf("channel_id" to channel)
navController.navigate(R.id.channel, bundle)
navController.navigate(R.id.channelFragment, bundle)
} else if (data.path!!.contains("/playlist")) {
Log.i(TAG, "URI Type: Playlist")
var playlist = data.query!!

View File

@ -69,7 +69,7 @@ class CommentsAdapter(
commentorImage.setOnClickListener {
val activity = holder.v.context as MainActivity
val bundle = bundleOf("channel_id" to comment.commentorUrl)
activity.navController.navigate(R.id.channel, bundle)
activity.navController.navigate(R.id.channelFragment, bundle)
try {
val mainMotionLayout =
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)

View File

@ -61,7 +61,7 @@ class RepliesAdapter(
commentorImage.setOnClickListener {
val activity = holder.v.context as MainActivity
val bundle = bundleOf("channel_id" to reply.commentorUrl)
activity.navController.navigate(R.id.channel, bundle)
activity.navController.navigate(R.id.channelFragment, bundle)
try {
val mainMotionLayout =
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)

View File

@ -117,7 +117,7 @@ class SearchViewHolder(
channelImage.setOnClickListener {
val activity = v.context as MainActivity
val bundle = bundleOf("channel_id" to item.uploaderUrl)
activity.navController.navigate(R.id.channel, bundle)
activity.navController.navigate(R.id.channelFragment, bundle)
}
}
@ -134,7 +134,7 @@ class SearchViewHolder(
v.setOnClickListener {
val activity = v.context as MainActivity
val bundle = bundleOf("channel_id" to item.url)
activity.navController.navigate(R.id.channel, bundle)
activity.navController.navigate(R.id.channelFragment, bundle)
}
// todo sub button
}

View File

@ -62,7 +62,7 @@ class SubscriptionAdapter(
channelImage.setOnClickListener {
val activity = holder.v.context as MainActivity
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
activity.navController.navigate(R.id.channel, bundle)
activity.navController.navigate(R.id.channelFragment, bundle)
try {
val mainMotionLayout =
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)

View File

@ -48,7 +48,7 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
root.setOnClickListener {
val activity = holder.v.context as MainActivity
val bundle = bundleOf("channel_id" to subscription.url)
activity.navController.navigate(R.id.channel, bundle)
activity.navController.navigate(R.id.channelFragment, bundle)
}
subscriptionSubscribe.setOnClickListener {
if (!isLoading) {

View File

@ -53,7 +53,7 @@ class TrendingAdapter(
channelImage.setOnClickListener {
val activity = holder.v.context as MainActivity
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
activity.navController.navigate(R.id.channel, bundle)
activity.navController.navigate(R.id.channelFragment, bundle)
try {
val mainMotionLayout =
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)

View File

@ -0,0 +1,96 @@
package com.github.libretube.adapters
import android.os.Bundle
import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.activities.MainActivity
import com.github.libretube.databinding.WatchHistoryRowBinding
import com.github.libretube.dialogs.VideoOptionsDialog
import com.github.libretube.fragments.PlayerFragment
import com.github.libretube.obj.WatchHistoryItem
import com.squareup.picasso.Picasso
class WatchHistoryAdapter(
private val watchHistory: MutableList<WatchHistoryItem>,
private val childFragmentManager: FragmentManager
) :
RecyclerView.Adapter<WatchHistoryViewHolder>() {
private val TAG = "WatchHistoryAdapter"
private lateinit var binding: WatchHistoryRowBinding
fun clear() {
val size = watchHistory.size
watchHistory.clear()
notifyItemRangeRemoved(0, size)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WatchHistoryViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
binding = WatchHistoryRowBinding.inflate(layoutInflater, parent, false)
return WatchHistoryViewHolder(binding.root)
}
override fun onBindViewHolder(holder: WatchHistoryViewHolder, position: Int) {
val video = watchHistory[position]
binding.apply {
videoTitle.text = video.title
channelName.text = video.uploader
uploadDate.text = video.uploadDate
thumbnailDuration.text = DateUtils.formatElapsedTime(video.duration?.toLong()!!)
Picasso.get().load(video.thumbnailUrl).into(thumbnail)
Picasso.get().load(video.uploaderAvatar).into(channelImage)
channelImage.setOnClickListener {
val activity = holder.v.context as MainActivity
val bundle = bundleOf("channel_id" to video.uploaderUrl)
activity.navController.navigate(R.id.channelFragment, bundle)
try {
val mainMotionLayout =
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
if (mainMotionLayout.progress == 0.toFloat()) {
mainMotionLayout.transitionToEnd()
activity.findViewById<MotionLayout>(R.id.playerMotionLayout)
.transitionToEnd()
}
} catch (e: Exception) {
}
}
root.setOnClickListener {
var bundle = Bundle()
bundle.putString("videoId", video.videoId)
var frag = PlayerFragment()
frag.arguments = bundle
val activity = holder.v.context as AppCompatActivity
activity.supportFragmentManager.beginTransaction()
.remove(PlayerFragment())
.commit()
activity.supportFragmentManager.beginTransaction()
.replace(R.id.container, frag)
.commitNow()
}
root.setOnLongClickListener {
VideoOptionsDialog(video.videoId!!, holder.v.context)
.show(childFragmentManager, VideoOptionsDialog.TAG)
true
}
}
}
override fun getItemCount(): Int {
return watchHistory.size
}
}
class WatchHistoryViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init {
}
}

View File

@ -8,6 +8,7 @@ import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R
import com.github.libretube.adapters.PlaylistsAdapter
@ -43,6 +44,17 @@ class LibraryFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
binding.playlistRecView.layoutManager = LinearLayoutManager(view.context)
token = PreferenceHelper.getToken(requireContext())
// hide watch history button of history disabled
val watchHistoryEnabled = PreferenceHelper.getBoolean(requireContext(), "watch_history_toggle", true)
if (!watchHistoryEnabled) {
binding.showWatchHistory.visibility = View.GONE
} else {
binding.showWatchHistory.setOnClickListener {
findNavController().navigate(R.id.watchHistoryFragment)
}
}
if (token != "") {
binding.boogh.visibility = View.GONE
binding.textLike.visibility = View.GONE

View File

@ -455,6 +455,11 @@ class PlayerFragment : Fragment() {
if (!relatedStreamsEnabled) toggleComments()
// prepare for autoplay
initAutoPlay()
val watchHistoryEnabled =
PreferenceHelper.getBoolean(requireContext(), "Watch_history_toggle", true)
if (watchHistoryEnabled) {
PreferenceHelper.addToWatchHistory(requireContext(), videoId!!, response)
}
}
}
}
@ -742,7 +747,7 @@ class PlayerFragment : Fragment() {
binding.playerChannel.setOnClickListener {
val activity = view.context as MainActivity
val bundle = bundleOf("channel_id" to response.uploaderUrl)
activity.navController.navigate(R.id.channel, bundle)
activity.navController.navigate(R.id.channelFragment, bundle)
activity.binding.mainMotionLayout.transitionToEnd()
binding.playerMotionLayout.transitionToEnd()
}

View File

@ -0,0 +1,45 @@
package com.github.libretube.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.adapters.WatchHistoryAdapter
import com.github.libretube.databinding.FragmentWatchHistoryBinding
import com.github.libretube.util.PreferenceHelper
class WatchHistoryFragment : Fragment() {
private val TAG = "WatchHistoryFragment"
private lateinit var binding: FragmentWatchHistoryBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentWatchHistoryBinding.inflate(layoutInflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val watchHistory = PreferenceHelper.getWatchHistory(requireContext())
val watchHistoryAdapter = WatchHistoryAdapter(watchHistory, childFragmentManager)
binding.watchHistoryRecView.adapter = watchHistoryAdapter
binding.clearHistory.setOnClickListener {
PreferenceHelper.removePreference(requireContext(), "watch_history")
watchHistoryAdapter.clear()
}
// reverse order
val linearLayoutManager = LinearLayoutManager(view.context)
linearLayoutManager.reverseLayout = true
linearLayoutManager.stackFromEnd = true
binding.watchHistoryRecView.layoutManager = linearLayoutManager
}
}

View File

@ -0,0 +1,12 @@
package com.github.libretube.obj
data class WatchHistoryItem(
val videoId: String?,
val title: String?,
val uploadDate: String?,
val uploader: String?,
val uploaderUrl: String?,
val uploaderAvatar: String?,
val thumbnailUrl: String?,
val duration: Int?
)

View File

@ -18,12 +18,20 @@ class AdvancedSettings : PreferenceFragmentCompat() {
val settingsActivity = activity as SettingsActivity
settingsActivity.binding.topBarTextView.text = getString(R.string.advanced)
// clear search history
val clearHistory = findPreference<Preference>("clear_history")
clearHistory?.setOnPreferenceClickListener {
PreferenceHelper.removePreference(requireContext(), "search_history")
true
}
// clear watch history
val clearWatchHistory = findPreference<Preference>("clear_watch_history")
clearWatchHistory?.setOnPreferenceClickListener {
PreferenceHelper.removePreference(requireContext(), "watch_history")
true
}
val resetSettings = findPreference<Preference>("reset_settings")
resetSettings?.setOnPreferenceClickListener {
showResetDialog()

View File

@ -4,11 +4,15 @@ import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import com.github.libretube.obj.CustomInstance
import com.github.libretube.obj.Streams
import com.github.libretube.obj.WatchHistoryItem
import com.google.common.reflect.TypeToken
import com.google.gson.Gson
import java.lang.reflect.Type
object PreferenceHelper {
private val TAG = "PreferenceHelper"
fun setString(context: Context, key: String?, value: String?) {
val editor = getDefaultSharedPreferencesEditor(context)
editor.putString(key, value)
@ -122,6 +126,48 @@ object PreferenceHelper {
editor.putStringSet("search_history", set).apply()
}
fun addToWatchHistory(context: Context, videoId: String, streams: Streams) {
val editor = getDefaultSharedPreferencesEditor(context)
val gson = Gson()
val watchHistoryItem = WatchHistoryItem(
videoId,
streams.title,
streams.uploadDate,
streams.uploader,
streams.uploaderUrl?.replace("/channel/", ""),
streams.uploaderAvatar,
streams.thumbnailUrl,
streams.duration
)
val watchHistory = getWatchHistory(context)
// delete entries that have the same videoId
var indexToRemove = Int.MAX_VALUE
watchHistory.forEachIndexed { index, item ->
if (item.videoId == videoId) indexToRemove = index
}
if (indexToRemove != Int.MAX_VALUE) watchHistory.removeAt(indexToRemove)
watchHistory += watchHistoryItem
val json = gson.toJson(watchHistory)
editor.putString("watch_history", json).apply()
}
fun getWatchHistory(context: Context): ArrayList<WatchHistoryItem> {
val settings = getDefaultSharedPreferences(context)
val gson = Gson()
val json: String = settings.getString("watch_history", "")!!
val type: Type = object : TypeToken<List<WatchHistoryItem?>?>() {}.type
return try {
gson.fromJson(json, type)
} catch (e: Exception) {
arrayListOf()
}
}
private fun getDefaultSharedPreferences(context: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(context)
}

View File

@ -6,6 +6,27 @@
android:layout_height="match_parent"
tools:context=".fragments.LibraryFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/showWatchHistory"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:background="?attr/selectableItemBackground"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/watch_history"
android:textSize="20sp"/>
</LinearLayout>
<RelativeLayout
android:id="@+id/loginOrRegister2"
android:layout_width="match_parent"
@ -64,6 +85,8 @@
</ScrollView>
</com.github.libretube.util.CustomSwipeToRefresh>
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/create_playlist"
android:layout_width="wrap_content"

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="8dp"
android:layout_marginBottom="8dp" >
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/watch_history"
android:layout_weight="1"
android:textSize="16sp" />
<ImageButton
android:id="@+id/clearHistory"
android:layout_gravity="center"
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_trash"/>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/watchHistoryRecView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:nestedScrollingEnabled="false"/>
</LinearLayout>

View File

@ -0,0 +1,106 @@
<?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:id="@+id/video_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:background="?android:attr/selectableItemBackground">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".45" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/thumbnail_card"
android:layout_width="0dp"
android:layout_height="0dp"
app:cardCornerRadius="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintEnd_toStartOf="@+id/guideline"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:strokeWidth="0dp">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:srcCompat="@tools:sample/backgrounds/scenic" />
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="5dp"
android:layout_marginBottom="3dp"
app:cardBackgroundColor="@color/duration_background_color"
app:cardCornerRadius="8dp"
app:cardElevation="0dp">
<TextView
android:id="@+id/thumbnail_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="6dp"
android:paddingTop="2dp"
android:paddingEnd="6dp"
android:paddingBottom="2dp"
android:textColor="@color/duration_text_color"
android:textSize="11sp"
tools:text="05:36" />
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/video_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/thumbnail_card"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/upload_date"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/thumbnail_card"
app:layout_constraintTop_toBottomOf="@id/video_title" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/channel_image"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toBottomOf="@id/upload_date" />
<TextView
android:id="@+id/channel_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/channel_image"
app:layout_constraintTop_toBottomOf="@id/upload_date" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -2,15 +2,15 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/home2"
android:id="@+id/homeFragment"
android:icon="@drawable/ic_home"
android:title="@string/startpage" />
<item
android:id="@+id/subscriptions"
android:id="@+id/subscriptionsFragment"
android:icon="@drawable/ic_subscriptions"
android:title="@string/subscriptions" />
<item
android:id="@+id/library"
android:id="@+id/libraryFragment"
android:icon="@drawable/ic_library"
android:title="@string/library" />

View File

@ -3,20 +3,20 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav"
app:startDestination="@id/home2">
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/home2"
android:id="@+id/homeFragment"
android:name="com.github.libretube.fragments.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/subscriptions"
android:id="@+id/subscriptionsFragment"
android:name="com.github.libretube.fragments.SubscriptionsFragment"
android:label="fragment_subscriptions"
tools:layout="@layout/fragment_subscriptions" />
<fragment
android:id="@+id/library"
android:id="@+id/libraryFragment"
android:name="com.github.libretube.fragments.LibraryFragment"
android:label="fragment_library"
tools:layout="@layout/fragment_library"></fragment>
@ -26,7 +26,7 @@
android:label="fragment_search"
tools:layout="@layout/fragment_search" />
<fragment
android:id="@+id/channel"
android:id="@+id/channelFragment"
android:name="com.github.libretube.fragments.ChannelFragment"
android:label="channel"
tools:layout="@layout/fragment_channel">
@ -44,7 +44,8 @@
app:argType="string" />
</fragment>
<fragment
android:id="@+id/settings"
android:name="com.github.libretube.SettingsActivity$SettingsFragment"
android:label="Settings" />
android:id="@+id/watchHistoryFragment"
android:name="com.github.libretube.fragments.WatchHistoryFragment"
android:label="@string/watch_history"
tools:layout="@layout/fragment_watch_history"/>
</navigation>

View File

@ -79,7 +79,7 @@
<string name="okay">OK</string>
<string name="history">History</string>
<string name="search_history">Search History</string>
<string name="clear_history">Clear History</string>
<string name="clear_history">Clear history</string>
<string name="music_songs">YT Music Songs</string>
<string name="music_videos">YT Music Videos</string>
<string name="music_albums">YT Music Albums</string>
@ -207,4 +207,5 @@
<string name="deleteAccount_summary">Delete your Piped account</string>
<string name="account">Account</string>
<string name="restore">Restore</string>
<string name="watch_history">Watch history</string>
</resources>

View File

@ -46,6 +46,21 @@
</PreferenceCategory>
<PreferenceCategory app:title="@string/watch_history">
<SwitchPreference
android:defaultValue="true"
android:icon="@drawable/ic_history_filled"
app:key="watch_history_toggle"
app:title="@string/watch_history" />
<Preference
android:icon="@drawable/ic_trash"
app:key="clear_watch_history"
app:title="@string/clear_history" />
</PreferenceCategory>
<PreferenceCategory>
<Preference