Fix all linting errors.

This commit is contained in:
Kavin 2022-05-21 09:02:04 +01:00
parent 940a3b20e7
commit 057cf3f02f
No known key found for this signature in database
GPG Key ID: 49451E4482CC5BCD
30 changed files with 536 additions and 253 deletions

View File

@ -2,7 +2,7 @@ package com.github.libretube
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.* import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith

View File

@ -6,15 +6,19 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.* import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.libretube.obj.PlaylistId import com.github.libretube.obj.PlaylistId
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import retrofit2.HttpException
import java.io.IOException import java.io.IOException
import retrofit2.HttpException
class AddtoPlaylistDialog : DialogFragment() { class AddtoPlaylistDialog : DialogFragment() {
private val TAG = "AddToPlaylistDialog" private val TAG = "AddToPlaylistDialog"
@ -49,6 +53,7 @@ class AddtoPlaylistDialog : DialogFragment() {
builder.create() builder.create()
} ?: throw IllegalStateException("Activity cannot be null") } ?: throw IllegalStateException("Activity cannot be null")
} }
private fun fetchPlaylists() { private fun fetchPlaylists() {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
@ -69,8 +74,11 @@ class AddtoPlaylistDialog : DialogFragment() {
for (playlist in response) { for (playlist in response) {
names.add(playlist.name!!) names.add(playlist.name!!)
} }
val arrayAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, names) val arrayAdapter =
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, names)
arrayAdapter.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item
)
spinner.adapter = arrayAdapter spinner.adapter = arrayAdapter
runOnUiThread { runOnUiThread {
button.setOnClickListener { button.setOnClickListener {
@ -83,6 +91,7 @@ class AddtoPlaylistDialog : DialogFragment() {
} }
run() run()
} }
private fun addToPlaylist(playlistId: String) { private fun addToPlaylist(playlistId: String) {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
@ -108,6 +117,7 @@ class AddtoPlaylistDialog : DialogFragment() {
} }
run() run()
} }
private fun Fragment?.runOnUiThread(action: () -> Unit) { private fun Fragment?.runOnUiThread(action: () -> Unit) {
this ?: return this ?: return
if (!isAdded) return // Fragment not attached to an Activity if (!isAdded) return // Fragment not attached to an Activity

View File

@ -3,7 +3,6 @@ package com.github.libretube
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils.substring
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -94,7 +93,10 @@ class ChannelFragment : Fragment() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE) val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
RetrofitInstance.api.isSubscribed(channel_id!!, sharedPref?.getString("token", "")!!) RetrofitInstance.api.isSubscribed(
channel_id!!,
sharedPref?.getString("token", "")!!
)
} catch (e: IOException) { } catch (e: IOException) {
println(e) println(e)
Log.e(TAG, "IOException, you might not have internet connection") Log.e(TAG, "IOException, you might not have internet connection")
@ -131,7 +133,10 @@ class ChannelFragment : Fragment() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE) val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
RetrofitInstance.api.subscribe(sharedPref?.getString("token", "")!!, Subscribe(channel_id)) RetrofitInstance.api.subscribe(
sharedPref?.getString("token", "")!!,
Subscribe(channel_id)
)
} catch (e: IOException) { } catch (e: IOException) {
println(e) println(e)
Log.e(TAG, "IOException, you might not have internet connection") Log.e(TAG, "IOException, you might not have internet connection")
@ -145,12 +150,16 @@ class ChannelFragment : Fragment() {
} }
run() run()
} }
private fun unsubscribe() { private fun unsubscribe() {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE) val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
RetrofitInstance.api.unsubscribe(sharedPref?.getString("token", "")!!, Subscribe(channel_id)) RetrofitInstance.api.unsubscribe(
sharedPref?.getString("token", "")!!,
Subscribe(channel_id)
)
} catch (e: IOException) { } catch (e: IOException) {
println(e) println(e)
Log.e(TAG, "IOException, you might not have internet connection") Log.e(TAG, "IOException, you might not have internet connection")
@ -186,12 +195,19 @@ class ChannelFragment : Fragment() {
runOnUiThread { runOnUiThread {
view.findViewById<ScrollView>(R.id.channel_scrollView).visibility = View.VISIBLE view.findViewById<ScrollView>(R.id.channel_scrollView).visibility = View.VISIBLE
val channelName = view.findViewById<TextView>(R.id.channel_name) val channelName = view.findViewById<TextView>(R.id.channel_name)
channelName.text = if (response.name?.length!! > 18) response.name.toString().substring(0, 16) + "..." else response.name channelName.text = if (response.name?.length!! > 18) response.name.toString()
.substring(0, 16) + "..." else response.name
val channelVerified = view.findViewById<ImageView>(R.id.channel_verified) val channelVerified = view.findViewById<ImageView>(R.id.channel_verified)
if (response.verified) channelVerified.visibility = View.VISIBLE if (response.verified) channelVerified.visibility = View.VISIBLE
view.findViewById<TextView>(R.id.channel_subs).text = resources.getString(R.string.subscribers, response.subscriberCount.formatShort()) view.findViewById<TextView>(R.id.channel_subs).text = resources.getString(
R.string.subscribers,
response.subscriberCount.formatShort()
)
val channelDescription = view.findViewById<TextView>(R.id.channel_description) val channelDescription = view.findViewById<TextView>(R.id.channel_description)
if (response.description?.trim() == "") channelDescription.visibility = View.GONE else channelDescription.text = response.description?.trim() if (response.description?.trim() == "")
channelDescription.visibility = View.GONE
else
channelDescription.text = response.description?.trim()
val bannerImage = view.findViewById<ImageView>(R.id.channel_banner) val bannerImage = view.findViewById<ImageView>(R.id.channel_banner)
val channelImage = view.findViewById<ImageView>(R.id.channel_image) val channelImage = view.findViewById<ImageView>(R.id.channel_image)
Picasso.get().load(response.bannerUrl).into(bannerImage) Picasso.get().load(response.bannerUrl).into(bannerImage)
@ -203,6 +219,7 @@ class ChannelFragment : Fragment() {
} }
run() run()
} }
private fun fetchNextPage() { private fun fetchNextPage() {
fun run() { fun run() {
@ -227,6 +244,7 @@ class ChannelFragment : Fragment() {
} }
run() run()
} }
private fun Fragment?.runOnUiThread(action: () -> Unit) { private fun Fragment?.runOnUiThread(action: () -> Unit) {
this ?: return this ?: return
if (!isAdded) return // Fragment not attached to an Activity if (!isAdded) return // Fragment not attached to an Activity

View File

@ -12,6 +12,7 @@ import androidx.core.os.bundleOf
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.setFragmentResult import androidx.fragment.app.setFragmentResult
import com.google.android.material.textfield.TextInputEditText
class CreatePlaylistDialog : DialogFragment() { class CreatePlaylistDialog : DialogFragment() {
override fun onCreateView( override fun onCreateView(
@ -35,7 +36,7 @@ class CreatePlaylistDialog : DialogFragment() {
dismiss() dismiss()
} }
val playlistName = rootView.findViewById<com.google.android.material.textfield.TextInputEditText>(R.id.playlist_name) val playlistName = rootView.findViewById<TextInputEditText>(R.id.playlist_name)
val createPlaylistBtn = rootView.findViewById<Button>(R.id.create_new_playlist) val createPlaylistBtn = rootView.findViewById<Button>(R.id.create_new_playlist)
createPlaylistBtn.setOnClickListener { createPlaylistBtn.setOnClickListener {
var listName = playlistName.text.toString() var listName = playlistName.text.toString()

View File

@ -6,7 +6,13 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.* import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.RadioButton
import android.widget.RadioGroup
import android.widget.Spinner
import android.widget.TextView
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -35,7 +41,11 @@ class DownloadDialog : DialogFragment() {
val inflater = requireActivity().layoutInflater val inflater = requireActivity().layoutInflater
var view: View = inflater.inflate(R.layout.dialog_download, null) var view: View = inflater.inflate(R.layout.dialog_download, null)
val videoSpinner = view.findViewById<Spinner>(R.id.video_spinner) val videoSpinner = view.findViewById<Spinner>(R.id.video_spinner)
val videoArrayAdapter = ArrayAdapter<String>(requireContext(), android.R.layout.simple_spinner_item, vidName) val videoArrayAdapter = ArrayAdapter<String>(
requireContext(),
android.R.layout.simple_spinner_item,
vidName
)
videoArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) videoArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
videoSpinner.adapter = videoArrayAdapter videoSpinner.adapter = videoArrayAdapter
videoSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { videoSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
@ -48,10 +58,15 @@ class DownloadDialog : DialogFragment() {
selectedVideo = position selectedVideo = position
Log.d(TAG, selectedVideo.toString()) Log.d(TAG, selectedVideo.toString())
} }
override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onNothingSelected(parent: AdapterView<*>?) {}
} }
val audioSpinner = view.findViewById<Spinner>(R.id.audio_spinner) val audioSpinner = view.findViewById<Spinner>(R.id.audio_spinner)
val audioArrayAdapter = ArrayAdapter<String>(requireContext(), android.R.layout.simple_spinner_item, audioName) val audioArrayAdapter = ArrayAdapter(
requireContext(),
android.R.layout.simple_spinner_item,
audioName
)
audioArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) audioArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
audioSpinner.adapter = audioArrayAdapter audioSpinner.adapter = audioArrayAdapter
audioSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { audioSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
@ -64,6 +79,7 @@ class DownloadDialog : DialogFragment() {
selectedAudio = position selectedAudio = position
Log.d(TAG, selectedAudio.toString()) Log.d(TAG, selectedAudio.toString())
} }
override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onNothingSelected(parent: AdapterView<*>?) {}
} }
val radioGroup = view.findViewById<RadioGroup>(R.id.radioGp) val radioGroup = view.findViewById<RadioGroup>(R.id.radioGp)
@ -97,6 +113,7 @@ class DownloadDialog : DialogFragment() {
builder.create() builder.create()
} ?: throw IllegalStateException("Activity cannot be null") } ?: throw IllegalStateException("Activity cannot be null")
} }
override fun onDestroy() { override fun onDestroy() {
vidName.clear() vidName.clear()
vidUrl.clear() vidUrl.clear()

View File

@ -1,6 +1,11 @@
package com.github.libretube package com.github.libretube
import android.app.* import android.app.DownloadManager
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -9,6 +14,7 @@ import android.graphics.Color
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Environment import android.os.Environment
import android.os.Environment.DIRECTORY_DOWNLOADS
import android.os.IBinder import android.os.IBinder
import android.util.Log import android.util.Log
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
@ -17,6 +23,7 @@ import com.arthenica.ffmpegkit.FFmpegKit
import java.io.File import java.io.File
var IS_DOWNLOAD_RUNNING = false var IS_DOWNLOAD_RUNNING = false
class DownloadService : Service() { class DownloadService : Service() {
val TAG = "DownloadService" val TAG = "DownloadService"
private var downloadId: Long = -1 private var downloadId: Long = -1
@ -25,6 +32,7 @@ class DownloadService : Service() {
private lateinit var audioUrl: String private lateinit var audioUrl: String
private lateinit var extension: String private lateinit var extension: String
private var duration: Int = 0 private var duration: Int = 0
// private lateinit var command: String // private lateinit var command: String
private lateinit var audioDir: File private lateinit var audioDir: File
private lateinit var videoDir: File private lateinit var videoDir: File
@ -81,6 +89,7 @@ class DownloadService : Service() {
return super.onStartCommand(intent, flags, startId) return super.onStartCommand(intent, flags, startId)
} }
override fun onBind(intent: Intent?): IBinder? { override fun onBind(intent: Intent?): IBinder? {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
@ -101,7 +110,10 @@ class DownloadService : Service() {
videoDir = File(f, "$videoId-video") videoDir = File(f, "$videoId-video")
try { try {
Log.e(TAG, "Directory make") Log.e(TAG, "Directory make")
registerReceiver(onDownloadComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) registerReceiver(
onDownloadComplete,
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
)
val request: DownloadManager.Request = val request: DownloadManager.Request =
DownloadManager.Request(Uri.parse(videoUrl)) DownloadManager.Request(Uri.parse(videoUrl))
.setTitle("Video") // Title of the Download Notification .setTitle("Video") // Title of the Download Notification
@ -113,7 +125,9 @@ class DownloadService : Service() {
val downloadManager: DownloadManager = val downloadManager: DownloadManager =
applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadId = downloadManager.enqueue(request) downloadId = downloadManager.enqueue(request)
if (audioUrl == "") { downloadId = 0L } if (audioUrl == "") {
downloadId = 0L
}
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
Log.e(TAG, "download error $e") Log.e(TAG, "download error $e")
try { try {
@ -155,9 +169,12 @@ class DownloadService : Service() {
val downloadManager: DownloadManager = val downloadManager: DownloadManager =
applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request) downloadManager.enqueue(request)
} catch (e: Exception) {} } catch (e: Exception) {
}
} else if (downloadId == 0L) { } else if (downloadId == 0L) {
val libreTube = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "LibreTube") val libreTube = File(
Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS), "LibreTube"
)
if (!libreTube.exists()) { if (!libreTube.exists()) {
libreTube.mkdirs() libreTube.mkdirs()
Log.e(TAG, "libreTube Directory make") Log.e(TAG, "libreTube Directory make")
@ -191,7 +208,8 @@ class DownloadService : Service() {
session.failStackTrace session.failStackTrace
) )
) )
val path = applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) val path =
applicationContext.getExternalFilesDir(DIRECTORY_DOWNLOADS)
val folder_main = ".tmp" val folder_main = ".tmp"
val f = File(path, folder_main) val f = File(path, folder_main)
f.deleteRecursively() f.deleteRecursively()
@ -243,10 +261,12 @@ class DownloadService : Service() {
notificationManager.createNotificationChannel(channel) notificationManager.createNotificationChannel(channel)
} }
} }
override fun onDestroy() { override fun onDestroy() {
try { try {
unregisterReceiver(onDownloadComplete) unregisterReceiver(onDownloadComplete)
} catch (e: Exception) {} } catch (e: Exception) {
}
IS_DOWNLOAD_RUNNING = false IS_DOWNLOAD_RUNNING = false
Log.d(TAG, "dl finished!") Log.d(TAG, "dl finished!")
super.onDestroy() super.onDestroy()

View File

@ -15,7 +15,6 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.github.libretube.adapters.TrendingAdapter import com.github.libretube.adapters.TrendingAdapter
import java.io.IOException import java.io.IOException
import okhttp3.*
import retrofit2.HttpException import retrofit2.HttpException
class Home : Fragment() { class Home : Fragment() {
@ -42,7 +41,10 @@ class Home : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val recyclerView = view.findViewById<RecyclerView>(R.id.recview) val recyclerView = view.findViewById<RecyclerView>(R.id.recview)
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
val grid = sharedPreferences.getString("grid", resources.getInteger(R.integer.grid_items).toString())!! val grid = sharedPreferences.getString(
"grid",
resources.getInteger(R.integer.grid_items).toString()
)!!
recyclerView.layoutManager = GridLayoutManager(view.context, grid.toInt()) recyclerView.layoutManager = GridLayoutManager(view.context, grid.toInt())
val progressbar = view.findViewById<ProgressBar>(R.id.progressBar) val progressbar = view.findViewById<ProgressBar>(R.id.progressBar)
fetchJson(progressbar, recyclerView) fetchJson(progressbar, recyclerView)
@ -58,7 +60,8 @@ class Home : Fragment() {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(requireContext())
RetrofitInstance.api.getTrending(sharedPreferences.getString("region", "US")!!) RetrofitInstance.api.getTrending(sharedPreferences.getString("region", "US")!!)
} catch (e: IOException) { } catch (e: IOException) {
println(e) println(e)
@ -80,6 +83,7 @@ class Home : Fragment() {
} }
run() run()
} }
private fun Fragment?.runOnUiThread(action: () -> Unit) { private fun Fragment?.runOnUiThread(action: () -> Unit) {
this ?: return this ?: return
if (!isAdded) return // Fragment not attached to an Activity if (!isAdded) return // Fragment not attached to an Activity

View File

@ -52,8 +52,8 @@ class Library : Fragment() {
view.findViewById<ImageView>(R.id.boogh2).visibility = View.GONE view.findViewById<ImageView>(R.id.boogh2).visibility = View.GONE
view.findViewById<TextView>(R.id.textLike2).visibility = View.GONE view.findViewById<TextView>(R.id.textLike2).visibility = View.GONE
fetchPlaylists(view) fetchPlaylists(view)
refreshLayout?.isEnabled = true refreshLayout.isEnabled = true
refreshLayout?.setOnRefreshListener { refreshLayout.setOnRefreshListener {
Log.d(TAG, "hmm") Log.d(TAG, "hmm")
fetchPlaylists(view) fetchPlaylists(view)
} }
@ -66,7 +66,7 @@ class Library : Fragment() {
createPlaylist("$playlistName", view) createPlaylist("$playlistName", view)
} }
} else { } else {
refreshLayout?.isEnabled = false refreshLayout.isEnabled = false
view.findViewById<Button>(R.id.create_playlist).visibility = View.GONE view.findViewById<Button>(R.id.create_playlist).visibility = View.GONE
with(view.findViewById<ImageView>(R.id.boogh2)) { with(view.findViewById<ImageView>(R.id.boogh2)) {
visibility = View.VISIBLE visibility = View.VISIBLE
@ -81,7 +81,7 @@ class Library : Fragment() {
private fun fetchPlaylists(view: View) { private fun fetchPlaylists(view: View) {
fun run() { fun run() {
refreshLayout?.isRefreshing = true refreshLayout.isRefreshing = true
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
RetrofitInstance.api.playlists(token) RetrofitInstance.api.playlists(token)
@ -95,7 +95,7 @@ class Library : Fragment() {
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated return@launchWhenCreated
} finally { } finally {
refreshLayout?.isRefreshing = false refreshLayout.isRefreshing = false
} }
if (response.isNotEmpty()) { if (response.isNotEmpty()) {
runOnUiThread { runOnUiThread {
@ -106,7 +106,10 @@ class Library : Fragment() {
visibility = View.GONE visibility = View.GONE
} }
} }
val playlistsAdapter = PlaylistsAdapter(response.toMutableList(), requireActivity()) val playlistsAdapter = PlaylistsAdapter(
response.toMutableList(),
requireActivity()
)
playlistRecyclerView.adapter = playlistsAdapter playlistRecyclerView.adapter = playlistsAdapter
} else { } else {
runOnUiThread { runOnUiThread {
@ -124,6 +127,7 @@ class Library : Fragment() {
} }
run() run()
} }
private fun createPlaylist(name: String, view: View) { private fun createPlaylist(name: String, view: View) {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {

View File

@ -15,8 +15,8 @@ import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.libretube.obj.Login import com.github.libretube.obj.Login
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import retrofit2.HttpException
import java.io.IOException import java.io.IOException
import retrofit2.HttpException
class LoginDialog : DialogFragment() { class LoginDialog : DialogFragment() {
private val TAG = "LoginDialog" private val TAG = "LoginDialog"
@ -35,7 +35,8 @@ class LoginDialog : DialogFragment() {
val sharedPref2 = context?.getSharedPreferences("username", Context.MODE_PRIVATE) val sharedPref2 = context?.getSharedPreferences("username", Context.MODE_PRIVATE)
val user = sharedPref2?.getString("username", "") val user = sharedPref2?.getString("username", "")
view = inflater.inflate(R.layout.dialog_logout, null) view = inflater.inflate(R.layout.dialog_logout, null)
view.findViewById<TextView>(R.id.user).text = view.findViewById<TextView>(R.id.user).text.toString() + " (" + user + ")" view.findViewById<TextView>(R.id.user).text =
view.findViewById<TextView>(R.id.user).text.toString() + " (" + user + ")"
view.findViewById<Button>(R.id.logout).setOnClickListener { view.findViewById<Button>(R.id.logout).setOnClickListener {
Toast.makeText(context, R.string.loggedout, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.loggedout, Toast.LENGTH_SHORT).show()
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE) val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
@ -76,11 +77,11 @@ class LoginDialog : DialogFragment() {
) )
view.findViewById<TextView>(R.id.title).text = appName view.findViewById<TextView>(R.id.title).text = appName
builder.setView(view) builder.setView(view)
builder.create() builder.create()
} ?: throw IllegalStateException("Activity cannot be null") } ?: throw IllegalStateException("Activity cannot be null")
} }
private fun login(login: Login) { private fun login(login: Login) {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
@ -108,7 +109,8 @@ class LoginDialog : DialogFragment() {
putString("token", response.token) putString("token", response.token)
apply() apply()
} }
val sharedPref2 = context?.getSharedPreferences("username", Context.MODE_PRIVATE) val sharedPref2 =
context?.getSharedPreferences("username", Context.MODE_PRIVATE)
with(sharedPref2!!.edit()) { with(sharedPref2!!.edit()) {
putString("username", login.username) putString("username", login.username)
apply() apply()
@ -119,6 +121,7 @@ class LoginDialog : DialogFragment() {
} }
run() run()
} }
private fun register(login: Login) { private fun register(login: Login) {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
@ -146,7 +149,8 @@ class LoginDialog : DialogFragment() {
putString("token", response.token) putString("token", response.token)
apply() apply()
} }
val sharedPref2 = context?.getSharedPreferences("username", Context.MODE_PRIVATE) val sharedPref2 =
context?.getSharedPreferences("username", Context.MODE_PRIVATE)
with(sharedPref2!!.edit()) { with(sharedPref2!!.edit()) {
putString("username", login.username) putString("username", login.username)
apply() apply()

View File

@ -12,7 +12,10 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.util.Log import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.* import android.view.View
import android.view.WindowInsets
import android.view.WindowInsetsController
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Button import android.widget.Button
import android.widget.LinearLayout import android.widget.LinearLayout
@ -40,25 +43,33 @@ class MainActivity : AppCompatActivity() {
DynamicColors.applyToActivityIfAvailable(this) DynamicColors.applyToActivityIfAvailable(this)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
RetrofitInstance.url = sharedPreferences.getString("instance", "https://pipedapi.kavin.rocks/")!! RetrofitInstance.url =
SponsorBlockSettings.sponsorBlockEnabled = sharedPreferences.getBoolean("sponsorblock_enabled_key", false) sharedPreferences.getString("instance", "https://pipedapi.kavin.rocks/")!!
SponsorBlockSettings.introEnabled = sharedPreferences.getBoolean("intro_category_key", false) SponsorBlockSettings.sponsorBlockEnabled =
SponsorBlockSettings.selfPromoEnabled = sharedPreferences.getBoolean("selfpromo_category_key", false) sharedPreferences.getBoolean("sponsorblock_enabled_key", false)
SponsorBlockSettings.interactionEnabled = sharedPreferences.getBoolean("interaction_category_key", false) SponsorBlockSettings.introEnabled =
SponsorBlockSettings.sponsorsEnabled = sharedPreferences.getBoolean("sponsors_category_key", false) sharedPreferences.getBoolean("intro_category_key", false)
SponsorBlockSettings.outroEnabled = sharedPreferences.getBoolean("outro_category_key", false) SponsorBlockSettings.selfPromoEnabled =
sharedPreferences.getBoolean("selfpromo_category_key", false)
SponsorBlockSettings.interactionEnabled =
sharedPreferences.getBoolean("interaction_category_key", false)
SponsorBlockSettings.sponsorsEnabled =
sharedPreferences.getBoolean("sponsors_category_key", false)
SponsorBlockSettings.outroEnabled =
sharedPreferences.getBoolean("outro_category_key", false)
updateAccentColor(this) updateAccentColor(this)
updateThemeMode(this) updateThemeMode(this)
updateLanguage(this) updateLanguage(this)
val connectivityManager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val connectivityManager =
this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkInfo = connectivityManager.activeNetworkInfo val networkInfo = connectivityManager.activeNetworkInfo
val isConnected = networkInfo != null && networkInfo.isConnected val isConnected = networkInfo != null && networkInfo.isConnected
if (!isConnected) { if (!isConnected) {
setContentView(R.layout.activity_nointernet) setContentView(R.layout.activity_nointernet)
findViewById<Button>(R.id.retry_button).setOnClickListener() { findViewById<Button>(R.id.retry_button).setOnClickListener {
recreate() recreate()
} }
} else { } else {
@ -137,10 +148,13 @@ class MainActivity : AppCompatActivity() {
if (data.host != null) { if (data.host != null) {
if (data.path != null) { if (data.path != null) {
// channel // channel
if (data.path!!.contains("/channel/") || data.path!!.contains("/c/") || data.path!!.contains("/user/")) { if (data.path!!.contains("/channel/") ||
data.path!!.contains("/c/") ||
data.path!!.contains("/user/")
) {
var channel = data.path var channel = data.path
channel = channel!!.replace("/c/", "") channel = channel!!.replace("/c/", "")
channel = channel!!.replace("/user/", "") channel = channel.replace("/user/", "")
val bundle = bundleOf("channel_id" to channel) val bundle = bundleOf("channel_id" to channel)
navController.navigate(R.id.channel, bundle) navController.navigate(R.id.channel, bundle)
} else if (data.path!!.contains("/playlist")) { } else if (data.path!!.contains("/playlist")) {
@ -157,11 +171,17 @@ class MainActivity : AppCompatActivity() {
playlist = playlist.replace("list=", "") playlist = playlist.replace("list=", "")
val bundle = bundleOf("playlist_id" to playlist) val bundle = bundleOf("playlist_id" to playlist)
navController.navigate(R.id.playlistFragment, bundle) navController.navigate(R.id.playlistFragment, bundle)
} else if (data.path!!.contains("/shorts/") || data.path!!.contains("/embed/") || data.path!!.contains("/v/")) { } else if (data.path!!.contains("/shorts/") ||
var watch = data.path!!.replace("/shorts/", "").replace("/v/", "").replace("/embed/", "") data.path!!.contains("/embed/") ||
var bundle = Bundle() data.path!!.contains("/v/")
) {
val watch = data.path!!
.replace("/shorts/", "")
.replace("/v/", "")
.replace("/embed/", "")
val bundle = Bundle()
bundle.putString("videoId", watch) bundle.putString("videoId", watch)
var frag = PlayerFragment() val frag = PlayerFragment()
frag.arguments = bundle frag.arguments = bundle
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.remove(PlayerFragment()) .remove(PlayerFragment())
@ -241,7 +261,9 @@ class MainActivity : AppCompatActivity() {
isFullScreen = false isFullScreen = false
} else { } else {
navController.popBackStack() navController.popBackStack()
if (navController.currentBackStackEntry == null && (parent as View).id != R.id.settings) { if (navController.currentBackStackEntry == null &&
(parent as View).id != R.id.settings
) {
super.onBackPressed() super.onBackPressed()
} }
} }
@ -250,6 +272,7 @@ class MainActivity : AppCompatActivity() {
moveTaskToBack(true) moveTaskToBack(true)
} }
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
val orientation = newConfig.orientation val orientation = newConfig.orientation
@ -261,6 +284,7 @@ class MainActivity : AppCompatActivity() {
setFullscreen() setFullscreen()
} }
} }
private fun setFullscreen() { private fun setFullscreen() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode = window.attributes.layoutInDisplayCutoutMode =
@ -285,6 +309,7 @@ class MainActivity : AppCompatActivity() {
) )
} }
} }
private fun unsetFullscreen() { private fun unsetFullscreen() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode = window.attributes.layoutInDisplayCutoutMode =
@ -299,7 +324,8 @@ class MainActivity : AppCompatActivity() {
} }
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_VISIBLE or View.SYSTEM_UI_FLAG_LAYOUT_STABLE) window.decorView.systemUiVisibility =
(View.SYSTEM_UI_FLAG_VISIBLE or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
} }
} }
@ -310,6 +336,7 @@ class MainActivity : AppCompatActivity() {
} }
} }
} }
fun Fragment.hideKeyboard() { fun Fragment.hideKeyboard() {
view?.let { activity?.hideKeyboard(it) } view?.let { activity?.hideKeyboard(it) }
} }

View File

@ -1,7 +1,28 @@
package com.github.libretube package com.github.libretube
import com.github.libretube.obj.* import com.github.libretube.obj.Channel
import retrofit2.http.* import com.github.libretube.obj.CommentsPage
import com.github.libretube.obj.Instances
import com.github.libretube.obj.Login
import com.github.libretube.obj.Message
import com.github.libretube.obj.Playlist
import com.github.libretube.obj.PlaylistId
import com.github.libretube.obj.Playlists
import com.github.libretube.obj.SearchResult
import com.github.libretube.obj.Segments
import com.github.libretube.obj.StreamItem
import com.github.libretube.obj.Streams
import com.github.libretube.obj.Subscribe
import com.github.libretube.obj.Subscribed
import com.github.libretube.obj.Subscription
import com.github.libretube.obj.Token
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
import retrofit2.http.Url
interface PipedApi { interface PipedApi {
@GET("trending") @GET("trending")
@ -14,7 +35,10 @@ interface PipedApi {
suspend fun getComments(@Path("videoId") videoId: String): CommentsPage suspend fun getComments(@Path("videoId") videoId: String): CommentsPage
@GET("sponsors/{videoId}") @GET("sponsors/{videoId}")
suspend fun getSegments(@Path("videoId") videoId: String, @Query("category") category: String): Segments suspend fun getSegments(
@Path("videoId") videoId: String,
@Query("category") category: String
): Segments
@GET("nextpage/comments/{videoId}") @GET("nextpage/comments/{videoId}")
suspend fun getCommentsNextPage( suspend fun getCommentsNextPage(
@ -75,10 +99,16 @@ interface PipedApi {
suspend fun subscriptions(@Header("Authorization") token: String): List<Subscription> suspend fun subscriptions(@Header("Authorization") token: String): List<Subscription>
@POST("subscribe") @POST("subscribe")
suspend fun subscribe(@Header("Authorization") token: String, @Body subscribe: Subscribe): Message suspend fun subscribe(
@Header("Authorization") token: String,
@Body subscribe: Subscribe
): Message
@POST("unsubscribe") @POST("unsubscribe")
suspend fun unsubscribe(@Header("Authorization") token: String, @Body subscribe: Subscribe): Message suspend fun unsubscribe(
@Header("Authorization") token: String,
@Body subscribe: Subscribe
): Message
@POST("import") @POST("import")
suspend fun importSubscriptions( suspend fun importSubscriptions(
@ -91,13 +121,22 @@ interface PipedApi {
suspend fun playlists(@Header("Authorization") token: String): List<Playlists> suspend fun playlists(@Header("Authorization") token: String): List<Playlists>
@POST("user/playlists/delete") @POST("user/playlists/delete")
suspend fun deletePlaylist(@Header("Authorization") token: String, @Body playlistId: PlaylistId): Message suspend fun deletePlaylist(
@Header("Authorization") token: String,
@Body playlistId: PlaylistId
): Message
@POST("user/playlists/create") @POST("user/playlists/create")
suspend fun createPlaylist(@Header("Authorization") token: String, @Body name: Playlists): PlaylistId suspend fun createPlaylist(
@Header("Authorization") token: String,
@Body name: Playlists
): PlaylistId
@POST("user/playlists/add") @POST("user/playlists/add")
suspend fun addToPlaylist(@Header("Authorization") token: String, @Body playlistId: PlaylistId): Message suspend fun addToPlaylist(
@Header("Authorization") token: String,
@Body playlistId: PlaylistId
): Message
@POST("user/playlists/remove") @POST("user/playlists/remove")
suspend fun removeFromPlaylist( suspend fun removeFromPlaylist(

View File

@ -1,12 +1,8 @@
package com.github.libretube package com.github.libretube
import android.app.Activity import android.app.Activity
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import com.google.android.material.color.DynamicColors
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import com.google.android.material.color.DynamicColors
class Player : Activity() { class Player : Activity() {

View File

@ -22,7 +22,14 @@ import android.view.ViewGroup
import android.view.animation.Animation import android.view.animation.Animation
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation import android.view.animation.RotateAnimation
import android.widget.* import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.ScrollView
import android.widget.TextView
import android.widget.Toast
import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
@ -61,12 +68,12 @@ import com.google.android.exoplayer2.util.RepeatModeUtil
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import org.chromium.net.CronetEngine
import retrofit2.HttpException
import java.io.IOException import java.io.IOException
import java.net.URLEncoder import java.net.URLEncoder
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.abs import kotlin.math.abs
import org.chromium.net.CronetEngine
import retrofit2.HttpException
var isFullScreen = false var isFullScreen = false
@ -207,34 +214,37 @@ class PlayerFragment : Fragment() {
} }
view.findViewById<RelativeLayout>(R.id.player_title_layout).setOnClickListener { view.findViewById<RelativeLayout>(R.id.player_title_layout).setOnClickListener {
if (playerDescription.isVisible){ if (playerDescription.isVisible) {
val image = view.findViewById<ImageView>(R.id.player_description_arrow) val image = view.findViewById<ImageView>(R.id.player_description_arrow)
image.clearAnimation() image.clearAnimation()
playerDescription.visibility = View.GONE playerDescription.visibility = View.GONE
} else { } else {
//toggle button // toggle button
val rotate = RotateAnimation( val rotate = RotateAnimation(
0F, 0F,
180F, 180F,
Animation.RELATIVE_TO_SELF, Animation.RELATIVE_TO_SELF,
0.5f, 0.5f,
Animation.RELATIVE_TO_SELF, Animation.RELATIVE_TO_SELF,
0.5f 0.5f
) )
rotate.duration = 100 rotate.duration = 100
rotate.interpolator = LinearInterpolator() rotate.interpolator = LinearInterpolator()
rotate.fillAfter = true rotate.fillAfter = true
val image = view.findViewById<ImageView>(R.id.player_description_arrow) val image = view.findViewById<ImageView>(R.id.player_description_arrow)
image.startAnimation(rotate) image.startAnimation(rotate)
playerDescription.visibility = View.VISIBLE playerDescription.visibility = View.VISIBLE
} }
} }
view.findViewById<com.google.android.material.card.MaterialCardView>(R.id.comments_toggle).setOnClickListener { view.findViewById<com.google.android.material.card.MaterialCardView>(R.id.comments_toggle)
commentsRecView.visibility = if (commentsRecView.isVisible) View.GONE else View.VISIBLE .setOnClickListener {
relatedRecView.visibility = if (relatedRecView.isVisible) View.GONE else View.VISIBLE commentsRecView.visibility =
if (!commentsLoaded!!) fetchComments() if (commentsRecView.isVisible) View.GONE else View.VISIBLE
} relatedRecView.visibility =
if (relatedRecView.isVisible) View.GONE else View.VISIBLE
if (!commentsLoaded!!) fetchComments()
}
// FullScreen button trigger // FullScreen button trigger
view.findViewById<ImageButton>(R.id.fullscreen).setOnClickListener { view.findViewById<ImageButton>(R.id.fullscreen).setOnClickListener {
@ -266,8 +276,9 @@ class PlayerFragment : Fragment() {
scrollView.viewTreeObserver scrollView.viewTreeObserver
.addOnScrollChangedListener { .addOnScrollChangedListener {
if (scrollView.getChildAt(0).bottom if (scrollView.getChildAt(0).bottom
== (scrollView.height + scrollView.scrollY) == (scrollView.height + scrollView.scrollY) &&
&& nextPage != null) { nextPage != null
) {
fetchNextComments() fetchNextComments()
} }
} }
@ -276,8 +287,8 @@ class PlayerFragment : Fragment() {
commentsRecView.layoutManager = LinearLayoutManager(view.context) commentsRecView.layoutManager = LinearLayoutManager(view.context)
commentsRecView.setItemViewCacheSize(20) commentsRecView.setItemViewCacheSize(20)
commentsRecView.setDrawingCacheEnabled(true) commentsRecView.isDrawingCacheEnabled = true
commentsRecView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH) commentsRecView.drawingCacheQuality = View.DRAWING_CACHE_QUALITY_HIGH
relatedRecView = view.findViewById(R.id.player_recView) relatedRecView = view.findViewById(R.id.player_recView)
relatedRecView.layoutManager = relatedRecView.layoutManager =
@ -287,7 +298,8 @@ class PlayerFragment : Fragment() {
override fun onStop() { override fun onStop() {
try { try {
exoPlayer.release() exoPlayer.release()
}catch (e: Exception){} } catch (e: Exception) {
}
super.onStop() super.onStop()
} }
@ -310,7 +322,7 @@ class PlayerFragment : Fragment() {
segmentData.segments.forEach { segment: Segment -> segmentData.segments.forEach { segment: Segment ->
val segmentStart = (segment.segment!![0] * 1000.0f).toLong() val segmentStart = (segment.segment!![0] * 1000.0f).toLong()
val segmentEnd = (segment.segment!![1] * 1000.0f).toLong() val segmentEnd = (segment.segment[1] * 1000.0f).toLong()
val currentPosition = exoPlayer.currentPosition val currentPosition = exoPlayer.currentPosition
if (currentPosition in segmentStart until segmentEnd) { if (currentPosition in segmentStart until segmentEnd) {
Toast.makeText(context, R.string.segment_skipped, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.segment_skipped, Toast.LENGTH_SHORT).show()
@ -383,10 +395,10 @@ class PlayerFragment : Fragment() {
runOnUiThread { runOnUiThread {
var subtitle = mutableListOf<SubtitleConfiguration>() var subtitle = mutableListOf<SubtitleConfiguration>()
if (response.subtitles!!.isNotEmpty()) { if (response.subtitles!!.isNotEmpty()) {
subtitle?.add( subtitle.add(
SubtitleConfiguration.Builder(response.subtitles!![0].url!!.toUri()) SubtitleConfiguration.Builder(response.subtitles[0].url!!.toUri())
.setMimeType(response.subtitles!![0].mimeType!!) // The correct MIME type (required). .setMimeType(response.subtitles[0].mimeType!!) // The correct MIME type (required).
.setLanguage(response.subtitles!![0].code) // The subtitle language (optional). .setLanguage(response.subtitles[0].code) // The subtitle language (optional).
.build() .build()
) )
} }
@ -426,7 +438,7 @@ class PlayerFragment : Fragment() {
defres != "" -> { defres != "" -> {
var foundRes = false var foundRes = false
run lit@{ run lit@{
response.videoStreams!!.forEachIndexed { index, pipedStream -> response.videoStreams.forEachIndexed { index, pipedStream ->
if (pipedStream.quality!!.contains(defres)) { if (pipedStream.quality!!.contains(defres)) {
foundRes = true foundRes = true
val dataSourceFactory: DataSource.Factory = val dataSourceFactory: DataSource.Factory =
@ -440,13 +452,18 @@ class PlayerFragment : Fragment() {
.createMediaSource(videoItem) .createMediaSource(videoItem)
var audioSource: MediaSource = var audioSource: MediaSource =
DefaultMediaSourceFactory(dataSourceFactory) DefaultMediaSourceFactory(dataSourceFactory)
.createMediaSource(fromUri(response.audioStreams!![0].url!!)) .createMediaSource(
if (response.videoStreams[index].quality == "720p" || response.videoStreams[index].quality == "1080p" || response.videoStreams[index].quality == "480p") { fromUri(response.audioStreams!![0].url!!)
)
if (response.videoStreams[index].quality == "720p" ||
response.videoStreams[index].quality == "1080p" ||
response.videoStreams[index].quality == "480p"
) {
audioSource = audioSource =
ProgressiveMediaSource.Factory(dataSourceFactory) ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource( .createMediaSource(
fromUri( fromUri(
response.audioStreams!![ response.audioStreams[
getMostBitRate( getMostBitRate(
response.audioStreams response.audioStreams
) )
@ -490,11 +507,14 @@ class PlayerFragment : Fragment() {
var audioSource: MediaSource = var audioSource: MediaSource =
DefaultMediaSourceFactory(dataSourceFactory) DefaultMediaSourceFactory(dataSourceFactory)
.createMediaSource(fromUri(response.audioStreams!![0].url!!)) .createMediaSource(fromUri(response.audioStreams!![0].url!!))
if (response.videoStreams[0].quality == "720p" || response.videoStreams[0].quality == "1080p" || response.videoStreams[0].quality == "480p") { if (response.videoStreams[0].quality == "720p" ||
response.videoStreams[0].quality == "1080p" ||
response.videoStreams[0].quality == "480p"
) {
audioSource = ProgressiveMediaSource.Factory(dataSourceFactory) audioSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource( .createMediaSource(
fromUri( fromUri(
response.audioStreams!![ response.audioStreams[
getMostBitRate( getMostBitRate(
response.audioStreams response.audioStreams
) )
@ -528,13 +548,15 @@ class PlayerFragment : Fragment() {
videosNameArray, videosNameArray,
DialogInterface.OnClickListener { _, which -> DialogInterface.OnClickListener { _, which ->
whichQuality = which whichQuality = which
if (response.subtitles!!.isNotEmpty()) { if (response.subtitles.isNotEmpty()) {
var subtitle = var subtitle =
mutableListOf<SubtitleConfiguration>() mutableListOf<SubtitleConfiguration>()
subtitle?.add( subtitle.add(
SubtitleConfiguration.Builder(response.subtitles!![0].url!!.toUri()) SubtitleConfiguration.Builder(
.setMimeType(response.subtitles!![0].mimeType!!) // The correct MIME type (required). response.subtitles[0].url!!.toUri()
.setLanguage(response.subtitles!![0].code) // The subtitle language (optional). )
.setMimeType(response.subtitles[0].mimeType!!) // The correct MIME type (required).
.setLanguage(response.subtitles[0].code) // The subtitle language (optional).
.build() .build()
) )
} }
@ -556,13 +578,18 @@ class PlayerFragment : Fragment() {
.createMediaSource(videoItem) .createMediaSource(videoItem)
var audioSource: MediaSource = var audioSource: MediaSource =
DefaultMediaSourceFactory(dataSourceFactory) DefaultMediaSourceFactory(dataSourceFactory)
.createMediaSource(fromUri(response.audioStreams!![0].url!!)) .createMediaSource(
if (response.videoStreams[which - 1].quality == "720p" || response.videoStreams[which - 1].quality == "1080p" || response.videoStreams[which - 1].quality == "480p") { fromUri(response.audioStreams!![0].url!!)
)
if (response.videoStreams[which - 1].quality == "720p" ||
response.videoStreams[which - 1].quality == "1080p" ||
response.videoStreams[which - 1].quality == "480p"
) {
audioSource = audioSource =
ProgressiveMediaSource.Factory(dataSourceFactory) ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource( .createMediaSource(
fromUri( fromUri(
response.audioStreams!![ response.audioStreams[
getMostBitRate( getMostBitRate(
response.audioStreams response.audioStreams
) )
@ -579,14 +606,17 @@ class PlayerFragment : Fragment() {
videosNameArray[which] videosNameArray[which]
} }
) )
val dialog = builder?.create() val dialog = builder.create()
dialog?.show() dialog.show()
} }
// Listener for play and pause icon change // Listener for play and pause icon change
exoPlayer!!.addListener(object : com.google.android.exoplayer2.Player.Listener { exoPlayer.addListener(object : com.google.android.exoplayer2.Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) { override fun onIsPlayingChanged(isPlaying: Boolean) {
if (isPlaying && SponsorBlockSettings.sponsorBlockEnabled) { if (isPlaying && SponsorBlockSettings.sponsorBlockEnabled) {
exoPlayerView.postDelayed(this@PlayerFragment::checkForSegments, 100) exoPlayerView.postDelayed(
this@PlayerFragment::checkForSegments,
100
)
} }
} }
@ -596,7 +626,8 @@ class PlayerFragment : Fragment() {
) { ) {
exoPlayerView.keepScreenOn = !( exoPlayerView.keepScreenOn = !(
playbackState == Player.STATE_IDLE || playbackState == Player.STATE_ENDED || playbackState == Player.STATE_IDLE ||
playbackState == Player.STATE_ENDED ||
!playWhenReady !playWhenReady
) )
@ -692,7 +723,8 @@ class PlayerFragment : Fragment() {
if (ActivityCompat.checkSelfPermission( if (ActivityCompat.checkSelfPermission(
requireContext(), requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission( ) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(
requireContext(), requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED ) != PackageManager.PERMISSION_GRANTED
@ -711,7 +743,7 @@ class PlayerFragment : Fragment() {
vidName.add("No video") vidName.add("No video")
var vidUrl = arrayListOf<String>() var vidUrl = arrayListOf<String>()
vidUrl.add("") vidUrl.add("")
for (vid in response.videoStreams!!) { for (vid in response.videoStreams) {
val name = vid.quality + " " + vid.format val name = vid.quality + " " + vid.format
vidName.add(name) vidName.add(name)
vidUrl.add(vid.url!!) vidUrl.add(vid.url!!)
@ -874,10 +906,6 @@ class PlayerFragment : Fragment() {
return index return index
} }
override fun onResume() {
super.onResume()
}
private fun fetchComments() { private fun fetchComments() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val commentsResponse = try { val commentsResponse = try {
@ -914,7 +942,7 @@ class PlayerFragment : Fragment() {
return@launchWhenCreated return@launchWhenCreated
} }
nextPage = response.nextpage nextPage = response.nextpage
commentsAdapter?.updateItems(response.comments!!) commentsAdapter?.updateItems(response.comments)
isLoading = false isLoading = false
} }
} }
@ -950,7 +978,12 @@ class PlayerFragment : Fragment() {
val scrollView = view?.findViewById<ScrollView>(R.id.player_scrollView) val scrollView = view?.findViewById<ScrollView>(R.id.player_scrollView)
scrollView?.getHitRect(bounds) scrollView?.getHitRect(bounds)
if (SDK_INT >= Build.VERSION_CODES.N && exoPlayer.isPlaying && (scrollView?.getLocalVisibleRect(bounds) == true || isFullScreen)) { if (SDK_INT >= Build.VERSION_CODES.N &&
exoPlayer.isPlaying && (
scrollView?.getLocalVisibleRect(bounds) == true ||
isFullScreen
)
) {
requireActivity().enterPictureInPictureMode() requireActivity().enterPictureInPictureMode()
} }
} }

View File

@ -48,6 +48,7 @@ class PlaylistFragment : Fragment() {
fetchPlaylist(view) fetchPlaylist(view)
} }
private fun fetchPlaylist(view: View) { private fun fetchPlaylist(view: View) {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
@ -66,14 +67,21 @@ class PlaylistFragment : Fragment() {
runOnUiThread { runOnUiThread {
view.findViewById<TextView>(R.id.playlist_name).text = response.name view.findViewById<TextView>(R.id.playlist_name).text = response.name
view.findViewById<TextView>(R.id.playlist_uploader).text = response.uploader view.findViewById<TextView>(R.id.playlist_uploader).text = response.uploader
view.findViewById<TextView>(R.id.playlist_totVideos).text = response.videos.toString() + " Videos" view.findViewById<TextView>(R.id.playlist_totVideos).text =
val sharedPref2 = context?.getSharedPreferences("username", Context.MODE_PRIVATE) response.videos.toString() + " Videos"
val sharedPref2 =
context?.getSharedPreferences("username", Context.MODE_PRIVATE)
val user = sharedPref2?.getString("username", "") val user = sharedPref2?.getString("username", "")
var isOwner = false var isOwner = false
if (response.uploaderUrl == null && response.uploader.equals(user, true)) { if (response.uploaderUrl == null && response.uploader.equals(user, true)) {
isOwner = true isOwner = true
} }
playlistAdapter = PlaylistAdapter(response.relatedStreams!!.toMutableList(), playlist_id!!, isOwner, requireActivity()) playlistAdapter = PlaylistAdapter(
response.relatedStreams!!.toMutableList(),
playlist_id!!,
isOwner,
requireActivity()
)
view.findViewById<RecyclerView>(R.id.playlist_recView).adapter = playlistAdapter view.findViewById<RecyclerView>(R.id.playlist_recView).adapter = playlistAdapter
val scrollView = view.findViewById<ScrollView>(R.id.playlist_scrollview) val scrollView = view.findViewById<ScrollView>(R.id.playlist_scrollview)
scrollView.viewTreeObserver scrollView.viewTreeObserver

View File

@ -1,6 +1,6 @@
package com.github.libretube package com.github.libretube
import java.util.* import java.util.LinkedList
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
class ResettableLazyManager { class ResettableLazyManager {
@ -25,8 +25,10 @@ interface Resettable {
fun reset() fun reset()
} }
class ResettableLazy<PROPTYPE>(val manager: ResettableLazyManager, val init: () -> PROPTYPE) : Resettable { class ResettableLazy<PROPTYPE>(val manager: ResettableLazyManager, val init: () -> PROPTYPE) :
@Volatile var lazyHolder = makeInitBlock() Resettable {
@Volatile
var lazyHolder = makeInitBlock()
operator fun getValue(thisRef: Any?, property: KProperty<*>): PROPTYPE { operator fun getValue(thisRef: Any?, property: KProperty<*>): PROPTYPE {
return lazyHolder.value return lazyHolder.value
@ -44,7 +46,8 @@ class ResettableLazy<PROPTYPE>(val manager: ResettableLazyManager, val init: ()
} }
} }
fun <PROPTYPE> resettableLazy(manager: ResettableLazyManager, init: () -> PROPTYPE): ResettableLazy<PROPTYPE> { fun <PROPTYPE> resettableLazy(manager: ResettableLazyManager, init: () -> PROPTYPE):
ResettableLazy<PROPTYPE> {
return ResettableLazy(manager, init) return ResettableLazy(manager, init)
} }

View File

@ -9,13 +9,15 @@ import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView import android.widget.AutoCompleteTextView
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView.* import android.widget.TextView.GONE
import android.widget.TextView.OnEditorActionListener
import android.widget.TextView.VISIBLE
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
@ -25,11 +27,11 @@ import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.adapters.SearchAdapter import com.github.libretube.adapters.SearchAdapter
import com.github.libretube.adapters.SearchHistoryAdapter import com.github.libretube.adapters.SearchHistoryAdapter
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.IOException
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException
class SearchFragment : Fragment() { class SearchFragment : Fragment() {
private val TAG = "SearchFragment" private val TAG = "SearchFragment"
@ -83,8 +85,7 @@ class SearchFragment : Fragment() {
.setTitle(getString(R.string.choose_filter)) .setTitle(getString(R.string.choose_filter))
.setSingleChoiceItems( .setSingleChoiceItems(
filterOptions, selectedFilter, filterOptions, selectedFilter,
DialogInterface.OnClickListener { DialogInterface.OnClickListener { _, id ->
_, id ->
tempSelectedItem = id tempSelectedItem = id
} }
) )
@ -154,8 +155,13 @@ class SearchFragment : Fragment() {
GlobalScope.launch { GlobalScope.launch {
fetchSuggestions(s.toString(), autoTextView) fetchSuggestions(s.toString(), autoTextView)
delay(1000) delay(1000)
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val sharedPreferences =
if (sharedPreferences.getBoolean("search_history_toggle", true)) addtohistory(s.toString()) PreferenceManager.getDefaultSharedPreferences(requireContext())
if (sharedPreferences.getBoolean(
"search_history_toggle",
true
)
) addtohistory(s.toString())
fetchSearch(s.toString()) fetchSearch(s.toString())
} }
} }
@ -200,10 +206,12 @@ class SearchFragment : Fragment() {
Log.e(TAG, "HttpException, unexpected response") Log.e(TAG, "HttpException, unexpected response")
return@launchWhenCreated return@launchWhenCreated
} }
val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, response) val adapter =
ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, response)
autoTextView.setAdapter(adapter) autoTextView.setAdapter(adapter)
} }
} }
private fun fetchSearch(query: String) { private fun fetchSearch(query: String) {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
@ -232,7 +240,11 @@ class SearchFragment : Fragment() {
if (!isLoading) { if (!isLoading) {
isLoading = true isLoading = true
val response = try { val response = try {
RetrofitInstance.api.getSearchResultsNextPage(query, apiSearchFilter, nextPage!!) RetrofitInstance.api.getSearchResultsNextPage(
query,
apiSearchFilter,
nextPage!!
)
} catch (e: IOException) { } catch (e: IOException) {
println(e) println(e)
Log.e(TAG, "IOException, you might not have internet connection") Log.e(TAG, "IOException, you might not have internet connection")
@ -256,7 +268,7 @@ class SearchFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
requireActivity().window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) requireActivity().window.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_HIDDEN)
} }
override fun onStop() { override fun onStop() {

View File

@ -1,7 +1,11 @@
package com.github.libretube package com.github.libretube
import android.Manifest import android.Manifest
import android.content.* import android.content.ContentResolver
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@ -24,13 +28,13 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.json.JSONObject
import org.json.JSONTokener
import retrofit2.HttpException
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import org.json.JSONObject
import org.json.JSONTokener
import retrofit2.HttpException
class SettingsActivity : class SettingsActivity :
AppCompatActivity(), AppCompatActivity(),
@ -61,7 +65,11 @@ class SettingsActivity :
.registerOnSharedPreferenceChangeListener(this) .registerOnSharedPreferenceChangeListener(this)
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, rootKey: String?) {} override fun onSharedPreferenceChanged(
sharedPreferences: SharedPreferences?,
rootKey: String?
) {
}
class SettingsFragment : PreferenceFragmentCompat() { class SettingsFragment : PreferenceFragmentCompat() {
val TAG = "Settings" val TAG = "Settings"
@ -71,66 +79,72 @@ class SettingsActivity :
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> getContent =
if (uri != null) { registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
try { if (uri != null) {
// Open a specific media item using ParcelFileDescriptor. try {
val resolver: ContentResolver = // Open a specific media item using ParcelFileDescriptor.
requireActivity() val resolver: ContentResolver =
.contentResolver requireActivity()
.contentResolver
// "rw" for read-and-write; // "rw" for read-and-write;
// "rwt" for truncating or overwriting existing file contents. // "rwt" for truncating or overwriting existing file contents.
// val readOnlyMode = "r" // val readOnlyMode = "r"
// uri - I have got from onActivityResult // uri - I have got from onActivityResult
val type = resolver.getType(uri) val type = resolver.getType(uri)
var inputStream: InputStream? = resolver.openInputStream(uri) var inputStream: InputStream? = resolver.openInputStream(uri)
val channels = ArrayList<String>() val channels = ArrayList<String>()
if (type == "application/json") { if (type == "application/json") {
val json = inputStream?.bufferedReader()?.readLines()?.get(0) val json = inputStream?.bufferedReader()?.readLines()?.get(0)
val jsonObject = JSONTokener(json).nextValue() as JSONObject val jsonObject = JSONTokener(json).nextValue() as JSONObject
Log.e(TAG, jsonObject.getJSONArray("subscriptions").toString()) Log.e(TAG, jsonObject.getJSONArray("subscriptions").toString())
for (i in 0 until jsonObject.getJSONArray("subscriptions").length()) { for (
var url = jsonObject.getJSONArray("subscriptions").getJSONObject(i).getString("url") i in 0 until jsonObject.getJSONArray("subscriptions")
url = url.replace("https://www.youtube.com/channel/", "") .length()
Log.e(TAG, url) ) {
channels.add(url) var url =
} jsonObject.getJSONArray("subscriptions").getJSONObject(i)
} else { .getString("url")
if (type == "application/zip") { url = url.replace("https://www.youtube.com/channel/", "")
val zis = ZipInputStream(inputStream) Log.e(TAG, url)
var entry: ZipEntry? = zis.nextEntry channels.add(url)
while (entry != null) { }
if (entry.name.endsWith(".csv")) { } else {
inputStream = zis if (type == "application/zip") {
break val zis = ZipInputStream(inputStream)
var entry: ZipEntry? = zis.nextEntry
while (entry != null) {
if (entry.name.endsWith(".csv")) {
inputStream = zis
break
}
entry = zis.nextEntry
} }
entry = zis.nextEntry
} }
}
inputStream?.bufferedReader()?.readLines()?.forEach { inputStream?.bufferedReader()?.readLines()?.forEach {
if (it.isNotBlank()) { if (it.isNotBlank()) {
val channelId = it.substringBefore(",") val channelId = it.substringBefore(",")
if (channelId.length == 24) if (channelId.length == 24)
channels.add(channelId) channels.add(channelId)
}
} }
} }
inputStream?.close()
subscribe(channels)
} catch (e: Exception) {
Log.e(TAG, e.toString())
Toast.makeText(
context,
R.string.error,
Toast.LENGTH_SHORT
).show()
} }
inputStream?.close()
subscribe(channels)
} catch (e: Exception) {
Log.e(TAG, e.toString())
Toast.makeText(
context,
R.string.error,
Toast.LENGTH_SHORT
).show()
} }
} }
}
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
} }
@ -155,7 +169,6 @@ class SettingsActivity :
val login = findPreference<Preference>("login_register") val login = findPreference<Preference>("login_register")
login?.setOnPreferenceClickListener { login?.setOnPreferenceClickListener {
val newFragment = LoginDialog() val newFragment = LoginDialog()
newFragment.show(childFragmentManager, "Login") newFragment.show(childFragmentManager, "Login")
true true
@ -200,7 +213,8 @@ class SettingsActivity :
if (ActivityCompat.checkSelfPermission( if (ActivityCompat.checkSelfPermission(
requireContext(), requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission( ) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(
requireContext(), requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED ) != PackageManager.PERMISSION_GRANTED
@ -245,7 +259,8 @@ class SettingsActivity :
val clearHistory = findPreference<Preference>("clear_history") val clearHistory = findPreference<Preference>("clear_history")
clearHistory?.setOnPreferenceClickListener { clearHistory?.setOnPreferenceClickListener {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(requireContext())
sharedPreferences.edit().remove("search_history").commit() sharedPreferences.edit().remove("search_history").commit()
true true
} }
@ -269,7 +284,10 @@ class SettingsActivity :
Html.fromHtml(licenseString) Html.fromHtml(licenseString)
} }
MaterialAlertDialogBuilder(view?.context!!) MaterialAlertDialogBuilder(view?.context!!)
.setPositiveButton(getString(R.string.okay), DialogInterface.OnClickListener { _, _ -> }) .setPositiveButton(
getString(R.string.okay),
DialogInterface.OnClickListener { _, _ -> }
)
.setMessage(licenseHtml) .setMessage(licenseHtml)
.create() .create()
.show() .show()
@ -327,7 +345,8 @@ class SettingsActivity :
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE) val sharedPref =
context?.getSharedPreferences("token", Context.MODE_PRIVATE)
RetrofitInstance.api.importSubscriptions( RetrofitInstance.api.importSubscriptions(
false, false,
sharedPref?.getString("token", "")!!, sharedPref?.getString("token", "")!!,
@ -359,5 +378,4 @@ class SettingsActivity :
intent = Intent(this, MainActivity::class.java) intent = Intent(this, MainActivity::class.java)
startActivity(intent) startActivity(intent)
} }
} }

View File

@ -14,7 +14,8 @@ import androidx.constraintlayout.motion.widget.MotionLayout
* *
*/ */
class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeSet? = null) : MotionLayout(context, attributeSet) { class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeSet? = null) :
MotionLayout(context, attributeSet) {
private val viewToDetectTouch by lazy { private val viewToDetectTouch by lazy {
findViewById<View>(R.id.main_container) // TODO move to Attributes findViewById<View>(R.id.main_container) // TODO move to Attributes

View File

@ -6,6 +6,7 @@ import androidx.preference.SwitchPreferenceCompat
class SponsorBlockSettings : PreferenceFragmentCompat() { class SponsorBlockSettings : PreferenceFragmentCompat() {
private val TAG = "SponsorBlockDialog" private val TAG = "SponsorBlockDialog"
companion object { companion object {
var sponsorBlockEnabled: Boolean = false var sponsorBlockEnabled: Boolean = false
var sponsorsEnabled: Boolean = false var sponsorsEnabled: Boolean = false

View File

@ -9,7 +9,12 @@ import android.view.ViewGroup
import android.view.animation.Animation import android.view.animation.Animation
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation import android.view.animation.RotateAnimation
import android.widget.* import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.RelativeLayout
import android.widget.ScrollView
import android.widget.TextView
import android.widget.Toast
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -19,9 +24,8 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.github.libretube.adapters.SubscriptionAdapter import com.github.libretube.adapters.SubscriptionAdapter
import com.github.libretube.adapters.SubscriptionChannelAdapter import com.github.libretube.adapters.SubscriptionChannelAdapter
import retrofit2.HttpException
import java.io.IOException import java.io.IOException
import retrofit2.HttpException
class Subscriptions : Fragment() { class Subscriptions : Fragment() {
val TAG = "SubFragment" val TAG = "SubFragment"
@ -60,7 +64,9 @@ class Subscriptions : Fragment() {
var feedRecView = view.findViewById<RecyclerView>(R.id.sub_feed) var feedRecView = view.findViewById<RecyclerView>(R.id.sub_feed)
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
val grid = sharedPreferences.getString("grid", resources.getInteger(R.integer.grid_items).toString())!! val grid = sharedPreferences.getString(
"grid", resources.getInteger(R.integer.grid_items).toString()
)!!
feedRecView.layoutManager = GridLayoutManager(view.context, grid.toInt()) feedRecView.layoutManager = GridLayoutManager(view.context, grid.toInt())
fetchFeed(feedRecView, progressBar, view) fetchFeed(feedRecView, progressBar, view)
@ -82,7 +88,7 @@ class Subscriptions : Fragment() {
channelRecView.visibility = View.VISIBLE channelRecView.visibility = View.VISIBLE
feedRecView.visibility = View.GONE feedRecView.visibility = View.GONE
//toggle button // toggle button
val rotate = RotateAnimation( val rotate = RotateAnimation(
0F, 0F,
180F, 180F,
@ -96,12 +102,11 @@ class Subscriptions : Fragment() {
rotate.fillAfter = true rotate.fillAfter = true
val image = view.findViewById<ImageView>(R.id.toggle) val image = view.findViewById<ImageView>(R.id.toggle)
image.startAnimation(rotate) image.startAnimation(rotate)
} else { } else {
channelRecView.visibility = View.GONE channelRecView.visibility = View.GONE
feedRecView.visibility = View.VISIBLE feedRecView.visibility = View.VISIBLE
//toggle button // toggle button
val image = view.findViewById<ImageView>(R.id.toggle) val image = view.findViewById<ImageView>(R.id.toggle)
image.clearAnimation() image.clearAnimation()
} }
@ -143,7 +148,7 @@ class Subscriptions : Fragment() {
} }
if (response.isNotEmpty()) { if (response.isNotEmpty()) {
subscriptionAdapter = SubscriptionAdapter(response) subscriptionAdapter = SubscriptionAdapter(response)
feedRecView?.adapter = subscriptionAdapter feedRecView.adapter = subscriptionAdapter
subscriptionAdapter?.updateItems() subscriptionAdapter?.updateItems()
} else { } else {
runOnUiThread { runOnUiThread {
@ -155,7 +160,8 @@ class Subscriptions : Fragment() {
visibility = View.VISIBLE visibility = View.VISIBLE
text = getString(R.string.emptyList) text = getString(R.string.emptyList)
} }
view.findViewById<RelativeLayout>(R.id.loginOrRegister).visibility = View.VISIBLE view.findViewById<RelativeLayout>(R.id.loginOrRegister)
.visibility = View.VISIBLE
} }
} }
progressBar.visibility = View.GONE progressBar.visibility = View.GONE
@ -181,7 +187,7 @@ class Subscriptions : Fragment() {
refreshLayout?.isRefreshing = false refreshLayout?.isRefreshing = false
} }
if (response.isNotEmpty()) { if (response.isNotEmpty()) {
channelRecView?.adapter = SubscriptionChannelAdapter(response.toMutableList()) channelRecView.adapter = SubscriptionChannelAdapter(response.toMutableList())
} else { } else {
Toast.makeText(context, R.string.subscribeIsEmpty, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.subscribeIsEmpty, Toast.LENGTH_SHORT).show()
} }
@ -189,12 +195,14 @@ class Subscriptions : Fragment() {
} }
run() run()
} }
override fun onDestroy() { override fun onDestroy() {
Log.e(TAG, "Destroyed") Log.e(TAG, "Destroyed")
super.onDestroy() super.onDestroy()
subscriptionAdapter = null subscriptionAdapter = null
view?.findViewById<RecyclerView>(R.id.sub_feed)?.adapter = null view?.findViewById<RecyclerView>(R.id.sub_feed)?.adapter = null
} }
private fun Fragment?.runOnUiThread(action: () -> Unit) { private fun Fragment?.runOnUiThread(action: () -> Unit) {
this ?: return this ?: return
if (!isAdded) return // Fragment not attached to an Activity if (!isAdded) return // Fragment not attached to an Activity

View File

@ -3,10 +3,11 @@ package com.github.libretube
import android.content.Context import android.content.Context
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import java.util.* import java.util.Locale
fun updateAccentColor(context: Context) { fun updateAccentColor(context: Context) {
val colorAccent = PreferenceManager.getDefaultSharedPreferences(context).getString("accent_color", "red") val colorAccent =
PreferenceManager.getDefaultSharedPreferences(context).getString("accent_color", "red")
when (colorAccent) { when (colorAccent) {
"my" -> context.setTheme(R.style.Theme_MY) "my" -> context.setTheme(R.style.Theme_MY)
"red" -> context.setTheme(R.style.Theme_Red) "red" -> context.setTheme(R.style.Theme_Red)
@ -18,7 +19,8 @@ fun updateAccentColor(context: Context) {
} }
fun updateThemeMode(context: Context) { fun updateThemeMode(context: Context) {
val themeMode = PreferenceManager.getDefaultSharedPreferences(context).getString("theme_togglee", "A") val themeMode =
PreferenceManager.getDefaultSharedPreferences(context).getString("theme_togglee", "A")
when (themeMode) { when (themeMode) {
"A" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) "A" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
"L" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) "L" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)

View File

@ -15,10 +15,12 @@ import com.github.libretube.formatShort
import com.github.libretube.obj.StreamItem import com.github.libretube.obj.StreamItem
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
class ChannelAdapter(private val videoFeed: MutableList<StreamItem>) : RecyclerView.Adapter<ChannelViewHolder>() { class ChannelAdapter(private val videoFeed: MutableList<StreamItem>) :
RecyclerView.Adapter<ChannelViewHolder>() {
override fun getItemCount(): Int { override fun getItemCount(): Int {
return videoFeed.size return videoFeed.size
} }
fun updateItems(newItems: List<StreamItem>) { fun updateItems(newItems: List<StreamItem>) {
videoFeed.addAll(newItems) videoFeed.addAll(newItems)
notifyDataSetChanged() notifyDataSetChanged()
@ -33,8 +35,11 @@ class ChannelAdapter(private val videoFeed: MutableList<StreamItem>) : RecyclerV
override fun onBindViewHolder(holder: ChannelViewHolder, position: Int) { override fun onBindViewHolder(holder: ChannelViewHolder, position: Int) {
val trending = videoFeed[position] val trending = videoFeed[position]
holder.v.findViewById<TextView>(R.id.channel_description).text = trending.title holder.v.findViewById<TextView>(R.id.channel_description).text = trending.title
holder.v.findViewById<TextView>(R.id.channel_views).text = trending.views.formatShort() + "" + DateUtils.getRelativeTimeSpanString(trending.uploaded!!) holder.v.findViewById<TextView>(R.id.channel_views).text =
holder.v.findViewById<TextView>(R.id.channel_duration).text = DateUtils.formatElapsedTime(trending.duration!!) trending.views.formatShort() + "" +
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
holder.v.findViewById<TextView>(R.id.channel_duration).text =
DateUtils.formatElapsedTime(trending.duration!!)
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.channel_thumbnail) val thumbnailImage = holder.v.findViewById<ImageView>(R.id.channel_thumbnail)
Picasso.get().load(trending.thumbnail).into(thumbnailImage) Picasso.get().load(trending.thumbnail).into(thumbnailImage)
holder.v.setOnClickListener { holder.v.setOnClickListener {
@ -52,6 +57,7 @@ class ChannelAdapter(private val videoFeed: MutableList<StreamItem>) : RecyclerV
} }
} }
} }
class ChannelViewHolder(val v: View) : RecyclerView.ViewHolder(v) { class ChannelViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init { init {
} }

View File

@ -14,7 +14,8 @@ import com.github.libretube.formatShort
import com.github.libretube.obj.Comment import com.github.libretube.obj.Comment
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
class CommentsAdapter(private val comments: MutableList<Comment>) : RecyclerView.Adapter<ViewHolder>() { class CommentsAdapter(private val comments: MutableList<Comment>) :
RecyclerView.Adapter<ViewHolder>() {
fun updateItems(newItems: List<Comment>) { fun updateItems(newItems: List<Comment>) {
var commentsSize = comments.size var commentsSize = comments.size
@ -23,16 +24,21 @@ class CommentsAdapter(private val comments: MutableList<Comment>) : RecyclerView
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
var commentsView = LayoutInflater.from(parent.context).inflate(R.layout.comments_row, parent, false) var commentsView =
LayoutInflater.from(parent.context).inflate(R.layout.comments_row, parent, false)
return ViewHolder(commentsView) return ViewHolder(commentsView)
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.v.findViewById<TextView>(R.id.comment_infos).text = comments[position].author.toString() + "" + comments[position].commentedTime.toString() holder.v.findViewById<TextView>(R.id.comment_infos).text =
holder.v.findViewById<TextView>(R.id.comment_text).text = comments[position].commentText.toString() comments[position].author.toString() +
"" + comments[position].commentedTime.toString()
holder.v.findViewById<TextView>(R.id.comment_text).text =
comments[position].commentText.toString()
val channelImage = holder.v.findViewById<ImageView>(R.id.commentor_image) val channelImage = holder.v.findViewById<ImageView>(R.id.commentor_image)
Picasso.get().load(comments[position].thumbnail).fit().centerCrop().into(channelImage) Picasso.get().load(comments[position].thumbnail).fit().centerCrop().into(channelImage)
holder.v.findViewById<TextView>(R.id.likes_textView).text = comments[position].likeCount?.toLong().formatShort() holder.v.findViewById<TextView>(R.id.likes_textView).text =
comments[position].likeCount?.toLong().formatShort()
if (comments[position].verified == true) { if (comments[position].verified == true) {
holder.v.findViewById<ImageView>(R.id.verified_imageView).visibility = View.VISIBLE holder.v.findViewById<ImageView>(R.id.verified_imageView).visibility = View.VISIBLE
} }

View File

@ -33,6 +33,7 @@ class PlaylistAdapter(
override fun getItemCount(): Int { override fun getItemCount(): Int {
return videoFeed.size return videoFeed.size
} }
fun updateItems(newItems: List<StreamItem>) { fun updateItems(newItems: List<StreamItem>) {
videoFeed.addAll(newItems) videoFeed.addAll(newItems)
notifyDataSetChanged() notifyDataSetChanged()
@ -48,7 +49,8 @@ class PlaylistAdapter(
val streamItem = videoFeed[position] val streamItem = videoFeed[position]
holder.v.findViewById<TextView>(R.id.playlist_title).text = streamItem.title holder.v.findViewById<TextView>(R.id.playlist_title).text = streamItem.title
holder.v.findViewById<TextView>(R.id.playlist_description).text = streamItem.uploaderName holder.v.findViewById<TextView>(R.id.playlist_description).text = streamItem.uploaderName
holder.v.findViewById<TextView>(R.id.playlist_duration).text = DateUtils.formatElapsedTime(streamItem.duration!!) holder.v.findViewById<TextView>(R.id.playlist_duration).text =
DateUtils.formatElapsedTime(streamItem.duration!!)
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.playlist_thumbnail) val thumbnailImage = holder.v.findViewById<ImageView>(R.id.playlist_thumbnail)
Picasso.get().load(streamItem.thumbnail).into(thumbnailImage) Picasso.get().load(streamItem.thumbnail).into(thumbnailImage)
holder.v.setOnClickListener { holder.v.setOnClickListener {
@ -68,17 +70,24 @@ class PlaylistAdapter(
val delete = holder.v.findViewById<ImageView>(R.id.delete_playlist) val delete = holder.v.findViewById<ImageView>(R.id.delete_playlist)
delete.visibility = View.VISIBLE delete.visibility = View.VISIBLE
delete.setOnClickListener { delete.setOnClickListener {
val sharedPref = holder.v.context.getSharedPreferences("token", Context.MODE_PRIVATE) val sharedPref = holder.v.context.getSharedPreferences(
"token",
Context.MODE_PRIVATE
)
val token = sharedPref?.getString("token", "")!! val token = sharedPref?.getString("token", "")!!
removeFromPlaylist(token, position) removeFromPlaylist(token, position)
} }
} }
} }
private fun removeFromPlaylist(token: String, position: Int) { private fun removeFromPlaylist(token: String, position: Int) {
fun run() { fun run() {
GlobalScope.launch { GlobalScope.launch {
val response = try { val response = try {
RetrofitInstance.api.removeFromPlaylist(token, PlaylistId(playlistId = playlistId, index = position)) RetrofitInstance.api.removeFromPlaylist(
token,
PlaylistId(playlistId = playlistId, index = position)
)
} catch (e: IOException) { } catch (e: IOException) {
println(e) println(e)
Log.e(TAG, "IOException, you might not have internet connection") Log.e(TAG, "IOException, you might not have internet connection")
@ -107,6 +116,7 @@ class PlaylistAdapter(
run() run()
} }
} }
class PlaylistViewHolder(val v: View) : RecyclerView.ViewHolder(v) { class PlaylistViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init { init {
} }

View File

@ -17,10 +17,10 @@ import com.github.libretube.obj.PlaylistId
import com.github.libretube.obj.Playlists import com.github.libretube.obj.Playlists
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import java.io.IOException
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException
class PlaylistsAdapter( class PlaylistsAdapter(
private val playlists: MutableList<Playlists>, private val playlists: MutableList<Playlists>,
@ -30,6 +30,7 @@ class PlaylistsAdapter(
override fun getItemCount(): Int { override fun getItemCount(): Int {
return playlists.size return playlists.size
} }
fun updateItems(newItems: List<Playlists>) { fun updateItems(newItems: List<Playlists>) {
playlists.addAll(newItems) playlists.addAll(newItems)
notifyDataSetChanged() notifyDataSetChanged()
@ -51,7 +52,10 @@ class PlaylistsAdapter(
builder.setTitle(R.string.deletePlaylist) builder.setTitle(R.string.deletePlaylist)
builder.setMessage(R.string.areYouSure) builder.setMessage(R.string.areYouSure)
builder.setPositiveButton(R.string.yes) { dialog, which -> builder.setPositiveButton(R.string.yes) { dialog, which ->
val sharedPref = holder.v.context.getSharedPreferences("token", Context.MODE_PRIVATE) val sharedPref = holder.v.context.getSharedPreferences(
"token",
Context.MODE_PRIVATE
)
val token = sharedPref?.getString("token", "")!! val token = sharedPref?.getString("token", "")!!
deletePlaylist(playlist.id!!, token, position) deletePlaylist(playlist.id!!, token, position)
} }
@ -66,6 +70,7 @@ class PlaylistsAdapter(
activity.navController.navigate(R.id.playlistFragment, bundle) activity.navController.navigate(R.id.playlistFragment, bundle)
} }
} }
private fun deletePlaylist(id: String, token: String, position: Int) { private fun deletePlaylist(id: String, token: String, position: Int) {
fun run() { fun run() {
GlobalScope.launch { GlobalScope.launch {
@ -99,6 +104,7 @@ class PlaylistsAdapter(
run() run()
} }
} }
class PlaylistsViewHolder(val v: View) : RecyclerView.ViewHolder(v) { class PlaylistsViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init { init {
} }

View File

@ -17,7 +17,8 @@ import com.github.libretube.formatShort
import com.github.libretube.obj.SearchItem import com.github.libretube.obj.SearchItem
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
class SearchAdapter(private val searchItems: MutableList<SearchItem>) : RecyclerView.Adapter<CustomViewHolder1>() { class SearchAdapter(private val searchItems: MutableList<SearchItem>) :
RecyclerView.Adapter<CustomViewHolder1>() {
fun updateItems(newItems: List<SearchItem>) { fun updateItems(newItems: List<SearchItem>) {
var searchItemsSize = searchItems.size var searchItemsSize = searchItems.size
@ -54,6 +55,7 @@ class SearchAdapter(private val searchItems: MutableList<SearchItem>) : Recycler
} }
} }
} }
class CustomViewHolder1(private val v: View) : RecyclerView.ViewHolder(v) { class CustomViewHolder1(private val v: View) : RecyclerView.ViewHolder(v) {
private fun bindWatch(item: SearchItem) { private fun bindWatch(item: SearchItem) {
@ -64,11 +66,16 @@ class CustomViewHolder1(private val v: View) : RecyclerView.ViewHolder(v) {
val channelImage = v.findViewById<ImageView>(R.id.search_channel_image) val channelImage = v.findViewById<ImageView>(R.id.search_channel_image)
Picasso.get().load(item.uploaderAvatar).fit().centerCrop().into(channelImage) Picasso.get().load(item.uploaderAvatar).fit().centerCrop().into(channelImage)
val title = v.findViewById<TextView>(R.id.search_description) val title = v.findViewById<TextView>(R.id.search_description)
title.text = if (item.title!!.length > 60) item.title?.substring(0, 60) + "..." else item.title title.text =
if (item.title!!.length > 60) item.title?.substring(0, 60) + "..." else item.title
val views = v.findViewById<TextView>(R.id.search_views) val views = v.findViewById<TextView>(R.id.search_views)
val viewsString = if (item.views?.toInt() != -1) item.views.formatShort() else "" val viewsString = if (item.views?.toInt() != -1) item.views.formatShort() else ""
val uploadDate = if (item.uploadedDate != null) item.uploadedDate else "" val uploadDate = if (item.uploadedDate != null) item.uploadedDate else ""
views.text = if (viewsString != "" && uploadDate != "") viewsString + "" + uploadDate else viewsString + uploadDate views.text =
if (viewsString != "" && uploadDate != "")
"$viewsString$uploadDate"
else
viewsString + uploadDate
val channelName = v.findViewById<TextView>(R.id.search_channel_name) val channelName = v.findViewById<TextView>(R.id.search_channel_name)
channelName.text = item.uploaderName channelName.text = item.uploaderName
v.setOnClickListener { v.setOnClickListener {
@ -90,13 +97,17 @@ class CustomViewHolder1(private val v: View) : RecyclerView.ViewHolder(v) {
activity.navController.navigate(R.id.channel, bundle) activity.navController.navigate(R.id.channel, bundle)
} }
} }
private fun bindChannel(item: SearchItem) { private fun bindChannel(item: SearchItem) {
val channelImage = v.findViewById<ImageView>(R.id.search_channel_image) val channelImage = v.findViewById<ImageView>(R.id.search_channel_image)
Picasso.get().load(item.thumbnail).fit().centerCrop().into(channelImage) Picasso.get().load(item.thumbnail).fit().centerCrop().into(channelImage)
val channelName = v.findViewById<TextView>(R.id.search_channel_name) val channelName = v.findViewById<TextView>(R.id.search_channel_name)
channelName.text = item.name channelName.text = item.name
val channelViews = v.findViewById<TextView>(R.id.search_views) val channelViews = v.findViewById<TextView>(R.id.search_views)
channelViews.text = v.context.getString(R.string.subscribers, item.subscribers.formatShort()) + "" + v.context.getString(R.string.videoCount, item.videos.toString()) channelViews.text = v.context.getString(
R.string.subscribers,
item.subscribers.formatShort()
) + "" + v.context.getString(R.string.videoCount, item.videos.toString())
v.setOnClickListener { v.setOnClickListener {
val activity = v.context as MainActivity val activity = v.context as MainActivity
val bundle = bundleOf("channel_id" to item.url) val bundle = bundleOf("channel_id" to item.url)
@ -104,6 +115,7 @@ class CustomViewHolder1(private val v: View) : RecyclerView.ViewHolder(v) {
} }
// todo sub button // todo sub button
} }
private fun bindPlaylist(item: SearchItem) { private fun bindPlaylist(item: SearchItem) {
val playlistImage = v.findViewById<ImageView>(R.id.search_thumbnail) val playlistImage = v.findViewById<ImageView>(R.id.search_thumbnail)
Picasso.get().load(item.thumbnail).fit().centerCrop().into(playlistImage) Picasso.get().load(item.thumbnail).fit().centerCrop().into(playlistImage)
@ -114,7 +126,9 @@ class CustomViewHolder1(private val v: View) : RecyclerView.ViewHolder(v) {
val playlistChannelName = v.findViewById<TextView>(R.id.search_name) val playlistChannelName = v.findViewById<TextView>(R.id.search_name)
playlistChannelName.text = item.uploaderName playlistChannelName.text = item.uploaderName
val playlistVideosNumber = v.findViewById<TextView>(R.id.search_playlist_videos) val playlistVideosNumber = v.findViewById<TextView>(R.id.search_playlist_videos)
if (item.videos?.toInt() != -1) playlistVideosNumber.text = v.context.getString(R.string.videoCount, item.videos.toString()) if (item.videos?.toInt() != -1)
playlistVideosNumber.text =
v.context.getString(R.string.videoCount, item.videos.toString())
v.setOnClickListener { v.setOnClickListener {
// playlist clicked // playlist clicked
val activity = v.context as MainActivity val activity = v.context as MainActivity

View File

@ -18,7 +18,8 @@ import com.github.libretube.formatShort
import com.github.libretube.obj.StreamItem import com.github.libretube.obj.StreamItem
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
class SubscriptionAdapter(private val videoFeed: List<StreamItem>) : RecyclerView.Adapter<SubscriptionViewHolder>() { class SubscriptionAdapter(private val videoFeed: List<StreamItem>) :
RecyclerView.Adapter<SubscriptionViewHolder>() {
// private var limitedVideoFeed: MutableList<String> = [""].toMutableList() // private var limitedVideoFeed: MutableList<String> = [""].toMutableList()
var i = 0 var i = 0
override fun getItemCount(): Int { override fun getItemCount(): Int {
@ -42,9 +43,13 @@ class SubscriptionAdapter(private val videoFeed: List<StreamItem>) : RecyclerVie
override fun onBindViewHolder(holder: SubscriptionViewHolder, position: Int) { override fun onBindViewHolder(holder: SubscriptionViewHolder, position: Int) {
val trending = videoFeed[position] val trending = videoFeed[position]
holder.v.findViewById<TextView>(R.id.textView_title).text = trending.title holder.v.findViewById<TextView>(R.id.textView_title).text = trending.title
holder.v.findViewById<TextView>(R.id.textView_channel).text = trending.uploaderName + "" + trending.views.formatShort() + "" + DateUtils.getRelativeTimeSpanString(trending.uploaded!!) holder.v.findViewById<TextView>(R.id.textView_channel).text =
trending.uploaderName + "" +
trending.views.formatShort() + "" +
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.thumbnail) val thumbnailImage = holder.v.findViewById<ImageView>(R.id.thumbnail)
holder.v.findViewById<TextView>(R.id.thumbnail_duration).text = DateUtils.formatElapsedTime(trending.duration!!) holder.v.findViewById<TextView>(R.id.thumbnail_duration).text =
DateUtils.formatElapsedTime(trending.duration!!)
val channelImage = holder.v.findViewById<ImageView>(R.id.channel_image) val channelImage = holder.v.findViewById<ImageView>(R.id.channel_image)
channelImage.setOnClickListener { channelImage.setOnClickListener {
val activity = holder.v.context as MainActivity val activity = holder.v.context as MainActivity
@ -76,6 +81,7 @@ class SubscriptionAdapter(private val videoFeed: List<StreamItem>) : RecyclerVie
} }
} }
} }
class SubscriptionViewHolder(val v: View) : RecyclerView.ViewHolder(v) { class SubscriptionViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init { init {
} }

View File

@ -12,12 +12,14 @@ import com.github.libretube.R
import com.github.libretube.obj.Subscription import com.github.libretube.obj.Subscription
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscription>) : RecyclerView.Adapter<SubscriptionChannelViewHolder>() { class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscription>) :
RecyclerView.Adapter<SubscriptionChannelViewHolder>() {
override fun getItemCount(): Int { override fun getItemCount(): Int {
return subscriptions.size return subscriptions.size
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubscriptionChannelViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
SubscriptionChannelViewHolder {
val layoutInflater = LayoutInflater.from(parent.context) val layoutInflater = LayoutInflater.from(parent.context)
val cell = layoutInflater.inflate(R.layout.channel_subscription_row, parent, false) val cell = layoutInflater.inflate(R.layout.channel_subscription_row, parent, false)
return SubscriptionChannelViewHolder(cell) return SubscriptionChannelViewHolder(cell)
@ -35,6 +37,7 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
} }
} }
} }
class SubscriptionChannelViewHolder(val v: View) : RecyclerView.ViewHolder(v) { class SubscriptionChannelViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init { init {
} }

View File

@ -18,7 +18,8 @@ import com.github.libretube.formatShort
import com.github.libretube.obj.StreamItem import com.github.libretube.obj.StreamItem
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
class TrendingAdapter(private val videoFeed: List<StreamItem>) : RecyclerView.Adapter<CustomViewHolder>() { class TrendingAdapter(private val videoFeed: List<StreamItem>) :
RecyclerView.Adapter<CustomViewHolder>() {
override fun getItemCount(): Int { override fun getItemCount(): Int {
return videoFeed.size return videoFeed.size
} }
@ -32,9 +33,13 @@ class TrendingAdapter(private val videoFeed: List<StreamItem>) : RecyclerView.Ad
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) { override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
val trending = videoFeed[position] val trending = videoFeed[position]
holder.v.findViewById<TextView>(R.id.textView_title).text = trending.title holder.v.findViewById<TextView>(R.id.textView_title).text = trending.title
holder.v.findViewById<TextView>(R.id.textView_channel).text = trending.uploaderName + "" + trending.views.formatShort() + "" + DateUtils.getRelativeTimeSpanString(trending.uploaded!!) holder.v.findViewById<TextView>(R.id.textView_channel).text =
trending.uploaderName + "" +
trending.views.formatShort() + "" +
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.thumbnail) val thumbnailImage = holder.v.findViewById<ImageView>(R.id.thumbnail)
holder.v.findViewById<TextView>(R.id.thumbnail_duration).text = DateUtils.formatElapsedTime(trending.duration!!) holder.v.findViewById<TextView>(R.id.thumbnail_duration).text =
DateUtils.formatElapsedTime(trending.duration!!)
val channelImage = holder.v.findViewById<ImageView>(R.id.channel_image) val channelImage = holder.v.findViewById<ImageView>(R.id.channel_image)
channelImage.setOnClickListener { channelImage.setOnClickListener {
val activity = holder.v.context as MainActivity val activity = holder.v.context as MainActivity
@ -73,6 +78,7 @@ class TrendingAdapter(private val videoFeed: List<StreamItem>) : RecyclerView.Ad
} }
} }
} }
class CustomViewHolder(val v: View) : RecyclerView.ViewHolder(v) { class CustomViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init { init {
} }

View File

@ -1,6 +1,6 @@
package com.github.libretube package com.github.libretube
import org.junit.Assert.* import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
/** /**