diff --git a/app/src/main/java/com/github/libretube/activities/MainActivity.kt b/app/src/main/java/com/github/libretube/activities/MainActivity.kt index e47cc3605..eb4b5eef5 100644 --- a/app/src/main/java/com/github/libretube/activities/MainActivity.kt +++ b/app/src/main/java/com/github/libretube/activities/MainActivity.kt @@ -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!! diff --git a/app/src/main/java/com/github/libretube/adapters/CommentsAdapter.kt b/app/src/main/java/com/github/libretube/adapters/CommentsAdapter.kt index b66c0a207..70ba92bd4 100644 --- a/app/src/main/java/com/github/libretube/adapters/CommentsAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/CommentsAdapter.kt @@ -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(R.id.mainMotionLayout) diff --git a/app/src/main/java/com/github/libretube/adapters/RepliesAdapter.kt b/app/src/main/java/com/github/libretube/adapters/RepliesAdapter.kt index 534ae0267..744b864ec 100644 --- a/app/src/main/java/com/github/libretube/adapters/RepliesAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/RepliesAdapter.kt @@ -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(R.id.mainMotionLayout) diff --git a/app/src/main/java/com/github/libretube/adapters/SearchAdapter.kt b/app/src/main/java/com/github/libretube/adapters/SearchAdapter.kt index 3706143eb..e57e740a8 100644 --- a/app/src/main/java/com/github/libretube/adapters/SearchAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/SearchAdapter.kt @@ -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 } diff --git a/app/src/main/java/com/github/libretube/adapters/SubscriptionAdapter.kt b/app/src/main/java/com/github/libretube/adapters/SubscriptionAdapter.kt index a0813914c..bab7f497d 100644 --- a/app/src/main/java/com/github/libretube/adapters/SubscriptionAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/SubscriptionAdapter.kt @@ -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(R.id.mainMotionLayout) diff --git a/app/src/main/java/com/github/libretube/adapters/SubscriptionChannelAdapter.kt b/app/src/main/java/com/github/libretube/adapters/SubscriptionChannelAdapter.kt index de1e78664..a53a7b8e2 100644 --- a/app/src/main/java/com/github/libretube/adapters/SubscriptionChannelAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/SubscriptionChannelAdapter.kt @@ -48,7 +48,7 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList(R.id.mainMotionLayout) diff --git a/app/src/main/java/com/github/libretube/adapters/WatchHistoryAdapter.kt b/app/src/main/java/com/github/libretube/adapters/WatchHistoryAdapter.kt new file mode 100644 index 000000000..e3ca4b0b2 --- /dev/null +++ b/app/src/main/java/com/github/libretube/adapters/WatchHistoryAdapter.kt @@ -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, + private val childFragmentManager: FragmentManager +) : + RecyclerView.Adapter() { + 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(R.id.mainMotionLayout) + if (mainMotionLayout.progress == 0.toFloat()) { + mainMotionLayout.transitionToEnd() + activity.findViewById(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 { + } +} diff --git a/app/src/main/java/com/github/libretube/fragments/LibraryFragment.kt b/app/src/main/java/com/github/libretube/fragments/LibraryFragment.kt index b66ce1fc3..893f7249c 100644 --- a/app/src/main/java/com/github/libretube/fragments/LibraryFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/LibraryFragment.kt @@ -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 diff --git a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt index e2575af2c..c2e73a982 100644 --- a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt @@ -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() } diff --git a/app/src/main/java/com/github/libretube/fragments/WatchHistoryFragment.kt b/app/src/main/java/com/github/libretube/fragments/WatchHistoryFragment.kt new file mode 100644 index 000000000..29e01175c --- /dev/null +++ b/app/src/main/java/com/github/libretube/fragments/WatchHistoryFragment.kt @@ -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 + } +} diff --git a/app/src/main/java/com/github/libretube/obj/WatchHistoryItem.kt b/app/src/main/java/com/github/libretube/obj/WatchHistoryItem.kt new file mode 100644 index 000000000..b8eab4111 --- /dev/null +++ b/app/src/main/java/com/github/libretube/obj/WatchHistoryItem.kt @@ -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? +) diff --git a/app/src/main/java/com/github/libretube/preferences/AdvancedSettings.kt b/app/src/main/java/com/github/libretube/preferences/AdvancedSettings.kt index 3fb39c7e8..91a38d072 100644 --- a/app/src/main/java/com/github/libretube/preferences/AdvancedSettings.kt +++ b/app/src/main/java/com/github/libretube/preferences/AdvancedSettings.kt @@ -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("clear_history") clearHistory?.setOnPreferenceClickListener { PreferenceHelper.removePreference(requireContext(), "search_history") true } + // clear watch history + val clearWatchHistory = findPreference("clear_watch_history") + clearWatchHistory?.setOnPreferenceClickListener { + PreferenceHelper.removePreference(requireContext(), "watch_history") + true + } + val resetSettings = findPreference("reset_settings") resetSettings?.setOnPreferenceClickListener { showResetDialog() diff --git a/app/src/main/java/com/github/libretube/util/PreferenceHelper.kt b/app/src/main/java/com/github/libretube/util/PreferenceHelper.kt index 310a02578..045ed4626 100644 --- a/app/src/main/java/com/github/libretube/util/PreferenceHelper.kt +++ b/app/src/main/java/com/github/libretube/util/PreferenceHelper.kt @@ -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 { + val settings = getDefaultSharedPreferences(context) + val gson = Gson() + val json: String = settings.getString("watch_history", "")!! + val type: Type = object : TypeToken?>() {}.type + return try { + gson.fromJson(json, type) + } catch (e: Exception) { + arrayListOf() + } + } + private fun getDefaultSharedPreferences(context: Context): SharedPreferences { return PreferenceManager.getDefaultSharedPreferences(context) } diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index a81923388..438835540 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -6,6 +6,27 @@ android:layout_height="match_parent" tools:context=".fragments.LibraryFragment"> + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/watch_history_row.xml b/app/src/main/res/layout/watch_history_row.xml new file mode 100644 index 000000000..1de9b89eb --- /dev/null +++ b/app/src/main/res/layout/watch_history_row.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/bottom_menu.xml b/app/src/main/res/menu/bottom_menu.xml index 809c8b2ee..f5badbc88 100644 --- a/app/src/main/res/menu/bottom_menu.xml +++ b/app/src/main/res/menu/bottom_menu.xml @@ -2,15 +2,15 @@ diff --git a/app/src/main/res/navigation/nav.xml b/app/src/main/res/navigation/nav.xml index 1fc46ab85..31d75575c 100644 --- a/app/src/main/res/navigation/nav.xml +++ b/app/src/main/res/navigation/nav.xml @@ -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"> @@ -26,7 +26,7 @@ android:label="fragment_search" tools:layout="@layout/fragment_search" /> @@ -44,7 +44,8 @@ app:argType="string" /> + android:id="@+id/watchHistoryFragment" + android:name="com.github.libretube.fragments.WatchHistoryFragment" + android:label="@string/watch_history" + tools:layout="@layout/fragment_watch_history"/> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3abd7ec36..0c1c5b73e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -79,7 +79,7 @@ OK History Search History - Clear History + Clear history YT Music Songs YT Music Videos YT Music Albums @@ -207,4 +207,5 @@ Delete your Piped account Account Restore + Watch history \ No newline at end of file diff --git a/app/src/main/res/xml/advanced_settings.xml b/app/src/main/res/xml/advanced_settings.xml index a9e92a024..ad17cd058 100644 --- a/app/src/main/res/xml/advanced_settings.xml +++ b/app/src/main/res/xml/advanced_settings.xml @@ -46,6 +46,21 @@ + + + + + + + +