Merge pull request #253 from Bnyro/theming

Accent colors and OLED Mode
This commit is contained in:
Farbod 2022-05-20 16:44:57 +04:30 committed by GitHub
commit 45f87649f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 1186 additions and 984 deletions

View File

@ -1,13 +1,11 @@
package com.github.libretube
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
@ -21,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.github.libretube", appContext.packageName)
}
}
}

View File

@ -14,7 +14,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_libretube_round"
android:supportsRtl="true"
android:theme="@style/Theme.LibreTube"
android:theme="@style/Theme.MY"
android:name=".myApp"
android:networkSecurityConfig="@xml/network_security_config"
android:largeHeap="true"

View File

@ -12,7 +12,6 @@ import androidx.core.text.HtmlCompat
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.github.libretube.adapters.PlaylistsAdapter
import com.github.libretube.obj.PlaylistId
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import retrofit2.HttpException
@ -29,14 +28,14 @@ class AddtoPlaylistDialog : DialogFragment() {
videoId = arguments?.getString("videoId")!!
val builder = MaterialAlertDialogBuilder(it)
// Get the layout inflater
val inflater = requireActivity().layoutInflater;
val inflater = requireActivity().layoutInflater
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
token = sharedPref?.getString("token","")!!
token = sharedPref?.getString("token", "")!!
var view: View = inflater.inflate(R.layout.dialog_addtoplaylist, null)
spinner = view.findViewById(R.id.playlists_spinner)
button = view.findViewById(R.id.addToPlaylist)
if(token!=""){
fetchPlaylists()
if (token != "") {
fetchPlaylists()
}
val typedValue = TypedValue()
this.requireActivity().theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true)
@ -51,64 +50,61 @@ class AddtoPlaylistDialog : DialogFragment() {
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
private fun fetchPlaylists(){
private fun fetchPlaylists() {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.playlists(token)
}catch(e: IOException) {
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
Toast.makeText(context,R.string.unknown_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
Toast.makeText(context,R.string.server_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
}
if (response.isNotEmpty()){
if (response.isNotEmpty()) {
var names = emptyList<String>().toMutableList()
for(playlist in response){
for (playlist in response) {
names.add(playlist.name!!)
}
val arrayAdapter = ArrayAdapter(requireContext(),android.R.layout.simple_spinner_item,names)
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = arrayAdapter
val arrayAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, names)
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = arrayAdapter
runOnUiThread {
button.setOnClickListener {
addToPlaylist(response[spinner.selectedItemPosition].id!!)
}
}
}else{
} else {
}
}
}
run()
}
private fun addToPlaylist(playlistId: String){
private fun addToPlaylist(playlistId: String) {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.addToPlaylist(token, PlaylistId(playlistId, videoId))
}catch(e: IOException) {
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
Toast.makeText(context,R.string.unknown_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
Toast.makeText(context,R.string.server_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
}
if (response.message == "ok"){
Toast.makeText(context,R.string.success, Toast.LENGTH_SHORT).show()
if (response.message == "ok") {
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
dialog?.dismiss()
}else{
Toast.makeText(context,R.string.fail, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, R.string.fail, Toast.LENGTH_SHORT).show()
}
}
}
run()
@ -118,4 +114,4 @@ class AddtoPlaylistDialog : DialogFragment() {
if (!isAdded) return // Fragment not attached to an Activity
activity?.runOnUiThread(action)
}
}
}

View File

@ -1,13 +1,10 @@
package com.github.libretube
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.text.TextUtils.substring
import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -23,18 +20,17 @@ import com.github.libretube.adapters.ChannelAdapter
import com.github.libretube.obj.Subscribe
import com.google.android.material.button.MaterialButton
import com.squareup.picasso.Picasso
import retrofit2.HttpException
import java.io.IOException
import retrofit2.HttpException
class ChannelFragment : Fragment() {
private var channel_id: String? = null
private val TAG = "ChannelFragment"
var nextPage: String? =null
var nextPage: String? = null
var channelAdapter: ChannelAdapter? = null
var isLoading = true
var isSubscribed: Boolean =false
var isSubscribed: Boolean = false
private var refreshLayout: SwipeRefreshLayout? = null
override fun onCreate(savedInstanceState: Bundle?) {
@ -45,20 +41,19 @@ class ChannelFragment : Fragment() {
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_channel, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
channel_id = channel_id!!.replace("/channel/","")
view.findViewById<TextView>(R.id.channel_name).text=channel_id
channel_id = channel_id!!.replace("/channel/", "")
view.findViewById<TextView>(R.id.channel_name).text = channel_id
val recyclerView = view.findViewById<RecyclerView>(R.id.channel_recView)
recyclerView.layoutManager = LinearLayoutManager(context)
refreshLayout = view.findViewById(R.id.channel_refresh)
@ -68,7 +63,7 @@ class ChannelFragment : Fragment() {
fetchChannel(view)
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
val subButton = view.findViewById<MaterialButton>(R.id.channel_subscribe)
if (sharedPref?.getString("token","") != "") {
if (sharedPref?.getString("token", "") != "") {
isSubscribed(subButton)
}
}
@ -81,28 +76,26 @@ class ChannelFragment : Fragment() {
scrollView.viewTreeObserver
.addOnScrollChangedListener {
if (scrollView.getChildAt(0).bottom
== (scrollView.height + scrollView.scrollY)) {
//scroll view is at bottom
if(nextPage!=null && !isLoading){
isLoading=true
refreshLayout?.isRefreshing = true;
== (scrollView.height + scrollView.scrollY)
) {
// scroll view is at bottom
if (nextPage != null && !isLoading) {
isLoading = true
refreshLayout?.isRefreshing = true
fetchNextPage()
}
}
}
}
private fun isSubscribed(button: MaterialButton){
private fun isSubscribed(button: MaterialButton) {
@SuppressLint("ResourceAsColor")
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
RetrofitInstance.api.isSubscribed(channel_id!!,sharedPref?.getString("token","")!!)
} catch(e: IOException) {
RetrofitInstance.api.isSubscribed(channel_id!!, sharedPref?.getString("token", "")!!)
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
@ -112,33 +105,34 @@ class ChannelFragment : Fragment() {
}
runOnUiThread {
if (response.subscribed==true){
isSubscribed=true
button.text=getString(R.string.unsubscribe)
if (response.subscribed == true) {
isSubscribed = true
button.text = getString(R.string.unsubscribe)
}
if(response.subscribed!=null){
button.setOnClickListener {
if(isSubscribed){
unsubscribe()
button.text=getString(R.string.subscribe)
}else{
subscribe()
button.text=getString(R.string.unsubscribe)
if (response.subscribed != null) {
button.setOnClickListener {
if (isSubscribed) {
unsubscribe()
button.text = getString(R.string.subscribe)
} else {
subscribe()
button.text = getString(R.string.unsubscribe)
}
}
}}
}
}
}
}
run()
}
private fun subscribe(){
private fun subscribe() {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
RetrofitInstance.api.subscribe(sharedPref?.getString("token","")!!, Subscribe(channel_id))
}catch(e: IOException) {
RetrofitInstance.api.subscribe(sharedPref?.getString("token", "")!!, Subscribe(channel_id))
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
@ -146,18 +140,18 @@ class ChannelFragment : Fragment() {
Log.e(TAG, "HttpException, unexpected response$e")
return@launchWhenCreated
}
isSubscribed=true
isSubscribed = true
}
}
run()
}
private fun unsubscribe(){
private fun unsubscribe() {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
RetrofitInstance.api.unsubscribe(sharedPref?.getString("token","")!!, Subscribe(channel_id))
}catch(e: IOException) {
RetrofitInstance.api.unsubscribe(sharedPref?.getString("token", "")!!, Subscribe(channel_id))
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
@ -165,37 +159,37 @@ class ChannelFragment : Fragment() {
Log.e(TAG, "HttpException, unexpected response")
return@launchWhenCreated
}
isSubscribed=false
isSubscribed = false
}
}
run()
}
private fun fetchChannel(view: View){
private fun fetchChannel(view: View) {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.getChannel(channel_id!!)
}catch(e: IOException) {
refreshLayout?.isRefreshing = false;
} catch (e: IOException) {
refreshLayout?.isRefreshing = false
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
} catch (e: HttpException) {
refreshLayout?.isRefreshing = false;
refreshLayout?.isRefreshing = false
Log.e(TAG, "HttpException, unexpected response")
return@launchWhenCreated
}
nextPage = response.nextpage
isLoading=false
refreshLayout?.isRefreshing = false;
isLoading = false
refreshLayout?.isRefreshing = false
runOnUiThread {
view.findViewById<ScrollView>(R.id.channel_scrollView).visibility = View.VISIBLE
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)
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)
if (response.description?.trim() == "") channelDescription.visibility = View.GONE else channelDescription.text = response.description?.trim()
val bannerImage = view.findViewById<ImageView>(R.id.channel_banner)
@ -204,18 +198,17 @@ class ChannelFragment : Fragment() {
Picasso.get().load(response.avatarUrl).into(channelImage)
channelAdapter = ChannelAdapter(response.relatedStreams!!.toMutableList())
view.findViewById<RecyclerView>(R.id.channel_recView).adapter = channelAdapter
}
}
}
run()
}
private fun fetchNextPage(){
private fun fetchNextPage() {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.getChannelNextPage(channel_id!!,nextPage!!)
RetrofitInstance.api.getChannelNextPage(channel_id!!, nextPage!!)
} catch (e: IOException) {
refreshLayout?.isRefreshing = false
println(e)
@ -223,12 +216,12 @@ class ChannelFragment : Fragment() {
return@launchWhenCreated
} catch (e: HttpException) {
refreshLayout?.isRefreshing = false
Log.e(TAG, "HttpException, unexpected response,"+e.response())
Log.e(TAG, "HttpException, unexpected response," + e.response())
return@launchWhenCreated
}
nextPage = response.nextpage
channelAdapter?.updateItems(response.relatedStreams!!)
isLoading=false
isLoading = false
refreshLayout?.isRefreshing = false
}
}
@ -244,8 +237,8 @@ class ChannelFragment : Fragment() {
val scrollView = view?.findViewById<ScrollView>(R.id.channel_scrollView)
scrollView?.viewTreeObserver?.removeOnScrollChangedListener {
}
channelAdapter=null
view?.findViewById<RecyclerView>(R.id.channel_recView)?.adapter=null
channelAdapter = null
view?.findViewById<RecyclerView>(R.id.channel_recView)?.adapter = null
super.onDestroyView()
}
}
}

View File

@ -39,11 +39,10 @@ class CreatePlaylistDialog : DialogFragment() {
val createPlaylistBtn = rootView.findViewById<Button>(R.id.create_new_playlist)
createPlaylistBtn.setOnClickListener {
var listName = playlistName.text.toString()
if(listName != "") {
if (listName != "") {
setFragmentResult("key_parent", bundleOf("playlistName" to "$listName"))
dismiss()
}
else {
} else {
Toast.makeText(context, R.string.emptyPlaylistName, Toast.LENGTH_LONG).show()
}
}

View File

@ -4,12 +4,11 @@ import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import com.google.android.exoplayer2.ui.PlayerView
import com.google.android.exoplayer2.ui.StyledPlayerControlView
import com.google.android.exoplayer2.ui.StyledPlayerView
internal class CustomExoPlayerView(
context: Context, attributeSet: AttributeSet? = null
context: Context,
attributeSet: AttributeSet? = null
) : StyledPlayerView(context, attributeSet) {
@SuppressLint("ClickableViewAccessibility")
@ -18,11 +17,11 @@ internal class CustomExoPlayerView(
MotionEvent.ACTION_DOWN -> {
if (isControllerFullyVisible) {
hideController()
}else {
} else {
showController()
}
}
}
return false
}
}
}

View File

@ -28,4 +28,4 @@ class CustomSwipeToRefresh(context: Context?, attrs: AttributeSet?) :
init {
mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
}
}
}

View File

