Merge branch 'libre-tube:master' into master

This commit is contained in:
XelXen 2022-07-14 22:48:25 +05:30 committed by GitHub
commit d1547fd9b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 1044 additions and 519 deletions

View File

@ -3,13 +3,13 @@
This represents the larger, bigger impact features and enhancements we have planned. Features are planned, but do not represent a commitment to develop and can change at any time. This represents the larger, bigger impact features and enhancements we have planned. Features are planned, but do not represent a commitment to develop and can change at any time.
## Contribute ## Contribute
Feel free to help us if you have any knowledge concerning the following planned features. Feel free to help us if you have any knowledge concerning the following planned features or anything else you imagine.
## Planned ## Planned
- Landscape mode support - Landscape mode support
- Rewrite of the Player UI
- Notifications for new streams - Notifications for new streams
## Not planned ## Not planned
- Google/MicroG Login - Google/MicroG Login
- Support for anything else than Android (like iOS, Linux) - Support for anything else than Android (like iOS, Linux)
- Support for Android TV

View File

@ -37,7 +37,7 @@
android:exported="true" android:exported="true"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="userPortrait" android:screenOrientation="user"
android:supportsPictureInPicture="true"> android:supportsPictureInPicture="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@ -21,3 +21,14 @@ const val MATRIX_URL = "https://matrix.to/#/#LibreTube:matrix.org"
const val DISCORD_URL = "https://discord.com/invite/Qc34xCj2GV" const val DISCORD_URL = "https://discord.com/invite/Qc34xCj2GV"
const val REDDIT_URL = "https://www.reddit.com/r/Libretube/" const val REDDIT_URL = "https://www.reddit.com/r/Libretube/"
const val TWITTER_URL = "https://twitter.com/libretube" const val TWITTER_URL = "https://twitter.com/libretube"
/**
* Share Dialog
*/
const val PIPED_FRONTEND_URL = "https://piped.kavin.rocks"
const val YOUTUBE_FRONTEND_URL = "https://www.youtube.com"
/**
* Retrofit Instance
*/
const val PIPED_API_URL = "https://pipedapi.kavin.rocks/"

View File

@ -0,0 +1,7 @@
package com.github.libretube
object Globals {
var isFullScreen = false
var isMiniPlayerVisible = false
var isCurrentViewMainSettings = true
}

View File

