diff --git a/app/src/main/java/com/github/libretube/adapters/PlaylistsAdapter.kt b/app/src/main/java/com/github/libretube/adapters/PlaylistsAdapter.kt index 931e7ba0f..37e633215 100644 --- a/app/src/main/java/com/github/libretube/adapters/PlaylistsAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/PlaylistsAdapter.kt @@ -67,7 +67,7 @@ class PlaylistsAdapter( builder.show() } root.setOnClickListener { - NavigationHelper.navigatePlaylist(root.context, playlist.id) + NavigationHelper.navigatePlaylist(root.context, playlist.id, true) } root.setOnLongClickListener { 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 dad2e931f..fa48060ea 100644 --- a/app/src/main/java/com/github/libretube/adapters/SearchAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/SearchAdapter.kt @@ -171,7 +171,7 @@ class SearchAdapter( root.context.getString(R.string.videoCount, item.videos.toString()) } root.setOnClickListener { - NavigationHelper.navigatePlaylist(root.context, item.url) + NavigationHelper.navigatePlaylist(root.context, item.url, false) } root.setOnLongClickListener { val playlistId = item.url!!.toID() 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 ac09a945b..1d306390c 100644 --- a/app/src/main/java/com/github/libretube/fragments/LibraryFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/LibraryFragment.kt @@ -9,6 +9,7 @@ import android.widget.Toast import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.github.libretube.Globals import com.github.libretube.R import com.github.libretube.adapters.PlaylistsAdapter @@ -44,7 +45,7 @@ class LibraryFragment : BaseFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.playlistRecView.layoutManager = LinearLayoutManager(view.context) + binding.playlistRecView.layoutManager = LinearLayoutManager(requireContext()) token = PreferenceHelper.getToken() // hide watch history button of history disabled @@ -59,8 +60,7 @@ class LibraryFragment : BaseFragment() { } if (token != "") { - binding.boogh.visibility = View.GONE - binding.textLike.visibility = View.GONE + binding.loginOrRegister.visibility = View.GONE fetchPlaylists() binding.playlistRefresh.isEnabled = true binding.playlistRefresh.setOnRefreshListener { @@ -103,26 +103,32 @@ class LibraryFragment : BaseFragment() { binding.playlistRefresh.isRefreshing = false } if (response.isNotEmpty()) { - runOnUiThread { - binding.boogh.visibility = View.GONE - binding.textLike.visibility = View.GONE - } + binding.loginOrRegister.visibility = View.GONE + val playlistsAdapter = PlaylistsAdapter( response.toMutableList(), childFragmentManager, requireActivity() ) + + // listen for playlists to become deleted + playlistsAdapter.registerAdapterDataObserver(object : + RecyclerView.AdapterDataObserver() { + override fun onChanged() { + Log.e(TAG, playlistsAdapter.itemCount.toString()) + if (playlistsAdapter.itemCount == 0) { + binding.loginOrRegister.visibility = View.VISIBLE + } + super.onChanged() + } + }) + binding.playlistRecView.adapter = playlistsAdapter } else { runOnUiThread { - binding.boogh.apply { - visibility = View.VISIBLE - setImageResource(R.drawable.ic_list) - } - binding.textLike.apply { - visibility = View.VISIBLE - text = getString(R.string.emptyList) - } + binding.loginOrRegister.visibility = View.VISIBLE + binding.boogh.setImageResource(R.drawable.ic_list) + binding.textLike.text = getString(R.string.emptyList) } } } diff --git a/app/src/main/java/com/github/libretube/fragments/PlaylistFragment.kt b/app/src/main/java/com/github/libretube/fragments/PlaylistFragment.kt index 059a5fa21..0aa9a15e4 100644 --- a/app/src/main/java/com/github/libretube/fragments/PlaylistFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/PlaylistFragment.kt @@ -14,7 +14,6 @@ import com.github.libretube.adapters.PlaylistAdapter import com.github.libretube.databinding.FragmentPlaylistBinding import com.github.libretube.dialogs.PlaylistOptionsDialog import com.github.libretube.extensions.BaseFragment -import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.toID import retrofit2.HttpException @@ -26,7 +25,7 @@ class PlaylistFragment : BaseFragment() { private var playlistId: String? = null private var isOwner: Boolean = false - var nextPage: String? = null + private var nextPage: String? = null private var playlistAdapter: PlaylistAdapter? = null private var isLoading = true @@ -34,6 +33,7 @@ class PlaylistFragment : BaseFragment() { super.onCreate(savedInstanceState) arguments?.let { playlistId = it.getString("playlist_id") + isOwner = it.getBoolean("isOwner") } } @@ -61,7 +61,8 @@ class PlaylistFragment : BaseFragment() { lifecycleScope.launchWhenCreated { val response = try { // load locally stored playlists with the auth api - RetrofitInstance.authApi.getPlaylist(playlistId!!) + if (isOwner) RetrofitInstance.authApi.getPlaylist(playlistId!!) + else RetrofitInstance.api.getPlaylist(playlistId!!) } catch (e: IOException) { println(e) Log.e(TAG, "IOException, you might not have internet connection") @@ -75,14 +76,10 @@ class PlaylistFragment : BaseFragment() { runOnUiThread { binding.playlistProgress.visibility = View.GONE binding.playlistName.text = response.name - binding.playlistUploader.text = response.uploader - binding.playlistTotVideos.text = + binding.uploader.text = response.uploader + binding.videoCount.text = getString(R.string.videoCount, response.videos.toString()) - val user = PreferenceHelper.getUsername() - // check whether the user owns the playlist - isOwner = response.uploaderUrl == null && response.uploader.equals(user, true) - // show playlist options binding.optionsMenu.setOnClickListener { val optionsDialog = @@ -100,6 +97,16 @@ class PlaylistFragment : BaseFragment() { requireActivity(), childFragmentManager ) + + // listen for playlist items to become deleted + playlistAdapter!!.registerAdapterDataObserver(object : + RecyclerView.AdapterDataObserver() { + override fun onChanged() { + binding.videoCount.text = + getString(R.string.videoCount, playlistAdapter!!.itemCount.toString()) + } + }) + binding.playlistRecView.adapter = playlistAdapter binding.playlistScrollview.viewTreeObserver .addOnScrollChangedListener { @@ -111,8 +118,6 @@ class PlaylistFragment : BaseFragment() { isLoading = true fetchNextPage() } - } else { - // scroll view is not at bottom } } @@ -155,7 +160,10 @@ class PlaylistFragment : BaseFragment() { lifecycleScope.launchWhenCreated { val response = try { // load locally stored playlists with the auth api - RetrofitInstance.authApi.getPlaylistNextPage( + if (isOwner) RetrofitInstance.authApi.getPlaylistNextPage( + playlistId!!, + nextPage!! + ) else RetrofitInstance.api.getPlaylistNextPage( playlistId!!, nextPage!! ) diff --git a/app/src/main/java/com/github/libretube/util/ImportHelper.kt b/app/src/main/java/com/github/libretube/util/ImportHelper.kt index 1c1480caa..2ca3852a5 100644 --- a/app/src/main/java/com/github/libretube/util/ImportHelper.kt +++ b/app/src/main/java/com/github/libretube/util/ImportHelper.kt @@ -15,52 +15,48 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import java.io.BufferedReader import java.io.FileOutputStream -import java.io.InputStream import java.io.InputStreamReader -import java.util.zip.ZipEntry -import java.util.zip.ZipInputStream class ImportHelper( private val activity: Activity ) { private val TAG = "ImportHelper" + /** + * Import subscriptions by a file uri + */ fun importSubscriptions(uri: Uri?) { if (uri == null) return try { - val type = activity.contentResolver.getType(uri) - - var inputStream: InputStream? = activity.contentResolver.openInputStream(uri) var channels = ArrayList() - if (type == "application/json") { + val fileType = activity.contentResolver.getType(uri) + + if (fileType == "application/json") { + // NewPipe subscriptions format val mapper = ObjectMapper() - val json = readTextFromUri(uri) + val json = readRawTextFromUri(uri) + val subscriptions = mapper.readValue(json, NewPipeSubscriptions::class.java) channels = subscriptions.subscriptions?.map { it.url?.replace("https://www.youtube.com/channel/", "")!! } as ArrayList - } else if (type == "application/zip") { - val zis = ZipInputStream(inputStream) - var entry: ZipEntry? = zis.nextEntry - - while (entry != null) { - if (entry.name.endsWith(".csv")) { - inputStream = zis - break + } else if ( + fileType == "text/csv" || + fileType == "text/comma-separated-values" + ) { + // import subscriptions from Google/YouTube Takeout + val inputStream = activity.contentResolver.openInputStream(uri) + BufferedReader(InputStreamReader(inputStream)).use { reader -> + var line: String? = reader.readLine() + while (line != null) { + val channelId = line.substringBefore(",") + if (channelId.length == 24) channels.add(channelId) + line = reader.readLine() } - entry = zis.nextEntry - inputStream?.bufferedReader()?.readLines()?.forEach { - if (it.isNotBlank()) { - val channelId = it.substringBefore(",") - if (channelId.length == 24) { - channels.add(channelId) - } - } - } - inputStream?.close() } + inputStream?.close() } else { - throw IllegalArgumentException("unsupported type") + throw IllegalArgumentException("Unsupported file type") } CoroutineScope(Dispatchers.IO).launch { @@ -78,7 +74,7 @@ class ImportHelper( } } - private fun readTextFromUri(uri: Uri): String { + private fun readRawTextFromUri(uri: Uri): String { val stringBuilder = StringBuilder() activity.contentResolver.openInputStream(uri)?.use { inputStream -> BufferedReader(InputStreamReader(inputStream)).use { reader -> diff --git a/app/src/main/java/com/github/libretube/util/NavigationHelper.kt b/app/src/main/java/com/github/libretube/util/NavigationHelper.kt index ad03e9820..219aa3dac 100644 --- a/app/src/main/java/com/github/libretube/util/NavigationHelper.kt +++ b/app/src/main/java/com/github/libretube/util/NavigationHelper.kt @@ -10,7 +10,10 @@ import com.github.libretube.activities.MainActivity import com.github.libretube.fragments.PlayerFragment object NavigationHelper { - fun navigateChannel(context: Context, channelId: String?) { + fun navigateChannel( + context: Context, + channelId: String? + ) { if (channelId != null) { val activity = context as MainActivity val bundle = bundleOf("channel_id" to channelId) @@ -28,7 +31,11 @@ object NavigationHelper { } } - fun navigateVideo(context: Context, videoId: String?, playlistId: String? = null) { + fun navigateVideo( + context: Context, + videoId: String?, + playlistId: String? = null + ) { if (videoId != null) { val bundle = Bundle() bundle.putString("videoId", videoId.toID()) @@ -45,10 +52,16 @@ object NavigationHelper { } } - fun navigatePlaylist(context: Context, playlistId: String?) { + fun navigatePlaylist( + context: Context, + playlistId: String?, + isOwner: Boolean + ) { if (playlistId != null) { val activity = context as MainActivity - val bundle = bundleOf("playlist_id" to playlistId) + val bundle = Bundle() + bundle.putString("playlist_id", playlistId) + bundle.putBoolean("isOwner", isOwner) activity.navController.navigate(R.id.playlistFragment, bundle) } } diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index e977bb1fb..4287fa15e 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -7,7 +7,7 @@ tools:context=".fragments.LibraryFragment"> diff --git a/app/src/main/res/layout/fragment_playlist.xml b/app/src/main/res/layout/fragment_playlist.xml index e4b2c1201..821883788 100644 --- a/app/src/main/res/layout/fragment_playlist.xml +++ b/app/src/main/res/layout/fragment_playlist.xml @@ -20,7 +20,7 @@