@ -12,7 +12,6 @@ import androidx.core.text.HtmlCompat
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
class DownloadDialog : DialogFragment() {
private val TAG = "DownloadDialog"
var vidName = arrayListOf<String>()
@ -37,9 +36,9 @@ class DownloadDialog : DialogFragment() {
val inflater = requireActivity().layoutInflater
var view: View = inflater.inflate(R.layout.dialog_download, null)
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)
videoSpinner.adapter=videoArrayAdapter
videoSpinner.adapter = videoArrayAdapter
videoSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>,
@ -48,12 +47,12 @@ class DownloadDialog : DialogFragment() {
id: Long
) {
selectedVideo = position
Log.d(TAG,selectedVideo.toString())
Log.d(TAG, selectedVideo.toString())
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
val audioSpinner = view.findViewById<Spinner>(R.id.audio_spinner)
val audioArrayAdapter = ArrayAdapter<String>(requireContext(),android.R.layout.simple_spinner_item,audioName)
val audioArrayAdapter = ArrayAdapter<String>(requireContext(), android.R.layout.simple_spinner_item, audioName)
audioArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
audioSpinner.adapter = audioArrayAdapter
audioSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
@ -64,7 +63,7 @@ class DownloadDialog : DialogFragment() {
id: Long
) {
selectedAudio = position
Log.d(TAG,selectedAudio.toString())
Log.d(TAG, selectedAudio.toString())
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
@ -72,16 +71,16 @@ class DownloadDialog : DialogFragment() {
radioGroup.setOnCheckedChangeListener { group, checkedId ->
val radio: RadioButton = view.findViewById(checkedId)
extension = radio.text.toString()
Log.d(TAG,extension)
Log.d(TAG, extension)
}
view.findViewById<Button>(R.id.download).setOnClickListener {
val intent = Intent(context,DownloadService::class.java)
intent.putExtra("videoId",videoId)
intent.putExtra("videoUrl",vidUrl[selectedVideo])
intent.putExtra("audioUrl",audioUrl[selectedAudio])
intent.putExtra("duration",duration)
intent.putExtra("extension",extension)
//intent.putExtra("command","-y -i ${response.videoStreams[which].url} -i ${response.audioStreams!![0].url} -c copy ${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/${videoId}.mkv")
val intent = Intent(context, DownloadService::class.java)
intent.putExtra("videoId", videoId)
intent.putExtra("videoUrl", vidUrl[selectedVideo])
intent.putExtra("audioUrl", audioUrl[selectedAudio])
intent.putExtra("duration", duration)
intent.putExtra("extension", extension)
// intent.putExtra("command","-y -i ${response.videoStreams[which].url} -i ${response.audioStreams!![0].url} -c copy ${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/${videoId}.mkv")
context?.startService(intent)
dismiss()
}

View File

@ -16,17 +16,16 @@ import androidx.core.app.NotificationManagerCompat
import com.arthenica.ffmpegkit.FFmpegKit
import java.io.File
var IS_DOWNLOAD_RUNNING = false
class DownloadService : Service(){
class DownloadService : Service() {
val TAG = "DownloadService"
private var downloadId: Long =-1
private var downloadId: Long = -1
private lateinit var videoId: String
private lateinit var videoUrl: String
private lateinit var audioUrl: String
private lateinit var extension: String
private var duration: Int = 0
//private lateinit var command: String
// private lateinit var command: String
private lateinit var audioDir: File
private lateinit var videoDir: File
lateinit var service: NotificationManager
@ -41,29 +40,31 @@ class DownloadService : Service(){
videoUrl = intent.getStringExtra("videoUrl")!!
audioUrl = intent.getStringExtra("audioUrl")!!
extension = intent.getStringExtra("extension")!!
//command = intent.getStringExtra("command")!!
duration = intent.getIntExtra("duration",1)
// command = intent.getStringExtra("command")!!
duration = intent.getIntExtra("duration", 1)
service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val chan = NotificationChannel("service",
"DownloadService", NotificationManager.IMPORTANCE_NONE)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
service.createNotificationChannel(chan)
"service"
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
}
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val chan = NotificationChannel(
"service",
"DownloadService", NotificationManager.IMPORTANCE_NONE
)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
service.createNotificationChannel(chan)
"service"
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
}
var pendingIntent: PendingIntent? = null
pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE)
} else {
PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT)
}
//Creating a notification and setting its various attributes
// Creating a notification and setting its various attributes
notification =
NotificationCompat.Builder(this@DownloadService, channelId)
.setSmallIcon(R.drawable.ic_download)
@ -75,7 +76,7 @@ class DownloadService : Service(){
.setProgress(100, 0, true)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
startForeground(1,notification.build())
startForeground(1, notification.build())
downloadManager()
return super.onStartCommand(intent, flags, startId)
@ -112,10 +113,10 @@ class DownloadService : Service(){
val downloadManager: DownloadManager =
applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadId = downloadManager.enqueue(request)
if(audioUrl==""){downloadId = 0L}
if (audioUrl == "") { downloadId = 0L }
} catch (e: IllegalArgumentException) {
Log.e(TAG, "download error $e")
try{
try {
downloadId = 0L
val request: DownloadManager.Request =
DownloadManager.Request(Uri.parse(audioUrl))
@ -128,36 +129,35 @@ class DownloadService : Service(){
val downloadManager: DownloadManager =
applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
}catch (e: Exception){
} catch (e: Exception) {
Log.e(TAG, "audio download error $e")
stopService(Intent(this,DownloadService::class.java))}
stopService(Intent(this, DownloadService::class.java))
}
}
}
private val onDownloadComplete: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
//Fetching the download id received with the broadcast
// Fetching the download id received with the broadcast
val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
//Checking if the received broadcast is for our enqueued download by matching download id
// Checking if the received broadcast is for our enqueued download by matching download id
if (downloadId == id) {
downloadId=0L
try{
val request: DownloadManager.Request =
DownloadManager.Request(Uri.parse(audioUrl))
.setTitle("Audio") // Title of the Download Notification
.setDescription("Downloading") // Description of the Download Notification
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) // Visibility of the download Notification
.setDestinationUri(Uri.fromFile(audioDir))
.setAllowedOverMetered(true) // Set if download is allowed on Mobile network
.setAllowedOverRoaming(true) //
val downloadManager: DownloadManager =
applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
}catch (e: Exception){}
}else if (downloadId == 0L){
val libreTube = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),"LibreTube")
downloadId = 0L
try {
val request: DownloadManager.Request =
DownloadManager.Request(Uri.parse(audioUrl))
.setTitle("Audio") // Title of the Download Notification
.setDescription("Downloading") // Description of the Download Notification
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) // Visibility of the download Notification
.setDestinationUri(Uri.fromFile(audioDir))
.setAllowedOverMetered(true) // Set if download is allowed on Mobile network
.setAllowedOverRoaming(true) //
val downloadManager: DownloadManager =
applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
} catch (e: Exception) {}
} else if (downloadId == 0L) {
val libreTube = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "LibreTube")
if (!libreTube.exists()) {
libreTube.mkdirs()
Log.e(TAG, "libreTube Directory make")
@ -165,18 +165,19 @@ class DownloadService : Service(){
Log.e(TAG, "libreTube Directory already have")
}
var command: String = when {
videoUrl=="" -> {
"-y -i $audioDir -c copy ${libreTube}/${videoId}-audio$extension"
videoUrl == "" -> {
"-y -i $audioDir -c copy $libreTube/$videoId-audio$extension"
}
audioUrl=="" -> {
"-y -i $videoDir -c copy ${libreTube}/${videoId}-video$extension"
audioUrl == "" -> {
"-y -i $videoDir -c copy $libreTube/$videoId-video$extension"
}
else -> {
"-y -i $videoDir -i $audioDir -c copy ${libreTube}/${videoId}$extension"
"-y -i $videoDir -i $audioDir -c copy $libreTube/${videoId}$extension"
}
}
notification.setContentTitle("Muxing")
FFmpegKit.executeAsync(command,
FFmpegKit.executeAsync(
command,
{ session ->
val state = session.state
val returnCode = session.returnCode
@ -194,7 +195,7 @@ class DownloadService : Service(){
val folder_main = ".tmp"
val f = File(path, folder_main)
f.deleteRecursively()
if (returnCode.toString()!="0"){
if (returnCode.toString() != "0") {
var builder = NotificationCompat.Builder(this@DownloadService, "failed")
.setSmallIcon(R.drawable.ic_download)
.setContentTitle(resources.getString(R.string.downloadfailed))
@ -207,13 +208,14 @@ class DownloadService : Service(){
}
}
stopForeground(true)
stopService(Intent(this@DownloadService,DownloadService::class.java))
stopService(Intent(this@DownloadService, DownloadService::class.java))
}, {
// CALLED WHEN SESSION PRINTS LOGS
Log.e(TAG,it.message.toString())
}) {
// CALLED WHEN SESSION PRINTS LOGS
Log.e(TAG, it.message.toString())
}
) {
// CALLED WHEN SESSION GENERATES STATISTICS
Log.e(TAG+"stat",it.time.toString())
Log.e(TAG + "stat", it.time.toString())
/*val progress = it.time/(10*duration!!)
if (progress<1){
notification
@ -221,7 +223,6 @@ class DownloadService : Service(){
service.notify(1,notification.build())
}*/
}
}
}
}
@ -245,10 +246,9 @@ class DownloadService : Service(){
override fun onDestroy() {
try {
unregisterReceiver(onDownloadComplete)
}catch (e: Exception){}
} catch (e: Exception) {}
IS_DOWNLOAD_RUNNING = false
Log.d(TAG,"dl finished!")
Log.d(TAG, "dl finished!")
super.onDestroy()
}
}

View File

@ -2,23 +2,21 @@ package com.github.libretube
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import okhttp3.*
import retrofit2.HttpException
import com.github.libretube.adapters.TrendingAdapter
import java.io.IOException
import okhttp3.*
import retrofit2.HttpException
class Home : Fragment() {
@ -31,51 +29,47 @@ class Home : Fragment() {
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
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 grid = sharedPreferences.getString("grid", resources.getInteger(R.integer.grid_items).toString())!!
recyclerView.layoutManager = GridLayoutManager(view.context, grid.toInt())
val progressbar = view.findViewById<ProgressBar>(R.id.progressBar)
fetchJson(progressbar,recyclerView)
fetchJson(progressbar, recyclerView)
refreshLayout = view.findViewById(R.id.home_refresh)
refreshLayout?.isEnabled = true
refreshLayout?.setOnRefreshListener {
Log.d(TAG,"hmm")
fetchJson(progressbar,recyclerView)
Log.d(TAG, "hmm")
fetchJson(progressbar, recyclerView)
}
}
private fun fetchJson(progressBar: ProgressBar, recyclerView: RecyclerView) {
private fun fetchJson(progressBar: ProgressBar, recyclerView: RecyclerView) {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
RetrofitInstance.api.getTrending(sharedPreferences.getString("region", "US")!!)
}catch(e: IOException) {
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
Toast.makeText(context,R.string.unknown_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
Toast.makeText(context,R.string.server_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
}finally {
} finally {
refreshLayout?.isRefreshing = false
}
runOnUiThread {
@ -84,8 +78,7 @@ class Home : Fragment() {
}
}
}
run()
run()
}
private fun Fragment?.runOnUiThread(action: () -> Unit) {
this ?: return
@ -94,9 +87,9 @@ class Home : Fragment() {
}
override fun onDestroyView() {
view?.findViewById<RecyclerView>(R.id.recview)?.adapter=null
view?.findViewById<RecyclerView>(R.id.recview)?.adapter = null
refreshLayout = null
Log.e(TAG,"destroyview")
Log.e(TAG, "destroyview")
super.onDestroyView()
}
}

View File

@ -17,9 +17,8 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.github.libretube.adapters.PlaylistsAdapter
import com.github.libretube.obj.Playlists
import retrofit2.HttpException
import java.io.IOException
import retrofit2.HttpException
class Library : Fragment() {
@ -30,12 +29,12 @@ class Library : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
@ -47,107 +46,104 @@ class Library : Fragment() {
playlistRecyclerView = view.findViewById(R.id.playlist_recView)
playlistRecyclerView.layoutManager = LinearLayoutManager(view.context)
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
token = sharedPref?.getString("token","")!!
token = sharedPref?.getString("token", "")!!
refreshLayout = view.findViewById(R.id.playlist_refresh)
if(token!="") {
view.findViewById<ImageView>(R.id.boogh2).visibility=View.GONE
view.findViewById<TextView>(R.id.textLike2).visibility=View.GONE
if (token != "") {
view.findViewById<ImageView>(R.id.boogh2).visibility = View.GONE
view.findViewById<TextView>(R.id.textLike2).visibility = View.GONE
fetchPlaylists(view)
refreshLayout?.isEnabled = true
refreshLayout?.setOnRefreshListener {
Log.d(TAG,"hmm")
Log.d(TAG, "hmm")
fetchPlaylists(view)
}
view.findViewById<Button>(R.id.create_playlist).setOnClickListener {
val newFragment = CreatePlaylistDialog()
newFragment.show(childFragmentManager, "Create Playlist")
}
childFragmentManager.setFragmentResultListener("key_parent", this) { _, result->
childFragmentManager.setFragmentResultListener("key_parent", this) { _, result ->
val playlistName = result.getString("playlistName")
createPlaylist("$playlistName", view);
createPlaylist("$playlistName", view)
}
} else{
} else {
refreshLayout?.isEnabled = false
view.findViewById<Button>(R.id.create_playlist).visibility = View.GONE
with(view.findViewById<ImageView>(R.id.boogh2)){
visibility=View.VISIBLE
with(view.findViewById<ImageView>(R.id.boogh2)) {
visibility = View.VISIBLE
setImageResource(R.drawable.ic_login)
}
with(view.findViewById<TextView>(R.id.textLike2)){
visibility=View.VISIBLE
with(view.findViewById<TextView>(R.id.textLike2)) {
visibility = View.VISIBLE
text = getString(R.string.please_login)
}
}
}
private fun fetchPlaylists(view: View){
private fun fetchPlaylists(view: View) {
fun run() {
refreshLayout?.isRefreshing = true
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.playlists(token)
}catch(e: IOException) {
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
Toast.makeText(context,R.string.unknown_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
Toast.makeText(context,R.string.server_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
}finally {
} finally {
refreshLayout?.isRefreshing = false
}
if (response.isNotEmpty()){
if (response.isNotEmpty()) {
runOnUiThread {
with(view.findViewById<ImageView>(R.id.boogh2)){
visibility=View.GONE
with(view.findViewById<ImageView>(R.id.boogh2)) {
visibility = View.GONE
}
with(view.findViewById<TextView>(R.id.textLike2)){
visibility=View.GONE
with(view.findViewById<TextView>(R.id.textLike2)) {
visibility = View.GONE
}
}
val playlistsAdapter = PlaylistsAdapter(response.toMutableList(),requireActivity())
playlistRecyclerView.adapter= playlistsAdapter
}else{
val playlistsAdapter = PlaylistsAdapter(response.toMutableList(), requireActivity())
playlistRecyclerView.adapter = playlistsAdapter
} else {
runOnUiThread {
with(view.findViewById<ImageView>(R.id.boogh2)){
visibility=View.VISIBLE
with(view.findViewById<ImageView>(R.id.boogh2)) {
visibility = View.VISIBLE
setImageResource(R.drawable.ic_list)
}
with(view.findViewById<TextView>(R.id.textLike2)){
visibility=View.VISIBLE
with(view.findViewById<TextView>(R.id.textLike2)) {
visibility = View.VISIBLE
text = getString(R.string.emptyList)
}
}
}
}
}
run()
}
private fun createPlaylist(name: String, view: View){
private fun createPlaylist(name: String, view: View) {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.createPlaylist(token, Playlists(name = name))
}catch(e: IOException) {
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
Toast.makeText(context,R.string.unknown_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response $e")
Toast.makeText(context,R.string.server_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
}
if (response != null){
Toast.makeText(context,R.string.playlistCreated, Toast.LENGTH_SHORT).show()
if (response != null) {
Toast.makeText(context, R.string.playlistCreated, Toast.LENGTH_SHORT).show()
fetchPlaylists(view)
}else{
} else {
}
}
}
run()

View File

@ -2,7 +2,6 @@ package com.github.libretube
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
import android.util.Log
import android.util.TypedValue
@ -15,15 +14,12 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.text.HtmlCompat
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import com.github.libretube.adapters.TrendingAdapter
import com.github.libretube.obj.Login
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.w3c.dom.Text
import retrofit2.HttpException
import java.io.IOException
import java.lang.Exception
import kotlin.math.log
class LoginDialog : DialogFragment() {
private val TAG = "LoginDialog"
@ -33,43 +29,43 @@ class LoginDialog : DialogFragment() {
return activity?.let {
val builder = MaterialAlertDialogBuilder(it)
// Get the layout inflater
val inflater = requireActivity().layoutInflater;
val inflater = requireActivity().layoutInflater
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
val token = sharedPref?.getString("token","")
val token = sharedPref?.getString("token", "")
var view: View
Log.e("dafaq",token!!)
if(token!=""){
Log.e("dafaq", token!!)
if (token != "") {
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.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 {
Toast.makeText(context,R.string.loggedout, Toast.LENGTH_SHORT).show()
val sharedPref = context?.getSharedPreferences("token",Context.MODE_PRIVATE)
with (sharedPref!!.edit()) {
putString("token","")
Toast.makeText(context, R.string.loggedout, Toast.LENGTH_SHORT).show()
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
with(sharedPref!!.edit()) {
putString("token", "")
apply()
}
dialog?.dismiss()
}
}else{
} else {
view = inflater.inflate(R.layout.dialog_login, null)
username=view.findViewById(R.id.username)
password=view.findViewById(R.id.password)
username = view.findViewById(R.id.username)
password = view.findViewById(R.id.password)
view.findViewById<Button>(R.id.login).setOnClickListener {
if(username.text.toString()!="" && password.text.toString()!=""){
val login = Login(username.text.toString(),password.text.toString())
login(login)
}else{
Toast.makeText(context,R.string.empty, Toast.LENGTH_SHORT).show()
if (username.text.toString() != "" && password.text.toString() != "") {
val login = Login(username.text.toString(), password.text.toString())
login(login)
} else {
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
}
}
view.findViewById<Button>(R.id.register).setOnClickListener {
if(username.text.toString()!="" && password.text.toString()!=""){
val login = Login(username.text.toString(),password.text.toString())
register(login)
}else{
Toast.makeText(context,R.string.empty, Toast.LENGTH_SHORT).show()
if (username.text.toString() != "" && password.text.toString() != "") {
val login = Login(username.text.toString(), password.text.toString())
register(login)
} else {
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
}
}
}
@ -88,83 +84,80 @@ class LoginDialog : DialogFragment() {
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
private fun login(login: Login){
private fun login(login: Login) {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.login(login)
}catch(e: IOException) {
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
Toast.makeText(context,R.string.unknown_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
Toast.makeText(context,R.string.server_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
} catch (e: Exception) {
Log.e(TAG,"dafaq?"+e.toString())
Log.e(TAG, "dafaq?" + e.toString())
return@launchWhenCreated
}
if (response.error!= null){
if (response.error != null) {
Toast.makeText(context, response.error, Toast.LENGTH_SHORT).show()
}else if(response.token!=null){
Toast.makeText(context,R.string.loggedIn, Toast.LENGTH_SHORT).show()
val sharedPref = context?.getSharedPreferences("token",Context.MODE_PRIVATE)
with (sharedPref!!.edit()) {
putString("token",response.token)
} else if (response.token != null) {
Toast.makeText(context, R.string.loggedIn, Toast.LENGTH_SHORT).show()
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
with(sharedPref!!.edit()) {
putString("token", response.token)
apply()
}
val sharedPref2 = context?.getSharedPreferences("username",Context.MODE_PRIVATE)
with (sharedPref2!!.edit()) {
putString("username",login.username)
val sharedPref2 = context?.getSharedPreferences("username", Context.MODE_PRIVATE)
with(sharedPref2!!.edit()) {
putString("username", login.username)
apply()
}
dialog?.dismiss()
}
}
}
run()
}
private fun register(login: Login){
private fun register(login: Login) {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.register(login)
}catch(e: IOException) {
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
Toast.makeText(context,R.string.unknown_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
Toast.makeText(context,R.string.server_error, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
} catch (e: Exception) {
Log.e(TAG,"dafaq?"+e.toString())
Log.e(TAG, "dafaq?" + e.toString())
return@launchWhenCreated
}
if (response.error!= null){
Toast.makeText(context, response.error, Toast.LENGTH_SHORT).show()
}else if(response.token!=null){
Toast.makeText(context,R.string.registered, Toast.LENGTH_SHORT).show()
val sharedPref = context?.getSharedPreferences("token",Context.MODE_PRIVATE)
with (sharedPref!!.edit()) {
putString("token",response.token)
if (response.error != null) {
Toast.makeText(context, response.error, Toast.LENGTH_SHORT).show()
} else if (response.token != null) {
Toast.makeText(context, R.string.registered, Toast.LENGTH_SHORT).show()
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
with(sharedPref!!.edit()) {
putString("token", response.token)
apply()
}
val sharedPref2 = context?.getSharedPreferences("username",Context.MODE_PRIVATE)
with (sharedPref2!!.edit()) {
putString("username",login.username)
val sharedPref2 = context?.getSharedPreferences("username", Context.MODE_PRIVATE)
with(sharedPref2!!.edit()) {
putString("username", login.username)
apply()
}
dialog?.dismiss()
}
}
}
run()
}
}

View File

@ -17,7 +17,6 @@ import android.view.inputmethod.InputMethodManager
import android.widget.Button
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintLayout
@ -30,13 +29,12 @@ import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.color.DynamicColors
import java.util.*
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
lateinit var bottomNavigationView: BottomNavigationView
lateinit var toolbar: Toolbar
lateinit var navController : NavController
lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
DynamicColors.applyToActivityIfAvailable(this)
@ -50,42 +48,21 @@ class MainActivity : AppCompatActivity() {
SponsorBlockSettings.sponsorsEnabled = sharedPreferences.getBoolean("sponsors_category_key", false)
SponsorBlockSettings.outroEnabled = sharedPreferences.getBoolean("outro_category_key", false)
val languageName = sharedPreferences.getString("language", "sys")
if (languageName != "") {
var locale = if (languageName != "sys" && "$languageName".length < 3 ){
Locale(languageName)
} else if ("$languageName".length > 3) {
Locale(languageName?.substring(0,2), languageName?.substring(4,6))
} else {
Locale.getDefault()
}
val res = resources
val dm = res.displayMetrics
val conf = res.configuration
conf.setLocale(locale)
Locale.setDefault(locale)
res.updateConfiguration(conf, dm)
}
when (sharedPreferences.getString("theme_togglee", "A")!!) {
"A" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
"L" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
"D" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}
updateAccentColor(this)
updateThemeMode(this)
updateLanguage(this)
val connectivityManager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkInfo=connectivityManager.activeNetworkInfo
val networkInfo = connectivityManager.activeNetworkInfo
val isConnected = networkInfo != null && networkInfo.isConnected
if (isConnected == false) {
if (!isConnected) {
setContentView(R.layout.activity_nointernet)
findViewById<Button>(R.id.retry_button).setOnClickListener() {
recreate()
}
} else {
setContentView(R.layout.activity_main)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
bottomNavigationView = findViewById(R.id.bottomNav)
@ -98,6 +75,7 @@ class MainActivity : AppCompatActivity() {
"library" -> navController.navigate(R.id.library)
}
bottomNavigationView.setBackgroundColor(0) // otherwise Navbar Theme doesn't change
bottomNavigationView.setOnItemSelectedListener {
when (it.itemId) {
R.id.home2 -> {
@ -106,12 +84,12 @@ class MainActivity : AppCompatActivity() {
true
}
R.id.subscriptions -> {
//navController.backQueue.clear()
// navController.backQueue.clear()
navController.navigate(R.id.subscriptions)
true
}
R.id.library -> {
//navController.backQueue.clear()
// navController.backQueue.clear()
navController.navigate(R.id.library)
true
}
@ -121,7 +99,7 @@ class MainActivity : AppCompatActivity() {
toolbar = findViewById(R.id.toolbar)
val typedValue = TypedValue()
this.theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true)
this.theme.resolveAttribute(R.attr.colorPrimary, typedValue, true)
val hexColor = String.format("#%06X", (0xFFFFFF and typedValue.data))
val appName = HtmlCompat.fromHtml(
"Libre<span style='color:$hexColor';>Tube</span>",
@ -129,12 +107,12 @@ class MainActivity : AppCompatActivity() {
)
toolbar.title = appName
toolbar.setNavigationOnClickListener{
//settings activity stuff
val intent = Intent(this, SettingsActivity::class.java)
startActivity(intent)
true
}
toolbar.setNavigationOnClickListener {
// settings activity stuff
val intent = Intent(this, SettingsActivity::class.java)
startActivity(intent)
true
}
toolbar.setOnMenuItemClickListener {
when (it.itemId) {
@ -146,134 +124,128 @@ class MainActivity : AppCompatActivity() {
false
}
}
}
override fun onStart() {
super.onStart()
val action: String? = intent?.action
val data: Uri? = intent?.data
Log.d(TAG, "dafaq"+data.toString())
Log.d(TAG, "dafaq" + data.toString())
if (data != null) {
Log.d("dafaq",data.host+" ${data.path} ")
if(data.host != null){
if(data.path != null){
//channel
if(data.path!!.contains("/channel/") || data.path!!.contains("/c/") || data.path!!.contains("/user/")){
var channel = data.path
channel = channel!!.replace("/c/","")
channel = channel!!.replace("/user/","")
val bundle = bundleOf("channel_id" to channel)
navController.navigate(R.id.channel,bundle)
}else if(data.path!!.contains("/playlist")){
var playlist = data.query!!
if (playlist.contains("&"))
{
var playlists = playlist.split("&")
for (v in playlists){
if (v.contains("list=")){
playlist = v
break
}
Log.d("dafaq", data.host + " ${data.path} ")
if (data.host != null) {
if (data.path != null) {
// channel
if (data.path!!.contains("/channel/") || data.path!!.contains("/c/") || data.path!!.contains("/user/")) {
var channel = data.path
channel = channel!!.replace("/c/", "")
channel = channel!!.replace("/user/", "")
val bundle = bundleOf("channel_id" to channel)
navController.navigate(R.id.channel, bundle)
} else if (data.path!!.contains("/playlist")) {
var playlist = data.query!!
if (playlist.contains("&")) {
var playlists = playlist.split("&")
for (v in playlists) {
if (v.contains("list=")) {
playlist = v
break
}
}
playlist = playlist.replace("list=","")
val bundle = bundleOf("playlist_id" to playlist)
navController.navigate(R.id.playlistFragment,bundle)
}else if(data.path!!.contains("/shorts/") || data.path!!.contains("/embed/") || data.path!!.contains("/v/")){
var watch = data.path!!.replace("/shorts/","").replace("/v/","").replace("/embed/","")
var bundle = Bundle()
bundle.putString("videoId",watch)
var frag = PlayerFragment()
frag.arguments = bundle
supportFragmentManager.beginTransaction()
.remove(PlayerFragment())
.commit()
supportFragmentManager.beginTransaction()
.replace(R.id.container, frag)
.commitNow()
Handler().postDelayed({
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
motionLayout.transitionToEnd()
motionLayout.transitionToStart()
}, 100)
}else if(data.path!!.contains("/watch") && data.query != null){
Log.d("dafaq",data.query!!)
var watch = data.query!!
if (watch.contains("&"))
{
var watches = watch.split("&")
for (v in watches){
if (v.contains("v=")){
watch = v
break
}
}
}
var bundle = Bundle()
bundle.putString("videoId",watch.replace("v=",""))
var frag = PlayerFragment()
frag.arguments = bundle
supportFragmentManager.beginTransaction()
.remove(PlayerFragment())
.commit()
supportFragmentManager.beginTransaction()
.replace(R.id.container, frag)
.commitNow()
Handler().postDelayed({
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
motionLayout.transitionToEnd()
motionLayout.transitionToStart()
}, 100)
}else{
var watch = data.path!!.replace("/","")
var bundle = Bundle()
bundle.putString("videoId",watch)
var frag = PlayerFragment()
frag.arguments = bundle
supportFragmentManager.beginTransaction()
.remove(PlayerFragment())
.commit()
supportFragmentManager.beginTransaction()
.replace(R.id.container, frag)
.commitNow()
Handler().postDelayed({
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
motionLayout.transitionToEnd()
motionLayout.transitionToStart()
}, 100)
}
playlist = playlist.replace("list=", "")
val bundle = bundleOf("playlist_id" to playlist)
navController.navigate(R.id.playlistFragment, bundle)
} else if (data.path!!.contains("/shorts/") || data.path!!.contains("/embed/") || data.path!!.contains("/v/")) {
var watch = data.path!!.replace("/shorts/", "").replace("/v/", "").replace("/embed/", "")
var bundle = Bundle()
bundle.putString("videoId", watch)
var frag = PlayerFragment()
frag.arguments = bundle
supportFragmentManager.beginTransaction()
.remove(PlayerFragment())
.commit()
supportFragmentManager.beginTransaction()
.replace(R.id.container, frag)
.commitNow()
Handler().postDelayed({
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
motionLayout.transitionToEnd()
motionLayout.transitionToStart()
}, 100)
} else if (data.path!!.contains("/watch") && data.query != null) {
Log.d("dafaq", data.query!!)
var watch = data.query!!
if (watch.contains("&")) {
var watches = watch.split("&")
for (v in watches) {
if (v.contains("v=")) {
watch = v
break
}
}
}
var bundle = Bundle()
bundle.putString("videoId", watch.replace("v=", ""))
var frag = PlayerFragment()
frag.arguments = bundle
supportFragmentManager.beginTransaction()
.remove(PlayerFragment())
.commit()
supportFragmentManager.beginTransaction()
.replace(R.id.container, frag)
.commitNow()
Handler().postDelayed({
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
motionLayout.transitionToEnd()
motionLayout.transitionToStart()
}, 100)
} else {
var watch = data.path!!.replace("/", "")
var bundle = Bundle()
bundle.putString("videoId", watch)
var frag = PlayerFragment()
frag.arguments = bundle
supportFragmentManager.beginTransaction()
.remove(PlayerFragment())
.commit()
supportFragmentManager.beginTransaction()
.replace(R.id.container, frag)
.commitNow()
Handler().postDelayed({
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
motionLayout.transitionToEnd()
motionLayout.transitionToStart()
}, 100)
}
}
}
}
}
override fun onBackPressed() {
try{
try {
val mainMotionLayout = findViewById<MotionLayout>(R.id.mainMotionLayout)
if (mainMotionLayout.progress == 0.toFloat()){
if (mainMotionLayout.progress == 0.toFloat()) {
mainMotionLayout.transitionToEnd()
findViewById<ConstraintLayout>(R.id.main_container).isClickable=false
findViewById<ConstraintLayout>(R.id.main_container).isClickable = false
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
motionLayout.transitionToEnd()
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
with(motionLayout) {
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
enableTransition(R.id.yt_transition,true)
enableTransition(R.id.yt_transition, true)
}
findViewById<LinearLayout>(R.id.linLayout).visibility=View.VISIBLE
isFullScreen=false
}else{
findViewById<LinearLayout>(R.id.linLayout).visibility = View.VISIBLE
isFullScreen = false
} else {
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()
}
}
}catch (e: Exception){
} catch (e: Exception) {
navController.popBackStack()
moveTaskToBack(true)
}
@ -303,15 +275,17 @@ class MainActivity : AppCompatActivity() {
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
@Suppress("DEPRECATION")
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_FULLSCREEN
window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
)
}
}
private fun unsetFullscreen(){
private fun unsetFullscreen() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
@ -335,7 +309,6 @@ class MainActivity : AppCompatActivity() {
(fragment as? PlayerFragment)?.onUserLeaveHint()
}
}
}
fun Fragment.hideKeyboard() {
view?.let { activity?.hideKeyboard(it) }

View File

@ -17,7 +17,10 @@ interface PipedApi {
suspend fun getSegments(@Path("videoId") videoId: String, @Query("category") category: String): Segments
@GET("nextpage/comments/{videoId}")
suspend fun getCommentsNextPage(@Path("videoId") videoId: String, @Query("nextpage") nextPage: String): CommentsPage
suspend fun getCommentsNextPage(
@Path("videoId") videoId: String,
@Query("nextpage") nextPage: String
): CommentsPage
@GET("search")
suspend fun getSearchResults(
@ -39,13 +42,19 @@ interface PipedApi {
suspend fun getChannel(@Path("channelId") channelId: String): Channel
@GET("nextpage/channel/{channelId}")
suspend fun getChannelNextPage(@Path("channelId") channelId: String, @Query("nextpage") nextPage: String): Channel
suspend fun getChannelNextPage(
@Path("channelId") channelId: String,
@Query("nextpage") nextPage: String
): Channel
@GET("playlists/{playlistId}")
suspend fun getPlaylist(@Path("playlistId") playlistId: String): Playlist
@GET("nextpage/playlists/{playlistId}")
suspend fun getPlaylistNextPage(@Path("playlistId") playlistId: String, @Query("nextpage") nextPage: String): Playlist
suspend fun getPlaylistNextPage(
@Path("playlistId") playlistId: String,
@Query("nextpage") nextPage: String
): Playlist
@POST("login")
suspend fun login(@Body login: Login): Token
@ -57,7 +66,10 @@ interface PipedApi {
suspend fun getFeed(@Query("authToken") token: String?): List<StreamItem>
@GET("subscribed")
suspend fun isSubscribed(@Query("channelId") channelId: String, @Header("Authorization") token: String): Subscribed
suspend fun isSubscribed(
@Query("channelId") channelId: String,
@Header("Authorization") token: String
): Subscribed
@GET("subscriptions")
suspend fun subscriptions(@Header("Authorization") token: String): List<Subscription>
@ -69,7 +81,11 @@ interface PipedApi {
suspend fun unsubscribe(@Header("Authorization") token: String, @Body subscribe: Subscribe): Message
@POST("import")
suspend fun importSubscriptions(@Query("override") override: Boolean, @Header("Authorization") token: String, @Body channels: List<String>): Message
suspend fun importSubscriptions(
@Query("override") override: Boolean,
@Header("Authorization") token: String,
@Body channels: List<String>
): Message
@GET("user/playlists")
suspend fun playlists(@Header("Authorization") token: String): List<Playlists>
@ -84,12 +100,12 @@ interface PipedApi {
suspend fun addToPlaylist(@Header("Authorization") token: String, @Body playlistId: PlaylistId): Message
@POST("user/playlists/remove")
suspend fun removeFromPlaylist(@Header("Authorization") token: String, @Body playlistId: PlaylistId): Message
suspend fun removeFromPlaylist(
@Header("Authorization") token: String,
@Body playlistId: PlaylistId
): Message
//only for fetching servers list
// only for fetching servers list
@GET
suspend fun getInstances(@Url url: String): List<Instances>
}
}

View File

@ -8,20 +8,11 @@ import com.google.android.material.color.DynamicColors
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class Player : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
DynamicColors.applyToActivityIfAvailable(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player)
}
}

View File

@ -58,13 +58,12 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.exoplayer2.util.RepeatModeUtil
import com.google.android.material.button.MaterialButton
import com.squareup.picasso.Picasso
import org.chromium.net.CronetEngine
import retrofit2.HttpException
import java.io.IOException
import java.net.URLEncoder
import java.util.concurrent.Executors
import kotlin.math.abs
import org.chromium.net.CronetEngine
import retrofit2.HttpException
var isFullScreen = false
@ -247,7 +246,6 @@ class PlayerFragment : Fragment() {
) {
fetchNextComments()
}
}
commentsRecView = view.findViewById(R.id.comments_recView)
@ -274,22 +272,21 @@ class PlayerFragment : Fragment() {
}
}
private fun checkForSegments()
{
private fun checkForSegments() {
if (!exoPlayer.isPlaying || !SponsorBlockSettings.sponsorBlockEnabled) return
exoPlayerView.postDelayed(this::checkForSegments, 100)
if(segmentData.segments.isEmpty() )
if (segmentData.segments.isEmpty())
return
segmentData.segments.forEach { segment: Segment ->
val segmentStart = (segment.segment!![0] * 1000.0f).toLong()
val segmentEnd = (segment.segment!![1] * 1000.0f).toLong()
val currentPosition = exoPlayer.currentPosition
if(currentPosition in segmentStart until segmentEnd) {
Toast.makeText(context,R.string.segment_skipped, Toast.LENGTH_SHORT).show()
exoPlayer.seekTo(segmentEnd);
if (currentPosition in segmentStart until segmentEnd) {
Toast.makeText(context, R.string.segment_skipped, Toast.LENGTH_SHORT).show()
exoPlayer.seekTo(segmentEnd)
}
}
}
@ -321,7 +318,7 @@ class PlayerFragment : Fragment() {
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
}
if(SponsorBlockSettings.sponsorBlockEnabled) {
if (SponsorBlockSettings.sponsorBlockEnabled) {
val categories: ArrayList<String> = arrayListOf()
if (SponsorBlockSettings.introEnabled) {
categories.add("intro")
@ -338,7 +335,7 @@ class PlayerFragment : Fragment() {
if (SponsorBlockSettings.outroEnabled) {
categories.add("outro")
}
if(categories.size > 0) {
if (categories.size > 0) {
segmentData = try {
RetrofitInstance.api.getSegments(
@ -401,10 +398,10 @@ class PlayerFragment : Fragment() {
exoPlayerView.setShowSubtitleButton(true)
exoPlayerView.setShowNextButton(false)
exoPlayerView.setShowPreviousButton(false)
exoPlayerView.setRepeatToggleModes(RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL);
exoPlayerView.setRepeatToggleModes(RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL)
// exoPlayerView.controllerShowTimeoutMs = 1500
exoPlayerView.controllerHideOnTouch = true
exoPlayer.setAudioAttributes(audioAttributes,true);
exoPlayer.setAudioAttributes(audioAttributes, true)
exoPlayerView.player = exoPlayer
val sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(requireContext())
@ -433,9 +430,11 @@ class PlayerFragment : Fragment() {
ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(
fromUri(
response.audioStreams!![getMostBitRate(
response.audioStreams
)].url!!
response.audioStreams!![
getMostBitRate(
response.audioStreams
)
].url!!
)
)
}
@ -479,9 +478,11 @@ class PlayerFragment : Fragment() {
audioSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(
fromUri(
response.audioStreams!![getMostBitRate(
response.audioStreams
)].url!!
response.audioStreams!![
getMostBitRate(
response.audioStreams
)
].url!!
)
)
}
@ -545,9 +546,11 @@ class PlayerFragment : Fragment() {
ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(
fromUri(
response.audioStreams!![getMostBitRate(
response.audioStreams
)].url!!
response.audioStreams!![
getMostBitRate(
response.audioStreams
)
].url!!
)
)
}
@ -555,7 +558,7 @@ class PlayerFragment : Fragment() {
MergingMediaSource(videoSource, audioSource)
exoPlayer.setMediaSource(mergeSource)
}
exoPlayer.seekTo(lastPosition);
exoPlayer.seekTo(lastPosition)
view.findViewById<TextView>(R.id.quality_text).text =
videosNameArray[which]
}
@ -566,8 +569,7 @@ class PlayerFragment : Fragment() {
// Listener for play and pause icon change
exoPlayer!!.addListener(object : com.google.android.exoplayer2.Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) {
if(isPlaying && SponsorBlockSettings.sponsorBlockEnabled)
{
if (isPlaying && SponsorBlockSettings.sponsorBlockEnabled) {
exoPlayerView.postDelayed(this@PlayerFragment::checkForSegments, 100)
}
}
@ -578,9 +580,9 @@ class PlayerFragment : Fragment() {
) {
exoPlayerView.keepScreenOn = !(
playbackState == Player.STATE_IDLE || playbackState == Player.STATE_ENDED ||
!playWhenReady
)
playbackState == Player.STATE_IDLE || playbackState == Player.STATE_ENDED ||
!playWhenReady
)
if (playWhenReady && playbackState == Player.STATE_READY) {
// media actually playing
@ -678,9 +680,9 @@ class PlayerFragment : Fragment() {
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
mainActivity,
@ -772,7 +774,6 @@ class PlayerFragment : Fragment() {
return@launchWhenCreated
}
runOnUiThread {
if (response.subscribed == true) {
isSubscribed = true
@ -864,25 +865,25 @@ class PlayerFragment : Fragment() {
super.onResume()
}
private fun fetchNextComments(){
lifecycleScope.launchWhenCreated {
if (!isLoading) {
isLoading = true
val response = try {
RetrofitInstance.api.getCommentsNextPage(videoId!!, nextPage!!)
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response," + e.response())
return@launchWhenCreated
}
nextPage = response.nextpage
commentsAdapter?.updateItems(response.comments!!)
isLoading = false
private fun fetchNextComments() {
lifecycleScope.launchWhenCreated {
if (!isLoading) {
isLoading = true
val response = try {
RetrofitInstance.api.getCommentsNextPage(videoId!!, nextPage!!)
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response," + e.response())
return@launchWhenCreated
}
nextPage = response.nextpage
commentsAdapter?.updateItems(response.comments!!)
isLoading = false
}
}
}
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
@ -898,7 +899,7 @@ class PlayerFragment : Fragment() {
view?.findViewById<FrameLayout>(R.id.top_bar)?.visibility = View.GONE
val mainActivity = activity as MainActivity
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
isFullScreen = false;
isFullScreen = false
} else {
with(motionLayout) {
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
@ -917,6 +918,6 @@ class PlayerFragment : Fragment() {
if (SDK_INT >= Build.VERSION_CODES.N && exoPlayer.isPlaying && (scrollView?.getLocalVisibleRect(bounds) == true || isFullScreen)) {
requireActivity().enterPictureInPictureMode()
};
}
}
}

View File

@ -3,27 +3,23 @@ package com.github.libretube
import android.content.Context
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ScrollView
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.adapters.ChannelAdapter
import com.github.libretube.adapters.PlaylistAdapter
import com.squareup.picasso.Picasso
import retrofit2.HttpException
import java.io.IOException
import retrofit2.HttpException
class PlaylistFragment : Fragment() {
private var playlist_id: String? = null
private val TAG = "PlaylistFragment"
var nextPage: String? =null
var nextPage: String? = null
var playlistAdapter: PlaylistAdapter? = null
var isLoading = true
override fun onCreate(savedInstanceState: Bundle?) {
@ -34,7 +30,8 @@ class PlaylistFragment : Fragment() {
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
@ -44,19 +41,19 @@ class PlaylistFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
playlist_id = playlist_id!!.replace("/playlist?list=","")
view.findViewById<TextView>(R.id.playlist_name).text=playlist_id
playlist_id = playlist_id!!.replace("/playlist?list=", "")
view.findViewById<TextView>(R.id.playlist_name).text = playlist_id
val recyclerView = view.findViewById<RecyclerView>(R.id.playlist_recView)
recyclerView.layoutManager = LinearLayoutManager(context)
fetchPlaylist(view)
}
private fun fetchPlaylist(view: View){
private fun fetchPlaylist(view: View) {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.getPlaylist(playlist_id!!)
}catch(e: IOException) {
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
@ -65,15 +62,15 @@ class PlaylistFragment : Fragment() {
return@launchWhenCreated
}
nextPage = response.nextpage
isLoading=false
isLoading = false
runOnUiThread {
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_totVideos).text=response.videos.toString()+" Videos"
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_totVideos).text = 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
if(response.uploaderUrl == null && response.uploader.equals(user, true)){
if (response.uploaderUrl == null && response.uploader.equals(user, true)) {
isOwner = true
}
playlistAdapter = PlaylistAdapter(response.relatedStreams!!.toMutableList(), playlist_id!!, isOwner, requireActivity())
@ -82,15 +79,15 @@ class PlaylistFragment : Fragment() {
scrollView.viewTreeObserver
.addOnScrollChangedListener {
if (scrollView.getChildAt(0).bottom
== (scrollView.height + scrollView.scrollY)) {
//scroll view is at bottom
if(nextPage!=null && !isLoading){
isLoading=true
== (scrollView.height + scrollView.scrollY)
) {
// scroll view is at bottom
if (nextPage != null && !isLoading) {
isLoading = true
fetchNextPage()
}
} else {
//scroll view is not at bottom
// scroll view is not at bottom
}
}
}
@ -99,24 +96,23 @@ class PlaylistFragment : Fragment() {
run()
}
private fun fetchNextPage(){
private fun fetchNextPage() {
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.getPlaylistNextPage(playlist_id!!,nextPage!!)
RetrofitInstance.api.getPlaylistNextPage(playlist_id!!, nextPage!!)
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response,"+e.response())
Log.e(TAG, "HttpException, unexpected response," + e.response())
return@launchWhenCreated
}
nextPage = response.nextpage
playlistAdapter?.updateItems(response.relatedStreams!!)
isLoading=false
isLoading = false
}
}
run()
@ -127,4 +123,4 @@ class PlaylistFragment : Fragment() {
if (!isAdded) return // Fragment not attached to an Activity
activity?.runOnUiThread(action)
}
}
}

View File

@ -8,13 +8,13 @@ class ResettableLazyManager {
val managedDelegates = LinkedList<Resettable>()
fun register(managed: Resettable) {
synchronized (managedDelegates) {
synchronized(managedDelegates) {
managedDelegates.add(managed)
}
}
fun reset() {
synchronized (managedDelegates) {
synchronized(managedDelegates) {
managedDelegates.forEach { it.reset() }
managedDelegates.clear()
}
@ -25,7 +25,7 @@ interface Resettable {
fun reset()
}
class ResettableLazy<PROPTYPE>(val manager: ResettableLazyManager, val init: ()->PROPTYPE): Resettable {
class ResettableLazy<PROPTYPE>(val manager: ResettableLazyManager, val init: () -> PROPTYPE) : Resettable {
@Volatile var lazyHolder = makeInitBlock()
operator fun getValue(thisRef: Any?, property: KProperty<*>): PROPTYPE {
@ -44,8 +44,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)
}
fun resettableManager(): ResettableLazyManager = ResettableLazyManager()
fun resettableManager(): ResettableLazyManager = ResettableLazyManager()

View File

@ -13,5 +13,4 @@ object RetrofitInstance {
.build()
.create(PipedApi::class.java)
}
}
}

View File

@ -23,38 +23,37 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.adapters.SearchAdapter
import com.github.libretube.adapters.SearchHistoryAdapter
import java.io.IOException
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.io.IOException
class SearchFragment : Fragment() {
private val TAG = "SearchFragment"
private var selectedFilter = 0
private var apiSearchFilter = "all"
private var nextPage : String? = null
private lateinit var searchRecView : RecyclerView
private var searchAdapter : SearchAdapter? = null
private var isLoading : Boolean = true
private var nextPage: String? = null
private lateinit var searchRecView: RecyclerView
private var searchAdapter: SearchAdapter? = null
private var isLoading: Boolean = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_search, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
searchRecView = view.findViewById<RecyclerView>(R.id.search_recycler)
@ -81,30 +80,37 @@ class SearchFragment : Fragment() {
MaterialAlertDialogBuilder(view.context)
.setTitle(getString(R.string.choose_filter))
.setSingleChoiceItems(filterOptions, selectedFilter, DialogInterface.OnClickListener {
_, id -> tempSelectedItem = id
})
.setPositiveButton(getString(R.string.okay), DialogInterface.OnClickListener { _, _ ->
selectedFilter = tempSelectedItem
apiSearchFilter = when (selectedFilter) {
0 -> "all"
1 -> "videos"
2 -> "channels"
3 -> "playlists"
4 -> "music_songs"
5 -> "music_videos"
6 -> "music_albums"
7 -> "music_playlists"
else -> "all"
.setSingleChoiceItems(
filterOptions, selectedFilter,
DialogInterface.OnClickListener {
_, id ->
tempSelectedItem = id
}
)
.setPositiveButton(
getString(R.string.okay),
DialogInterface.OnClickListener { _, _ ->
selectedFilter = tempSelectedItem
apiSearchFilter = when (selectedFilter) {
0 -> "all"
1 -> "videos"
2 -> "channels"
3 -> "playlists"
4 -> "music_songs"
5 -> "music_videos"
6 -> "music_albums"
7 -> "music_playlists"
else -> "all"
}
fetchSearch(autoTextView.text.toString())
})
}
)
.setNegativeButton(getString(R.string.cancel), null)
.create()
.show()
}
//show search history
// show search history
searchRecView.visibility = GONE
historyRecycler.visibility = VISIBLE
@ -129,7 +135,6 @@ class SearchFragment : Fragment() {
count: Int,
after: Int
) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
@ -143,7 +148,6 @@ class SearchFragment : Fragment() {
if (!searchRecView.canScrollVertically(1)) {
fetchNextSearchItems(autoTextView.text.toString())
}
}
GlobalScope.launch {
@ -167,22 +171,23 @@ class SearchFragment : Fragment() {
}
}
}
})
autoTextView.setOnEditorActionListener(OnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
hideKeyboard()
autoTextView.dismissDropDown()
return@OnEditorActionListener true
autoTextView.setOnEditorActionListener(
OnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
hideKeyboard()
autoTextView.dismissDropDown()
return@OnEditorActionListener true
}
false
}
false
})
)
autoTextView.setOnItemClickListener { _, _, _, _ ->
hideKeyboard()
}
}
private fun fetchSuggestions(query: String, autoTextView: AutoCompleteTextView){
private fun fetchSuggestions(query: String, autoTextView: AutoCompleteTextView) {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.getSuggestions(query)
@ -198,7 +203,7 @@ class SearchFragment : Fragment() {
autoTextView.setAdapter(adapter)
}
}
private fun fetchSearch(query: String){
private fun fetchSearch(query: String) {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.getSearchResults(query, apiSearchFilter)
@ -211,22 +216,22 @@ class SearchFragment : Fragment() {
return@launchWhenCreated
}
nextPage = response.nextpage
if(response.items!!.isNotEmpty()){
runOnUiThread {
searchAdapter = SearchAdapter(response.items)
searchRecView.adapter = searchAdapter
}
if (response.items!!.isNotEmpty()) {
runOnUiThread {
searchAdapter = SearchAdapter(response.items)
searchRecView.adapter = searchAdapter
}
}
isLoading = false
}
}
private fun fetchNextSearchItems(query: String){
private fun fetchNextSearchItems(query: String) {
lifecycleScope.launchWhenCreated {
if (!isLoading) {
isLoading = true
val response = try {
RetrofitInstance.api.getSearchResultsNextPage(query,apiSearchFilter,nextPage!!)
RetrofitInstance.api.getSearchResultsNextPage(query, apiSearchFilter, nextPage!!)
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
@ -240,7 +245,7 @@ class SearchFragment : Fragment() {
isLoading = false
}
}
}
}
private fun Fragment?.runOnUiThread(action: () -> Unit) {
this ?: return
@ -263,18 +268,14 @@ class SearchFragment : Fragment() {
var historyList = getHistory()
if (historyList.size != 0 && query == historyList.get(historyList.size - 1)) {
return
} else if (query == "") {
return
} else {
historyList = historyList + query
}
if (historyList.size > 10) {
historyList = historyList.takeLast(10)
}
@ -293,7 +294,5 @@ class SearchFragment : Fragment() {
} catch (e: Exception) {
return emptyList()
}
}
}

View File

@ -15,7 +15,6 @@ import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
@ -25,26 +24,31 @@ import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import org.json.JSONObject
import org.json.JSONTokener
import retrofit2.HttpException
import java.io.IOException
import java.io.InputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import org.json.JSONObject
import org.json.JSONTokener
import retrofit2.HttpException
class SettingsActivity : AppCompatActivity(),
SharedPreferences.OnSharedPreferenceChangeListener{
class SettingsActivity :
AppCompatActivity(),
SharedPreferences.OnSharedPreferenceChangeListener {
override fun onCreate(savedInstanceState: Bundle?) {
DynamicColors.applyToActivityIfAvailable(this)
updateAccentColor(this)
updateThemeMode(this)
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
overridePendingTransition(50, 50);
overridePendingTransition(50, 50)
}
val view = this.findViewById<View>(android.R.id.content)
view.setAlpha(0F);
view.animate().alpha(1F).setDuration(300);
view.alpha = 0F
view.animate().alpha(1F).duration = 300
setContentView(R.layout.activity_settings)
if (savedInstanceState == null) {
supportFragmentManager
@ -55,12 +59,10 @@ class SettingsActivity : AppCompatActivity(),
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, rootKey: String?) {}
class SettingsFragment : PreferenceFragmentCompat() {
val TAG = "Settings"
@ -70,7 +72,6 @@ class SettingsActivity : AppCompatActivity(),
override fun onCreate(savedInstanceState: Bundle?) {
getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
if (uri != null) {
try {
// Open a specific media item using ParcelFileDescriptor.
@ -80,23 +81,23 @@ class SettingsActivity : AppCompatActivity(),
// "rw" for read-and-write;
// "rwt" for truncating or overwriting existing file contents.
//val readOnlyMode = "r"
// val readOnlyMode = "r"
// uri - I have got from onActivityResult
val type = resolver.getType(uri)
var inputStream: InputStream? = resolver.openInputStream(uri)
val channels = ArrayList<String>()
if(type == "application/json"){
if (type == "application/json") {
val json = inputStream?.bufferedReader()?.readLines()?.get(0)
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()) {
var url = jsonObject.getJSONArray("subscriptions").getJSONObject(i).getString("url")
url = url.replace("https://www.youtube.com/channel/","")
Log.e(TAG,url)
url = url.replace("https://www.youtube.com/channel/", "")
Log.e(TAG, url)
channels.add(url)
}
}else {
} else {
if (type == "application/zip") {
val zis = ZipInputStream(inputStream)
var entry: ZipEntry? = zis.nextEntry
@ -129,8 +130,6 @@ class SettingsActivity : AppCompatActivity(),
).show()
}
}
}
super.onCreate(savedInstanceState)
}
@ -165,7 +164,7 @@ class SettingsActivity : AppCompatActivity(),
val sponsorblock = findPreference<Preference>("sponsorblock")
sponsorblock?.setOnPreferenceClickListener {
val newFragment = SponsorBlockSettings()
parentFragmentManager.beginTransaction()
parentFragmentManager.beginTransaction()
.replace(R.id.settings, newFragment)
.commitNow()
true
@ -175,7 +174,7 @@ class SettingsActivity : AppCompatActivity(),
importFromYt?.setOnPreferenceClickListener {
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
val token = sharedPref?.getString("token", "")!!
//check StorageAccess
// check StorageAccess
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Log.d("myz", "" + Build.VERSION.SDK_INT)
if (ContextCompat.checkSelfPermission(
@ -185,11 +184,13 @@ class SettingsActivity : AppCompatActivity(),
!= PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this.requireActivity(), arrayOf(
this.requireActivity(),
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.MANAGE_EXTERNAL_STORAGE
), 1
) //permission request code is just an int
),
1
) // permission request code is just an int
} else if (token != "") {
getContent.launch("*/*")
} else {
@ -200,9 +201,9 @@ class SettingsActivity : AppCompatActivity(),
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this.requireActivity(),
@ -223,11 +224,15 @@ class SettingsActivity : AppCompatActivity(),
val themeToggle = findPreference<ListPreference>("theme_togglee")
themeToggle?.setOnPreferenceChangeListener { _, newValue ->
when (newValue.toString()) {
"A" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
"L" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
"D" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}
val refresh = Intent(context, SettingsActivity::class.java)
startActivity(refresh)
true
}
val accentColor = findPreference<Preference>("accent_color")
accentColor?.setOnPreferenceChangeListener { _, _ ->
val refresh = Intent(context, SettingsActivity::class.java)
startActivity(refresh)
true
}
@ -255,16 +260,16 @@ class SettingsActivity : AppCompatActivity(),
val license = findPreference<Preference>("license")
license?.setOnPreferenceClickListener {
val licenseString = view?.context?.assets!!.open("gpl3.html").bufferedReader().use{
val licenseString = view?.context?.assets!!.open("gpl3.html").bufferedReader().use {
it.readText()
}
val licenseHtml = if (Build.VERSION.SDK_INT >= 24) {
Html.fromHtml(licenseString,1)
Html.fromHtml(licenseString, 1)
} else {
Html.fromHtml(licenseString)
}
AlertDialog.Builder(view?.context!!)
.setPositiveButton(getString(R.string.okay), DialogInterface.OnClickListener{ _,_ -> })
.setPositiveButton(getString(R.string.okay), DialogInterface.OnClickListener { _, _ -> })
.setMessage(licenseHtml)
.create()
.show()
@ -318,7 +323,6 @@ class SettingsActivity : AppCompatActivity(),
activity?.runOnUiThread(action)
}
private fun subscribe(channels: List<String>) {
fun run() {
lifecycleScope.launchWhenCreated {

View File

@ -3,13 +3,10 @@ package com.github.libretube
import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.util.Log
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.recyclerview.widget.RecyclerView
/**
*
@ -20,7 +17,7 @@ import androidx.recyclerview.widget.RecyclerView
class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeSet? = null) : MotionLayout(context, attributeSet) {
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
}
private val viewRect = Rect()
private var touchStarted = false
@ -33,7 +30,6 @@ class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeS
startId: Int,
endId: Int
) {
}
override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
@ -49,7 +45,6 @@ class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeS
positive: Boolean,
progress: Float
) {
}
})
@ -59,7 +54,6 @@ class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeS
startId: Int,
endId: Int
) {
}
override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
@ -78,7 +72,6 @@ class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeS
positive: Boolean,
progress: Float
) {
}
})
}
@ -91,15 +84,18 @@ class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeS
transitionListenerList += listener
}
private val gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
transitionToEnd()
return false
private val gestureDetector = GestureDetector(
context,
object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
transitionToEnd()
return false
}
}
})
)
override fun onTouchEvent(event: MotionEvent): Boolean {
//gestureDetector.onTouchEvent(event)
// gestureDetector.onTouchEvent(event)
when (event.actionMasked) {
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
touchStarted = false
@ -112,4 +108,4 @@ class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeS
}
return touchStarted && super.onTouchEvent(event)
}
}
}

View File

@ -52,4 +52,4 @@ class SponsorBlockSettings : PreferenceFragmentCompat() {
true
}
}
}
}

View File

@ -3,29 +3,28 @@ package com.github.libretube
import android.content.Context
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.github.libretube.adapters.SubscriptionAdapter
import com.github.libretube.adapters.SubscriptionChannelAdapter
import java.io.IOException
import org.chromium.base.ThreadUtils.runOnUiThread
import retrofit2.HttpException
import java.io.IOException
class Subscriptions : Fragment() {
val TAG = "SubFragment"
lateinit var token: String
var isLoaded = false
private var subscriptionAdapter: SubscriptionAdapter? =null
private var subscriptionAdapter: SubscriptionAdapter? = null
private var refreshLayout: SwipeRefreshLayout? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -34,7 +33,8 @@ class Subscriptions : Fragment() {
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
@ -44,14 +44,14 @@ class Subscriptions : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
token = sharedPref?.getString("token","")!!
token = sharedPref?.getString("token", "")!!
refreshLayout = view.findViewById(R.id.sub_refresh)
if(token!=""){
view.findViewById<RelativeLayout>(R.id.loginOrRegister).visibility=View.GONE
if (token != "") {
view.findViewById<RelativeLayout>(R.id.loginOrRegister).visibility = View.GONE
refreshLayout?.isEnabled = true
var progressBar = view.findViewById<ProgressBar>(R.id.sub_progress)
progressBar.visibility=View.VISIBLE
progressBar.visibility = View.VISIBLE
var channelRecView = view.findViewById<RecyclerView>(R.id.sub_channels)
@ -78,8 +78,7 @@ class Subscriptions : Fragment() {
}
channelRecView.visibility = View.VISIBLE
feedRecView.visibility = View.GONE
}
else {
} else {
channelRecView.visibility = View.GONE
feedRecView.visibility = View.VISIBLE
}
@ -89,14 +88,14 @@ class Subscriptions : Fragment() {
scrollView.viewTreeObserver
.addOnScrollChangedListener {
if (scrollView.getChildAt(0).bottom
== (scrollView.height + scrollView.scrollY)) {
//scroll view is at bottom
if(isLoaded){
== (scrollView.height + scrollView.scrollY)
) {
// scroll view is at bottom
if (isLoaded) {
refreshLayout?.isRefreshing = true
subscriptionAdapter?.updateItems()
refreshLayout?.isRefreshing = false
}
}
}
} else {
@ -109,8 +108,8 @@ class Subscriptions : Fragment() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.getFeed(token)
}catch(e: IOException) {
Log.e(TAG,e.toString())
} catch (e: IOException) {
Log.e(TAG, e.toString())
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
} catch (e: HttpException) {
@ -119,25 +118,25 @@ class Subscriptions : Fragment() {
} finally {
refreshLayout?.isRefreshing = false
}
if (response.isNotEmpty()){
if (response.isNotEmpty()) {
subscriptionAdapter = SubscriptionAdapter(response)
feedRecView?.adapter= subscriptionAdapter
feedRecView?.adapter = subscriptionAdapter
subscriptionAdapter?.updateItems()
}else{
} else {
runOnUiThread {
with(view.findViewById<ImageView>(R.id.boogh)){
visibility=View.VISIBLE
with(view.findViewById<ImageView>(R.id.boogh)) {
visibility = View.VISIBLE
setImageResource(R.drawable.ic_list)
}
with(view.findViewById<TextView>(R.id.textLike)){
visibility=View.VISIBLE
with(view.findViewById<TextView>(R.id.textLike)) {
visibility = View.VISIBLE
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
isLoaded=true
progressBar.visibility = View.GONE
isLoaded = true
}
}
run()
@ -148,8 +147,8 @@ class Subscriptions : Fragment() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.subscriptions(token)
}catch(e: IOException) {
Log.e(TAG,e.toString())
} catch (e: IOException) {
Log.e(TAG, e.toString())
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
} catch (e: HttpException) {
@ -158,25 +157,24 @@ class Subscriptions : Fragment() {
} finally {
refreshLayout?.isRefreshing = false
}
if (response.isNotEmpty()){
channelRecView?.adapter=SubscriptionChannelAdapter(response.toMutableList())
}else{
Toast.makeText(context,R.string.subscribeIsEmpty, Toast.LENGTH_SHORT).show()
if (response.isNotEmpty()) {
channelRecView?.adapter = SubscriptionChannelAdapter(response.toMutableList())
} else {
Toast.makeText(context, R.string.subscribeIsEmpty, Toast.LENGTH_SHORT).show()
}
}
}
run()
}
override fun onDestroy() {
Log.e(TAG,"Destroyed")
Log.e(TAG, "Destroyed")
super.onDestroy()
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) {
this ?: return
if (!isAdded) return // Fragment not attached to an Activity
activity?.runOnUiThread(action)
}
}

View File

@ -0,0 +1,53 @@
package com.github.libretube
import android.content.Context
import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.PreferenceManager
import java.util.*
fun updateAccentColor(context: Context) {
val colorAccent = PreferenceManager.getDefaultSharedPreferences(context).getString("accent_color", "red")
when (colorAccent) {
"my" -> context.setTheme(R.style.Theme_MY)
"red" -> context.setTheme(R.style.Theme_Red)
"blue" -> context.setTheme(R.style.Theme_Blue)
"yellow" -> context.setTheme(R.style.Theme_Yellow)
"green" -> context.setTheme(R.style.Theme_Green)
"purple" -> context.setTheme(R.style.Theme_Purple)
}
}
fun updateThemeMode(context: Context) {
val themeMode = PreferenceManager.getDefaultSharedPreferences(context).getString("theme_togglee", "A")
when (themeMode) {
"A" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
"L" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
"D" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
"O" -> oledMode(context)
}
}
fun oledMode(context: Context) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
context.setTheme(R.style.Theme_OLED)
}
fun updateLanguage(context: Context) {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
val languageName = sharedPreferences.getString("language", "sys")
if (languageName != "") {
var locale = if (languageName != "sys" && "$languageName".length < 3) {
Locale(languageName)
} else if ("$languageName".length > 3) {
Locale(languageName?.substring(0, 2), languageName?.substring(4, 6))
} else {
Locale.getDefault()
}
val res = context.resources
val dm = res.displayMetrics
val conf = res.configuration
conf.setLocale(locale)
Locale.setDefault(locale)
res.updateConfiguration(conf, dm)
}
}

View File

@ -15,31 +15,31 @@ import com.github.libretube.formatShort
import com.github.libretube.obj.StreamItem
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 {
return videoFeed.size
}
fun updateItems(newItems: List<StreamItem>){
fun updateItems(newItems: List<StreamItem>) {
videoFeed.addAll(newItems)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cell = layoutInflater.inflate(R.layout.video_channel_row,parent,false)
val cell = layoutInflater.inflate(R.layout.video_channel_row, parent, false)
return ChannelViewHolder(cell)
}
override fun onBindViewHolder(holder: ChannelViewHolder, position: Int) {
val trending = videoFeed[position]
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 = 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)
Picasso.get().load(trending.thumbnail).into(thumbnailImage)
holder.v.setOnClickListener{
holder.v.setOnClickListener {
var bundle = Bundle()
bundle.putString("videoId",trending.url!!.replace("/watch?v=",""))
bundle.putString("videoId", trending.url!!.replace("/watch?v=", ""))
var frag = PlayerFragment()
frag.arguments = bundle
val activity = holder.v.context as AppCompatActivity
@ -52,7 +52,7 @@ class ChannelAdapter(private val videoFeed: MutableList<StreamItem>): RecyclerVi
}
}
}
class ChannelViewHolder(val v: View): RecyclerView.ViewHolder(v){
class ChannelViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init {
}
}
}

View File

@ -1,6 +1,5 @@
package com.github.libretube.adapters
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -15,9 +14,9 @@ import com.github.libretube.formatShort
import com.github.libretube.obj.Comment
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
comments.addAll(newItems)
notifyItemRangeInserted(commentsSize, newItems.size)
@ -43,7 +42,7 @@ class CommentsAdapter(private val comments: MutableList<Comment>): RecyclerView
if (comments[position].hearted == true) {
holder.v.findViewById<ImageView>(R.id.hearted_imageView).visibility = View.VISIBLE
}
channelImage.setOnClickListener{
channelImage.setOnClickListener {
val activity = holder.v.context as MainActivity
val bundle = bundleOf("channel_id" to comments[position].commentorUrl)
activity.navController.navigate(R.id.channel, bundle)
@ -53,8 +52,7 @@ class CommentsAdapter(private val comments: MutableList<Comment>): RecyclerView
mainMotionLayout.transitionToEnd()
activity.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
}
}catch (e: Exception){
} catch (e: Exception) {
}
}
}
@ -62,10 +60,9 @@ class CommentsAdapter(private val comments: MutableList<Comment>): RecyclerView
override fun getItemCount(): Int {
return comments.size
}
}
class ViewHolder(val v: View): RecyclerView.ViewHolder(v){
class ViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init {
}
}

View File

@ -18,24 +18,29 @@ import com.github.libretube.RetrofitInstance
import com.github.libretube.obj.PlaylistId
import com.github.libretube.obj.StreamItem
import com.squareup.picasso.Picasso
import java.io.IOException
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.io.IOException
class PlaylistAdapter(private val videoFeed: MutableList<StreamItem>, private val playlistId: String, private val isOwner: Boolean, private val activity: Activity): RecyclerView.Adapter<PlaylistViewHolder>() {
class PlaylistAdapter(
private val videoFeed: MutableList<StreamItem>,
private val playlistId: String,
private val isOwner: Boolean,
private val activity: Activity
) : RecyclerView.Adapter<PlaylistViewHolder>() {
private val TAG = "PlaylistAdapter"
override fun getItemCount(): Int {
return videoFeed.size
}
fun updateItems(newItems: List<StreamItem>){
fun updateItems(newItems: List<StreamItem>) {
videoFeed.addAll(newItems)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cell = layoutInflater.inflate(R.layout.playlist_row,parent,false)
val cell = layoutInflater.inflate(R.layout.playlist_row, parent, false)
return PlaylistViewHolder(cell)
}
@ -46,9 +51,9 @@ class PlaylistAdapter(private val videoFeed: MutableList<StreamItem>, private va
holder.v.findViewById<TextView>(R.id.playlist_duration).text = DateUtils.formatElapsedTime(streamItem.duration!!)
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.playlist_thumbnail)
Picasso.get().load(streamItem.thumbnail).into(thumbnailImage)
holder.v.setOnClickListener{
holder.v.setOnClickListener {
var bundle = Bundle()
bundle.putString("videoId",streamItem.url!!.replace("/watch?v=",""))
bundle.putString("videoId", streamItem.url!!.replace("/watch?v=", ""))
var frag = PlayerFragment()
frag.arguments = bundle
val activity = holder.v.context as AppCompatActivity
@ -59,34 +64,33 @@ class PlaylistAdapter(private val videoFeed: MutableList<StreamItem>, private va
.replace(R.id.container, frag)
.commitNow()
}
if(isOwner){
if (isOwner) {
val delete = holder.v.findViewById<ImageView>(R.id.delete_playlist)
delete.visibility = View.VISIBLE
delete.setOnClickListener {
val sharedPref = holder.v.context.getSharedPreferences("token", Context.MODE_PRIVATE)
val token = sharedPref?.getString("token","")!!
val token = sharedPref?.getString("token", "")!!
removeFromPlaylist(token, position)
}
}
}
private fun removeFromPlaylist(token: String, position: Int) {
fun run() {
GlobalScope.launch{
GlobalScope.launch {
val response = try {
RetrofitInstance.api.removeFromPlaylist(token, PlaylistId(playlistId = playlistId, index = position))
}catch(e: IOException) {
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launch
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
return@launch
}finally {
} finally {
}
try{
if(response.message == "ok"){
Log.d(TAG,"deleted!")
try {
if (response.message == "ok") {
Log.d(TAG, "deleted!")
videoFeed.removeAt(position)
// FIXME: This needs to run on UI thread?
activity.runOnUiThread { notifyDataSetChanged() }
@ -95,17 +99,15 @@ class PlaylistAdapter(private val videoFeed: MutableList<StreamItem>, private va
view.findViewById<ImageView>(R.id.boogh2).visibility=View.VISIBLE
}*/
}
}catch (e:Exception){
Log.e(TAG,e.toString())
} catch (e: Exception) {
Log.e(TAG, e.toString())
}
}
}
run()
}
}
class PlaylistViewHolder(val v: View): RecyclerView.ViewHolder(v){
class PlaylistViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init {
}
}
}

View File

@ -17,25 +17,27 @@ import com.github.libretube.RetrofitInstance
import com.github.libretube.obj.PlaylistId
import com.github.libretube.obj.Playlists
import com.squareup.picasso.Picasso
import java.io.IOException
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.io.IOException
class PlaylistsAdapter(private val playlists: MutableList<Playlists>, private val activity: Activity): RecyclerView.Adapter<PlaylistsViewHolder>() {
class PlaylistsAdapter(
private val playlists: MutableList<Playlists>,
private val activity: Activity
) : RecyclerView.Adapter<PlaylistsViewHolder>() {
val TAG = "PlaylistsAdapter"
override fun getItemCount(): Int {
return playlists.size
}
fun updateItems(newItems: List<Playlists>){
fun updateItems(newItems: List<Playlists>) {
playlists.addAll(newItems)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistsViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cell = layoutInflater.inflate(R.layout.playlists_row,parent,false)
val cell = layoutInflater.inflate(R.layout.playlists_row, parent, false)
return PlaylistsViewHolder(cell)
}
@ -50,7 +52,7 @@ class PlaylistsAdapter(private val playlists: MutableList<Playlists>, private va
builder.setMessage(R.string.areYouSure)
builder.setPositiveButton(R.string.yes) { dialog, which ->
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)
}
builder.setNegativeButton(R.string.cancel) { dialog, which ->
@ -58,31 +60,29 @@ class PlaylistsAdapter(private val playlists: MutableList<Playlists>, private va
builder.show()
}
holder.v.setOnClickListener {
//playlists clicked
// playlists clicked
val activity = holder.v.context as MainActivity
val bundle = bundleOf("playlist_id" to playlist.id)
activity.navController.navigate(R.id.playlistFragment,bundle)
activity.navController.navigate(R.id.playlistFragment, bundle)
}
}
private fun deletePlaylist(id: String, token: String, position: Int) {
fun run() {
GlobalScope.launch{
GlobalScope.launch {
val response = try {
RetrofitInstance.api.deletePlaylist(token, PlaylistId(id))
}catch(e: IOException) {
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launch
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
return@launch
}finally {
} finally {
}
try{
if(response.message == "ok"){
Log.d(TAG,"deleted!")
try {
if (response.message == "ok") {
Log.d(TAG, "deleted!")
playlists.removeAt(position)
// FIXME: This needs to run on UI thread?
activity.runOnUiThread { notifyDataSetChanged() }
@ -91,18 +91,15 @@ class PlaylistsAdapter(private val playlists: MutableList<Playlists>, private va
view.findViewById<ImageView>(R.id.boogh2).visibility=View.VISIBLE
}*/
}
}catch (e:Exception){
Log.e(TAG,e.toString())
} catch (e: Exception) {
Log.e(TAG, e.toString())
}
}
}
run()
}
}
class PlaylistsViewHolder(val v: View): RecyclerView.ViewHolder(v){
class PlaylistsViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init {
}
}

View File

@ -17,10 +17,9 @@ import com.github.libretube.formatShort
import com.github.libretube.obj.SearchItem
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
searchItems.addAll(newItems)
notifyItemRangeInserted(searchItemsSize, newItems.size)
@ -38,7 +37,7 @@ class SearchAdapter(private val searchItems: MutableList<SearchItem>): RecyclerV
else -> throw IllegalArgumentException("Invalid type")
}
val layoutInflater = LayoutInflater.from(parent.context)
val cell = layoutInflater.inflate(layout,parent,false)
val cell = layoutInflater.inflate(layout, parent, false)
return CustomViewHolder1(cell)
}
@ -48,14 +47,14 @@ class SearchAdapter(private val searchItems: MutableList<SearchItem>): RecyclerV
override fun getItemViewType(position: Int): Int {
return when {
searchItems[position].url!!.startsWith("/watch",false) -> 0
searchItems[position].url!!.startsWith("/channel",false) -> 1
searchItems[position].url!!.startsWith("/playlist",false) -> 2
searchItems[position].url!!.startsWith("/watch", false) -> 0
searchItems[position].url!!.startsWith("/channel", false) -> 1
searchItems[position].url!!.startsWith("/playlist", false) -> 2
else -> 3
}
}
}
class CustomViewHolder1(private val v: View): RecyclerView.ViewHolder(v){
class CustomViewHolder1(private val v: View) : RecyclerView.ViewHolder(v) {
private fun bindWatch(item: SearchItem) {
val thumbnailImage = v.findViewById<ImageView>(R.id.search_thumbnail)
@ -72,9 +71,9 @@ class CustomViewHolder1(private val v: View): RecyclerView.ViewHolder(v){
views.text = if (viewsString != "" && uploadDate != "") viewsString + "" + uploadDate else viewsString + uploadDate
val channelName = v.findViewById<TextView>(R.id.search_channel_name)
channelName.text = item.uploaderName
v.setOnClickListener{
v.setOnClickListener {
var bundle = Bundle()
bundle.putString("videoId",item.url!!.replace("/watch?v=",""))
bundle.putString("videoId", item.url!!.replace("/watch?v=", ""))
var frag = PlayerFragment()
frag.arguments = bundle
val activity = v.context as AppCompatActivity
@ -101,9 +100,9 @@ class CustomViewHolder1(private val v: View): RecyclerView.ViewHolder(v){
v.setOnClickListener {
val activity = v.context as MainActivity
val bundle = bundleOf("channel_id" to item.url)
activity.navController.navigate(R.id.channel,bundle)
activity.navController.navigate(R.id.channel, bundle)
}
//todo sub button
// todo sub button
}
private fun bindPlaylist(item: SearchItem) {
val playlistImage = v.findViewById<ImageView>(R.id.search_thumbnail)
@ -117,20 +116,20 @@ class CustomViewHolder1(private val v: View): RecyclerView.ViewHolder(v){
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())
v.setOnClickListener {
//playlist clicked
// playlist clicked
val activity = v.context as MainActivity
val bundle = bundleOf("playlist_id" to item.url)
activity.navController.navigate(R.id.playlistFragment,bundle)
activity.navController.navigate(R.id.playlistFragment, bundle)
}
}
fun bind(searchItem: SearchItem) {
when {
searchItem.url!!.startsWith("/watch",false) -> bindWatch(searchItem)
searchItem.url!!.startsWith("/channel",false) -> bindChannel(searchItem)
searchItem.url!!.startsWith("/playlist",false) -> bindPlaylist(searchItem)
searchItem.url!!.startsWith("/watch", false) -> bindWatch(searchItem)
searchItem.url!!.startsWith("/channel", false) -> bindChannel(searchItem)
searchItem.url!!.startsWith("/playlist", false) -> bindPlaylist(searchItem)
else -> {
}
}
}
}
}

View File

@ -11,16 +11,17 @@ import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.google.android.material.imageview.ShapeableImageView
class SearchHistoryAdapter(private val context: Context, private var historyList: List<String> , private val editText : AutoCompleteTextView) :
class SearchHistoryAdapter(
private val context: Context,
private var historyList: List<String>,
private val editText: AutoCompleteTextView
) :
RecyclerView.Adapter<SearchHistoryViewHolder>() {
override fun getItemCount(): Int {
return historyList.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHistoryViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cell = layoutInflater.inflate(R.layout.searchhistory_row, parent, false)
@ -31,7 +32,6 @@ class SearchHistoryAdapter(private val context: Context, private var historyList
val history = historyList[position]
holder.v.findViewById<TextView>(R.id.history_text).text = history
holder.v.findViewById<ShapeableImageView>(R.id.delete_history).setOnClickListener {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
@ -52,4 +52,4 @@ class SearchHistoryAdapter(private val context: Context, private var historyList
class SearchHistoryViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init {
}
}
}

View File

@ -18,35 +18,35 @@ import com.github.libretube.formatShort
import com.github.libretube.obj.StreamItem
import com.squareup.picasso.Picasso
class SubscriptionAdapter(private val videoFeed: List<StreamItem>): RecyclerView.Adapter<SubscriptionViewHolder>() {
//private var limitedVideoFeed: MutableList<String> = [""].toMutableList()
class SubscriptionAdapter(private val videoFeed: List<StreamItem>) : RecyclerView.Adapter<SubscriptionViewHolder>() {
// private var limitedVideoFeed: MutableList<String> = [""].toMutableList()
var i = 0
override fun getItemCount(): Int {
return i
}
fun updateItems(){
//limitedVideoFeed.add("")
fun updateItems() {
// limitedVideoFeed.add("")
i += 10
if(i>videoFeed.size)
i=videoFeed.size
if (i > videoFeed.size)
i = videoFeed.size
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubscriptionViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cell = layoutInflater.inflate(R.layout.trending_row,parent,false)
val cell = layoutInflater.inflate(R.layout.trending_row, parent, false)
return SubscriptionViewHolder(cell)
}
override fun onBindViewHolder(holder: SubscriptionViewHolder, position: Int) {
val trending = videoFeed[position]
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)
holder.v.findViewById<TextView>(R.id.thumbnail_duration).text = DateUtils.formatElapsedTime(trending.duration!!)
val channelImage = holder.v.findViewById<ImageView>(R.id.channel_image)
channelImage.setOnClickListener{
channelImage.setOnClickListener {
val activity = holder.v.context as MainActivity
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
activity.navController.navigate(R.id.channel, bundle)
@ -56,15 +56,14 @@ class SubscriptionAdapter(private val videoFeed: List<StreamItem>): RecyclerView
mainMotionLayout.transitionToEnd()
activity.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
}
}catch (e: Exception){
} catch (e: Exception) {
}
}
Picasso.get().load(trending.thumbnail).into(thumbnailImage)
Picasso.get().load(trending.uploaderAvatar).into(channelImage)
holder.v.setOnClickListener{
holder.v.setOnClickListener {
var bundle = Bundle()
bundle.putString("videoId",trending.url!!.replace("/watch?v=",""))
bundle.putString("videoId", trending.url!!.replace("/watch?v=", ""))
var frag = PlayerFragment()
frag.arguments = bundle
val activity = holder.v.context as AppCompatActivity
@ -77,7 +76,7 @@ class SubscriptionAdapter(private val videoFeed: List<StreamItem>): RecyclerView
}
}
}
class SubscriptionViewHolder(val v: View): RecyclerView.ViewHolder(v){
class SubscriptionViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init {
}
}
}

View File

@ -12,30 +12,30 @@ import com.github.libretube.R
import com.github.libretube.obj.Subscription
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 {
return subscriptions.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubscriptionChannelViewHolder {
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)
}
override fun onBindViewHolder(holder: SubscriptionChannelViewHolder, position: Int) {
val subscription = subscriptions[position]
holder.v.findViewById<TextView>(R.id.subscription_channel_name).text=subscription.name
holder.v.findViewById<TextView>(R.id.subscription_channel_name).text = subscription.name
val avatar = holder.v.findViewById<ImageView>(R.id.subscription_channel_image)
Picasso.get().load(subscription.avatar).into(avatar)
holder.v.setOnClickListener{
holder.v.setOnClickListener {
val activity = holder.v.context as MainActivity
val bundle = bundleOf("channel_id" to subscription.url)
activity.navController.navigate(R.id.channel,bundle)
activity.navController.navigate(R.id.channel, bundle)
}
}
}
class SubscriptionChannelViewHolder(val v: View): RecyclerView.ViewHolder(v){
class SubscriptionChannelViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init {
}
}
}

View File

@ -18,25 +18,25 @@ import com.github.libretube.formatShort
import com.github.libretube.obj.StreamItem
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 {
return videoFeed.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cell = layoutInflater.inflate(R.layout.trending_row,parent,false)
val cell = layoutInflater.inflate(R.layout.trending_row, parent, false)
return CustomViewHolder(cell)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
val trending = videoFeed[position]
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)
holder.v.findViewById<TextView>(R.id.thumbnail_duration).text = DateUtils.formatElapsedTime(trending.duration!!)
val channelImage = holder.v.findViewById<ImageView>(R.id.channel_image)
channelImage.setOnClickListener{
channelImage.setOnClickListener {
val activity = holder.v.context as MainActivity
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
activity.navController.navigate(R.id.channel, bundle)
@ -46,23 +46,21 @@ class TrendingAdapter(private val videoFeed: List<StreamItem>): RecyclerView.Ada
mainMotionLayout.transitionToEnd()
activity.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
}
}catch (e: Exception){
} catch (e: Exception) {
}
}
if (trending.thumbnail!!.isEmpty()) {
} else{
} else {
Picasso.get().load(trending.thumbnail).into(thumbnailImage)
}
if (trending.uploaderAvatar!!.isEmpty()) {
} else{
} else {
Picasso.get().load(trending.uploaderAvatar).into(channelImage)
}
holder.v.setOnClickListener{
holder.v.setOnClickListener {
var bundle = Bundle()
bundle.putString("videoId",trending.url!!.replace("/watch?v=",""))
bundle.putString("videoId", trending.url!!.replace("/watch?v=", ""))
var frag = PlayerFragment()
frag.arguments = bundle
val activity = holder.v.context as AppCompatActivity
@ -75,7 +73,7 @@ class TrendingAdapter(private val videoFeed: List<StreamItem>): RecyclerView.Ada
}
}
}
class CustomViewHolder(val v: View): RecyclerView.ViewHolder(v){
class CustomViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
init {
}
}
}

View File

@ -2,6 +2,4 @@ package com.github.libretube
import android.app.Application
class myApp : Application() {
}
class myApp : Application()

View File

@ -7,6 +7,6 @@ data class ChapterSegment(
var title: String?,
var image: String?,
var start: Int?
){
constructor(): this("","",-1)
) {
constructor() : this("", "", -1)
}

View File

@ -14,7 +14,6 @@ data class Comment(
val pinned: Boolean?,
val thumbnail: String?,
val verified: Boolean?
){
constructor(): this("", "","","","",null,0,null,"",null)
) {
constructor() : this("", "", "", "", "", null, 0, null, "", null)
}

View File

@ -4,9 +4,9 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties
@JsonIgnoreProperties(ignoreUnknown = true)
data class CommentsPage(
val comments: MutableList<Comment> = arrayListOf(),
val disabled: Boolean? = null,
val nextpage: String? = "",
){
constructor(): this(arrayListOf(),null,"")
val comments: MutableList<Comment> = arrayListOf(),
val disabled: Boolean? = null,
val nextpage: String? = "",
) {
constructor() : this(arrayListOf(), null, "")
}

View File

@ -18,6 +18,6 @@ data class PipedStream(
var width: Int?,
var height: Int?,
var fps: Int?
){
constructor(): this("","","","","",null,-1,-1,-1,-1,-1,-1,-1,-1)
) {
constructor() : this("", "", "", "", "", null, -1, -1, -1, -1, -1, -1, -1, -1)
}

View File

@ -9,7 +9,7 @@ data class SearchItem(
var uploaderName: String?,
var uploaded: Long?,
var shortDescription: String?,
//Video only attributes
// Video only attributes
var title: String?,
var uploaderUrl: String?,
var uploaderAvatar: String?,
@ -17,12 +17,12 @@ data class SearchItem(
var duration: Long?,
var views: Long?,
var uploaderVerified: Boolean?,
//Channel and Playlist attributes
// Channel and Playlist attributes
var name: String? = null,
var description: String? = null,
var subscribers: Long? = -1,
var videos: Long? = -1,
var verified: Boolean? = null
){
constructor() : this("","","",0,"","","","","",0,0,null)
) {
constructor() : this("", "", "", 0, "", "", "", "", "", 0, 0, null)
}

View File

@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties
@JsonIgnoreProperties(ignoreUnknown = true)
data class SearchResult(
val items: MutableList<SearchItem>? = arrayListOf(),
val nextpage: String? ="",
val suggestion: String?="",
val nextpage: String? = "",
val suggestion: String? = "",
val corrected: Boolean? = null
)

View File

@ -7,6 +7,6 @@ data class Segment(
val actionType: String?,
val category: String?,
val segment: List<Float>?
){
constructor(): this("", "", arrayListOf())
) {
constructor() : this("", "", arrayListOf())
}

View File

@ -5,6 +5,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties
@JsonIgnoreProperties(ignoreUnknown = true)
data class Segments(
val segments: MutableList<Segment> = arrayListOf()
){
constructor(): this(arrayListOf())
) {
constructor() : this(arrayListOf())
}

View File

@ -16,6 +16,6 @@ data class StreamItem(
var uploaderVerified: Boolean?,
var uploaded: Long?,
var shortDescription: String?
){
constructor() : this("","","","","","","",0,0,null,0,"")
) {
constructor() : this("", "", "", "", "", "", "", 0, 0, null, 0, "")
}

View File

@ -26,7 +26,9 @@ data class Streams(
val livestream: Boolean?,
val proxyUrl: String?,
val chapters: List<ChapterSegment>?
){
constructor(): this("","","","","","","","","","",null,-1,-1,-1,-1, emptyList(), emptyList(),
emptyList(), emptyList(), null,"", emptyList())
) {
constructor() : this(
"", "", "", "", "", "", "", "", "", "", null, -1, -1, -1, -1, emptyList(), emptyList(),
emptyList(), emptyList(), null, "", emptyList()
)
}

View File

@ -9,6 +9,6 @@ data class Subtitle(
val name: String?,
val code: String?,
val autoGenerated: Boolean?
){
constructor(): this("","","","",null)
) {
constructor() : this("", "", "", "", null)
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5L16,16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zM6.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zM9.5,8C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5s1.5,0.67 1.5,1.5S10.33,8 9.5,8zM14.5,8c-0.83,0 -1.5,-0.67 -1.5,-1.5S13.67,5 14.5,5s1.5,0.67 1.5,1.5S15.33,8 14.5,8zM17.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S16.67,9 17.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
</vector>

View File

@ -50,7 +50,7 @@
android:layout_marginStart="24dp"
android:background="@android:color/transparent"
android:text="@string/subscribe"
android:textColor="@color/colorPrimary"
android:textColor="?attr/colorPrimary"
android:visibility="gone"
app:layout_constraintStart_toEndOf="@+id/search_channel_image" />

View File

@ -97,6 +97,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="15dp"
android:text="" />

View File

@ -236,7 +236,7 @@
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:backgroundTint="?android:attr/colorBackground"
android:backgroundTint="?attr/colorOnPrimary"
android:drawableLeft="@drawable/ic_bell"
android:drawableTint="?android:attr/textColorPrimary"
android:text="@string/subscribe"

View File

@ -1,36 +1,104 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.LibreTube" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/md_theme_dark_primary</item>
<item name="colorOnPrimary">@color/md_theme_dark_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_dark_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_dark_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_dark_secondary</item>
<item name="colorOnSecondary">@color/md_theme_dark_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_dark_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_dark_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_dark_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_dark_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_dark_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_dark_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_dark_error</item>
<item name="colorErrorContainer">@color/md_theme_dark_errorContainer</item>
<item name="colorOnError">@color/md_theme_dark_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_dark_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_dark_background</item>
<item name="colorOnBackground">@color/md_theme_dark_onBackground</item>
<item name="colorSurface">@color/md_theme_dark_surface</item>
<item name="colorOnSurface">@color/md_theme_dark_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_dark_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_dark_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_dark_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_dark_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_dark_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_dark_primaryInverse</item>
<style name="Theme.MY" parent="Theme.Material3.Dark.NoActionBar"></style>
<style name="Theme.Red" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/red_dark_accentLight</item> // container
<item name="colorOnPrimary">@color/red_dark_accentDark</item> // title
<item name="colorPrimaryContainer">@color/red_dark_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/red_dark_accentLight</item> //
<item name="colorSecondary">@color/red_dark_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/red_dark_accentDark</item> //
<item name="colorSecondaryContainer">@color/red_dark_accentDark</item> // navbar surround
<!-- <item name="colorOnSecondaryContainer">@color/red_dark_background</item> --> // navbar icon fill
<item name="android:colorBackground">@color/red_dark_background</item> // background
<item name="colorOnBackground">@color/red_dark_background</item>
<item name="android:statusBarColor" tools:targetApi="m">@android:color/transparent</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Blue" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/blue_dark_accentLight</item> // container
<item name="colorOnPrimary">@color/blue_dark_accentDark</item> // title
<item name="colorPrimaryContainer">@color/blue_dark_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/blue_dark_accentLight</item> //
<item name="colorSecondary">@color/blue_dark_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/blue_dark_accentDark</item> //
<item name="colorSecondaryContainer">@color/blue_dark_accentDark</item> // navbar surround
<!-- <item name="colorOnSecondaryContainer">@color/blue_dark_background</item> --> // navbar icon fill
<item name="android:colorBackground">@color/blue_dark_background</item> // background
<item name="colorOnBackground">@color/blue_dark_background</item>
<item name="android:statusBarColor" tools:targetApi="m">@android:color/transparent</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Yellow" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/yellow_dark_accentLight</item> // container
<item name="colorOnPrimary">@color/yellow_dark_accentDark</item> // title
<item name="colorPrimaryContainer">@color/yellow_dark_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/yellow_dark_accentLight</item> //
<item name="colorSecondary">@color/yellow_dark_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/yellow_dark_accentDark</item> //
<item name="colorSecondaryContainer">@color/yellow_dark_accentDark</item> // navbar surround
<!-- <item name="colorOnSecondaryContainer">@color/yellow_dark_background</item> --> // navbar icon fill
<item name="android:colorBackground">@color/yellow_dark_background</item> // background
<item name="colorOnBackground">@color/yellow_dark_background</item>
<item name="android:statusBarColor" tools:targetApi="m">@android:color/transparent</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Green" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/green_dark_accentLight</item> // container
<item name="colorOnPrimary">@color/green_dark_accentDark</item> // title
<item name="colorPrimaryContainer">@color/green_dark_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/green_dark_accentLight</item> //
<item name="colorSecondary">@color/green_dark_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/green_dark_accentDark</item> //
<item name="colorSecondaryContainer">@color/green_dark_accentDark</item> // navbar surround
<!-- <item name="colorOnSecondaryContainer">@color/green_dark_background</item> --> // navbar icon fill
<item name="android:colorBackground">@color/green_dark_background</item> // background
<item name="colorOnBackground">@color/green_dark_background</item>
<item name="android:statusBarColor" tools:targetApi="m">@android:color/transparent</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Purple" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/purple_dark_accentLight</item> // container
<item name="colorOnPrimary">@color/purple_dark_accentDark</item> // title
<item name="colorPrimaryContainer">@color/purple_dark_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/purple_dark_accentLight</item> //
<item name="colorSecondary">@color/purple_dark_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/purple_dark_accentDark</item> //
<item name="colorSecondaryContainer">@color/purple_dark_accentDark</item> // navbar surround
<!-- <item name="colorOnSecondaryContainer">@color/purple_dark_background</item> --> // navbar icon fill
<item name="android:colorBackground">@color/purple_dark_background</item> // background
<item name="colorOnBackground">@color/purple_dark_background</item>
<item name="android:statusBarColor" tools:targetApi="m">@android:color/transparent</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.OLED" parent="Theme.Material3.Dark.NoActionBar">
<item name="android:colorBackground">@color/black</item>
</style>
</resources>

View File

@ -1,37 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.LibreTube" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/md_theme_light_primary</item>
<item name="colorOnPrimary">@color/md_theme_light_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_light_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_light_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_light_secondary</item>
<item name="colorOnSecondary">@color/md_theme_light_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_light_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_light_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_light_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_light_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_light_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_light_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_light_error</item>
<item name="colorErrorContainer">@color/md_theme_light_errorContainer</item>
<item name="colorOnError">@color/md_theme_light_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_light_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_light_background</item>
<item name="colorOnBackground">@color/md_theme_light_onBackground</item>
<item name="colorSurface">@color/md_theme_light_surface</item>
<item name="colorOnSurface">@color/md_theme_light_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_light_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_light_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_light_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_light_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_light_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_light_primaryInverse</item>
<style name="Theme.MY" parent="Theme.Material3.Light.NoActionBar"></style>
<style name="Theme.Red" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/red_light_accentLight</item> // container
<item name="colorOnPrimary">@color/red_light_accentDark</item> // title
<item name="colorPrimaryContainer">@color/red_light_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/red_light_accentLight</item> //
<item name="colorSecondary">@color/red_light_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/red_light_accentDark</item> //
<item name="colorSecondaryContainer">@color/red_light_accentDark</item> // navbar surround
<item name="colorOnSecondaryContainer">@color/red_light_background</item> // navbar icon fill
<item name="android:colorBackground">@color/red_light_background</item> // background
<item name="colorOnBackground">@color/red_light_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Blue" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/blue_light_accentLight</item> // container
<item name="colorOnPrimary">@color/blue_light_accentDark</item> // title
<item name="colorPrimaryContainer">@color/blue_light_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/blue_light_accentLight</item> //
<item name="colorSecondary">@color/blue_light_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/blue_light_accentDark</item> //
<item name="colorSecondaryContainer">@color/blue_light_accentDark</item> // navbar surround
<item name="colorOnSecondaryContainer">@color/blue_light_background</item> // navbar icon fill
<item name="android:colorBackground">@color/blue_light_background</item> // background
<item name="colorOnBackground">@color/blue_light_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Yellow" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/yellow_light_accentLight</item> // container
<item name="colorOnPrimary">@color/yellow_light_accentDark</item> // title
<item name="colorPrimaryContainer">@color/yellow_light_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/yellow_light_accentLight</item> //
<item name="colorSecondary">@color/yellow_light_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/yellow_light_accentDark</item> //
<item name="colorSecondaryContainer">@color/yellow_light_accentDark</item> // navbar surround
<item name="colorOnSecondaryContainer">@color/yellow_light_background</item> // navbar icon fill
<item name="android:colorBackground">@color/yellow_light_background</item> // background
<item name="colorOnBackground">@color/yellow_light_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Green" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/green_light_accentLight</item> // container
<item name="colorOnPrimary">@color/green_light_accentDark</item> // title
<item name="colorPrimaryContainer">@color/green_light_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/green_light_accentLight</item> //
<item name="colorSecondary">@color/green_light_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/green_light_accentDark</item> //
<item name="colorSecondaryContainer">@color/green_light_accentDark</item> // navbar surround
<item name="colorOnSecondaryContainer">@color/green_light_background</item> // navbar icon fill
<item name="android:colorBackground">@color/green_light_background</item> // background
<item name="colorOnBackground">@color/green_light_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Purple" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/purple_light_accentLight</item> // container
<item name="colorOnPrimary">@color/purple_light_accentDark</item> // title
<item name="colorPrimaryContainer">@color/purple_light_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/purple_light_accentLight</item> //
<item name="colorSecondary">@color/purple_light_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/purple_light_accentDark</item> //
<item name="colorSecondaryContainer">@color/purple_light_accentDark</item> // navbar surround
<item name="colorOnSecondaryContainer">@color/purple_light_background</item> // navbar icon fill
<item name="android:colorBackground">@color/purple_light_background</item> // background
<item name="colorOnBackground">@color/purple_light_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
</resources>

View File

@ -463,12 +463,32 @@
<item>@string/systemDefault</item>
<item>@string/lightTheme</item>
<item>@string/darkTheme</item>
<item>@string/oledTheme</item>
</string-array>
<string-array name="themesValue">
<item>A</item>
<item>L</item>
<item>D</item>
<item>O</item>
</string-array>
<string-array name="accents">
<item>@string/material_you</item>
<item>@string/color_red</item>
<item>@string/color_blue</item>
<item>@string/color_yellow</item>
<item>@string/color_green</item>
<item>@string/color_purple</item>
</string-array>
<string-array name="accentsValue">
<item>my</item>
<item>red</item>
<item>blue</item>
<item>yellow</item>
<item>green</item>
<item>purple</item>
</string-array>
<string-array name="tabs">

View File

@ -6,59 +6,44 @@
<color name="duration_background_color">#AA000000</color>
<color name="duration_text_color">#EEFFFFFF</color>
<color name="colorPrimary">#B81B3B</color>
<color name="md_theme_light_primary">#B81B3B</color>
<color name="md_theme_light_onPrimary">#FFFFFF</color>
<color name="md_theme_light_primaryContainer">#FFDADC</color>
<color name="md_theme_light_onPrimaryContainer">#40000B</color>
<color name="md_theme_light_secondary">#9C4145</color>
<color name="md_theme_light_onSecondary">#FFFFFF</color>
<color name="md_theme_light_secondaryContainer">#FFD9DA</color>
<color name="md_theme_light_onSecondaryContainer">#400008</color>
<color name="md_theme_light_tertiary">#98470F</color>
<color name="md_theme_light_onTertiary">#FFFFFF</color>
<color name="md_theme_light_tertiaryContainer">#FFDBC8</color>
<color name="md_theme_light_onTertiaryContainer">#341100</color>
<color name="md_theme_light_error">#BA1B1B</color>
<color name="md_theme_light_errorContainer">#FFDAD4</color>
<color name="md_theme_light_onError">#FFFFFF</color>
<color name="md_theme_light_onErrorContainer">#410001</color>
<color name="md_theme_light_background">#FCFCFC</color>
<color name="md_theme_light_onBackground">#201A1A</color>
<color name="md_theme_light_surface">#FCFCFC</color>
<color name="md_theme_light_onSurface">#201A1A</color>
<color name="md_theme_light_surfaceVariant">#F4DDDD</color>
<color name="md_theme_light_onSurfaceVariant">#524343</color>
<color name="md_theme_light_outline">#847373</color>
<color name="md_theme_light_inverseOnSurface">#FBEDED</color>
<color name="md_theme_light_inverseSurface">#362F2F</color>
<color name="md_theme_light_primaryInverse">#FFB3B8</color>
<color name="md_theme_dark_primary">#FFB3B8</color>
<color name="md_theme_dark_onPrimary">#680018</color>
<color name="md_theme_dark_primaryContainer">#920026</color>
<color name="md_theme_dark_onPrimaryContainer">#FFDADC</color>
<color name="md_theme_dark_secondary">#FFB2B3</color>
<color name="md_theme_dark_onSecondary">#60131B</color>
<color name="md_theme_dark_secondaryContainer">#7D2A2F</color>
<color name="md_theme_dark_onSecondaryContainer">#FFD9DA</color>
<color name="md_theme_dark_tertiary">#FFB68C</color>
<color name="md_theme_dark_onTertiary">#552100</color>
<color name="md_theme_dark_tertiaryContainer">#783200</color>
<color name="md_theme_dark_onTertiaryContainer">#FFDBC8</color>
<color name="md_theme_dark_error">#FFB4A9</color>
<color name="md_theme_dark_errorContainer">#930006</color>
<color name="md_theme_dark_onError">#680003</color>
<color name="md_theme_dark_onErrorContainer">#FFDAD4</color>
<color name="md_theme_dark_background">#201A1A</color>
<color name="md_theme_dark_onBackground">#ECDFDF</color>
<color name="md_theme_dark_surface">#201A1A</color>
<color name="md_theme_dark_onSurface">#ECDFDF</color>
<color name="md_theme_dark_surfaceVariant">#524343</color>
<color name="md_theme_dark_onSurfaceVariant">#D7C2C2</color>
<color name="md_theme_dark_outline">#9F8C8C</color>
<color name="md_theme_dark_inverseOnSurface">#201A1A</color>
<color name="md_theme_dark_inverseSurface">#ECDFDF</color>
<color name="md_theme_dark_primaryInverse">#B81B3B</color>
<color name="seed">#BD1F3E</color>
<color name="error">#BA1B1B</color>
<color name="red_light_accentLight">#F1395E</color>
<color name="red_light_accentDark">#B81B3B</color>
<color name="red_light_background">#FFC3C3</color>
<color name="red_dark_accentLight">#F1395E</color>
<color name="red_dark_accentDark">#8F001D</color>
<color name="red_dark_background">#1E0D0D</color>
<color name="blue_light_accentLight">#2196F3</color>
<color name="blue_light_accentDark">#0E4B67</color>
<color name="blue_light_background">#DDF0FF</color>
<color name="blue_dark_accentLight">#2196F3</color>
<color name="blue_dark_accentDark">#0E4B67</color>
<color name="blue_dark_background">#080C20</color>
<color name="yellow_light_accentLight">#F3E570</color>
<color name="yellow_light_accentDark">#EDE06A</color>
<color name="yellow_light_background">#FFF9C8</color>
<color name="yellow_dark_accentLight">#E2EF55</color>
<color name="yellow_dark_accentDark">#999520</color>
<color name="yellow_dark_background">#1C1A05</color>
<color name="green_light_accentLight">#8BC34A</color>
<color name="green_light_accentDark">#5BD861</color>
<color name="green_light_background">#E8FFCE</color>
<color name="green_dark_accentLight">#8BC34A</color>
<color name="green_dark_accentDark">#155C1B</color>
<color name="green_dark_background">#131C09</color>
<color name="purple_light_accentLight">#db1fb6</color>
<color name="purple_light_accentDark">#d42cb2</color>
<color name="purple_light_background">#FFDAE6</color>
<color name="purple_dark_accentLight">#9621AA</color>
<color name="purple_dark_accentDark">#371377</color>
<color name="purple_dark_background">#120B20</color>
</resources>

View File

@ -101,4 +101,12 @@
<string name="category_outro">Endcards/Credits</string>
<string name="category_outro_description">Credits or when the YouTube endcards appear. Not for conclusions with information.</string>
<string name="license">License</string>
<string name="color_accent">Color Accent</string>
<string name="color_red">Red</string>
<string name="color_blue">Blue</string>
<string name="color_yellow">Yellow</string>
<string name="color_green">Green</string>
<string name="color_purple">Purple</string>
<string name="oledTheme">OLED Theme</string>
<string name="material_you">Material You</string>
</resources>

View File

@ -1,33 +1,89 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.LibreTube" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/md_theme_light_primary</item>
<item name="colorOnPrimary">@color/md_theme_light_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_light_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_light_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_light_secondary</item>
<item name="colorOnSecondary">@color/md_theme_light_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_light_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_light_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_light_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_light_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_light_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_light_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_light_error</item>
<item name="colorErrorContainer">@color/md_theme_light_errorContainer</item>
<item name="colorOnError">@color/md_theme_light_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_light_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_light_background</item>
<item name="colorOnBackground">@color/md_theme_light_onBackground</item>
<item name="colorSurface">@color/md_theme_light_surface</item>
<item name="colorOnSurface">@color/md_theme_light_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_light_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_light_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_light_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_light_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_light_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_light_primaryInverse</item>
<style name="Theme.MY" parent="Theme.Material3.Light.NoActionBar"></style>
<style name="Theme.Red" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/red_light_accentLight</item> // container
<item name="colorOnPrimary">@color/red_light_accentDark</item> // title
<item name="colorPrimaryContainer">@color/red_light_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/red_light_accentLight</item> //
<item name="colorSecondary">@color/red_light_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/red_light_accentDark</item> //
<item name="colorSecondaryContainer">@color/red_light_accentDark</item> // navbar surround
<item name="colorOnSecondaryContainer">@color/red_light_background</item> // navbar icon fill
<item name="android:colorBackground">@color/red_light_background</item> // background
<item name="colorOnBackground">@color/red_light_background</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Blue" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/blue_light_accentLight</item> // container
<item name="colorOnPrimary">@color/blue_light_accentDark</item> // title
<item name="colorPrimaryContainer">@color/blue_light_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/blue_light_accentLight</item> //
<item name="colorSecondary">@color/blue_light_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/blue_light_accentDark</item> //
<item name="colorSecondaryContainer">@color/blue_light_accentDark</item> // navbar surround
<item name="colorOnSecondaryContainer">@color/blue_light_background</item> // navbar icon fill
<item name="android:colorBackground">@color/blue_light_background</item> // background
<item name="colorOnBackground">@color/blue_light_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Yellow" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/yellow_light_accentLight</item> // container
<item name="colorOnPrimary">@color/yellow_light_accentDark</item> // title
<item name="colorPrimaryContainer">@color/yellow_light_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/yellow_light_accentLight</item> //
<item name="colorSecondary">@color/yellow_light_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/yellow_light_accentDark</item> //
<item name="colorSecondaryContainer">@color/yellow_light_accentDark</item> // navbar surround
<item name="colorOnSecondaryContainer">@color/yellow_light_background</item> // navbar icon fill
<item name="android:colorBackground">@color/yellow_light_background</item> // background
<item name="colorOnBackground">@color/yellow_light_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Green" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/green_light_accentLight</item> // container
<item name="colorOnPrimary">@color/green_light_accentDark</item> // title
<item name="colorPrimaryContainer">@color/green_light_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/green_light_accentLight</item> //
<item name="colorSecondary">@color/green_light_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/green_light_accentDark</item> //
<item name="colorSecondaryContainer">@color/green_light_accentDark</item> // navbar surround
<item name="colorOnSecondaryContainer">@color/green_light_background</item> // navbar icon fill
<item name="android:colorBackground">@color/green_light_background</item> // background
<item name="colorOnBackground">@color/green_light_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Purple" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/purple_light_accentLight</item> // container
<item name="colorOnPrimary">@color/purple_light_accentDark</item> // title
<item name="colorPrimaryContainer">@color/purple_light_accentLight</item> //
<item name="colorOnPrimaryContainer">@color/purple_light_accentLight</item> //
<item name="colorSecondary">@color/purple_light_accentLight</item> // Settings Categories
<item name="colorOnSecondary">@color/purple_light_accentDark</item> //
<item name="colorSecondaryContainer">@color/purple_light_accentDark</item> // navbar surround
<item name="colorOnSecondaryContainer">@color/purple_light_background</item> // navbar icon fill
<item name="android:colorBackground">@color/purple_light_background</item> // background
<item name="colorOnBackground">@color/purple_light_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>

View File

@ -71,6 +71,15 @@
android:icon="@drawable/ic_theme"
/>
<ListPreference
app:title="@string/color_accent"
app:key="accent_color"
app:entries="@array/accents"
app:entryValues="@array/accentsValue"
app:defaultValue="red"
android:icon="@drawable/ic_color"
/>
<androidx.preference.Preference
app:title="@string/sponsorblock"
app:key="sponsorblock"

View File

@ -1,8 +1,7 @@
package com.github.libretube
import org.junit.Test
import org.junit.Assert.*
import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
@ -14,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
}