@ -3,7 +3,6 @@ package com.github.libretube.activities
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.res.Configuration import android.content.res.Configuration
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@ -25,10 +24,11 @@ import androidx.fragment.app.Fragment
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import com.github.libretube.Globals
import com.github.libretube.PIPED_API_URL
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.databinding.ActivityMainBinding import com.github.libretube.databinding.ActivityMainBinding
import com.github.libretube.fragments.PlayerFragment import com.github.libretube.fragments.PlayerFragment
import com.github.libretube.fragments.isFullScreen
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.services.ClosingService import com.github.libretube.services.ClosingService
import com.github.libretube.util.ConnectionHelper import com.github.libretube.util.ConnectionHelper
@ -36,8 +36,8 @@ import com.github.libretube.util.CronetHelper
import com.github.libretube.util.LocaleHelper import com.github.libretube.util.LocaleHelper
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
import com.github.libretube.util.ThemeHelper import com.github.libretube.util.ThemeHelper
import com.google.android.material.color.DynamicColors
import com.google.android.material.elevation.SurfaceColors import com.google.android.material.elevation.SurfaceColors
import com.google.android.material.navigation.NavigationBarView
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
val TAG = "MainActivity" val TAG = "MainActivity"
@ -48,20 +48,9 @@ class MainActivity : AppCompatActivity() {
private var startFragmentId = R.id.homeFragment private var startFragmentId = R.id.homeFragment
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
/** // set the app theme (e.g. Material You)
* apply dynamic colors if enabled
*/
val materialColorsEnabled = PreferenceHelper
.getString(this, "accent_color", "purple") == "my"
if (materialColorsEnabled) {
// apply dynamic colors to the current activity
DynamicColors.applyToActivityIfAvailable(this)
// apply dynamic colors to the all other activities
DynamicColors.applyToActivitiesIfAvailable(application)
}
// set the theme
ThemeHelper.updateTheme(this) ThemeHelper.updateTheme(this)
// set the language // set the language
LocaleHelper.updateLanguage(this) LocaleHelper.updateLanguage(this)
@ -73,14 +62,14 @@ class MainActivity : AppCompatActivity() {
CronetHelper.initCronet(this.applicationContext) CronetHelper.initCronet(this.applicationContext)
RetrofitInstance.url = RetrofitInstance.url =
PreferenceHelper.getString(this, "selectInstance", "https://pipedapi.kavin.rocks/")!! PreferenceHelper.getString(this, "selectInstance", PIPED_API_URL)!!
// set auth instance // set auth instance
RetrofitInstance.authUrl = RetrofitInstance.authUrl =
if (PreferenceHelper.getBoolean(this, "auth_instance_toggle", false)) { if (PreferenceHelper.getBoolean(this, "auth_instance_toggle", false)) {
PreferenceHelper.getString( PreferenceHelper.getString(
this, this,
"selectAuthInstance", "selectAuthInstance",
"https://pipedapi.kavin.rocks/" PIPED_API_URL
)!! )!!
} else { } else {
RetrofitInstance.url RetrofitInstance.url
@ -94,8 +83,6 @@ class MainActivity : AppCompatActivity() {
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
navController = findNavController(R.id.fragment) navController = findNavController(R.id.fragment)
binding.bottomNav.setupWithNavController(navController) binding.bottomNav.setupWithNavController(navController)
@ -124,6 +111,16 @@ class MainActivity : AppCompatActivity() {
// navigate to the default fragment // navigate to the default fragment
navController.navigate(startFragmentId) navController.navigate(startFragmentId)
val labelVisibilityMode = when (
PreferenceHelper.getString(this, "label_visibility", "always")
) {
"always" -> NavigationBarView.LABEL_VISIBILITY_LABELED
"selected" -> NavigationBarView.LABEL_VISIBILITY_SELECTED
"never" -> NavigationBarView.LABEL_VISIBILITY_UNLABELED
else -> NavigationBarView.LABEL_VISIBILITY_AUTO
}
binding.bottomNav.labelVisibilityMode = labelVisibilityMode
binding.bottomNav.setOnItemSelectedListener { binding.bottomNav.setOnItemSelectedListener {
// clear backstack if it's the start fragment // clear backstack if it's the start fragment
if (startFragmentId == it.itemId) navController.backQueue.clear() if (startFragmentId == it.itemId) navController.backQueue.clear()
@ -142,12 +139,6 @@ class MainActivity : AppCompatActivity() {
false false
} }
/**
* don't remove this line
* this prevents reselected items at the bottomNav to be duplicated in the backstack
*/
binding.bottomNav.setOnItemReselectedListener {}
binding.toolbar.title = ThemeHelper.getStyledAppName(this) binding.toolbar.title = ThemeHelper.getStyledAppName(this)
binding.toolbar.setNavigationOnClickListener { binding.toolbar.setNavigationOnClickListener {
@ -293,14 +284,15 @@ class MainActivity : AppCompatActivity() {
binding.mainMotionLayout.transitionToEnd() binding.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) val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
// set the animation duration
motionLayout.setTransitionDuration(250)
motionLayout.transitionToEnd() motionLayout.transitionToEnd()
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
with(motionLayout) { with(motionLayout) {
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0) 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 findViewById<LinearLayout>(R.id.linLayout).visibility = View.VISIBLE
isFullScreen = false Globals.isFullScreen = false
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {

View File

@ -5,25 +5,15 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.databinding.ActivityNointernetBinding import com.github.libretube.databinding.ActivityNointernetBinding
import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.util.ConnectionHelper import com.github.libretube.util.ConnectionHelper
import com.github.libretube.util.ThemeHelper import com.github.libretube.util.ThemeHelper
import com.google.android.material.color.DynamicColors
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
class NoInternetActivity : AppCompatActivity() { class NoInternetActivity : AppCompatActivity() {
private lateinit var binding: ActivityNointernetBinding private lateinit var binding: ActivityNointernetBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
/** ThemeHelper.updateTheme(this)
* apply dynamic colors if enabled
*/
val materialColorsEnabled = PreferenceHelper
.getString(this, "accent_color", "purple") == "my"
if (materialColorsEnabled) {
DynamicColors.applyToActivityIfAvailable(this)
}
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityNointernetBinding.inflate(layoutInflater) binding = ActivityNointernetBinding.inflate(layoutInflater)

View File

@ -1,28 +1,22 @@
package com.github.libretube.activities package com.github.libretube.activities
import android.app.NotificationManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import com.github.libretube.Globals
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.databinding.ActivitySettingsBinding import com.github.libretube.databinding.ActivitySettingsBinding
import com.github.libretube.preferences.MainSettings import com.github.libretube.preferences.MainSettings
import com.github.libretube.util.ThemeHelper import com.github.libretube.util.ThemeHelper
import com.google.android.material.color.DynamicColors
var isCurrentViewMainSettings = true
var requireMainActivityRestart = false
class SettingsActivity : AppCompatActivity() { class SettingsActivity : AppCompatActivity() {
val TAG = "SettingsActivity" val TAG = "SettingsActivity"
lateinit var binding: ActivitySettingsBinding lateinit var binding: ActivitySettingsBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
DynamicColors.applyToActivityIfAvailable(this)
ThemeHelper.updateTheme(this) ThemeHelper.updateTheme(this)
// makes the preference dialogs use material dialogs // apply the theme for the preference dialogs
setTheme(R.style.MaterialAlertDialog) setTheme(R.style.MaterialAlertDialog)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -49,21 +43,11 @@ class SettingsActivity : AppCompatActivity() {
} }
override fun onBackPressed() { override fun onBackPressed() {
if (isCurrentViewMainSettings) { if (Globals.isCurrentViewMainSettings) {
if (requireMainActivityRestart) { super.onBackPressed()
requireMainActivityRestart = false
// kill player notification
val nManager =
this.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
nManager.cancelAll()
ThemeHelper.restartMainActivity(this)
ActivityCompat.finishAffinity(this)
} else {
super.onBackPressed()
}
finishAndRemoveTask() finishAndRemoveTask()
} else { } else {
isCurrentViewMainSettings = true Globals.isCurrentViewMainSettings = true
supportFragmentManager supportFragmentManager
.beginTransaction() .beginTransaction()
.replace(R.id.settings, MainSettings()) .replace(R.id.settings, MainSettings())

View File

@ -27,7 +27,7 @@ class ChaptersAdapter(
chapterTitle.text = chapter.title chapterTitle.text = chapter.title
root.setOnClickListener { root.setOnClickListener {
val chapterStart = chapter.start!!.toLong() * 1000 // s -> ms val chapterStart = chapter.start!! * 1000 // s -> ms
exoPlayer.seekTo(chapterStart) exoPlayer.seekTo(chapterStart)
} }
} }

View File

@ -3,6 +3,7 @@ package com.github.libretube.adapters
import android.os.Bundle import android.os.Bundle
import android.text.format.DateUtils import android.text.format.DateUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
@ -17,8 +18,15 @@ import com.github.libretube.dialogs.PlaylistOptionsDialog
import com.github.libretube.dialogs.VideoOptionsDialog import com.github.libretube.dialogs.VideoOptionsDialog
import com.github.libretube.fragments.PlayerFragment import com.github.libretube.fragments.PlayerFragment
import com.github.libretube.obj.SearchItem import com.github.libretube.obj.SearchItem
import com.github.libretube.obj.Subscribe
import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.util.RetrofitInstance
import com.github.libretube.util.formatShort import com.github.libretube.util.formatShort
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.IOException
class SearchAdapter( class SearchAdapter(
private val searchItems: MutableList<SearchItem>, private val searchItems: MutableList<SearchItem>,
@ -134,6 +142,78 @@ class SearchAdapter(
val bundle = bundleOf("channel_id" to item.url) val bundle = bundleOf("channel_id" to item.url)
activity.navController.navigate(R.id.channelFragment, bundle) activity.navController.navigate(R.id.channelFragment, bundle)
} }
val channelId = item.url?.replace("/channel/", "")!!
val token = PreferenceHelper.getToken(root.context)
// only show subscribe button if logged in
if (token != "") isSubscribed(channelId, token, binding)
}
}
private fun isSubscribed(channelId: String, token: String, binding: ChannelSearchRowBinding) {
var isSubscribed = false
// check whether the user subscribed to the channel
CoroutineScope(Dispatchers.Main).launch {
val response = try {
RetrofitInstance.authApi.isSubscribed(
channelId,
token
)
} catch (e: Exception) {
return@launch
}
// if subscribed change text to unsubscribe
if (response.subscribed == true) {
isSubscribed = true
binding.searchSubButton.text = binding.root.context.getString(R.string.unsubscribe)
}
// make sub button visible and set the on click listeners to (un)subscribe
if (response.subscribed != null) {
binding.searchSubButton.visibility = View.VISIBLE
binding.searchSubButton.setOnClickListener {
if (!isSubscribed) {
subscribe(token, channelId)
binding.searchSubButton.text =
binding.root.context.getString(R.string.unsubscribe)
isSubscribed = true
} else {
unsubscribe(token, channelId)
binding.searchSubButton.text =
binding.root.context.getString(R.string.subscribe)
isSubscribed = false
}
}
}
}
}
private fun subscribe(token: String, channelId: String) {
CoroutineScope(Dispatchers.IO).launch {
try {
RetrofitInstance.authApi.subscribe(
token,
Subscribe(channelId)
)
} catch (e: Exception) {
return@launch
}
}
}
private fun unsubscribe(token: String, channelId: String) {
CoroutineScope(Dispatchers.IO).launch {
try {
RetrofitInstance.authApi.unsubscribe(
token,
Subscribe(channelId)
)
} catch (e: IOException) {
return@launch
}
} }
} }

View File

@ -17,8 +17,6 @@ import com.squareup.picasso.Picasso
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.io.IOException
class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscription>) : class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscription>) :
RecyclerView.Adapter<SubscriptionChannelViewHolder>() { RecyclerView.Adapter<SubscriptionChannelViewHolder>() {
@ -68,17 +66,14 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
private fun subscribe(context: Context, channelId: String) { private fun subscribe(context: Context, channelId: String) {
fun run() { fun run() {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val response = try { try {
val token = PreferenceHelper.getToken(context) val token = PreferenceHelper.getToken(context)
RetrofitInstance.authApi.subscribe( RetrofitInstance.authApi.subscribe(
token, token,
Subscribe(channelId) Subscribe(channelId)
) )
} catch (e: IOException) { } catch (e: Exception) {
println(e) Log.e(TAG, e.toString())
Log.e(TAG, "IOException, you might not have internet connection")
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
} }
subscribed = true subscribed = true
isLoading = false isLoading = false
@ -90,17 +85,14 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
private fun unsubscribe(context: Context, channelId: String) { private fun unsubscribe(context: Context, channelId: String) {
fun run() { fun run() {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val response = try { try {
val token = PreferenceHelper.getToken(context) val token = PreferenceHelper.getToken(context)
RetrofitInstance.authApi.unsubscribe( RetrofitInstance.authApi.unsubscribe(
token, token,
Subscribe(channelId) Subscribe(channelId)
) )
} catch (e: IOException) { } catch (e: Exception) {
println(e) Log.e(TAG, e.toString())
Log.e(TAG, "IOException, you might not have internet connection")
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
} }
subscribed = false subscribed = false
isLoading = false isLoading = false

View File

@ -7,7 +7,6 @@ import android.widget.Toast
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.activities.requireMainActivityRestart
import com.github.libretube.databinding.DialogDeleteAccountBinding import com.github.libretube.databinding.DialogDeleteAccountBinding
import com.github.libretube.obj.DeleteUserRequest import com.github.libretube.obj.DeleteUserRequest
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
@ -55,10 +54,10 @@ class DeleteAccountDialog : DialogFragment() {
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated return@launchWhenCreated
} }
requireMainActivityRestart = true
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
logout() logout()
dialog?.dismiss() val restartDialog = RequireRestartDialog()
restartDialog.show(childFragmentManager, "RequireRestartDialog")
} }
} }
run() run()

View File

@ -11,6 +11,7 @@ import android.util.Log
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.Toast import android.widget.Toast
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.view.size
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.libretube.R import com.github.libretube.R
@ -28,7 +29,6 @@ class DownloadDialog : DialogFragment() {
private val TAG = "DownloadDialog" private val TAG = "DownloadDialog"
private lateinit var binding: DialogDownloadBinding private lateinit var binding: DialogDownloadBinding
private lateinit var streams: Streams
private lateinit var videoId: String private lateinit var videoId: String
private var duration = 0 private var duration = 0
@ -40,7 +40,7 @@ class DownloadDialog : DialogFragment() {
val builder = MaterialAlertDialogBuilder(it) val builder = MaterialAlertDialogBuilder(it)
binding = DialogDownloadBinding.inflate(layoutInflater) binding = DialogDownloadBinding.inflate(layoutInflater)
fetchStreams() fetchAvailableSources()
// request storage permissions if not granted yet // request storage permissions if not granted yet
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@ -83,10 +83,10 @@ class DownloadDialog : DialogFragment() {
} ?: throw IllegalStateException("Activity cannot be null") } ?: throw IllegalStateException("Activity cannot be null")
} }
private fun fetchStreams() { private fun fetchAvailableSources() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
RetrofitInstance.api.getStreams(videoId!!) RetrofitInstance.api.getStreams(videoId)
} catch (e: IOException) { } catch (e: IOException) {
println(e) println(e)
Log.e(TAG, "IOException, you might not have internet connection") Log.e(TAG, "IOException, you might not have internet connection")
@ -102,8 +102,8 @@ class DownloadDialog : DialogFragment() {
} }
private fun initDownloadOptions(streams: Streams) { private fun initDownloadOptions(streams: Streams) {
var vidName = arrayListOf<String>() val vidName = arrayListOf<String>()
var vidUrl = arrayListOf<String>() val vidUrl = arrayListOf<String>()
// add empty selection // add empty selection
vidName.add(getString(R.string.no_video)) vidName.add(getString(R.string.no_video))
@ -111,13 +111,15 @@ class DownloadDialog : DialogFragment() {
// add all available video streams // add all available video streams
for (vid in streams.videoStreams!!) { for (vid in streams.videoStreams!!) {
val name = vid.quality + " " + vid.format if (vid.url != null) {
vidName.add(name) val name = vid.quality + " " + vid.format
vidUrl.add(vid.url!!) vidName.add(name)
vidUrl.add(vid.url!!)
}
} }
var audioName = arrayListOf<String>() val audioName = arrayListOf<String>()
var audioUrl = arrayListOf<String>() val audioUrl = arrayListOf<String>()
// add empty selection // add empty selection
audioName.add(getString(R.string.no_audio)) audioName.add(getString(R.string.no_audio))
@ -125,11 +127,14 @@ class DownloadDialog : DialogFragment() {
// add all available audio streams // add all available audio streams
for (audio in streams.audioStreams!!) { for (audio in streams.audioStreams!!) {
val name = audio.quality + " " + audio.format if (audio.url != null) {
audioName.add(name) val name = audio.quality + " " + audio.format
audioUrl.add(audio.url!!) audioName.add(name)
audioUrl.add(audio.url!!)
}
} }
// initialize the video sources
val videoArrayAdapter = ArrayAdapter( val videoArrayAdapter = ArrayAdapter(
requireContext(), requireContext(),
android.R.layout.simple_spinner_item, android.R.layout.simple_spinner_item,
@ -137,8 +142,9 @@ class DownloadDialog : DialogFragment() {
) )
videoArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) videoArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.videoSpinner.adapter = videoArrayAdapter binding.videoSpinner.adapter = videoArrayAdapter
binding.videoSpinner.setSelection(1) if (binding.videoSpinner.size >= 1) binding.videoSpinner.setSelection(1)
// initialize the audio sources
val audioArrayAdapter = ArrayAdapter( val audioArrayAdapter = ArrayAdapter(
requireContext(), requireContext(),
android.R.layout.simple_spinner_item, android.R.layout.simple_spinner_item,
@ -146,7 +152,7 @@ class DownloadDialog : DialogFragment() {
) )
audioArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) audioArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.audioSpinner.adapter = audioArrayAdapter binding.audioSpinner.adapter = audioArrayAdapter
binding.audioSpinner.setSelection(1) if (binding.audioSpinner.size >= 1) binding.audioSpinner.setSelection(1)
binding.download.setOnClickListener { binding.download.setOnClickListener {
val selectedAudioUrl = audioUrl[binding.audioSpinner.selectedItemPosition] val selectedAudioUrl = audioUrl[binding.audioSpinner.selectedItemPosition]

View File

@ -7,7 +7,6 @@ import android.widget.Toast
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.activities.requireMainActivityRestart
import com.github.libretube.databinding.DialogLoginBinding import com.github.libretube.databinding.DialogLoginBinding
import com.github.libretube.obj.Login import com.github.libretube.obj.Login
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
@ -82,9 +81,9 @@ class LoginDialog : DialogFragment() {
Toast.makeText(context, R.string.loggedIn, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.loggedIn, Toast.LENGTH_SHORT).show()
PreferenceHelper.setToken(requireContext(), response.token!!) PreferenceHelper.setToken(requireContext(), response.token!!)
PreferenceHelper.setUsername(requireContext(), login.username!!) PreferenceHelper.setUsername(requireContext(), login.username!!)
requireMainActivityRestart = true val restartDialog = RequireRestartDialog()
restartDialog.show(parentFragmentManager, "RequireRestartDialog")
dialog?.dismiss() dialog?.dismiss()
activity?.recreate()
} }
} }
} }

View File

@ -5,7 +5,6 @@ import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.activities.requireMainActivityRestart
import com.github.libretube.databinding.DialogLogoutBinding import com.github.libretube.databinding.DialogLogoutBinding
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.util.ThemeHelper import com.github.libretube.util.ThemeHelper
@ -25,7 +24,6 @@ class LogoutDialog : DialogFragment() {
binding.user.text = binding.user.text =
binding.user.text.toString() + " (" + user + ")" binding.user.text.toString() + " (" + user + ")"
binding.logout.setOnClickListener { binding.logout.setOnClickListener {
requireMainActivityRestart = true
Toast.makeText(context, R.string.loggedout, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.loggedout, Toast.LENGTH_SHORT).show()
PreferenceHelper.setToken(requireContext(), "") PreferenceHelper.setToken(requireContext(), "")
dialog?.dismiss() dialog?.dismiss()

View File

@ -0,0 +1,25 @@
package com.github.libretube.dialogs
import android.app.Dialog
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import com.github.libretube.R
import com.github.libretube.util.ThemeHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder
class RequireRestartDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.require_restart)
.setMessage(R.string.require_restart_message)
.setPositiveButton(R.string.okay) { _, _ ->
activity?.recreate()
ThemeHelper.restartMainActivity(requireContext())
}
.setNegativeButton(R.string.cancel) { _, _ -> }
.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}

View File

@ -4,7 +4,9 @@ import android.app.Dialog
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.github.libretube.PIPED_FRONTEND_URL
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.YOUTUBE_FRONTEND_URL
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -30,8 +32,8 @@ class ShareDialog(
shareOptions shareOptions
) { _, which -> ) { _, which ->
val host = when (which) { val host = when (which) {
0 -> "https://piped.kavin.rocks" 0 -> PIPED_FRONTEND_URL
1 -> "https://youtube.com" 1 -> YOUTUBE_FRONTEND_URL
// only available for custom instances // only available for custom instances
else -> instanceUrl else -> instanceUrl
} }
@ -57,7 +59,7 @@ class ShareDialog(
val instancePref = PreferenceHelper.getString( val instancePref = PreferenceHelper.getString(
requireContext(), requireContext(),
"selectInstance", "selectInstance",
"https://pipedapi.kavin.rocks" PIPED_FRONTEND_URL
) )
// get the api urls of the other custom instances // get the api urls of the other custom instances

View File

@ -16,7 +16,6 @@ import com.github.libretube.obj.Subscribe
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
import com.github.libretube.util.formatShort import com.github.libretube.util.formatShort
import com.google.android.material.button.MaterialButton
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException import java.io.IOException
@ -58,7 +57,7 @@ class ChannelFragment : Fragment() {
binding.channelRefresh.isRefreshing = true binding.channelRefresh.isRefreshing = true
fetchChannel() fetchChannel()
if (PreferenceHelper.getToken(requireContext()) != "") { if (PreferenceHelper.getToken(requireContext()) != "") {
isSubscribed(binding.channelSubscribe) isSubscribed()
} }
} }
refreshChannel() refreshChannel()
@ -81,7 +80,7 @@ class ChannelFragment : Fragment() {
} }
} }
private fun isSubscribed(button: MaterialButton) { private fun isSubscribed() {
@SuppressLint("ResourceAsColor") @SuppressLint("ResourceAsColor")
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
@ -91,28 +90,26 @@ class ChannelFragment : Fragment() {
channelId!!, channelId!!,
token token
) )
} catch (e: IOException) { } catch (e: Exception) {
println(e) Log.e(TAG, e.toString())
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
return@launchWhenCreated return@launchWhenCreated
} }
runOnUiThread { runOnUiThread {
if (response.subscribed == true) { if (response.subscribed == true) {
isSubscribed = true isSubscribed = true
button.text = getString(R.string.unsubscribe) binding.channelSubscribe.text = getString(R.string.unsubscribe)
} }
if (response.subscribed != null) { if (response.subscribed != null) {
button.setOnClickListener { binding.channelSubscribe.apply {
if (isSubscribed) { setOnClickListener {
unsubscribe() text = if (isSubscribed) {
button.text = getString(R.string.subscribe) unsubscribe()
} else { getString(R.string.subscribe)
subscribe() } else {
button.text = getString(R.string.unsubscribe) subscribe()
getString(R.string.unsubscribe)
}
} }
} }
} }
@ -125,19 +122,14 @@ class ChannelFragment : Fragment() {
private fun subscribe() { private fun subscribe() {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { try {
val token = PreferenceHelper.getToken(requireContext()) val token = PreferenceHelper.getToken(requireContext())
RetrofitInstance.authApi.subscribe( RetrofitInstance.authApi.subscribe(
token, token,
Subscribe(channelId) Subscribe(channelId)
) )
} catch (e: IOException) { } catch (e: Exception) {
println(e) Log.e(TAG, e.toString())
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response$e")
return@launchWhenCreated
} }
isSubscribed = true isSubscribed = true
} }
@ -148,19 +140,14 @@ class ChannelFragment : Fragment() {
private fun unsubscribe() { private fun unsubscribe() {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { try {
val token = PreferenceHelper.getToken(requireContext()) val token = PreferenceHelper.getToken(requireContext())
RetrofitInstance.authApi.unsubscribe( RetrofitInstance.authApi.unsubscribe(
token, token,
Subscribe(channelId) Subscribe(channelId)
) )
} catch (e: IOException) { } catch (e: Exception) {
println(e) Log.e(TAG, e.toString())
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
return@launchWhenCreated
} }
isSubscribed = false isSubscribed = false
} }

View File

@ -10,6 +10,7 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.Globals
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.adapters.PlaylistsAdapter import com.github.libretube.adapters.PlaylistsAdapter
import com.github.libretube.databinding.FragmentLibraryBinding import com.github.libretube.databinding.FragmentLibraryBinding
@ -77,7 +78,7 @@ class LibraryFragment : Fragment() {
override fun onResume() { override fun onResume() {
// optimize CreatePlaylistFab bottom margin if miniPlayer active // optimize CreatePlaylistFab bottom margin if miniPlayer active
val layoutParams = binding.createPlaylist.layoutParams as ViewGroup.MarginLayoutParams val layoutParams = binding.createPlaylist.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.bottomMargin = if (isMiniPlayerVisible) 180 else 64 layoutParams.bottomMargin = if (Globals.isMiniPlayerVisible) 180 else 64
binding.createPlaylist.layoutParams = layoutParams binding.createPlaylist.layoutParams = layoutParams
super.onResume() super.onResume()
} }

View File

@ -6,6 +6,8 @@ import android.app.PictureInPictureParams
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Color
import android.graphics.Rect import android.graphics.Rect
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@ -31,6 +33,7 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.Globals
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.activities.MainActivity import com.github.libretube.activities.MainActivity
import com.github.libretube.activities.hideKeyboard import com.github.libretube.activities.hideKeyboard
@ -53,6 +56,7 @@ import com.github.libretube.obj.Streams
import com.github.libretube.obj.Subscribe import com.github.libretube.obj.Subscribe
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.services.IS_DOWNLOAD_RUNNING import com.github.libretube.services.IS_DOWNLOAD_RUNNING
import com.github.libretube.util.BackgroundMode
import com.github.libretube.util.CronetHelper import com.github.libretube.util.CronetHelper
import com.github.libretube.util.DescriptionAdapter import com.github.libretube.util.DescriptionAdapter
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
@ -93,9 +97,6 @@ import java.io.IOException
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.abs import kotlin.math.abs
var isFullScreen = false
var isMiniPlayerVisible = false
class PlayerFragment : Fragment() { class PlayerFragment : Fragment() {
private val TAG = "PlayerFragment" private val TAG = "PlayerFragment"
@ -137,8 +138,11 @@ class PlayerFragment : Fragment() {
private lateinit var title: String private lateinit var title: String
private lateinit var uploader: String private lateinit var uploader: String
private lateinit var thumbnailUrl: String private lateinit var thumbnailUrl: String
private lateinit var chapters: List<ChapterSegment>
private val sponsorBlockPrefs = SponsorBlockPrefs() private val sponsorBlockPrefs = SponsorBlockPrefs()
private var autoRotationEnabled = true
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.let { arguments?.let {
@ -162,6 +166,34 @@ class PlayerFragment : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
hideKeyboard() hideKeyboard()
// save whether auto rotation is enabled
autoRotationEnabled = PreferenceHelper.getBoolean(
requireContext(),
"auto_fullscreen",
false
)
val mainActivity = activity as MainActivity
if (autoRotationEnabled) {
// enable auto rotation
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
onConfigurationChanged(resources.configuration)
} else {
// go to portrait mode
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
}
// save whether related streams and autoplay are enabled
autoplay = PreferenceHelper.getBoolean(
requireContext(),
"autoplay",
false
)
relatedStreamsEnabled = PreferenceHelper.getBoolean(
requireContext(),
"related_streams_toggle",
true
)
setSponsorBlockPrefs() setSponsorBlockPrefs()
createExoPlayer(view) createExoPlayer(view)
initializeTransitionLayout(view) initializeTransitionLayout(view)
@ -205,11 +237,11 @@ class PlayerFragment : Fragment() {
val mainMotionLayout = val mainMotionLayout =
mainActivity.binding.mainMotionLayout mainActivity.binding.mainMotionLayout
if (currentId == eId) { if (currentId == eId) {
isMiniPlayerVisible = true Globals.isMiniPlayerVisible = true
exoPlayerView.useController = false exoPlayerView.useController = false
mainMotionLayout.progress = 1F mainMotionLayout.progress = 1F
} else if (currentId == sId) { } else if (currentId == sId) {
isMiniPlayerVisible = false Globals.isMiniPlayerVisible = false
exoPlayerView.useController = true exoPlayerView.useController = true
mainMotionLayout.progress = 0F mainMotionLayout.progress = 0F
} }
@ -228,19 +260,17 @@ class PlayerFragment : Fragment() {
binding.playerMotionLayout.transitionToStart() binding.playerMotionLayout.transitionToStart()
binding.closeImageView.setOnClickListener { binding.closeImageView.setOnClickListener {
isMiniPlayerVisible = false Globals.isMiniPlayerVisible = false
binding.playerMotionLayout.transitionToEnd() binding.playerMotionLayout.transitionToEnd()
val mainActivity = activity as MainActivity val mainActivity = activity as MainActivity
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
mainActivity.supportFragmentManager.beginTransaction() mainActivity.supportFragmentManager.beginTransaction()
.remove(this) .remove(this)
.commit() .commit()
} }
playerBinding.closeImageButton.setOnClickListener { playerBinding.closeImageButton.setOnClickListener {
isMiniPlayerVisible = false Globals.isMiniPlayerVisible = false
binding.playerMotionLayout.transitionToEnd() binding.playerMotionLayout.transitionToEnd()
val mainActivity = activity as MainActivity val mainActivity = activity as MainActivity
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
mainActivity.supportFragmentManager.beginTransaction() mainActivity.supportFragmentManager.beginTransaction()
.remove(this) .remove(this)
.commit() .commit()
@ -259,9 +289,7 @@ class PlayerFragment : Fragment() {
// video description and chapters toggle // video description and chapters toggle
binding.playerTitleLayout.setOnClickListener { binding.playerTitleLayout.setOnClickListener {
binding.playerDescriptionArrow.animate().rotationBy(180F).setDuration(250).start() toggleDescription()
binding.descLinLayout.visibility =
if (binding.descLinLayout.isVisible) View.GONE else View.VISIBLE
} }
binding.commentsToggle.setOnClickListener { binding.commentsToggle.setOnClickListener {
@ -269,10 +297,12 @@ class PlayerFragment : Fragment() {
} }
// FullScreen button trigger // FullScreen button trigger
// hide fullscreen button if auto rotation enabled
playerBinding.fullscreen.visibility = if (autoRotationEnabled) View.GONE else View.VISIBLE
playerBinding.fullscreen.setOnClickListener { playerBinding.fullscreen.setOnClickListener {
// hide player controller // hide player controller
exoPlayerView.hideController() exoPlayerView.hideController()
if (!isFullScreen) { if (!Globals.isFullScreen) {
// go to fullscreen mode // go to fullscreen mode
setFullscreen() setFullscreen()
} else { } else {
@ -340,27 +370,27 @@ class PlayerFragment : Fragment() {
val fullscreenOrientationPref = PreferenceHelper val fullscreenOrientationPref = PreferenceHelper
.getString(requireContext(), "fullscreen_orientation", "ratio") .getString(requireContext(), "fullscreen_orientation", "ratio")
val scaleFactor = 1.3F scaleControls(1.3F)
playerBinding.exoPlayPause.scaleX = scaleFactor
playerBinding.exoPlayPause.scaleY = scaleFactor
val orientation = when (fullscreenOrientationPref) { if (!autoRotationEnabled) {
"ratio" -> { // different orientations of the video are only available when auto rotation is disabled
val videoSize = exoPlayer.videoSize val orientation = when (fullscreenOrientationPref) {
// probably a youtube shorts video "ratio" -> {
Log.e(TAG, videoSize.height.toString() + " " + videoSize.width.toString()) val videoSize = exoPlayer.videoSize
if (videoSize.height > videoSize.width) ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT // probably a youtube shorts video
// a video with normal aspect ratio if (videoSize.height > videoSize.width) ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
else ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE // a video with normal aspect ratio
else ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
}
"auto" -> ActivityInfo.SCREEN_ORIENTATION_USER
"landscape" -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
"portrait" -> ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
} }
"auto" -> ActivityInfo.SCREEN_ORIENTATION_USER mainActivity.requestedOrientation = orientation
"landscape" -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
"portrait" -> ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
} }
mainActivity.requestedOrientation = orientation
isFullScreen = true Globals.isFullScreen = true
} }
private fun unsetFullscreen() { private fun unsetFullscreen() {
@ -375,14 +405,26 @@ class PlayerFragment : Fragment() {
playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen) playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen)
playerBinding.exoTitle.visibility = View.INVISIBLE playerBinding.exoTitle.visibility = View.INVISIBLE
val scaleFactor = 1F scaleControls(1F)
if (!autoRotationEnabled) {
// switch back to portrait mode if auto rotation disabled
val mainActivity = activity as MainActivity
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
}
Globals.isFullScreen = false
}
private fun scaleControls(scaleFactor: Float) {
playerBinding.exoPlayPause.scaleX = scaleFactor playerBinding.exoPlayPause.scaleX = scaleFactor
playerBinding.exoPlayPause.scaleY = scaleFactor playerBinding.exoPlayPause.scaleY = scaleFactor
}
val mainActivity = activity as MainActivity private fun toggleDescription() {
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT binding.playerDescriptionArrow.animate().rotationBy(180F).setDuration(250).start()
binding.descLinLayout.visibility =
isFullScreen = false if (binding.descLinLayout.isVisible) View.GONE else View.VISIBLE
} }
private fun toggleComments() { private fun toggleComments() {
@ -492,16 +534,12 @@ class PlayerFragment : Fragment() {
uploader = response.uploader!! uploader = response.uploader!!
thumbnailUrl = response.thumbnailUrl!! thumbnailUrl = response.thumbnailUrl!!
// save whether related streams and autoplay are enabled
autoplay = PreferenceHelper.getBoolean(requireContext(), "autoplay", false)
relatedStreamsEnabled =
PreferenceHelper.getBoolean(requireContext(), "related_streams_toggle", true)
// save related streams for autoplay // save related streams for autoplay
relatedStreams = response.relatedStreams relatedStreams = response.relatedStreams
runOnUiThread { runOnUiThread {
// set media sources for the player // set media sources for the player
setResolutionAndSubtitles(view, response) setResolutionAndSubtitles(response)
prepareExoPlayerView() prepareExoPlayerView()
initializePlayerView(view, response) initializePlayerView(view, response)
seekToWatchPosition() seekToWatchPosition()
@ -682,7 +720,6 @@ class PlayerFragment : Fragment() {
setShowSubtitleButton(true) setShowSubtitleButton(true)
setShowNextButton(false) setShowNextButton(false)
setShowPreviousButton(false) setShowPreviousButton(false)
setRepeatToggleModes(RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL)
// controllerShowTimeoutMs = 1500 // controllerShowTimeoutMs = 1500
controllerHideOnTouch = true controllerHideOnTouch = true
useController = false useController = false
@ -709,7 +746,33 @@ class PlayerFragment : Fragment() {
enableDoubleTapToSeek() enableDoubleTapToSeek()
// init the chapters recyclerview // init the chapters recyclerview
if (response.chapters != null) initializeChapters(response.chapters) if (response.chapters != null) {
chapters = response.chapters
initializeChapters()
}
// set default playback speed
val playbackSpeed =
PreferenceHelper.getString(requireContext(), "playback_speed", "1F")!!
val playbackSpeeds = context?.resources?.getStringArray(R.array.playbackSpeed)!!
val playbackSpeedValues =
context?.resources?.getStringArray(R.array.playbackSpeedValues)!!
exoPlayer.setPlaybackSpeed(playbackSpeed.toFloat())
val speedIndex = playbackSpeedValues.indexOf(playbackSpeed)
playerBinding.speedText.text = playbackSpeeds[speedIndex]
// change playback speed button
playerBinding.speedText.setOnClickListener {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.change_playback_speed)
.setItems(playbackSpeeds) { _, index ->
// set the new playback speed
val newPlaybackSpeed = playbackSpeedValues[index].toFloat()
exoPlayer.setPlaybackSpeed(newPlaybackSpeed)
playerBinding.speedText.text = playbackSpeeds[index]
}
.show()
}
// Listener for play and pause icon change // Listener for play and pause icon change
exoPlayer.addListener(object : Player.Listener { exoPlayer.addListener(object : Player.Listener {
@ -777,11 +840,37 @@ class PlayerFragment : Fragment() {
} }
}) })
// repeat toggle button
playerBinding.repeatToggle.setOnClickListener {
if (exoPlayer.repeatMode == RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL) {
// turn off repeat mode
exoPlayer.repeatMode = RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE
playerBinding.repeatToggle.setColorFilter(Color.GRAY)
} else {
exoPlayer.repeatMode = RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL
playerBinding.repeatToggle.setColorFilter(Color.WHITE)
}
}
// share button // share button
binding.relPlayerShare.setOnClickListener { binding.relPlayerShare.setOnClickListener {
val shareDialog = ShareDialog(videoId!!, false) val shareDialog = ShareDialog(videoId!!, false)
shareDialog.show(childFragmentManager, "ShareDialog") shareDialog.show(childFragmentManager, "ShareDialog")
} }
binding.relPlayerBackground.setOnClickListener {
// pause the current player
exoPlayer.pause()
// start the background mode
BackgroundMode
.getInstance()
.playOnBackgroundMode(
requireContext(),
videoId!!
)
}
// check if livestream // check if livestream
if (response.duration!! > 0) { if (response.duration!! > 0) {
// download clicked // download clicked
@ -850,7 +939,7 @@ class PlayerFragment : Fragment() {
if (token != "") { if (token != "") {
val channelId = response.uploaderUrl?.replace("/channel/", "") val channelId = response.uploaderUrl?.replace("/channel/", "")
isSubscribed(binding.playerSubscribe, channelId!!) isSubscribed(binding.playerSubscribe, channelId!!)
binding.save.setOnClickListener { binding.relPlayerSave.setOnClickListener {
val newFragment = AddtoPlaylistDialog() val newFragment = AddtoPlaylistDialog()
val bundle = Bundle() val bundle = Bundle()
bundle.putString("videoId", videoId) bundle.putString("videoId", videoId)
@ -903,6 +992,12 @@ class PlayerFragment : Fragment() {
) )
} }
private fun disableDoubleTapToSeek() {
// disable fast forward and rewind by double tapping
binding.forwardFL.visibility = View.GONE
binding.rewindFL.visibility = View.GONE
}
// toggle the visibility of the player controller // toggle the visibility of the player controller
private fun toggleController() { private fun toggleController() {
if (exoPlayerView.isControllerFullyVisible) exoPlayerView.hideController() if (exoPlayerView.isControllerFullyVisible) exoPlayerView.hideController()
@ -917,10 +1012,15 @@ class PlayerFragment : Fragment() {
} }
override fun onScrubMove(timeBar: TimeBar, position: Long) { override fun onScrubMove(timeBar: TimeBar, position: Long) {
exoPlayer.seekTo(position) val minTimeDiff = 10 * 1000 // 10s
// get the difference between the new and the old position
val diff = abs(exoPlayer.currentPosition - position)
// seek only when the difference is greater than 10 seconds
if (diff >= minTimeDiff) exoPlayer.seekTo(position)
} }
override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) { override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) {
exoPlayer.seekTo(position)
exoPlayer.play() exoPlayer.play()
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
exoPlayerView.hideController() exoPlayerView.hideController()
@ -929,15 +1029,69 @@ class PlayerFragment : Fragment() {
}) })
} }
private fun initializeChapters(chapters: List<ChapterSegment>) { private fun initializeChapters() {
if (chapters.isNotEmpty()) { if (chapters.isNotEmpty()) {
// enable chapters in the video description
binding.chaptersRecView.layoutManager = binding.chaptersRecView.layoutManager =
LinearLayoutManager(this.context, LinearLayoutManager.HORIZONTAL, false) LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
binding.chaptersRecView.adapter = ChaptersAdapter(chapters, exoPlayer) binding.chaptersRecView.adapter = ChaptersAdapter(chapters, exoPlayer)
binding.chaptersRecView.visibility = View.VISIBLE binding.chaptersRecView.visibility = View.VISIBLE
// enable the chapters dialog in the player
val titles = mutableListOf<String>()
chapters.forEach {
titles += it.title!!
}
playerBinding.chapterLL.visibility = View.VISIBLE
playerBinding.chapterLL.setOnClickListener {
if (Globals.isFullScreen) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.chapters)
.setItems(titles.toTypedArray()) { _, index ->
val position = chapters[index].start!! * 1000
exoPlayer.seekTo(position)
}
.show()
} else {
toggleDescription()
}
}
setCurrentChapterName()
} }
} }
// set the name of the video chapter in the exoPlayerView
private fun setCurrentChapterName() {
// call the function again in 100ms
exoPlayerView.postDelayed(this::setCurrentChapterName, 100)
val chapterName = getCurrentChapterName()
// change the chapter name textView text to the chapterName
if (chapterName != null && chapterName != playerBinding.chapterName.text) {
playerBinding.chapterName.text = chapterName
}
}
// get the name of the currently played chapter
private fun getCurrentChapterName(): String? {
val currentPosition = exoPlayer.currentPosition
var chapterName: String? = null
chapters.forEach {
// check whether the chapter start is greater than the current player position
if (currentPosition >= it.start!! * 1000) {
// save chapter title if found
chapterName = it.title
}
}
return chapterName
}
private fun setMediaSource( private fun setMediaSource(
subtitle: MutableList<SubtitleConfiguration>, subtitle: MutableList<SubtitleConfiguration>,
videoUri: Uri, videoUri: Uri,
@ -960,7 +1114,7 @@ class PlayerFragment : Fragment() {
exoPlayer.setMediaSource(mergeSource) exoPlayer.setMediaSource(mergeSource)
} }
private fun setResolutionAndSubtitles(view: View, response: Streams) { private fun setResolutionAndSubtitles(response: Streams) {
val videoFormatPreference = val videoFormatPreference =
PreferenceHelper.getString(requireContext(), "player_video_format", "WEBM") PreferenceHelper.getString(requireContext(), "player_video_format", "WEBM")
val defres = PreferenceHelper.getString(requireContext(), "default_res", "")!! val defres = PreferenceHelper.getString(requireContext(), "default_res", "")!!
@ -976,8 +1130,8 @@ class PlayerFragment : Fragment() {
for (vid in response.videoStreams!!) { for (vid in response.videoStreams!!) {
// append quality to list if it has the preferred format (e.g. MPEG) // append quality to list if it has the preferred format (e.g. MPEG)
if (vid.format.equals(videoFormatPreference)) { // preferred format if (vid.format.equals(videoFormatPreference) && vid.url != null) { // preferred format
videosNameArray += vid.quality!! videosNameArray += vid.quality.toString()
videosUrlArray += vid.url!!.toUri() videosUrlArray += vid.url!!.toUri()
} else if (vid.quality.equals("LBRY") && vid.format.equals("MP4")) { // LBRY MP4 format) } else if (vid.quality.equals("LBRY") && vid.format.equals("MP4")) { // LBRY MP4 format)
videosNameArray += "LBRY MP4" videosNameArray += "LBRY MP4"
@ -1039,7 +1193,7 @@ class PlayerFragment : Fragment() {
} }
} }
playerBinding.qualityLinLayout.setOnClickListener { playerBinding.qualityText.setOnClickListener {
// Dialog for quality selection // Dialog for quality selection
val builder: MaterialAlertDialogBuilder? = activity?.let { val builder: MaterialAlertDialogBuilder? = activity?.let {
MaterialAlertDialogBuilder(it) MaterialAlertDialogBuilder(it)
@ -1074,13 +1228,8 @@ class PlayerFragment : Fragment() {
} }
private fun createExoPlayer(view: View) { private fun createExoPlayer(view: View) {
val playbackSpeed =
PreferenceHelper.getString(requireContext(), "playback_speed", "1F")?.toFloat()
// multiply by thousand: s -> ms
val bufferingGoal = val bufferingGoal =
PreferenceHelper.getString(requireContext(), "buffering_goal", "50")?.toInt()!! * 1000 PreferenceHelper.getString(requireContext(), "buffering_goal", "50")?.toInt()!! * 1000
val seekIncrement =
PreferenceHelper.getString(requireContext(), "seek_increment", "5")?.toLong()!! * 1000
val cronetEngine: CronetEngine = CronetHelper.getCronetEngine() val cronetEngine: CronetEngine = CronetHelper.getCronetEngine()
val cronetDataSourceFactory: CronetDataSource.Factory = val cronetDataSourceFactory: CronetDataSource.Factory =
@ -1112,13 +1261,9 @@ class PlayerFragment : Fragment() {
exoPlayer = ExoPlayer.Builder(view.context) exoPlayer = ExoPlayer.Builder(view.context)
.setMediaSourceFactory(DefaultMediaSourceFactory(dataSourceFactory)) .setMediaSourceFactory(DefaultMediaSourceFactory(dataSourceFactory))
.setLoadControl(loadControl) .setLoadControl(loadControl)
.setSeekBackIncrementMs(seekIncrement)
.setSeekForwardIncrementMs(seekIncrement)
.build() .build()
exoPlayer.setAudioAttributes(audioAttributes, true) exoPlayer.setAudioAttributes(audioAttributes, true)
exoPlayer.setPlaybackSpeed(playbackSpeed!!)
} }
private fun initializePlayerNotification(c: Context) { private fun initializePlayerNotification(c: Context) {
@ -1145,13 +1290,18 @@ class PlayerFragment : Fragment() {
} }
} }
// lock the player
private fun lockPlayer(isLocked: Boolean) { private fun lockPlayer(isLocked: Boolean) {
val visibility = if (isLocked) View.VISIBLE else View.GONE val visibility = if (isLocked) View.VISIBLE else View.GONE
playerBinding.exoTopBarRight.visibility = visibility playerBinding.exoTopBarRight.visibility = visibility
playerBinding.exoPlayPause.visibility = visibility playerBinding.exoPlayPause.visibility = visibility
playerBinding.exoBottomBar.visibility = visibility playerBinding.exoBottomBar.visibility = visibility
playerBinding.closeImageButton.visibility = visibility playerBinding.closeImageButton.visibility = visibility
playerBinding.exoTitle.visibility = visibility playerBinding.exoTitle.visibility = visibility
// disable double tap to seek when the player is locked
if (isLocked) enableDoubleTapToSeek() else disableDoubleTapToSeek()
} }
private fun isSubscribed(button: MaterialButton, channel_id: String) { private fun isSubscribed(button: MaterialButton, channel_id: String) {
@ -1198,7 +1348,7 @@ class PlayerFragment : Fragment() {
private fun subscribe(channel_id: String) { private fun subscribe(channel_id: String) {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { try {
val token = PreferenceHelper.getToken(requireContext()) val token = PreferenceHelper.getToken(requireContext())
RetrofitInstance.authApi.subscribe( RetrofitInstance.authApi.subscribe(
token, token,
@ -1221,7 +1371,7 @@ class PlayerFragment : Fragment() {
private fun unsubscribe(channel_id: String) { private fun unsubscribe(channel_id: String) {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { try {
val token = PreferenceHelper.getToken(requireContext()) val token = PreferenceHelper.getToken(requireContext())
RetrofitInstance.authApi.unsubscribe( RetrofitInstance.authApi.unsubscribe(
token, token,
@ -1305,28 +1455,19 @@ class PlayerFragment : Fragment() {
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) { override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode) super.onPictureInPictureModeChanged(isInPictureInPictureMode)
if (isInPictureInPictureMode) { if (isInPictureInPictureMode) {
// hide and disable exoPlayer controls
exoPlayerView.hideController() exoPlayerView.hideController()
exoPlayerView.useController = false exoPlayerView.useController = false
binding.linLayout.visibility = View.GONE
with(binding.playerMotionLayout) { unsetFullscreen()
getConstraintSet(R.id.start).constrainHeight(R.id.player, -1)
enableTransition(R.id.yt_transition, false)
}
binding.mainContainer.isClickable = true
val mainActivity = activity as MainActivity Globals.isFullScreen = false
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
isFullScreen = false
} else { } else {
with(binding.playerMotionLayout) { // enable exoPlayer controls again
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
enableTransition(R.id.yt_transition, true)
}
exoPlayerView.useController = true exoPlayerView.useController = true
binding.linLayout.visibility = View.VISIBLE
binding.mainContainer.isClickable = false // switch back to portrait mode
unsetFullscreen()
} }
} }
@ -1335,7 +1476,7 @@ class PlayerFragment : Fragment() {
binding.playerScrollView.getHitRect(bounds) binding.playerScrollView.getHitRect(bounds)
if (SDK_INT >= Build.VERSION_CODES.O && if (SDK_INT >= Build.VERSION_CODES.O &&
exoPlayer.isPlaying && (binding.playerScrollView.getLocalVisibleRect(bounds) || isFullScreen) exoPlayer.isPlaying && (binding.playerScrollView.getLocalVisibleRect(bounds) || Globals.isFullScreen)
) { ) {
activity?.enterPictureInPictureMode(updatePipParams()) activity?.enterPictureInPictureMode(updatePipParams())
} }
@ -1344,4 +1485,19 @@ class PlayerFragment : Fragment() {
private fun updatePipParams() = PictureInPictureParams.Builder() private fun updatePipParams() = PictureInPictureParams.Builder()
.setActions(emptyList()) .setActions(emptyList())
.build() .build()
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
if (autoRotationEnabled) {
val orientation = newConfig.orientation
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
// go to fullscreen mode
setFullscreen()
} else {
// exit fullscreen if not landscape
unsetFullscreen()
}
}
}
} }

View File

@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties
data class ChapterSegment( data class ChapterSegment(
var title: String?, var title: String?,
var image: String?, var image: String?,
var start: Int? var start: Long?
) { ) {
constructor() : this("", "", -1) constructor() : this("", "", -1)
} }

View File

@ -0,0 +1,11 @@
package com.github.libretube.obj
/**
* object for saving the download type
*/
object DownloadType {
const val AUDIO = 0
const val VIDEO = 1
const val MUX = 2
const val NONE = 3
}

View File

@ -5,7 +5,7 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.activities.SettingsActivity import com.github.libretube.activities.SettingsActivity
import com.github.libretube.activities.requireMainActivityRestart import com.github.libretube.dialogs.RequireRestartDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
class AdvancedSettings : PreferenceFragmentCompat() { class AdvancedSettings : PreferenceFragmentCompat() {
@ -48,8 +48,8 @@ class AdvancedSettings : PreferenceFragmentCompat() {
// clear login token // clear login token
PreferenceHelper.setToken(requireContext(), "") PreferenceHelper.setToken(requireContext(), "")
requireMainActivityRestart = true val restartDialog = RequireRestartDialog()
activity?.recreate() restartDialog.show(childFragmentManager, "RequireRestartDialog")
} }
.setNegativeButton(getString(R.string.cancel)) { _, _ -> } .setNegativeButton(getString(R.string.cancel)) { _, _ -> }
.setTitle(R.string.reset) .setTitle(R.string.reset)

View File

@ -6,7 +6,7 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.activities.SettingsActivity import com.github.libretube.activities.SettingsActivity
import com.github.libretube.activities.requireMainActivityRestart import com.github.libretube.dialogs.RequireRestartDialog
import com.github.libretube.util.ThemeHelper import com.github.libretube.util.ThemeHelper
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
@ -18,18 +18,18 @@ class AppearanceSettings : PreferenceFragmentCompat() {
val settingsActivity = activity as SettingsActivity val settingsActivity = activity as SettingsActivity
settingsActivity.changeTopBarText(getString(R.string.appearance)) settingsActivity.changeTopBarText(getString(R.string.appearance))
val themeToggle = findPreference<ListPreference>("theme_togglee") val themeToggle = findPreference<ListPreference>("theme_toggle")
themeToggle?.setOnPreferenceChangeListener { _, _ -> themeToggle?.setOnPreferenceChangeListener { _, _ ->
requireMainActivityRestart = true val restartDialog = RequireRestartDialog()
activity?.recreate() restartDialog.show(childFragmentManager, "RequireRestartDialog")
true true
} }
val accentColor = findPreference<ListPreference>("accent_color") val accentColor = findPreference<ListPreference>("accent_color")
updateAccentColorValues(accentColor!!) updateAccentColorValues(accentColor!!)
accentColor.setOnPreferenceChangeListener { _, _ -> accentColor.setOnPreferenceChangeListener { _, _ ->
requireMainActivityRestart = true val restartDialog = RequireRestartDialog()
activity?.recreate() restartDialog.show(childFragmentManager, "RequireRestartDialog")
true true
} }
@ -41,13 +41,22 @@ class AppearanceSettings : PreferenceFragmentCompat() {
val gridColumns = findPreference<ListPreference>("grid") val gridColumns = findPreference<ListPreference>("grid")
gridColumns?.setOnPreferenceChangeListener { _, _ -> gridColumns?.setOnPreferenceChangeListener { _, _ ->
requireMainActivityRestart = true val restartDialog = RequireRestartDialog()
restartDialog.show(childFragmentManager, "RequireRestartDialog")
true true
} }
val hideTrending = findPreference<SwitchPreference>("hide_trending_page") val hideTrending = findPreference<SwitchPreference>("hide_trending_page")
hideTrending?.setOnPreferenceChangeListener { _, _ -> hideTrending?.setOnPreferenceChangeListener { _, _ ->
requireMainActivityRestart = true val restartDialog = RequireRestartDialog()
restartDialog.show(childFragmentManager, "RequireRestartDialog")
true
}
val labelVisibilityMode = findPreference<ListPreference>("label_visibility")
labelVisibilityMode?.setOnPreferenceChangeListener { _, _ ->
val restartDialog = RequireRestartDialog()
restartDialog.show(childFragmentManager, "RequireRestartDialog")
true true
} }
} }

View File

@ -21,11 +21,11 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.activities.SettingsActivity import com.github.libretube.activities.SettingsActivity
import com.github.libretube.activities.requireMainActivityRestart
import com.github.libretube.dialogs.CustomInstanceDialog import com.github.libretube.dialogs.CustomInstanceDialog
import com.github.libretube.dialogs.DeleteAccountDialog import com.github.libretube.dialogs.DeleteAccountDialog
import com.github.libretube.dialogs.LoginDialog import com.github.libretube.dialogs.LoginDialog
import com.github.libretube.dialogs.LogoutDialog import com.github.libretube.dialogs.LogoutDialog
import com.github.libretube.dialogs.RequireRestartDialog
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
import org.json.JSONObject import org.json.JSONObject
import org.json.JSONTokener import org.json.JSONTokener
@ -119,7 +119,8 @@ class InstanceSettings : PreferenceFragmentCompat() {
// fetchInstance() // fetchInstance()
initCustomInstances(instance!!) initCustomInstances(instance!!)
instance.setOnPreferenceChangeListener { _, newValue -> instance.setOnPreferenceChangeListener { _, newValue ->
requireMainActivityRestart = true val restartDialog = RequireRestartDialog()
restartDialog.show(childFragmentManager, "RequireRestartDialog")
RetrofitInstance.url = newValue.toString() RetrofitInstance.url = newValue.toString()
if (!PreferenceHelper.getBoolean(requireContext(), "auth_instance_toggle", false)) { if (!PreferenceHelper.getBoolean(requireContext(), "auth_instance_toggle", false)) {
RetrofitInstance.authUrl = newValue.toString() RetrofitInstance.authUrl = newValue.toString()
@ -136,22 +137,24 @@ class InstanceSettings : PreferenceFragmentCompat() {
authInstance.isVisible = false authInstance.isVisible = false
} }
authInstance.setOnPreferenceChangeListener { _, newValue -> authInstance.setOnPreferenceChangeListener { _, newValue ->
requireMainActivityRestart = true
// save new auth url // save new auth url
RetrofitInstance.authUrl = newValue.toString() RetrofitInstance.authUrl = newValue.toString()
RetrofitInstance.lazyMgr.reset() RetrofitInstance.lazyMgr.reset()
logout() logout()
val restartDialog = RequireRestartDialog()
restartDialog.show(childFragmentManager, "RequireRestartDialog")
true true
} }
val authInstanceToggle = findPreference<SwitchPreference>("auth_instance_toggle") val authInstanceToggle = findPreference<SwitchPreference>("auth_instance_toggle")
authInstanceToggle?.setOnPreferenceChangeListener { _, newValue -> authInstanceToggle?.setOnPreferenceChangeListener { _, newValue ->
requireMainActivityRestart = true
authInstance.isVisible = newValue == true authInstance.isVisible = newValue == true
logout() logout()
// either use new auth url or the normal api url if auth instance disabled // either use new auth url or the normal api url if auth instance disabled
RetrofitInstance.authUrl = if (newValue == false) RetrofitInstance.url RetrofitInstance.authUrl = if (newValue == false) RetrofitInstance.url
else authInstance.value else authInstance.value
val restartDialog = RequireRestartDialog()
restartDialog.show(childFragmentManager, "RequireRestartDialog")
true true
} }

View File

@ -7,9 +7,9 @@ import androidx.preference.ListPreference
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.github.libretube.BuildConfig import com.github.libretube.BuildConfig
import com.github.libretube.Globals
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.activities.isCurrentViewMainSettings import com.github.libretube.dialogs.RequireRestartDialog
import com.github.libretube.activities.requireMainActivityRestart
import com.github.libretube.util.ThemeHelper import com.github.libretube.util.ThemeHelper
import com.github.libretube.util.checkUpdate import com.github.libretube.util.checkUpdate
@ -25,7 +25,8 @@ class MainSettings : PreferenceFragmentCompat() {
val region = findPreference<Preference>("region") val region = findPreference<Preference>("region")
region?.setOnPreferenceChangeListener { _, _ -> region?.setOnPreferenceChangeListener { _, _ ->
requireMainActivityRestart = true val restartDialog = RequireRestartDialog()
restartDialog.show(childFragmentManager, "RequireRestartDialog")
true true
} }
@ -93,7 +94,7 @@ class MainSettings : PreferenceFragmentCompat() {
} }
private fun navigateToSettingsFragment(newFragment: Fragment) { private fun navigateToSettingsFragment(newFragment: Fragment) {
isCurrentViewMainSettings = false Globals.isCurrentViewMainSettings = false
parentFragmentManager.beginTransaction() parentFragmentManager.beginTransaction()
.replace(R.id.settings, newFragment) .replace(R.id.settings, newFragment)
.commitNow() .commitNow()

View File

@ -1,7 +1,9 @@
package com.github.libretube.preferences package com.github.libretube.preferences
import android.os.Bundle import android.os.Bundle
import androidx.preference.ListPreference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.activities.SettingsActivity import com.github.libretube.activities.SettingsActivity
@ -13,5 +15,20 @@ class PlayerSettings : PreferenceFragmentCompat() {
val settingsActivity = activity as SettingsActivity val settingsActivity = activity as SettingsActivity
settingsActivity.changeTopBarText(getString(R.string.audio_video)) settingsActivity.changeTopBarText(getString(R.string.audio_video))
val playerOrientation = findPreference<ListPreference>("fullscreen_orientation")
val autoRotateToFullscreen = findPreference<SwitchPreferenceCompat>("auto_fullscreen")
// only show the player orientation option if auto fullscreen is disabled
playerOrientation?.isEnabled != PreferenceHelper.getBoolean(
requireContext(),
"auto_fullscreen",
false
)
autoRotateToFullscreen?.setOnPreferenceChangeListener { _, newValue ->
playerOrientation?.isEnabled = newValue != true
true
}
} }
} }

View File

@ -19,6 +19,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import com.arthenica.ffmpegkit.FFmpegKit import com.arthenica.ffmpegkit.FFmpegKit
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.obj.DownloadType
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
import java.io.File import java.io.File
@ -26,17 +27,19 @@ var IS_DOWNLOAD_RUNNING = false
class DownloadService : Service() { class DownloadService : Service() {
val TAG = "DownloadService" val TAG = "DownloadService"
private lateinit var notification: NotificationCompat.Builder
private var downloadId: Long = -1 private var downloadId: Long = -1
private lateinit var videoId: String private lateinit var videoId: String
private lateinit var videoUrl: String private lateinit var videoUrl: String
private lateinit var audioUrl: String private lateinit var audioUrl: String
private lateinit var extension: String private lateinit var extension: String
private var duration: Int = 0 private var duration: Int = 0
private var downloadType: Int = 3
private lateinit var audioDir: File private lateinit var audioDir: File
private lateinit var videoDir: File private lateinit var videoDir: File
private lateinit var notification: NotificationCompat.Builder
private lateinit var downloadType: String
private lateinit var libretubeDir: File private lateinit var libretubeDir: File
private lateinit var tempDir: File private lateinit var tempDir: File
override fun onCreate() { override fun onCreate() {
@ -50,11 +53,11 @@ class DownloadService : Service() {
audioUrl = intent.getStringExtra("audioUrl")!! audioUrl = intent.getStringExtra("audioUrl")!!
duration = intent.getIntExtra("duration", 1) duration = intent.getIntExtra("duration", 1)
extension = PreferenceHelper.getString(this, "video_format", ".mp4")!! extension = PreferenceHelper.getString(this, "video_format", ".mp4")!!
downloadType = if (audioUrl != "" && videoUrl != "") "mux" downloadType = if (audioUrl != "" && videoUrl != "") DownloadType.MUX
else if (audioUrl != "") "audio" else if (audioUrl != "") DownloadType.AUDIO
else if (videoUrl != "") "video" else if (videoUrl != "") DownloadType.VIDEO
else "none" else DownloadType.NONE
if (downloadType != "none") { if (downloadType != DownloadType.NONE) {
downloadNotification(intent) downloadNotification(intent)
downloadManager() downloadManager()
} else { } else {
@ -108,7 +111,7 @@ class DownloadService : Service() {
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE) IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
) )
when (downloadType) { when (downloadType) {
"mux" -> { DownloadType.MUX -> {
audioDir = File(tempDir, "$videoId-audio") audioDir = File(tempDir, "$videoId-audio")
videoDir = File(tempDir, "$videoId-video") videoDir = File(tempDir, "$videoId-video")
downloadId = downloadManagerRequest( downloadId = downloadManagerRequest(
@ -118,7 +121,7 @@ class DownloadService : Service() {
videoDir videoDir
) )
} }
"video" -> { DownloadType.VIDEO -> {
videoDir = File(libretubeDir, "$videoId-video") videoDir = File(libretubeDir, "$videoId-video")
downloadId = downloadManagerRequest( downloadId = downloadManagerRequest(
getString(R.string.video), getString(R.string.video),
@ -127,7 +130,7 @@ class DownloadService : Service() {
videoDir videoDir
) )
} }
"audio" -> { DownloadType.AUDIO -> {
audioDir = File(libretubeDir, "$videoId-audio") audioDir = File(libretubeDir, "$videoId-audio")
downloadId = downloadManagerRequest( downloadId = downloadManagerRequest(
getString(R.string.audio), getString(R.string.audio),
@ -148,7 +151,7 @@ class DownloadService : Service() {
val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) 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) { if (downloadId == id) {
if (downloadType == "mux") { if (downloadType == DownloadType.MUX) {
downloadManagerRequest( downloadManagerRequest(
getString(R.string.audio), getString(R.string.audio),
getString(R.string.downloading), getString(R.string.downloading),

View File

@ -2,13 +2,14 @@ package com.github.libretube.util
import android.content.Context import android.content.Context
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
object ConnectionHelper { object ConnectionHelper {
fun isNetworkAvailable(context: Context): Boolean { fun isNetworkAvailable(context: Context): Boolean {
val connectivityManager = val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
// this seems to not recognize vpn connections
/*
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val nw = connectivityManager.activeNetwork ?: return false val nw = connectivityManager.activeNetwork ?: return false
val actNw = connectivityManager.getNetworkCapabilities(nw) ?: return false val actNw = connectivityManager.getNetworkCapabilities(nw) ?: return false
@ -28,5 +29,8 @@ object ConnectionHelper {
} else { } else {
return connectivityManager.activeNetworkInfo?.isConnected ?: false return connectivityManager.activeNetworkInfo?.isConnected ?: false
} }
*/
return connectivityManager.activeNetworkInfo?.isConnected ?: false
} }
} }

View File

@ -12,35 +12,60 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
import com.google.android.material.color.DynamicColors
object ThemeHelper { object ThemeHelper {
fun updateTheme(context: Context) { fun updateTheme(activity: AppCompatActivity) {
updateAccentColor(context) val themeMode = PreferenceHelper.getString(activity, "theme_toggle", "A")!!
updateThemeMode(context) val blackModeEnabled = themeMode == "O"
updateAccentColor(activity, blackModeEnabled)
updateThemeMode(themeMode)
} }
private fun updateAccentColor(context: Context) { private fun updateAccentColor(
when (PreferenceHelper.getString(context, "accent_color", "purple")) { activity: AppCompatActivity,
"my" -> context.setTheme(R.style.MaterialYou) blackThemeEnabled: Boolean
"red" -> context.setTheme(R.style.Theme_Red) ) {
"blue" -> context.setTheme(R.style.Theme_Blue) val theme = when (
"yellow" -> context.setTheme(R.style.Theme_Yellow) PreferenceHelper.getString(
"green" -> context.setTheme(R.style.Theme_Green) activity,
"purple" -> context.setTheme(R.style.Theme_Purple) "accent_color",
} "purple"
} )
) {
private fun updateThemeMode(context: Context) { "my" -> {
when (PreferenceHelper.getString(context, "theme_togglee", "A")) { applyDynamicColors(activity)
"A" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) if (blackThemeEnabled) R.style.MaterialYou_Black
"L" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) else R.style.MaterialYou
"D" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
"O" -> {
context.setTheme(R.style.OLED)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
} }
"red" -> if (blackThemeEnabled) R.style.Theme_Red_Black else R.style.Theme_Red
"blue" -> if (blackThemeEnabled) R.style.Theme_Blue_Black else R.style.Theme_Blue
"yellow" -> if (blackThemeEnabled) R.style.Theme_Yellow_Black else R.style.Theme_Yellow
"green" -> if (blackThemeEnabled) R.style.Theme_Green_Black else R.style.Theme_Green
"purple" -> if (blackThemeEnabled) R.style.Theme_Purple_Black else R.style.Theme_Purple
else -> if (blackThemeEnabled) R.style.Theme_Purple_Black else R.style.Theme_Purple
} }
activity.setTheme(theme)
}
private fun applyDynamicColors(activity: AppCompatActivity) {
/**
* apply dynamic colors to the activity
*/
DynamicColors.applyToActivityIfAvailable(activity)
}
private fun updateThemeMode(themeMode: String) {
val mode = when (themeMode) {
"A" -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
"L" -> AppCompatDelegate.MODE_NIGHT_NO
"D" -> AppCompatDelegate.MODE_NIGHT_YES
"O" -> AppCompatDelegate.MODE_NIGHT_YES
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
AppCompatDelegate.setDefaultNightMode(mode)
} }
fun changeIcon(context: Context, newLogoActivityAlias: String) { fun changeIcon(context: Context, newLogoActivityAlias: String) {

View File

@ -5,32 +5,34 @@ import android.os.Looper
import android.view.View import android.view.View
class DoubleClickListener( class DoubleClickListener(
private val doubleClickTimeLimitMills: Long = 300, private val doubleClickTimeLimitMills: Long = 200,
private val callback: Callback private val callback: Callback
) : View.OnClickListener { ) : View.OnClickListener {
private var lastClicked: Long = -1L private var lastClicked: Long = -1L
private var doubleClicked: Boolean = false
override fun onClick(v: View?) { override fun onClick(v: View?) {
lastClicked = when { lastClicked = when {
lastClicked == -1L -> { lastClicked == -1L -> {
doubleClicked = false checkForSingleClick()
System.currentTimeMillis() System.currentTimeMillis()
} }
isDoubleClicked() -> { isDoubleClicked() -> {
doubleClicked = true
callback.doubleClicked() callback.doubleClicked()
-1L -1L
} }
else -> { else -> {
Handler(Looper.getMainLooper()).postDelayed({ checkForSingleClick()
if (!doubleClicked) callback.singleClicked()
}, doubleClickTimeLimitMills)
System.currentTimeMillis() System.currentTimeMillis()
} }
} }
} }
private fun checkForSingleClick() {
Handler(Looper.getMainLooper()).postDelayed({
if (lastClicked != -1L) callback.singleClicked()
}, doubleClickTimeLimitMills)
}
private fun getTimeDiff(from: Long, to: Long): Long { private fun getTimeDiff(from: Long, to: Long): Long {
return to - from return to - from
} }

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="@android:color/white"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M8.59,16.59L13.17,12 8.59,7.41 10,6l6,6 -6,6 -1.41,-1.41z" />
</vector>

View File

@ -2,13 +2,9 @@
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="?android:attr/colorControlNormal" android:tint="?android:attr/colorControlNormal"
android:viewportWidth="36" android:viewportWidth="48"
android:viewportHeight="36"> android:viewportHeight="48">
<path <path
android:fillColor="#FF000000" android:fillColor="#FF000000"
android:pathData="M23.38,16.77l0.6,-0.6A5,5 0,0 0,24 9.1L18.71,3.84a5,5 0,0 0,-7.07 0L3.09,12.39a5,5 0,0 0,0 7.07l5.26,5.26a5,5 0,0 0,7.07 0l0.45,-0.45 2.1,2.2h3.44v3h3.69v1.63L28,34h6L34,27.45ZM14.82,10.18L9.37,15.64a1,1 0,0 1,-1.41 0l-0.4,-0.4a1,1 0,0 1,0 -1.41L13,8.36a1,1 0,0 1,1.41 0l0.4,0.4A1,1 0,0 1,14.82 10.18ZM32,32L28.86,32l-1.77,-1.76v-2.8L23.41,27.44v-3L18.8,24.44l-1.52,-1.61L22,18.18 32,28.28Z" /> android:pathData="M14,30.45q2.7,0 4.575,-1.875T20.45,24q0,-2.7 -1.875,-4.575T14,17.55q-2.7,0 -4.575,1.875T7.55,24q0,2.7 1.875,4.575T14,30.45ZM14,36q-5,0 -8.5,-3.5T2,24q0,-5 3.5,-8.5T14,12q4.25,0 7.125,2.325t4.025,5.925h17.2L46,23.9l-7.3,7.3 -4.2,-4.2 -4.2,4.2 -3.55,-3.55h-1.6q-0.95,3.75 -4.025,6.05T14,36Z" />
<path
android:fillAlpha="0"
android:fillColor="#FF000000"
android:pathData="M0,0h36v36h-36z" />
</vector> </vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:attr/colorControlNormal"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#FF000000"
android:pathData="M32.9,21.5 L28.4,26l1.45,1.8 1.85,-1.85q-0.35,3.3 -2.45,5.6T24,33.85h-1.35q-0.55,0 -1.25,-0.25l-1.9,1.55q1,0.45 2.125,0.825 1.125,0.375 2.375,0.375 4.15,0 7.05,-3 2.9,-3 3.15,-7.85l2.1,2.3 1.45,-1.8ZM15.1,30.85 L19.6,26.35 18.15,24.55 16.3,26.4q0.35,-3.3 2.45,-5.6T24,18.5h1.35q0.55,0 1.25,0.25l1.9,-1.55q-1,-0.45 -2.125,-0.825Q25.25,16 24,16q-4.15,0 -7.05,3 -2.9,3 -3.15,7.85l-2.1,-2.3 -1.45,1.8ZM7,42q-1.2,0 -2.1,-0.9Q4,40.2 4,39L4,13.35q0,-1.15 0.9,-2.075 0.9,-0.925 2.1,-0.925h7.35L18,6h12l3.65,4.35L41,10.35q1.15,0 2.075,0.925Q44,12.2 44,13.35L44,39q0,1.2 -0.925,2.1 -0.925,0.9 -2.075,0.9Z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:attr/colorControlNormal"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#FF000000"
android:pathData="M16.4,42H9q-1.2,0 -2.1,-0.9Q6,40.2 6,39V24q0,-3.75 1.425,-7.025 1.425,-3.275 3.85,-5.7 2.425,-2.425 5.7,-3.85Q20.25,6 24,6q3.75,0 7.025,1.425 3.275,1.425 5.7,3.85 2.425,2.425 3.85,5.7Q42,20.25 42,24v15q0,1.2 -0.9,2.1 -0.9,0.9 -2.1,0.9h-7.4V27.2H39V24q0,-6.25 -4.375,-10.625T24,9q-6.25,0 -10.625,4.375T9,24v3.2h7.4Z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:attr/colorControlNormal"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#FF000000"
android:pathData="m42,24 l-8.45,11.95q-0.65,0.9 -1.55,1.475 -0.9,0.575 -2,0.575H9q-1.25,0 -2.125,-0.875T6,35V13q0,-1.25 0.875,-2.125T9,10h21q1.1,0 2,0.575 0.9,0.575 1.55,1.475Z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:attr/colorControlNormal"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#FF000000"
android:pathData="m7,8 l3.7,7.6h6.5L13.5,8h4.45l3.7,7.6h6.5L24.45,8h4.45l3.7,7.6h6.5L35.4,8H41q1.2,0 2.1,0.9 0.9,0.9 0.9,2.1v26q0,1.2 -0.9,2.1 -0.9,0.9 -2.1,0.9H7q-1.2,0 -2.1,-0.9Q4,38.2 4,37V11q0,-1.2 0.9,-2.1Q5.8,8 7,8Z" />
</vector>

View File

@ -1,12 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:attr/colorControlNormal"
android:viewportWidth="244.86"
android:viewportHeight="244.86">
<path
android:fillColor="#FF000000"
android:pathData="M240.9,38.89c-2.43,-1.31 -5.39,-1.17 -7.69,0.35l-64.63,42.81V64.38c0,-14.48 -11.78,-26.25 -26.25,-26.25H26.25C11.78,38.13 0,49.91 0,64.38v116.09c0,14.47 11.78,26.25 26.25,26.25h116.09c14.47,0 26.25,-11.78 26.25,-26.25v-17.67l64.63,42.81c1.25,0.83 2.69,1.25 4.14,1.25c1.22,0 2.44,-0.3 3.55,-0.89c2.43,-1.31 3.95,-3.85 3.95,-6.61V45.5C244.86,42.74 243.34,40.2 240.9,38.89zM153.59,180.47c0,6.2 -5.05,11.25 -11.25,11.25H26.25c-6.2,0 -11.25,-5.05 -11.25,-11.25V64.38c0,-6.2 5.05,-11.25 11.25,-11.25h116.09c6.2,0 11.25,5.05 11.25,11.25v31.64v52.82V180.47zM229.86,185.39l-61.27,-40.58v-44.76l61.27,-40.58V185.39z"
android:strokeWidth="10"
android:strokeColor="#000000" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M7,7h10v3l4,-4 -4,-4v3L5,5v6h2L7,7zM17,17L7,17v-3l-4,4 4,4v-3h12v-6h-2v4z" />
</vector>

View File

@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="48"
android:viewportWidth="48" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="m24.1,38 l5.7,-5.65 -5.7,-5.65 -2.1,2.1 2.15,2.15q-1.4,0.05 -2.725,-0.45 -1.325,-0.5 -2.375,-1.55 -1,-1 -1.525,-2.3 -0.525,-1.3 -0.525,-2.6 0,-0.85 0.225,-1.7t0.625,-1.65l-2.2,-2.2q-0.85,1.25 -1.25,2.65T14,24q0,1.9 0.75,3.75t2.2,3.3q1.45,1.45 3.25,2.175 1.8,0.725 3.7,0.775L22,35.9ZM32.35,29.5q0.85,-1.25 1.25,-2.65T34,24q0,-1.9 -0.725,-3.775T31.1,16.9q-1.45,-1.45 -3.275,-2.15t-3.725,-0.7L26,12.1 23.9,10l-5.7,5.65 5.7,5.65 2.1,-2.1 -2.2,-2.2q1.35,0 2.75,0.525t2.4,1.525q1,1 1.525,2.3 0.525,1.3 0.525,2.6 0,0.85 -0.225,1.7t-0.625,1.65ZM24,44q-4.1,0 -7.75,-1.575 -3.65,-1.575 -6.375,-4.3 -2.725,-2.725 -4.3,-6.375Q4,28.1 4,24q0,-4.15 1.575,-7.8 1.575,-3.65 4.3,-6.35 2.725,-2.7 6.375,-4.275Q19.9,4 24,4q4.15,0 7.8,1.575 3.65,1.575 6.35,4.275 2.7,2.7 4.275,6.35Q44,19.85 44,24q0,4.1 -1.575,7.75 -1.575,3.65 -4.275,6.375t-6.35,4.3Q28.15,44 24,44Z"/>
</vector>

View File

@ -2,12 +2,9 @@
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="?android:attr/colorControlNormal" android:tint="?android:attr/colorControlNormal"
android:viewportWidth="28" android:viewportWidth="48"
android:viewportHeight="28"> android:viewportHeight="48">
<path <path
android:fillColor="#212121" android:fillColor="#FF000000"
android:pathData="M5.25,5.5C3.455,5.5 2,6.955 2,8.75V19.25C2,21.045 3.455,22.5 5.25,22.5H14.75C16.545,22.5 18,21.045 18,19.25V8.75C18,6.955 16.545,5.5 14.75,5.5H5.25Z" /> android:pathData="M7,40q-1.2,0 -2.1,-0.9Q4,38.2 4,37V11q0,-1.2 0.9,-2.1Q5.8,8 7,8h26q1.2,0 2.1,0.9 0.9,0.9 0.9,2.1v10.75l8,-8v20.5l-8,-8V37q0,1.2 -0.9,2.1 -0.9,0.9 -2.1,0.9Z" />
<path
android:fillColor="#212121"
android:pathData="M23.123,20.643L19.5,17.094V10.999L23.112,7.371C23.899,6.58 25.248,7.138 25.248,8.253V19.75C25.248,20.858 23.914,21.418 23.123,20.643Z" />
</vector> </vector>

View File

@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layoutDescription="@xml/activity_main_scene" app:layoutDescription="@xml/activity_main_scene"
tools:context=".MainActivity"> tools:context=".activities.MainActivity">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"

View File

@ -1,34 +1,33 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:background="?android:attr/selectableItemBackground"> android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal">
<de.hdodenhof.circleimageview.CircleImageView <de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/search_channel_image" android:id="@+id/search_channel_image"
android:layout_width="100dp" android:layout_width="90dp"
android:layout_height="100dp" android:layout_height="90dp"
android:layout_marginStart="36dp" android:layout_marginStart="45dp"
android:layout_marginEnd="45dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<LinearLayout <LinearLayout
android:id="@+id/constraintLayout" android:id="@+id/search_channel_info"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="40dp" android:layout_gravity="center"
android:orientation="vertical" android:layout_marginEnd="10dp"
app:layout_constraintBottom_toBottomOf="parent" android:orientation="vertical">
app:layout_constraintStart_toEndOf="@+id/search_channel_image"
app:layout_constraintTop_toTopOf="parent">
<TextView <TextView
android:id="@+id/search_channel_name" android:id="@+id/search_channel_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:textSize="16sp" /> android:textSize="16sp" />
@ -37,6 +36,15 @@
android:id="@+id/search_views" android:id="@+id/search_views"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<TextView
android:id="@+id/search_sub_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="@string/subscribe"
android:textColor="?attr/colorPrimary"
android:visibility="gone" />
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout>

View File

@ -33,9 +33,9 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:backgroundTint="?attr/colorOnPrimary" android:layout_centerVertical="true"
android:text="@string/unsubscribe" android:text="@string/subscribe"
android:textColor="@android:color/white" android:textColor="?android:attr/textColorPrimary"
android:textSize="11sp" android:textSize="12sp"
app:cornerRadius="20dp" /> app:cornerRadius="20dp" />
</RelativeLayout> </RelativeLayout>

View File

@ -9,6 +9,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:animateLayoutChanges="true"
android:orientation="vertical" android:orientation="vertical"
android:paddingStart="20dp" android:paddingStart="20dp"
android:paddingEnd="20dp" android:paddingEnd="20dp"
@ -23,8 +24,8 @@
<de.hdodenhof.circleimageview.CircleImageView <de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/commentor_image" android:id="@+id/commentor_image"
android:layout_width="30dp" android:layout_width="36dp"
android:layout_height="30dp" android:layout_height="36dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
app:srcCompat="@mipmap/ic_launcher" /> app:srcCompat="@mipmap/ic_launcher" />
@ -52,7 +53,7 @@
android:id="@+id/verified_imageView" android:id="@+id/verified_imageView"
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:layout_marginLeft="4dp" android:layout_marginStart="4dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:visibility="gone" android:visibility="gone"
app:srcCompat="@drawable/ic_verified" /> app:srcCompat="@drawable/ic_verified" />
@ -61,7 +62,7 @@
android:id="@+id/pinned_imageView" android:id="@+id/pinned_imageView"
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:layout_marginLeft="4dp" android:layout_marginStart="4dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:visibility="gone" android:visibility="gone"
app:srcCompat="@drawable/ic_pinned" /> app:srcCompat="@drawable/ic_pinned" />
@ -85,7 +86,7 @@
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:layout_marginRight="6dp" android:layout_marginEnd="6dp"
app:srcCompat="@drawable/ic_thumb_up" /> app:srcCompat="@drawable/ic_thumb_up" />
<TextView <TextView
@ -98,7 +99,7 @@
android:id="@+id/hearted_imageView" android:id="@+id/hearted_imageView"
android:layout_width="14dp" android:layout_width="14dp"
android:layout_height="14dp" android:layout_height="14dp"
android:layout_marginLeft="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:visibility="gone" android:visibility="gone"
app:srcCompat="@drawable/ic_hearted" /> app:srcCompat="@drawable/ic_hearted" />
@ -110,6 +111,7 @@
android:id="@+id/replies_recView" android:id="@+id/replies_recView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:background="@null" android:background="@null"
android:nestedScrollingEnabled="false" /> android:nestedScrollingEnabled="false" />

View File

@ -30,7 +30,7 @@
android:id="@+id/close_imageButton" android:id="@+id/close_imageButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="5dp" android:layout_marginEnd="5dp"
android:background="#00FFFFFF" android:background="#00FFFFFF"
android:padding="@dimen/exo_icon_padding" android:padding="@dimen/exo_icon_padding"
android:src="@drawable/ic_close" android:src="@drawable/ic_close"
@ -74,95 +74,122 @@
android:id="@+id/aspect_ratio_button" android:id="@+id/aspect_ratio_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp" android:layout_marginHorizontal="5dp"
android:background="#00FFFFFF" android:background="?attr/selectableItemBackground"
android:padding="@dimen/exo_icon_padding" android:padding="@dimen/exo_icon_padding"
android:src="@drawable/ic_aspect_ratio" /> android:src="@drawable/ic_aspect_ratio" />
<LinearLayout <TextView
android:id="@+id/quality_linLayout" android:id="@+id/speed_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginHorizontal="5dp"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/exo_icon_padding"
android:text="1x"
android:textColor="#FFFFFF" />
<TextView <TextView
android:id="@+id/quality_text" android:id="@+id/quality_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:padding="@dimen/exo_icon_padding" android:layout_marginHorizontal="5dp"
android:text="@string/hls" android:background="?attr/selectableItemBackground"
android:textColor="#FFFFFF" /> android:padding="@dimen/exo_icon_padding"
android:text="@string/hls"
android:textColor="#FFFFFF" />
<ImageView
android:id="@+id/quality_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/exo_icon_padding"
android:src="@drawable/ic_arrow_down"
app:tint="@android:color/white" />
</LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<FrameLayout <LinearLayout
android:id="@id/exo_bottom_bar" android:id="@id/exo_bottom_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/exo_styled_bottom_bar_height" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:layout_marginTop="@dimen/exo_styled_bottom_bar_margin_top" android:layout_marginTop="@dimen/exo_styled_bottom_bar_margin_top"
android:layoutDirection="ltr"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:id="@id/exo_time" android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start" android:layout_marginBottom="-5dp"
android:layout_marginBottom="10dp"
android:layoutDirection="ltr"
android:paddingStart="@dimen/exo_styled_bottom_bar_time_padding" android:paddingStart="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingLeft="@dimen/exo_styled_bottom_bar_time_padding" android:paddingLeft="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingEnd="@dimen/exo_styled_bottom_bar_time_padding" android:paddingEnd="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingRight="@dimen/exo_styled_bottom_bar_time_padding"> android:paddingRight="@dimen/exo_styled_bottom_bar_time_padding">
<TextView <LinearLayout
android:id="@id/exo_position" android:id="@id/exo_time"
style="@style/ExoStyledControls.TimeText.Position" /> android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp">
<TextView style="@style/ExoStyledControls.TimeText.Separator" /> <TextView
android:id="@id/exo_position"
style="@style/ExoStyledControls.TimeText.Position" />
<TextView <TextView style="@style/ExoStyledControls.TimeText.Separator" />
android:id="@id/exo_duration"
style="@style/ExoStyledControls.TimeText.Duration" />
</LinearLayout> <TextView
android:id="@id/exo_duration"
style="@style/ExoStyledControls.TimeText.Duration" />
<LinearLayout </LinearLayout>
android:id="@id/exo_basic_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:layout_marginBottom="10dp"
android:layoutDirection="ltr">
<ImageButton <LinearLayout
android:id="@id/exo_repeat_toggle" android:id="@+id/chapterLL"
style="@style/ExoStyledControls.Button.Bottom.RepeatToggle" /> android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:visibility="invisible">
<ImageButton <TextView
android:id="@id/exo_subtitle" android:id="@+id/chapter_name"
style="@style/ExoStyledControls.Button.Bottom.CC" /> android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/white" />
<ImageButton <ImageView
android:id="@id/exo_settings" android:layout_width="15dp"
style="@style/ExoStyledControls.Button.Bottom.Settings" /> android:layout_height="15dp"
android:layout_gravity="center"
android:layout_marginStart="3dp"
android:src="@drawable/ic_arrow_right" />
<ImageButton </LinearLayout>
android:id="@+id/fullscreen"
style="@style/ExoStyledControls.Button.Bottom.FullScreen" <LinearLayout
android:src="@drawable/ic_fullscreen" android:id="@id/exo_basic_controls"
app:tint="@android:color/white" /> android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/repeat_toggle"
style="@style/PlayerButton"
android:src="@drawable/ic_repeat"
app:tint="@android:color/darker_gray" />
<ImageButton
android:id="@id/exo_subtitle"
style="@style/PlayerButton" />
<ImageButton
android:id="@+id/fullscreen"
style="@style/PlayerButton"
android:src="@drawable/ic_fullscreen"
app:tint="@android:color/white" />
</LinearLayout>
</LinearLayout> </LinearLayout>
@ -187,18 +214,6 @@
</LinearLayout> </LinearLayout>
</FrameLayout>
<LinearLayout
android:id="@id/exo_minimal_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginBottom="@dimen/exo_styled_minimal_controls_margin_bottom"
android:gravity="center_vertical"
android:layoutDirection="ltr"
android:orientation="horizontal">
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.github.libretube.views.CustomSwipeToRefresh xmlns:android="http://schemas.android.com/apk/res/android" <com.github.libretube.views.CustomSwipeToRefresh xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/channel_refresh" android:id="@+id/channel_refresh"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -74,15 +75,15 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/channel_subscribe" android:id="@+id/channel_subscribe"
style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:drawableLeft="@drawable/ic_bell_small"
android:backgroundTint="?attr/colorOnPrimary" android:drawableTint="?android:attr/textColorPrimary"
android:drawableStart="@drawable/ic_bell_small"
android:drawableTint="@android:color/white"
android:text="@string/subscribe" android:text="@string/subscribe"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textSize="11sp" /> android:textSize="12sp"
app:cornerRadius="20dp" />
</LinearLayout> </LinearLayout>

View File

@ -49,7 +49,7 @@
android:id="@+id/player_description_arrow" android:id="@+id/player_description_arrow"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:layout_alignParentEnd="true"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
@ -73,7 +73,7 @@
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="3dp" android:layout_marginStart="3dp"
android:layout_marginTop="2dp"> android:layout_marginTop="2dp">
<ImageView <ImageView
@ -93,7 +93,7 @@
android:layout_width="12dp" android:layout_width="12dp"
android:layout_height="12dp" android:layout_height="12dp"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_marginLeft="5dp" android:layout_marginStart="5dp"
android:rotation="180" android:rotation="180"
android:src="@drawable/ic_like" /> android:src="@drawable/ic_like" />
@ -119,7 +119,6 @@
android:id="@+id/chapters_recView" android:id="@+id/chapters_recView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/comments_toggle"
android:layout_marginHorizontal="3dp" android:layout_marginHorizontal="3dp"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"
android:nestedScrollingEnabled="false" android:nestedScrollingEnabled="false"
@ -193,7 +192,7 @@
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="25dp" android:layout_height="25dp"
android:padding="2dp" android:padding="2dp"
android:src="@drawable/ic_player" /> android:src="@drawable/ic_videocam" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -203,7 +202,22 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/save" android:id="@+id/relPlayer_background"
style="@style/PlayerActionsLayout">
<ImageView
android:layout_width="24dp"
android:layout_height="25dp"
android:src="@drawable/ic_headphones" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/audio" />
</LinearLayout>
<LinearLayout
android:id="@+id/relPlayer_save"
style="@style/PlayerActionsLayout"> style="@style/PlayerActionsLayout">
<ImageView <ImageView
@ -245,7 +259,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginLeft="5dp" android:layout_marginStart="5dp"
android:layout_toStartOf="@+id/player_subscribe" android:layout_toStartOf="@+id/player_subscribe"
android:layout_toEndOf="@+id/player_channelImage" android:layout_toEndOf="@+id/player_channelImage"
android:ellipsize="end" android:ellipsize="end"
@ -265,7 +279,7 @@
android:drawableTint="?android:attr/textColorPrimary" android:drawableTint="?android:attr/textColorPrimary"
android:text="@string/subscribe" android:text="@string/subscribe"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textSize="12dp" android:textSize="12sp"
app:cornerRadius="11dp" /> app:cornerRadius="11dp" />
</RelativeLayout> </RelativeLayout>
@ -297,7 +311,7 @@
android:id="@+id/commentsToggle_textView" android:id="@+id/commentsToggle_textView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="8dp" android:layout_marginStart="8dp"
android:text="@string/comments" android:text="@string/comments"
android:textSize="17sp" android:textSize="17sp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -307,7 +321,7 @@
android:id="@+id/commentsToggle_imageView" android:id="@+id/commentsToggle_imageView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="3dp" android:layout_marginEnd="3dp"
android:src="@drawable/ic_arrow_up_down" android:src="@drawable/ic_arrow_up_down"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -357,49 +371,64 @@
android:id="@+id/player" android:id="@+id/player"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface" android:background="@android:color/black"
app:layout_constraintBottom_toBottomOf="@id/main_container" app:layout_constraintBottom_toBottomOf="@id/main_container"
app:layout_constraintStart_toStartOf="@id/main_container" app:layout_constraintStart_toStartOf="@id/main_container"
app:layout_constraintTop_toTopOf="@id/main_container" app:layout_constraintTop_toTopOf="@id/main_container"
app:show_buffering="when_playing"> app:show_buffering="when_playing">
<FrameLayout <!-- double tap to rewind/forward overlay -->
android:id="@+id/forwardFL" <LinearLayout
android:layout_width="wrap_content" android:id="@+id/doubleTapOverlayLL"
android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center_vertical|end"
android:layout_marginVertical="50dp"> android:layout_marginVertical="50dp">
<ImageButton <!-- double tap rewind btn -->
android:id="@+id/forwardBTN" <FrameLayout
android:layout_width="150dp" android:id="@+id/rewindFL"
android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?android:selectableItemBackgroundBorderless" android:layout_weight=".35">
android:clickable="false"
android:src="@drawable/ic_forward"
android:visibility="invisible"
app:tint="@android:color/white" />
</FrameLayout> <ImageButton
android:id="@+id/rewindBTN"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:selectableItemBackgroundBorderless"
android:clickable="false"
android:src="@drawable/ic_rewind"
android:visibility="invisible"
app:tint="@android:color/white" />
<FrameLayout </FrameLayout>
android:id="@+id/rewindFL"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|start"
android:layout_marginVertical="50dp">
<ImageButton <!-- place holder for the center controls -->
android:id="@+id/rewindBTN" <LinearLayout
android:layout_width="150dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?android:selectableItemBackgroundBorderless" android:layout_weight=".30" />
android:clickable="false"
android:src="@drawable/ic_rewind"
android:visibility="invisible"
app:tint="@android:color/white" />
</FrameLayout> <!-- double tap forward btn -->
<FrameLayout
android:id="@+id/forwardFL"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight=".35">
<ImageButton
android:id="@+id/forwardBTN"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:selectableItemBackgroundBorderless"
android:clickable="false"
android:src="@drawable/ic_forward"
android:visibility="invisible"
app:tint="@android:color/white" />
</FrameLayout>
</LinearLayout>
</com.github.libretube.views.CustomExoPlayerView> </com.github.libretube.views.CustomExoPlayerView>

View File

@ -42,7 +42,7 @@
android:id="@+id/verified_imageView" android:id="@+id/verified_imageView"
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:layout_marginLeft="4dp" android:layout_marginStart="4dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:visibility="gone" android:visibility="gone"
app:srcCompat="@drawable/ic_verified" /> app:srcCompat="@drawable/ic_verified" />
@ -51,7 +51,7 @@
android:id="@+id/pinned_imageView" android:id="@+id/pinned_imageView"
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:layout_marginLeft="4dp" android:layout_marginStart="4dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:visibility="gone" android:visibility="gone"
app:srcCompat="@drawable/ic_pinned" /> app:srcCompat="@drawable/ic_pinned" />
@ -75,7 +75,7 @@
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:layout_marginRight="6dp" android:layout_marginEnd="6dp"
app:srcCompat="@drawable/ic_thumb_up" /> app:srcCompat="@drawable/ic_thumb_up" />
<TextView <TextView
@ -88,7 +88,7 @@
android:id="@+id/hearted_imageView" android:id="@+id/hearted_imageView"
android:layout_width="14dp" android:layout_width="14dp"
android:layout_height="14dp" android:layout_height="14dp"
android:layout_marginLeft="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:visibility="gone" android:visibility="gone"
app:srcCompat="@drawable/ic_hearted" /> app:srcCompat="@drawable/ic_hearted" />

View File

@ -227,4 +227,6 @@
<string name="telegram">تيليجرام</string> <string name="telegram">تيليجرام</string>
<string name="reddit">ريديت</string> <string name="reddit">ريديت</string>
<string name="twitter">تويتر</string> <string name="twitter">تويتر</string>
<string name="turnInternetOn">يرجى الاتصال بالإنترنت عن طريق تشغيل WiFi أو بيانات الجوال.</string>
<string name="open">فتح …</string>
</resources> </resources>

View File

@ -229,4 +229,5 @@
<string name="discord">Discord</string> <string name="discord">Discord</string>
<string name="open">ıq…</string> <string name="open">ıq…</string>
<string name="turnInternetOn">Zəhmət olmasa, WiFi və ya mobil datanı yandırmaqla internetə qoşulun.</string> <string name="turnInternetOn">Zəhmət olmasa, WiFi və ya mobil datanı yandırmaqla internetə qoşulun.</string>
<string name="chapters">Bölmələr</string>
</resources> </resources>

View File

@ -227,4 +227,7 @@
<string name="community">Comunidad</string> <string name="community">Comunidad</string>
<string name="discord">Discord</string> <string name="discord">Discord</string>
<string name="twitter">Twitter</string> <string name="twitter">Twitter</string>
<string name="turnInternetOn">Por favor, conéctese a Internet activando el WiFi o los datos móviles.</string>
<string name="open">Abrir…</string>
<string name="chapters">Capítulos</string>
</resources> </resources>

View File

@ -227,4 +227,7 @@
<string name="telegram">Telegram</string> <string name="telegram">Telegram</string>
<string name="community">Communauté</string> <string name="community">Communauté</string>
<string name="discord">Discord</string> <string name="discord">Discord</string>
<string name="open">Ouvrir …</string>
<string name="turnInternetOn">Veuillez vous connecter à l\'internet en activant le WiFi ou les données mobiles.</string>
<string name="chapters">Chapitres</string>
</resources> </resources>

View File

@ -227,4 +227,6 @@
<string name="matrix">Matrix</string> <string name="matrix">Matrix</string>
<string name="telegram">Telegram</string> <string name="telegram">Telegram</string>
<string name="reddit">Reddit</string> <string name="reddit">Reddit</string>
<string name="turnInternetOn">Connettiti a Internet attivando Wi-Fi o dati mobili.</string>
<string name="open">Apri …</string>
</resources> </resources>

View File

@ -227,4 +227,8 @@
<string name="portrait">לאורך</string> <string name="portrait">לאורך</string>
<string name="aspect_ratio">יחס תצוגת וידאו</string> <string name="aspect_ratio">יחס תצוגת וידאו</string>
<string name="twitter">טוויטר</string> <string name="twitter">טוויטר</string>
<string name="turnInternetOn">נא להתחבר לאינטרנט על ידי הפעלת הרשת האלחוטית או הנתונים הסלולריים.</string>
<string name="open">פתיחה…</string>
<string name="chapters">פרקים</string>
<string name="change_playback_speed">מהירות נגינה</string>
</resources> </resources>

View File

@ -8,6 +8,13 @@
</style> </style>
<style name="MaterialYou.Black" parent="MaterialYou">
<item name="android:colorBackground">@android:color/black</item>
<item name="colorSurface">@android:color/black</item>
</style>
<style name="Theme.Red" parent="Theme.Material3.Dark.NoActionBar"> <style name="Theme.Red" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/red_dark_accentLight</item> <item name="colorPrimary">@color/red_dark_accentLight</item>
@ -26,6 +33,13 @@
</style> </style>
<style name="Theme.Red.Black" parent="Theme.Red">
<item name="android:colorBackground">@android:color/black</item>
<item name="colorSurface">@android:color/black</item>
</style>
<style name="Theme.Blue" parent="Theme.Material3.Dark.NoActionBar"> <style name="Theme.Blue" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/blue_dark_accentLight</item> <item name="colorPrimary">@color/blue_dark_accentLight</item>
@ -44,6 +58,13 @@
</style> </style>
<style name="Theme.Blue.Black" parent="Theme.Blue">
<item name="android:colorBackground">@android:color/black</item>
<item name="colorSurface">@android:color/black</item>
</style>
<style name="Theme.Yellow" parent="Theme.Material3.Dark.NoActionBar"> <style name="Theme.Yellow" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/yellow_dark_accentLight</item> <item name="colorPrimary">@color/yellow_dark_accentLight</item>
@ -62,6 +83,13 @@
</style> </style>
<style name="Theme.Yellow.Black" parent="Theme.Yellow">
<item name="android:colorBackground">@android:color/black</item>
<item name="colorSurface">@android:color/black</item>
</style>
<style name="Theme.Green" parent="Theme.Material3.Dark.NoActionBar"> <style name="Theme.Green" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/green_dark_accentLight</item> <item name="colorPrimary">@color/green_dark_accentLight</item>
@ -80,6 +108,13 @@
</style> </style>
<style name="Theme.Green.Black" parent="Theme.Green">
<item name="android:colorBackground">@android:color/black</item>
<item name="colorSurface">@android:color/black</item>
</style>
<style name="Theme.Purple" parent="Theme.Material3.Dark.NoActionBar"> <style name="Theme.Purple" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/purple_dark_accentLight</item> <item name="colorPrimary">@color/purple_dark_accentLight</item>
@ -98,4 +133,11 @@
</style> </style>
<style name="Theme.Purple.Black" parent="Theme.Purple">
<item name="android:colorBackground">@android:color/black</item>
<item name="colorSurface">@android:color/black</item>
</style>
</resources> </resources>

View File

@ -1,58 +1,75 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="loggedout">Desconectado com sucesso!</string> <string name="loggedout">Sessão terminada.</string>
<string name="choose_quality_dialog">Qualidade</string> <string name="choose_quality_dialog">Qualidade</string>
<string name="search_hint">Procurar</string> <string name="search_hint">Procurar</string>
<string name="subscribe">Inscrever-se</string> <string name="subscribe">Subscrever</string>
<string name="unsubscribe">CANCELAR INSCRIÇÃO</string> <string name="unsubscribe">Cancelar subscrição</string>
<string name="share">Compartilhar</string> <string name="share">Partilhar</string>
<string name="yes">Sim</string> <string name="yes">Sim</string>
<string name="username">Nome de usuário</string> <string name="username">Nome de utilizador</string>
<string name="login">Login</string> <string name="login">Iniciar sessão</string>
<string name="register">Registrar</string> <string name="register">Registar</string>
<string name="logout">Sair</string> <string name="logout">Sair</string>
<string name="cancel">Cancelar</string> <string name="cancel">Cancelar</string>
<string name="already_logged_in">Você já está logado, você pode sair da sua conta.</string> <string name="already_logged_in">Sessão já iniciada. Pode sair da sua conta.</string>
<string name="login_first">Por favor, faça o login e tente novamente!</string> <string name="login_first">Inicie sessão e tente novamente.</string>
<string name="instances">Escolha uma instância</string> <string name="instances">Escolha uma instância</string>
<string name="customInstance">Adicionar uma instância personalizada</string> <string name="customInstance">Instância personalizada</string>
<string name="region">Escolha uma região</string> <string name="region">Região</string>
<string name="importsuccess">Inscrito com sucesso!</string> <string name="importsuccess">Subscrito</string>
<string name="subscribeIsEmpty">Inscreva-se em alguns canais primeiro!</string> <string name="subscribeIsEmpty">Tem que subscrever um canal.</string>
<string name="cannotDownload">Não é possível baixar esta transmissão!</string> <string name="cannotDownload">Não foi possível descarregar a emissão.</string>
<string name="dlcomplete">O download foi concluído!</string> <string name="dlcomplete">Descarga terminada.</string>
<string name="downloadfailed">Falha no download.</string> <string name="downloadfailed">Falha ao descarregar.</string>
<string name="app_theme">Tema do aplicativo</string> <string name="app_theme">Tema</string>
<string name="server_error">O servidor encontrou um problema. Talvez tente outra instância\?</string> <string name="server_error">Existe um problema com o servidor. Experimentar outra instância\?</string>
<string name="unknown_error">Erro de rede!</string> <string name="unknown_error">Erro de rede.</string>
<string name="error">Algo deu errado!</string> <string name="error">Ocorreu um erro.</string>
<string name="notgmail">Esta não é sua conta do Gmail!</string> <string name="notgmail">isto é apenas para uma conta Piped.</string>
<string name="defres">Resolução de vídeo padrão</string> <string name="defres">Resolução de vídeo padrão</string>
<string name="grid">Número de colunas da grade</string> <string name="grid">Colunas da grelha</string>
<string name="emptyList">Não há nada aqui!</string> <string name="emptyList">Nada para ver aqui.</string>
<string name="deletePlaylist">Excluir playlist</string> <string name="deletePlaylist">Eliminar lista de reprodução</string>
<string name="areYouSure">Tem certeza de que deseja excluir esta playlist\?</string> <string name="areYouSure">Tem certeza de que deseja eliminar esta lista\?</string>
<string name="createPlaylist">Criar playlist</string> <string name="createPlaylist">Criar lista de reprodução</string>
<string name="playlistCreated">Playlist criada!</string> <string name="playlistCreated">Lista de reprodução criada.</string>
<string name="playlistName">Nome da playlist</string> <string name="playlistName">Nome da lista de reprodução</string>
<string name="addToPlaylist">Adicionar a playlist</string> <string name="addToPlaylist">Adicionar à lista de reprodução</string>
<string name="success">Sucesso!</string> <string name="success">Feito.</string>
<string name="download">Download</string> <string name="download">Descarregar</string>
<string name="save">Salvar</string> <string name="save">Guardar</string>
<string name="loggedIn">Login feito com sucesso!</string> <string name="loggedIn">Sessão iniciada.</string>
<string name="password">Senha</string> <string name="password">Palavra-passe</string>
<string name="registered">Registrado com sucesso! Agora você pode se inscrever nos canais que desejar.</string> <string name="registered">Registo efetuado. Agora já pode subscrever canais.</string>
<string name="login_register">Entrar/Registrar</string> <string name="login_register">Entrar/Registar</string>
<string name="please_login">Por favor, faça login ou registre-se nas configurações primeiro!</string> <string name="please_login">deve iniciar sessão ou registar-se nas definições.</string>
<string name="dlisinprogress">Outro download já está em andamento, por favor aguarde até que seja concluído!</string> <string name="dlisinprogress">Já existe uma descarga em curso. Por favor aguarde.</string>
<string name="vlc">Abrir no VLC</string> <string name="vlc">Abrir no VLC</string>
<string name="vlcerror">Não é possível abrir no VLC. Talvez ainda não esteja instalado.</string> <string name="vlcerror">Não foi possível abrir no VLC. Talvez não esteja instalado.</string>
<string name="import_from_yt">Importar inscrições do YouTube</string> <string name="import_from_yt">Importar inscrições</string>
<string name="empty">Nome de usuário e senha não podem estar vazios!</string> <string name="empty">Não indicou o nome de utilizador ou a palavra-passe.</string>
<string name="fail">Falhou</string> <string name="fail">Falha :(</string>
<string name="about">Sobre</string> <string name="about">Acerca</string>
<string name="videos">Vídeos</string> <string name="videos">Vídeos</string>
<string name="library">Biblioteca</string> <string name="library">Biblioteca</string>
<string name="startpage">Início</string> <string name="startpage">Início</string>
<string name="subscriptions">Subscrições</string> <string name="subscriptions">Subscrições</string>
<string name="instance">Instância</string>
<string name="videoCount">%1$s vídeos</string>
<string name="comments">Comentários</string>
<string name="retry">Tentar novamente</string>
<string name="customization">Ajustes</string>
<string name="noInternet">Tem que estar ligado à Internet.</string>
<string name="import_from_yt_summary">De YouTube ou NewPipe</string>
<string name="changeLanguage">Idioma</string>
<string name="emptyPlaylistName">Uma lista de reprodução não pode estar vazia</string>
<string name="systemLanguage">Sistema</string>
<string name="systemDefault">Sistema</string>
<string name="lightTheme">Claro</string>
<string name="darkTheme">Escuro</string>
<string name="subscribers">%1$s subscritores</string>
<string name="settings">Definições</string>
<string name="location">Localização</string>
<string name="website">Site</string>
</resources> </resources>

View File

@ -28,7 +28,7 @@
<string name="subscribeIsEmpty">Сначала подпишитесь на некоторые каналы.</string> <string name="subscribeIsEmpty">Сначала подпишитесь на некоторые каналы.</string>
<string name="cannotDownload">Невозможно скачать этот поток.</string> <string name="cannotDownload">Невозможно скачать этот поток.</string>
<string name="dlcomplete">Загрузка завершена.</string> <string name="dlcomplete">Загрузка завершена.</string>
<string name="dlisinprogress">Пожалуйста, подождите, пока все загрузки завершаться.</string> <string name="dlisinprogress">Пожалуйста, подождите, пока все загрузки завершатся.</string>
<string name="downloadfailed">Загрузка не удалась.</string> <string name="downloadfailed">Загрузка не удалась.</string>
<string name="vlc">Открыть в VLC</string> <string name="vlc">Открыть в VLC</string>
<string name="vlcerror">Не удаётся открыть в VLC. Возможно, он не установлен.</string> <string name="vlcerror">Не удаётся открыть в VLC. Возможно, он не установлен.</string>
@ -64,7 +64,7 @@
<string name="import_from_yt_summary">Из YouTube или NewPipe</string> <string name="import_from_yt_summary">Из YouTube или NewPipe</string>
<string name="emptyPlaylistName">Название плейлиста не может быть пустым</string> <string name="emptyPlaylistName">Название плейлиста не может быть пустым</string>
<string name="comments">Комментарии</string> <string name="comments">Комментарии</string>
<string name="noInternet">Отсутствует соединение с сетью</string> <string name="noInternet">Сначала подключитесь к Интернету.</string>
<string name="videoCount">%1$s видео</string> <string name="videoCount">%1$s видео</string>
<string name="retry">Попробовать снова</string> <string name="retry">Попробовать снова</string>
<string name="settings">Настройки</string> <string name="settings">Настройки</string>
@ -227,4 +227,6 @@
<string name="matrix">Matrix</string> <string name="matrix">Matrix</string>
<string name="telegram">Telegram</string> <string name="telegram">Telegram</string>
<string name="twitter">Twitter</string> <string name="twitter">Twitter</string>
<string name="turnInternetOn">Пожалуйста, подключитесь к Интернету, включив WiFi или мобильный интернет.</string>
<string name="open">Открыть …</string>
</resources> </resources>

View File

@ -229,4 +229,6 @@
<string name="discord">Discord</string> <string name="discord">Discord</string>
<string name="open">ık …</string> <string name="open">ık …</string>
<string name="turnInternetOn">Lütfen, WiFi veya mobil verileri açarak internete bağlanın.</string> <string name="turnInternetOn">Lütfen, WiFi veya mobil verileri açarak internete bağlanın.</string>
<string name="chapters">Bölümler</string>
<string name="change_playback_speed">Oynatma hızı</string>
</resources> </resources>

View File

@ -229,4 +229,6 @@
<string name="telegram">Telegram</string> <string name="telegram">Telegram</string>
<string name="turnInternetOn">请打开 WiFi 或移动数据连接到互联网。</string> <string name="turnInternetOn">请打开 WiFi 或移动数据连接到互联网。</string>
<string name="open">打开…</string> <string name="open">打开…</string>
<string name="chapters">章节</string>
<string name="change_playback_speed">播放速度</string>
</resources> </resources>

View File

@ -709,4 +709,16 @@
<item>portrait</item> <item>portrait</item>
</string-array> </string-array>
<string-array name="labelVisibility">
<item>@string/always</item>
<item>@string/selected</item>
<item>@string/never</item>
</string-array>
<string-array name="labelVisibilityValues">
<item>always</item>
<item>selected</item>
<item>never</item>
</string-array>
</resources> </resources>

View File

@ -7,9 +7,9 @@
<color name="red_light_accentDark">#EF5350</color> <color name="red_light_accentDark">#EF5350</color>
<color name="red_light_background">#EDE3E4</color> <color name="red_light_background">#EDE3E4</color>
<color name="red_dark_accentLight">#B71C1C</color> <color name="red_dark_accentLight">#C96052</color>
<color name="red_dark_accentDark">#B71C1C</color> <color name="red_dark_accentDark">#D25545</color>
<color name="red_dark_background">#130808</color> <color name="red_dark_background">#1B0B09</color>
<color name="blue_light_accentLight">#1F75FE</color> <color name="blue_light_accentLight">#1F75FE</color>
<color name="blue_light_accentDark">#66A1FE</color> <color name="blue_light_accentDark">#66A1FE</color>
@ -17,15 +17,15 @@
<color name="blue_dark_accentLight">#1F75FE</color> <color name="blue_dark_accentLight">#1F75FE</color>
<color name="blue_dark_accentDark">#0146B6</color> <color name="blue_dark_accentDark">#0146B6</color>
<color name="blue_dark_background">#131426</color> <color name="blue_dark_background">#0A0B15</color>
<color name="yellow_light_accentLight">#FFA000</color> <color name="yellow_light_accentLight">#FFA000</color>
<color name="yellow_light_accentDark">#FFB300</color> <color name="yellow_light_accentDark">#FFB300</color>
<color name="yellow_light_background">#FFF8E1</color> <color name="yellow_light_background">#FFF8E1</color>
<color name="yellow_dark_accentLight">#FFCA28</color> <color name="yellow_dark_accentLight">#D9B95C</color>
<color name="yellow_dark_accentDark">#FF8F00</color> <color name="yellow_dark_accentDark">#D1B956</color>
<color name="yellow_dark_background">#0E0D04</color> <color name="yellow_dark_background">#19160B</color>
<color name="green_light_accentLight">#7CB342</color> <color name="green_light_accentLight">#7CB342</color>
<color name="green_light_accentDark">#8BC34A</color> <color name="green_light_accentDark">#8BC34A</color>
@ -39,8 +39,8 @@
<color name="purple_light_accentDark">#B39DDB</color> <color name="purple_light_accentDark">#B39DDB</color>
<color name="purple_light_background">#EFEBF6</color> <color name="purple_light_background">#EFEBF6</color>
<color name="purple_dark_accentLight">#7E57C2</color> <color name="purple_dark_accentLight">#AA6F6B</color>
<color name="purple_dark_accentDark">#311B92</color> <color name="purple_dark_accentDark">#8F415B</color>
<color name="purple_dark_background">#1D0B20</color> <color name="purple_dark_background">#201015</color>
</resources> </resources>

View File

@ -229,4 +229,14 @@
<string name="twitter">Twitter</string> <string name="twitter">Twitter</string>
<string name="turnInternetOn">Please connect to the internet by turning on WiFi or mobile data.</string> <string name="turnInternetOn">Please connect to the internet by turning on WiFi or mobile data.</string>
<string name="open">Open …</string> <string name="open">Open …</string>
<string name="chapters">Chapters</string>
<string name="change_playback_speed">Playback speed</string>
<string name="require_restart">Restart required</string>
<string name="require_restart_message">This change requires an app restart. Do you want to restart the app now? Otherwise the changes will be applied on the next app restart.</string>
<string name="navLabelVisibility">Navbar label visibility</string>
<string name="always">Always</string>
<string name="selected">Selected</string>
<string name="never">Never</string>
<string name="autoRotatePlayer">Auto fullscreen</string>
<string name="autoRotatePlayer_summary">Automatically switch to player fullscreen when the device gets turned.</string>
</resources> </resources>

View File

@ -15,16 +15,6 @@
</style> </style>
<style name="OLED">
<item name="android:colorBackground">@android:color/black</item>
<item name="colorSurface">@android:color/black</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="MaterialAlertDialog"> <style name="MaterialAlertDialog">
<item name="alertDialogTheme">@style/ThemeOverlay.Material3.MaterialAlertDialog</item> <item name="alertDialogTheme">@style/ThemeOverlay.Material3.MaterialAlertDialog</item>
@ -114,4 +104,14 @@
</style> </style>
<style name="PlayerButton">
<item name="android:padding">9dp</item>
<item name="android:layout_height">36dp</item>
<item name="android:layout_width">36dp</item>
<item name="android:layout_gravity">center</item>
<item name="android:background">?attr/selectableItemBackground</item>
</style>
</resources> </resources>

View File

@ -9,7 +9,7 @@
app:defaultValue="A" app:defaultValue="A"
app:entries="@array/themes" app:entries="@array/themes"
app:entryValues="@array/themesValue" app:entryValues="@array/themesValue"
app:key="theme_togglee" app:key="theme_toggle"
app:title="@string/app_theme" app:title="@string/app_theme"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
@ -51,6 +51,15 @@
app:title="@string/hideTrendingPage" app:title="@string/hideTrendingPage"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<ListPreference
android:icon="@drawable/ic_label"
app:defaultValue="always"
app:entries="@array/labelVisibility"
app:entryValues="@array/labelVisibilityValues"
app:key="label_visibility"
app:title="@string/navLabelVisibility"
app:useSimpleSummaryProvider="true" />
<ListPreference <ListPreference
android:icon="@drawable/ic_grid" android:icon="@drawable/ic_grid"
app:defaultValue="@integer/grid_items" app:defaultValue="@integer/grid_items"

View File

@ -14,7 +14,7 @@
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<ListPreference <ListPreference
android:icon="@drawable/ic_player" android:icon="@drawable/ic_videocam"
app:defaultValue="WEBM" app:defaultValue="WEBM"
app:entries="@array/playerVideoFormats" app:entries="@array/playerVideoFormats"
app:entryValues="@array/playerVideoFormats" app:entryValues="@array/playerVideoFormats"
@ -60,17 +60,26 @@
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:icon="@drawable/ic_play_filled" android:icon="@drawable/ic_play_filled"
android:summary="@string/autoplay_summary" android:summary="@string/autoplay_summary"
app:defaultValue="true"
app:key="autoplay" app:key="autoplay"
app:title="@string/player_autoplay" /> app:title="@string/player_autoplay" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:icon="@drawable/ic_pause_filled" android:icon="@drawable/ic_pause_filled"
android:summary="@string/pauseOnScreenOff_summary" android:summary="@string/pauseOnScreenOff_summary"
app:defaultValue="false"
app:key="pause_screen_off" app:key="pause_screen_off"
app:title="@string/pauseOnScreenOff" /> app:title="@string/pauseOnScreenOff" />
<SwitchPreferenceCompat
android:icon="@drawable/ic_rotating_circle"
android:summary="@string/autoRotatePlayer_summary"
app:defaultValue="false"
app:key="auto_fullscreen"
app:title="@string/autoRotatePlayer" />
<ListPreference <ListPreference
android:icon="@drawable/ic_fullscreen" android:icon="@drawable/ic_flip"
app:defaultValue="ratio" app:defaultValue="ratio"
app:entries="@array/fullscreenOrientation" app:entries="@array/fullscreenOrientation"
app:entryValues="@array/fullscreenOrientationValues" app:entryValues="@array/fullscreenOrientationValues"

View File

@ -45,7 +45,7 @@
app:title="@string/sponsorblock" /> app:title="@string/sponsorblock" />
<Preference <Preference
android:icon="@drawable/ic_player" android:icon="@drawable/ic_movie"
app:key="player" app:key="player"
app:summary="@string/player_summary" app:summary="@string/player_summary"
app:title="@string/audio_video" /> app:title="@string/audio_video" />