mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-28 07:50:31 +05:30
Merge branch 'master' into themes
This commit is contained in:
commit
ec4b105233
@ -154,7 +154,7 @@ interface PipedApi {
|
|||||||
suspend fun renamePlaylist(
|
suspend fun renamePlaylist(
|
||||||
@Header("Authorization") token: String,
|
@Header("Authorization") token: String,
|
||||||
@Body playlistId: PlaylistId
|
@Body playlistId: PlaylistId
|
||||||
)
|
): PlaylistId
|
||||||
|
|
||||||
@POST("user/playlists/delete")
|
@POST("user/playlists/delete")
|
||||||
suspend fun deletePlaylist(
|
suspend fun deletePlaylist(
|
||||||
|
@ -6,16 +6,21 @@ import com.github.libretube.R
|
|||||||
import com.github.libretube.api.obj.Playlist
|
import com.github.libretube.api.obj.Playlist
|
||||||
import com.github.libretube.api.obj.PlaylistId
|
import com.github.libretube.api.obj.PlaylistId
|
||||||
import com.github.libretube.api.obj.Playlists
|
import com.github.libretube.api.obj.Playlists
|
||||||
|
import com.github.libretube.constants.YOUTUBE_FRONTEND_URL
|
||||||
import com.github.libretube.db.DatabaseHolder
|
import com.github.libretube.db.DatabaseHolder
|
||||||
import com.github.libretube.db.obj.LocalPlaylist
|
import com.github.libretube.db.obj.LocalPlaylist
|
||||||
import com.github.libretube.enums.PlaylistType
|
import com.github.libretube.enums.PlaylistType
|
||||||
import com.github.libretube.extensions.TAG
|
import com.github.libretube.extensions.TAG
|
||||||
import com.github.libretube.extensions.awaitQuery
|
import com.github.libretube.extensions.awaitQuery
|
||||||
|
import com.github.libretube.extensions.toID
|
||||||
import com.github.libretube.extensions.toLocalPlaylistItem
|
import com.github.libretube.extensions.toLocalPlaylistItem
|
||||||
import com.github.libretube.extensions.toStreamItem
|
import com.github.libretube.extensions.toStreamItem
|
||||||
import com.github.libretube.extensions.toastFromMainThread
|
import com.github.libretube.extensions.toastFromMainThread
|
||||||
|
import com.github.libretube.obj.ImportPlaylist
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.util.PreferenceHelper
|
||||||
import com.github.libretube.util.ProxyHelper
|
import com.github.libretube.util.ProxyHelper
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
@ -46,9 +51,9 @@ object PlaylistsHelper {
|
|||||||
return playlists
|
return playlists
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getPlaylist(playlistType: PlaylistType, playlistId: String): Playlist {
|
suspend fun getPlaylist(playlistId: String): Playlist {
|
||||||
// load locally stored playlists with the auth api
|
// load locally stored playlists with the auth api
|
||||||
return when (playlistType) {
|
return when (getPrivatePlaylistType(playlistId)) {
|
||||||
PlaylistType.PRIVATE -> RetrofitInstance.authApi.getPlaylist(playlistId)
|
PlaylistType.PRIVATE -> RetrofitInstance.authApi.getPlaylist(playlistId)
|
||||||
PlaylistType.PUBLIC -> RetrofitInstance.api.getPlaylist(playlistId)
|
PlaylistType.PUBLIC -> RetrofitInstance.api.getPlaylist(playlistId)
|
||||||
PlaylistType.LOCAL -> {
|
PlaylistType.LOCAL -> {
|
||||||
@ -65,7 +70,10 @@ object PlaylistsHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun createPlaylist(playlistName: String, appContext: Context, onSuccess: () -> Unit) {
|
suspend fun createPlaylist(
|
||||||
|
playlistName: String,
|
||||||
|
appContext: Context
|
||||||
|
): String? {
|
||||||
if (!loggedIn()) {
|
if (!loggedIn()) {
|
||||||
awaitQuery {
|
awaitQuery {
|
||||||
DatabaseHolder.Database.localPlaylistsDao().createPlaylist(
|
DatabaseHolder.Database.localPlaylistsDao().createPlaylist(
|
||||||
@ -75,8 +83,9 @@ object PlaylistsHelper {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onSuccess.invoke()
|
return awaitQuery {
|
||||||
return
|
DatabaseHolder.Database.localPlaylistsDao().getAll()
|
||||||
|
}.last().playlist.id.toString()
|
||||||
}
|
}
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.authApi.createPlaylist(
|
RetrofitInstance.authApi.createPlaylist(
|
||||||
@ -85,22 +94,25 @@ object PlaylistsHelper {
|
|||||||
)
|
)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
appContext.toastFromMainThread(R.string.unknown_error)
|
appContext.toastFromMainThread(R.string.unknown_error)
|
||||||
return
|
return null
|
||||||
} catch (e: HttpException) {
|
} catch (e: HttpException) {
|
||||||
Log.e(TAG(), e.toString())
|
Log.e(TAG(), e.toString())
|
||||||
appContext.toastFromMainThread(R.string.server_error)
|
appContext.toastFromMainThread(R.string.server_error)
|
||||||
return
|
return null
|
||||||
}
|
}
|
||||||
if (response.playlistId != null) {
|
if (response.playlistId != null) {
|
||||||
appContext.toastFromMainThread(R.string.playlistCreated)
|
appContext.toastFromMainThread(R.string.playlistCreated)
|
||||||
onSuccess.invoke()
|
return response.playlistId!!
|
||||||
} else {
|
|
||||||
appContext.toastFromMainThread(R.string.unknown_error)
|
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun addToPlaylist(playlistId: String, videoId: String): Boolean {
|
suspend fun addToPlaylist(playlistId: String, vararg videoIds: String): Boolean {
|
||||||
if (!loggedIn()) {
|
if (!loggedIn()) {
|
||||||
|
val localPlaylist = DatabaseHolder.Database.localPlaylistsDao().getAll()
|
||||||
|
.first { it.playlist.id.toString() == playlistId }
|
||||||
|
|
||||||
|
for (videoId in videoIds) {
|
||||||
val localPlaylistItem = RetrofitInstance.api.getStreams(videoId).toLocalPlaylistItem(playlistId, videoId)
|
val localPlaylistItem = RetrofitInstance.api.getStreams(videoId).toLocalPlaylistItem(playlistId, videoId)
|
||||||
awaitQuery {
|
awaitQuery {
|
||||||
// avoid duplicated videos in a playlist
|
// avoid duplicated videos in a playlist
|
||||||
@ -108,8 +120,6 @@ object PlaylistsHelper {
|
|||||||
|
|
||||||
// add the new video to the database
|
// add the new video to the database
|
||||||
DatabaseHolder.Database.localPlaylistsDao().addPlaylistVideo(localPlaylistItem)
|
DatabaseHolder.Database.localPlaylistsDao().addPlaylistVideo(localPlaylistItem)
|
||||||
val localPlaylist = DatabaseHolder.Database.localPlaylistsDao().getAll()
|
|
||||||
.first { it.playlist.id.toString() == playlistId }
|
|
||||||
|
|
||||||
if (localPlaylist.playlist.thumbnailUrl == "") {
|
if (localPlaylist.playlist.thumbnailUrl == "") {
|
||||||
// set the new playlist thumbnail URL
|
// set the new playlist thumbnail URL
|
||||||
@ -119,16 +129,20 @@ object PlaylistsHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return RetrofitInstance.authApi.addToPlaylist(
|
return RetrofitInstance.authApi.addToPlaylist(
|
||||||
token,
|
token,
|
||||||
PlaylistId(playlistId, videoId)
|
PlaylistId(
|
||||||
|
playlistId = playlistId,
|
||||||
|
videoIds = videoIds.toList()
|
||||||
|
)
|
||||||
).message == "ok"
|
).message == "ok"
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun renamePlaylist(playlistId: String, newName: String) {
|
suspend fun renamePlaylist(playlistId: String, newName: String): Boolean {
|
||||||
if (!loggedIn()) {
|
if (!loggedIn()) {
|
||||||
val playlist = awaitQuery {
|
val playlist = awaitQuery {
|
||||||
DatabaseHolder.Database.localPlaylistsDao().getAll()
|
DatabaseHolder.Database.localPlaylistsDao().getAll()
|
||||||
@ -137,16 +151,16 @@ object PlaylistsHelper {
|
|||||||
awaitQuery {
|
awaitQuery {
|
||||||
DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(playlist)
|
DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(playlist)
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
RetrofitInstance.authApi.renamePlaylist(
|
return RetrofitInstance.authApi.renamePlaylist(
|
||||||
token,
|
token,
|
||||||
PlaylistId(
|
PlaylistId(
|
||||||
playlistId = playlistId,
|
playlistId = playlistId,
|
||||||
newName = newName
|
newName = newName
|
||||||
)
|
)
|
||||||
)
|
).playlistId != null
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun removeFromPlaylist(playlistId: String, index: Int) {
|
suspend fun removeFromPlaylist(playlistId: String, index: Int) {
|
||||||
@ -175,11 +189,49 @@ object PlaylistsHelper {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPrivateType(): PlaylistType {
|
suspend fun importPlaylists(appContext: Context, playlists: List<ImportPlaylist>) {
|
||||||
|
for (playlist in playlists) {
|
||||||
|
val playlistId = createPlaylist(playlist.name!!, appContext) ?: continue
|
||||||
|
addToPlaylist(
|
||||||
|
playlistId,
|
||||||
|
*playlist.videos.map {
|
||||||
|
it.substringAfter("=")
|
||||||
|
}.toTypedArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun exportPlaylists(): List<ImportPlaylist> {
|
||||||
|
val playlists = getPlaylists()
|
||||||
|
val importLists = mutableListOf<ImportPlaylist>()
|
||||||
|
runBlocking {
|
||||||
|
val tasks = playlists.map {
|
||||||
|
async {
|
||||||
|
val list = getPlaylist(it.id!!)
|
||||||
|
importLists.add(
|
||||||
|
ImportPlaylist(
|
||||||
|
name = list.name,
|
||||||
|
type = "playlist",
|
||||||
|
visibility = "private",
|
||||||
|
videos = list.relatedStreams.orEmpty().map {
|
||||||
|
YOUTUBE_FRONTEND_URL + "/watch?v=" + it.url!!.toID()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.forEach {
|
||||||
|
it.await()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return importLists
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPrivatePlaylistType(): PlaylistType {
|
||||||
return if (loggedIn()) PlaylistType.PRIVATE else PlaylistType.LOCAL
|
return if (loggedIn()) PlaylistType.PRIVATE else PlaylistType.LOCAL
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPrivateType(playlistId: String): PlaylistType {
|
fun getPrivatePlaylistType(playlistId: String): PlaylistType {
|
||||||
if (playlistId.all { it.isDigit() }) return PlaylistType.LOCAL
|
if (playlistId.all { it.isDigit() }) return PlaylistType.LOCAL
|
||||||
if (playlistId.matches(pipedPlaylistRegex)) return PlaylistType.PRIVATE
|
if (playlistId.matches(pipedPlaylistRegex)) return PlaylistType.PRIVATE
|
||||||
return PlaylistType.PUBLIC
|
return PlaylistType.PUBLIC
|
||||||
|
@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
|||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
data class ContentItem(
|
data class ContentItem(
|
||||||
var url: String? = null,
|
var url: String? = null,
|
||||||
|
val type: String? = null,
|
||||||
var thumbnail: String? = null,
|
var thumbnail: String? = null,
|
||||||
var uploaderName: String? = null,
|
var uploaderName: String? = null,
|
||||||
var uploaded: Long? = null,
|
var uploaded: Long? = null,
|
||||||
@ -16,6 +17,7 @@ data class ContentItem(
|
|||||||
var uploadedDate: String? = null,
|
var uploadedDate: String? = null,
|
||||||
var duration: Long? = null,
|
var duration: Long? = null,
|
||||||
var views: Long? = null,
|
var views: Long? = null,
|
||||||
|
var isShort: Boolean? = null,
|
||||||
var uploaderVerified: Boolean? = null,
|
var uploaderVerified: Boolean? = null,
|
||||||
// Channel and Playlist attributes
|
// Channel and Playlist attributes
|
||||||
var name: String? = null,
|
var name: String? = null,
|
||||||
|
@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
|||||||
data class PlaylistId(
|
data class PlaylistId(
|
||||||
var playlistId: String? = null,
|
var playlistId: String? = null,
|
||||||
var videoId: String? = null,
|
var videoId: String? = null,
|
||||||
|
var videoIds: List<String>? = null,
|
||||||
var newName: String? = null,
|
var newName: String? = null,
|
||||||
var index: Int = -1
|
var index: Int = -1
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
|||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
data class StreamItem(
|
data class StreamItem(
|
||||||
var url: String? = null,
|
var url: String? = null,
|
||||||
|
val type: String? = null,
|
||||||
var title: String? = null,
|
var title: String? = null,
|
||||||
var thumbnail: String? = null,
|
var thumbnail: String? = null,
|
||||||
var uploaderName: String? = null,
|
var uploaderName: String? = null,
|
||||||
@ -15,5 +16,6 @@ data class StreamItem(
|
|||||||
var views: Long? = null,
|
var views: Long? = null,
|
||||||
var uploaderVerified: Boolean? = null,
|
var uploaderVerified: Boolean? = null,
|
||||||
var uploaded: Long? = null,
|
var uploaded: Long? = null,
|
||||||
var shortDescription: String? = null
|
var shortDescription: String? = null,
|
||||||
|
val isShort: Boolean = false
|
||||||
)
|
)
|
||||||
|
@ -49,8 +49,6 @@ object PreferenceKeys {
|
|||||||
const val CLEAR_CUSTOM_INSTANCES = "clearCustomInstances"
|
const val CLEAR_CUSTOM_INSTANCES = "clearCustomInstances"
|
||||||
const val LOGIN_REGISTER = "login_register"
|
const val LOGIN_REGISTER = "login_register"
|
||||||
const val DELETE_ACCOUNT = "delete_account"
|
const val DELETE_ACCOUNT = "delete_account"
|
||||||
const val IMPORT_SUBS = "import_from_yt"
|
|
||||||
const val EXPORT_SUBS = "export_subs"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Player
|
* Player
|
||||||
@ -85,6 +83,9 @@ object PreferenceKeys {
|
|||||||
const val QUEUE_AUTO_INSERT_RELATED = "queue_insert_related_videos"
|
const val QUEUE_AUTO_INSERT_RELATED = "queue_insert_related_videos"
|
||||||
const val PLAYER_SWIPE_CONTROLS = "player_swipe_controls"
|
const val PLAYER_SWIPE_CONTROLS = "player_swipe_controls"
|
||||||
const val PLAYER_SCREEN_BRIGHTNESS = "player_screen_brightness"
|
const val PLAYER_SCREEN_BRIGHTNESS = "player_screen_brightness"
|
||||||
|
const val PLAYER_PINCH_CONTROL = "player_pinch_control"
|
||||||
|
const val CAPTIONS_SIZE = "captions_size"
|
||||||
|
const val DOUBLE_TAP_TO_SEEK = "double_tap_seek"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Background mode
|
* Background mode
|
||||||
@ -98,12 +99,17 @@ object PreferenceKeys {
|
|||||||
const val NOTIFICATION_ENABLED = "notification_toggle"
|
const val NOTIFICATION_ENABLED = "notification_toggle"
|
||||||
const val CHECKING_FREQUENCY = "checking_frequency"
|
const val CHECKING_FREQUENCY = "checking_frequency"
|
||||||
const val REQUIRED_NETWORK = "required_network"
|
const val REQUIRED_NETWORK = "required_network"
|
||||||
const val LAST_STREAM_VIDEO_ID = "last_stream_video_id"
|
|
||||||
const val IGNORED_NOTIFICATION_CHANNELS = "ignored_notification_channels"
|
const val IGNORED_NOTIFICATION_CHANNELS = "ignored_notification_channels"
|
||||||
const val NOTIFICATION_TIME_ENABLED = "notification_time"
|
const val NOTIFICATION_TIME_ENABLED = "notification_time"
|
||||||
const val NOTIFICATION_START_TIME = "notification_start_time"
|
const val NOTIFICATION_START_TIME = "notification_start_time"
|
||||||
const val NOTIFICATION_END_TIME = "notification_end_time"
|
const val NOTIFICATION_END_TIME = "notification_end_time"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscriptions
|
||||||
|
*/
|
||||||
|
const val LAST_STREAM_VIDEO_ID = "last_stream_video_id"
|
||||||
|
const val LAST_WATCHED_FEED_TIME = "last_watched_feed_time"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advanced
|
* Advanced
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.github.libretube.obj
|
||||||
|
|
||||||
|
data class ImportPlaylist(
|
||||||
|
val name: String? = null,
|
||||||
|
val type: String? = null,
|
||||||
|
val visibility: String? = null,
|
||||||
|
val videos: List<String> = listOf()
|
||||||
|
)
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.github.libretube.obj
|
||||||
|
|
||||||
|
data class ImportPlaylistFile(
|
||||||
|
val format: String? = null,
|
||||||
|
val version: Int? = null,
|
||||||
|
val playlists: List<ImportPlaylist>? = null
|
||||||
|
)
|
@ -353,6 +353,14 @@ class BackgroundMode : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the service when app is removed from the task manager.
|
||||||
|
*/
|
||||||
|
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||||
|
super.onTaskRemoved(rootIntent)
|
||||||
|
onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* destroy the [BackgroundMode] foreground service
|
* destroy the [BackgroundMode] foreground service
|
||||||
*/
|
*/
|
||||||
|
@ -9,6 +9,7 @@ import android.os.Build
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
@ -16,6 +17,8 @@ import com.github.libretube.constants.IntentData
|
|||||||
import com.github.libretube.databinding.ActivityOfflinePlayerBinding
|
import com.github.libretube.databinding.ActivityOfflinePlayerBinding
|
||||||
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||||
import com.github.libretube.ui.base.BaseActivity
|
import com.github.libretube.ui.base.BaseActivity
|
||||||
|
import com.github.libretube.ui.extensions.setAspectRatio
|
||||||
|
import com.github.libretube.ui.models.PlayerViewModel
|
||||||
import com.github.libretube.util.DownloadHelper
|
import com.github.libretube.util.DownloadHelper
|
||||||
import com.github.libretube.util.PlayerHelper
|
import com.github.libretube.util.PlayerHelper
|
||||||
import com.google.android.exoplayer2.ExoPlayer
|
import com.google.android.exoplayer2.ExoPlayer
|
||||||
@ -32,6 +35,7 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
private lateinit var player: ExoPlayer
|
private lateinit var player: ExoPlayer
|
||||||
private lateinit var playerView: StyledPlayerView
|
private lateinit var playerView: StyledPlayerView
|
||||||
private lateinit var playerBinding: ExoStyledPlayerControlViewBinding
|
private lateinit var playerBinding: ExoStyledPlayerControlViewBinding
|
||||||
|
private val playerViewModel: PlayerViewModel by viewModels()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
hideSystemBars()
|
hideSystemBars()
|
||||||
@ -154,6 +158,16 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
|
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
playerViewModel.isFullscreen.value = true
|
||||||
|
super.onResume()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
playerViewModel.isFullscreen.value = false
|
||||||
|
super.onPause()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
player.release()
|
player.release()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
@ -169,6 +183,7 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
enterPictureInPictureMode(
|
enterPictureInPictureMode(
|
||||||
PictureInPictureParams.Builder()
|
PictureInPictureParams.Builder()
|
||||||
.setActions(emptyList())
|
.setActions(emptyList())
|
||||||
|
.setAspectRatio(player.videoSize.width, player.videoSize.height)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -71,10 +71,10 @@ class SearchAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
return when {
|
return when (searchItems[position].type) {
|
||||||
searchItems[position].url!!.startsWith("/watch", false) -> 0
|
"stream" -> 0
|
||||||
searchItems[position].url!!.startsWith("/channel", false) -> 1
|
"channel" -> 1
|
||||||
searchItems[position].url!!.startsWith("/playlist", false) -> 2
|
"playlist" -> 2
|
||||||
else -> 3
|
else -> 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView.LayoutManager
|
|||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.api.obj.StreamItem
|
import com.github.libretube.api.obj.StreamItem
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
|
import com.github.libretube.databinding.AllCaughtUpRowBinding
|
||||||
import com.github.libretube.databinding.TrendingRowBinding
|
import com.github.libretube.databinding.TrendingRowBinding
|
||||||
import com.github.libretube.databinding.VideoRowBinding
|
import com.github.libretube.databinding.VideoRowBinding
|
||||||
import com.github.libretube.extensions.formatShort
|
import com.github.libretube.extensions.formatShort
|
||||||
@ -44,6 +45,10 @@ class VideosAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return if (streamItems[position].type == "caught") CAUGHT_UP_TYPE else NORMAL_TYPE
|
||||||
|
}
|
||||||
|
|
||||||
fun updateItems() {
|
fun updateItems() {
|
||||||
val oldSize = index
|
val oldSize = index
|
||||||
index += 10
|
index += 10
|
||||||
@ -59,6 +64,7 @@ class VideosAdapter(
|
|||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideosViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideosViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
return when {
|
return when {
|
||||||
|
viewType == CAUGHT_UP_TYPE -> VideosViewHolder(AllCaughtUpRowBinding.inflate(layoutInflater, parent, false))
|
||||||
forceMode in listOf(ForceMode.TRENDING, ForceMode.RELATED, ForceMode.HOME) -> VideosViewHolder(TrendingRowBinding.inflate(layoutInflater, parent, false))
|
forceMode in listOf(ForceMode.TRENDING, ForceMode.RELATED, ForceMode.HOME) -> VideosViewHolder(TrendingRowBinding.inflate(layoutInflater, parent, false))
|
||||||
forceMode == ForceMode.CHANNEL -> VideosViewHolder(VideoRowBinding.inflate(layoutInflater, parent, false))
|
forceMode == ForceMode.CHANNEL -> VideosViewHolder(VideoRowBinding.inflate(layoutInflater, parent, false))
|
||||||
PreferenceHelper.getBoolean(
|
PreferenceHelper.getBoolean(
|
||||||
@ -74,7 +80,7 @@ class VideosAdapter(
|
|||||||
val video = streamItems[position]
|
val video = streamItems[position]
|
||||||
|
|
||||||
// hide the item if there was an extractor error
|
// hide the item if there was an extractor error
|
||||||
if (video.title == null) {
|
if (video.title == null && video.type != "caught") {
|
||||||
holder.itemView.visibility = View.GONE
|
holder.itemView.visibility = View.GONE
|
||||||
holder.itemView.layoutParams = RecyclerView.LayoutParams(0, 0)
|
holder.itemView.layoutParams = RecyclerView.LayoutParams(0, 0)
|
||||||
return
|
return
|
||||||
@ -191,5 +197,8 @@ class VideosAdapter(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val NORMAL_TYPE = 0
|
||||||
|
private const val CAUGHT_UP_TYPE = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,9 @@ class WatchHistoryAdapter(
|
|||||||
RecyclerView.Adapter<WatchHistoryViewHolder>() {
|
RecyclerView.Adapter<WatchHistoryViewHolder>() {
|
||||||
|
|
||||||
fun removeFromWatchHistory(position: Int) {
|
fun removeFromWatchHistory(position: Int) {
|
||||||
|
val history = watchHistory[position]
|
||||||
query {
|
query {
|
||||||
DatabaseHolder.Database.watchHistoryDao().delete(watchHistory[position])
|
DatabaseHolder.Database.watchHistoryDao().delete(history)
|
||||||
}
|
}
|
||||||
watchHistory.removeAt(position)
|
watchHistory.removeAt(position)
|
||||||
notifyItemRemoved(position)
|
notifyItemRemoved(position)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.github.libretube.ui.base
|
package com.github.libretube.ui.base
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.preference.EditTextPreference
|
import androidx.preference.EditTextPreference
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
@ -70,4 +71,10 @@ open class BasePreferenceFragment : PreferenceFragmentCompat() {
|
|||||||
else -> super.onDisplayPreferenceDialog(preference)
|
else -> super.onDisplayPreferenceDialog(preference)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Fragment?.runOnUiThread(action: () -> Unit) {
|
||||||
|
this ?: return
|
||||||
|
if (!isAdded) return // Fragment not attached to an Activity
|
||||||
|
activity?.runOnUiThread(action)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,11 +31,10 @@ class CreatePlaylistDialog(
|
|||||||
val listName = binding.playlistName.text.toString()
|
val listName = binding.playlistName.text.toString()
|
||||||
if (listName != "") {
|
if (listName != "") {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
PlaylistsHelper.createPlaylist(listName, requireContext().applicationContext) {
|
PlaylistsHelper.createPlaylist(listName, requireContext().applicationContext)
|
||||||
onSuccess.invoke()
|
onSuccess.invoke()
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(context, R.string.emptyPlaylistName, Toast.LENGTH_LONG).show()
|
Toast.makeText(context, R.string.emptyPlaylistName, Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,9 @@ import com.github.libretube.api.obj.Login
|
|||||||
import com.github.libretube.databinding.DialogLoginBinding
|
import com.github.libretube.databinding.DialogLoginBinding
|
||||||
import com.github.libretube.extensions.TAG
|
import com.github.libretube.extensions.TAG
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.util.PreferenceHelper
|
||||||
|
import com.github.libretube.util.TextUtils
|
||||||
import com.github.libretube.util.ThemeHelper
|
import com.github.libretube.util.ThemeHelper
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import retrofit2.HttpException
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
class LoginDialog : DialogFragment() {
|
class LoginDialog : DialogFragment() {
|
||||||
private lateinit var binding: DialogLoginBinding
|
private lateinit var binding: DialogLoginBinding
|
||||||
@ -34,7 +33,20 @@ class LoginDialog : DialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.register.setOnClickListener {
|
binding.register.setOnClickListener {
|
||||||
if (isInsertionValid()) {
|
if (isEmail(binding.username.text.toString())) {
|
||||||
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(R.string.privacy_alert)
|
||||||
|
.setMessage(R.string.username_email)
|
||||||
|
.setNegativeButton(R.string.proceed) { _, _ ->
|
||||||
|
signIn(
|
||||||
|
binding.username.text.toString(),
|
||||||
|
binding.password.text.toString(),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.setPositiveButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
} else if (isInsertionValid()) {
|
||||||
signIn(
|
signIn(
|
||||||
binding.username.text.toString(),
|
binding.username.text.toString(),
|
||||||
binding.password.text.toString(),
|
binding.password.text.toString(),
|
||||||
@ -65,15 +77,9 @@ class LoginDialog : DialogFragment() {
|
|||||||
} else {
|
} else {
|
||||||
RetrofitInstance.authApi.login(login)
|
RetrofitInstance.authApi.login(login)
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
|
||||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
|
||||||
return@launchWhenCreated
|
|
||||||
} catch (e: HttpException) {
|
|
||||||
Log.e(TAG(), "HttpException, unexpected response")
|
|
||||||
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
|
||||||
return@launchWhenCreated
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG(), e.toString())
|
||||||
|
Toast.makeText(context, e.localizedMessage, Toast.LENGTH_SHORT).show()
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,4 +102,8 @@ class LoginDialog : DialogFragment() {
|
|||||||
activity?.recreate()
|
activity?.recreate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isEmail(text: String): Boolean {
|
||||||
|
return TextUtils.EMAIL_REGEX.toRegex().matches(text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
package com.github.libretube.ui.dialogs
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.InputType
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.api.PlaylistsHelper
|
||||||
|
import com.github.libretube.databinding.DialogTextPreferenceBinding
|
||||||
|
import com.github.libretube.extensions.TAG
|
||||||
|
import com.github.libretube.extensions.toastFromMainThread
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class RenamePlaylistDialog(
|
||||||
|
private val playlistId: String,
|
||||||
|
private val currentPlaylistName: String
|
||||||
|
) : DialogFragment() {
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
val binding = DialogTextPreferenceBinding.inflate(layoutInflater)
|
||||||
|
binding.input.inputType = InputType.TYPE_CLASS_TEXT
|
||||||
|
binding.input.hint = getString(R.string.playlistName)
|
||||||
|
binding.input.setText(currentPlaylistName)
|
||||||
|
|
||||||
|
return MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(R.string.renamePlaylist)
|
||||||
|
.setView(binding.root)
|
||||||
|
.setPositiveButton(R.string.okay) { _, _ ->
|
||||||
|
val input = binding.input.text.toString()
|
||||||
|
if (input == "") {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
R.string.emptyPlaylistName,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
return@setPositiveButton
|
||||||
|
}
|
||||||
|
if (input == currentPlaylistName) return@setPositiveButton
|
||||||
|
val appContext = requireContext().applicationContext
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val success = try {
|
||||||
|
PlaylistsHelper.renamePlaylist(playlistId, binding.input.text.toString())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG(), e.toString())
|
||||||
|
e.localizedMessage?.let { appContext.toastFromMainThread(it) }
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
if (success) {
|
||||||
|
appContext.toastFromMainThread(R.string.success)
|
||||||
|
} else {
|
||||||
|
appContext.toastFromMainThread(R.string.server_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
@ -216,6 +216,12 @@ class ChannelFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load selected chip content if it's not videos tab.
|
||||||
|
possibleTabs.firstOrNull { binding.tabChips.checkedChipId == it.chipId }?.let {
|
||||||
|
val tab = tabs.first { tab -> tab.name == it.identifierName }
|
||||||
|
loadTab(tab)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadTab(tab: ChannelTab) {
|
private fun loadTab(tab: ChannelTab) {
|
||||||
|
@ -108,7 +108,7 @@ class HomeFragment : BaseFragment() {
|
|||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
makeVisible(binding.playlistsRV, binding.playlistsTV)
|
makeVisible(binding.playlistsRV, binding.playlistsTV)
|
||||||
binding.playlistsRV.layoutManager = LinearLayoutManager(context)
|
binding.playlistsRV.layoutManager = LinearLayoutManager(context)
|
||||||
binding.playlistsRV.adapter = PlaylistsAdapter(playlists.toMutableList(), PlaylistsHelper.getPrivateType())
|
binding.playlistsRV.adapter = PlaylistsAdapter(playlists.toMutableList(), PlaylistsHelper.getPrivatePlaylistType())
|
||||||
binding.playlistsRV.adapter?.registerAdapterDataObserver(object :
|
binding.playlistsRV.adapter?.registerAdapterDataObserver(object :
|
||||||
RecyclerView.AdapterDataObserver() {
|
RecyclerView.AdapterDataObserver() {
|
||||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||||
|
@ -123,7 +123,7 @@ class LibraryFragment : BaseFragment() {
|
|||||||
|
|
||||||
val playlistsAdapter = PlaylistsAdapter(
|
val playlistsAdapter = PlaylistsAdapter(
|
||||||
playlists.toMutableList(),
|
playlists.toMutableList(),
|
||||||
PlaylistsHelper.getPrivateType()
|
PlaylistsHelper.getPrivatePlaylistType()
|
||||||
)
|
)
|
||||||
|
|
||||||
// listen for playlists to become deleted
|
// listen for playlists to become deleted
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.github.libretube.ui.fragments
|
package com.github.libretube.ui.fragments
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.ActivityManager
|
|
||||||
import android.app.PictureInPictureParams
|
import android.app.PictureInPictureParams
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
@ -143,7 +142,10 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
*/
|
*/
|
||||||
private lateinit var exoPlayer: ExoPlayer
|
private lateinit var exoPlayer: ExoPlayer
|
||||||
private lateinit var trackSelector: DefaultTrackSelector
|
private lateinit var trackSelector: DefaultTrackSelector
|
||||||
private lateinit var segmentData: SegmentData
|
|
||||||
|
/**
|
||||||
|
* Chapters and comments
|
||||||
|
*/
|
||||||
private lateinit var chapters: List<ChapterSegment>
|
private lateinit var chapters: List<ChapterSegment>
|
||||||
private val comments: MutableList<Comment> = mutableListOf()
|
private val comments: MutableList<Comment> = mutableListOf()
|
||||||
private var commentsNextPage: String? = null
|
private var commentsNextPage: String? = null
|
||||||
@ -159,6 +161,12 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
*/
|
*/
|
||||||
private lateinit var nowPlayingNotification: NowPlayingNotification
|
private lateinit var nowPlayingNotification: NowPlayingNotification
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SponsorBlock
|
||||||
|
*/
|
||||||
|
private lateinit var segmentData: SegmentData
|
||||||
|
private var sponsorBlockEnabled = PlayerHelper.sponsorBlockEnabled
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
arguments?.let {
|
arguments?.let {
|
||||||
@ -295,6 +303,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
mainActivity.supportFragmentManager.beginTransaction()
|
mainActivity.supportFragmentManager.beginTransaction()
|
||||||
.remove(this)
|
.remove(this)
|
||||||
.commit()
|
.commit()
|
||||||
|
BackgroundHelper.stopBackgroundPlay(requireContext())
|
||||||
}
|
}
|
||||||
playerBinding.closeImageButton.setOnClickListener {
|
playerBinding.closeImageButton.setOnClickListener {
|
||||||
viewModel.isFullscreen.value = false
|
viewModel.isFullscreen.value = false
|
||||||
@ -303,12 +312,12 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
mainActivity.supportFragmentManager.beginTransaction()
|
mainActivity.supportFragmentManager.beginTransaction()
|
||||||
.remove(this)
|
.remove(this)
|
||||||
.commit()
|
.commit()
|
||||||
|
BackgroundHelper.stopBackgroundPlay(requireContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.playImageView.setOnClickListener {
|
val playPauseClickListner = View.OnClickListener {
|
||||||
if (!exoPlayer.isPlaying) {
|
if (!exoPlayer.isPlaying) {
|
||||||
// start or go on playing
|
// start or go on playing
|
||||||
binding.playImageView.setImageResource(R.drawable.ic_pause)
|
|
||||||
if (exoPlayer.playbackState == Player.STATE_ENDED) {
|
if (exoPlayer.playbackState == Player.STATE_ENDED) {
|
||||||
// restart video if finished
|
// restart video if finished
|
||||||
exoPlayer.seekTo(0)
|
exoPlayer.seekTo(0)
|
||||||
@ -316,10 +325,11 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
exoPlayer.play()
|
exoPlayer.play()
|
||||||
} else {
|
} else {
|
||||||
// pause the video
|
// pause the video
|
||||||
binding.playImageView.setImageResource(R.drawable.ic_play)
|
|
||||||
exoPlayer.pause()
|
exoPlayer.pause()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
playerBinding.playPauseBTN.setOnClickListener(playPauseClickListner)
|
||||||
|
binding.playImageView.setOnClickListener(playPauseClickListner)
|
||||||
|
|
||||||
// video description and chapters toggle
|
// video description and chapters toggle
|
||||||
binding.playerTitleLayout.setOnClickListener {
|
binding.playerTitleLayout.setOnClickListener {
|
||||||
@ -354,6 +364,17 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val updateSbImageResource = {
|
||||||
|
playerBinding.sbToggle.setImageResource(
|
||||||
|
if (sponsorBlockEnabled) R.drawable.ic_sb_enabled else R.drawable.ic_sb_disabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
updateSbImageResource()
|
||||||
|
playerBinding.sbToggle.setOnClickListener {
|
||||||
|
sponsorBlockEnabled = !sponsorBlockEnabled
|
||||||
|
updateSbImageResource()
|
||||||
|
}
|
||||||
|
|
||||||
// share button
|
// share button
|
||||||
binding.relPlayerShare.setOnClickListener {
|
binding.relPlayerShare.setOnClickListener {
|
||||||
if (!this::streams.isInitialized) return@setOnClickListener
|
if (!this::streams.isInitialized) return@setOnClickListener
|
||||||
@ -517,6 +538,8 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
|
|
||||||
Handler(Looper.getMainLooper()).postDelayed(this::checkForSegments, 100)
|
Handler(Looper.getMainLooper()).postDelayed(this::checkForSegments, 100)
|
||||||
|
|
||||||
|
if (!sponsorBlockEnabled) return
|
||||||
|
|
||||||
if (!::segmentData.isInitialized || segmentData.segments.isEmpty()) return
|
if (!::segmentData.isInitialized || segmentData.segments.isEmpty()) return
|
||||||
|
|
||||||
val currentPosition = exoPlayer.currentPosition
|
val currentPosition = exoPlayer.currentPosition
|
||||||
@ -535,12 +558,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (PlayerHelper.sponsorBlockNotifications) {
|
if (PlayerHelper.sponsorBlockNotifications) {
|
||||||
Toast
|
Toast.makeText(context, R.string.segment_skipped, Toast.LENGTH_SHORT).show()
|
||||||
.makeText(
|
|
||||||
context,
|
|
||||||
R.string.segment_skipped,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip the segment automatically
|
// skip the segment automatically
|
||||||
@ -553,7 +571,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun playVideo() {
|
private fun playVideo() {
|
||||||
|
// reset the player view
|
||||||
playerBinding.exoProgress.clearSegments()
|
playerBinding.exoProgress.clearSegments()
|
||||||
|
playerBinding.sbToggle.visibility = View.GONE
|
||||||
|
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
streams = try {
|
streams = try {
|
||||||
@ -641,6 +661,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
ObjectMapper().writeValueAsString(categories)
|
ObjectMapper().writeValueAsString(categories)
|
||||||
)
|
)
|
||||||
playerBinding.exoProgress.setSegments(segmentData.segments)
|
playerBinding.exoProgress.setSegments(segmentData.segments)
|
||||||
|
runOnUiThread {
|
||||||
|
playerBinding.sbToggle.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -729,7 +752,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
val captionStyle = PlayerHelper.getCaptionStyle(requireContext())
|
val captionStyle = PlayerHelper.getCaptionStyle(requireContext())
|
||||||
exoPlayerView.subtitleView?.apply {
|
exoPlayerView.subtitleView?.apply {
|
||||||
setApplyEmbeddedFontSizes(false)
|
setApplyEmbeddedFontSizes(false)
|
||||||
setFixedTextSize(TEXT_SIZE_TYPE_ABSOLUTE, 18F)
|
setFixedTextSize(TEXT_SIZE_TYPE_ABSOLUTE, PlayerHelper.captionsTextSize)
|
||||||
if (!PlayerHelper.useSystemCaptionStyle) return
|
if (!PlayerHelper.useSystemCaptionStyle) return
|
||||||
setApplyEmbeddedStyles(captionStyle == CaptionStyleCompat.DEFAULT)
|
setApplyEmbeddedStyles(captionStyle == CaptionStyleCompat.DEFAULT)
|
||||||
setStyle(captionStyle)
|
setStyle(captionStyle)
|
||||||
@ -793,6 +816,11 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
// Listener for play and pause icon change
|
// Listener for play and pause icon change
|
||||||
exoPlayer.addListener(object : Player.Listener {
|
exoPlayer.addListener(object : Player.Listener {
|
||||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
|
if (isPlaying) {
|
||||||
|
// Stop [BackgroundMode] service if it is running.
|
||||||
|
BackgroundHelper.stopBackgroundPlay(requireContext())
|
||||||
|
}
|
||||||
|
|
||||||
if (isPlaying && PlayerHelper.sponsorBlockEnabled) {
|
if (isPlaying && PlayerHelper.sponsorBlockEnabled) {
|
||||||
Handler(Looper.getMainLooper()).postDelayed(
|
Handler(Looper.getMainLooper()).postDelayed(
|
||||||
this@PlayerFragment::checkForSegments,
|
this@PlayerFragment::checkForSegments,
|
||||||
@ -801,6 +829,18 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onEvents(player: Player, events: Player.Events) {
|
||||||
|
super.onEvents(player, events)
|
||||||
|
if (events.containsAny(
|
||||||
|
Player.EVENT_PLAYBACK_STATE_CHANGED,
|
||||||
|
Player.EVENT_IS_PLAYING_CHANGED,
|
||||||
|
Player.EVENT_PLAY_WHEN_READY_CHANGED
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
updatePlayPauseButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||||
exoPlayerView.keepScreenOn = !(
|
exoPlayerView.keepScreenOn = !(
|
||||||
playbackState == Player.STATE_IDLE ||
|
playbackState == Player.STATE_IDLE ||
|
||||||
@ -819,23 +859,12 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
playNextVideo()
|
playNextVideo()
|
||||||
}
|
}
|
||||||
|
|
||||||
when (playbackState) {
|
if (playbackState == Player.STATE_READY) {
|
||||||
Player.STATE_READY -> {
|
|
||||||
// media actually playing
|
// media actually playing
|
||||||
transitioning = false
|
transitioning = false
|
||||||
binding.playImageView.setImageResource(R.drawable.ic_pause)
|
|
||||||
// update the PiP params to use the correct aspect ratio
|
// update the PiP params to use the correct aspect ratio
|
||||||
if (usePiP()) activity?.setPictureInPictureParams(getPipParams())
|
if (usePiP()) activity?.setPictureInPictureParams(getPipParams())
|
||||||
}
|
}
|
||||||
Player.STATE_ENDED -> {
|
|
||||||
// video has finished
|
|
||||||
binding.playImageView.setImageResource(R.drawable.ic_restart)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// player in any other state
|
|
||||||
binding.playImageView.setImageResource(R.drawable.ic_play)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// save the watch position when paused
|
// save the watch position when paused
|
||||||
if (playbackState == PlaybackState.STATE_PAUSED) {
|
if (playbackState == PlaybackState.STATE_PAUSED) {
|
||||||
@ -938,6 +967,22 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updatePlayPauseButton() {
|
||||||
|
if (exoPlayer.isPlaying) {
|
||||||
|
// video is playing
|
||||||
|
binding.playImageView.setImageResource(R.drawable.ic_pause)
|
||||||
|
playerBinding.playPauseBTN.setImageResource(R.drawable.ic_pause)
|
||||||
|
} else if (exoPlayer.playbackState == Player.STATE_ENDED) {
|
||||||
|
// video has finished
|
||||||
|
binding.playImageView.setImageResource(R.drawable.ic_restart)
|
||||||
|
playerBinding.playPauseBTN.setImageResource(R.drawable.ic_restart)
|
||||||
|
} else {
|
||||||
|
// player in any other state
|
||||||
|
binding.playImageView.setImageResource(R.drawable.ic_play)
|
||||||
|
playerBinding.playPauseBTN.setImageResource(R.drawable.ic_play)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initializeRelatedVideos(relatedStreams: List<StreamItem>?) {
|
private fun initializeRelatedVideos(relatedStreams: List<StreamItem>?) {
|
||||||
if (!PlayerHelper.relatedStreamsEnabled) return
|
if (!PlayerHelper.relatedStreamsEnabled) return
|
||||||
|
|
||||||
@ -1376,26 +1421,19 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val backgroundModeRunning = isServiceRunning(requireContext(), BackgroundMode::class.java)
|
val backgroundModeRunning = BackgroundHelper.isServiceRunning(requireContext(), BackgroundMode::class.java)
|
||||||
|
|
||||||
return exoPlayer.isPlaying && !backgroundModeRunning
|
return exoPlayer.isPlaying && !backgroundModeRunning
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isServiceRunning(context: Context, serviceClass: Class<*>): Boolean {
|
|
||||||
val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
for (service in manager.getRunningServices(Int.MAX_VALUE)) {
|
|
||||||
if (serviceClass.name == service.service.className) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
super.onConfigurationChanged(newConfig)
|
super.onConfigurationChanged(newConfig)
|
||||||
|
|
||||||
if (!PlayerHelper.autoRotationEnabled) return
|
if (!PlayerHelper.autoRotationEnabled) return
|
||||||
|
|
||||||
|
// If in PiP mode, orientation is given as landscape.
|
||||||
|
if (SDK_INT >= Build.VERSION_CODES.N && activity?.isInPictureInPictureMode == true) return
|
||||||
|
|
||||||
when (newConfig.orientation) {
|
when (newConfig.orientation) {
|
||||||
// go to fullscreen mode
|
// go to fullscreen mode
|
||||||
Configuration.ORIENTATION_LANDSCAPE -> setFullscreen()
|
Configuration.ORIENTATION_LANDSCAPE -> setFullscreen()
|
||||||
|
@ -99,7 +99,7 @@ class PlaylistFragment : BaseFragment() {
|
|||||||
binding.playlistScrollview.visibility = View.GONE
|
binding.playlistScrollview.visibility = View.GONE
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
PlaylistsHelper.getPlaylist(playlistType, playlistId!!)
|
PlaylistsHelper.getPlaylist(playlistId!!)
|
||||||
} 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")
|
||||||
|
@ -10,6 +10,7 @@ import androidx.fragment.app.activityViewModels
|
|||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.api.obj.StreamItem
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
import com.github.libretube.databinding.FragmentSubscriptionsBinding
|
import com.github.libretube.databinding.FragmentSubscriptionsBinding
|
||||||
import com.github.libretube.ui.adapters.LegacySubscriptionAdapter
|
import com.github.libretube.ui.adapters.LegacySubscriptionAdapter
|
||||||
@ -144,6 +145,15 @@ class SubscriptionsFragment : BaseFragment() {
|
|||||||
4 -> feed.sortedBy { it.uploaderName }
|
4 -> feed.sortedBy { it.uploaderName }
|
||||||
5 -> feed.sortedBy { it.uploaderName }.reversed()
|
5 -> feed.sortedBy { it.uploaderName }.reversed()
|
||||||
else -> feed
|
else -> feed
|
||||||
|
}.toMutableList()
|
||||||
|
|
||||||
|
// add an "all caught up item"
|
||||||
|
if (sortOrder == 0) {
|
||||||
|
val lastCheckedFeedTime = PreferenceHelper.getLastCheckedFeedTime()
|
||||||
|
val caughtUpIndex = feed.indexOfFirst { (it.uploaded ?: 0L) / 1000 < lastCheckedFeedTime }
|
||||||
|
if (caughtUpIndex > 0) {
|
||||||
|
sortedFeed.add(caughtUpIndex, StreamItem(type = "caught"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.subChannelsContainer.visibility = View.GONE
|
binding.subChannelsContainer.visibility = View.GONE
|
||||||
@ -158,6 +168,8 @@ class SubscriptionsFragment : BaseFragment() {
|
|||||||
showAllAtOnce = false
|
showAllAtOnce = false
|
||||||
)
|
)
|
||||||
binding.subFeed.adapter = subscriptionAdapter
|
binding.subFeed.adapter = subscriptionAdapter
|
||||||
|
|
||||||
|
PreferenceHelper.updateLastFeedWatchedTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showSubscriptions() {
|
private fun showSubscriptions() {
|
||||||
|
@ -89,7 +89,7 @@ class WatchHistoryFragment : BaseFragment() {
|
|||||||
// observe changes
|
// observe changes
|
||||||
watchHistoryAdapter.registerAdapterDataObserver(object :
|
watchHistoryAdapter.registerAdapterDataObserver(object :
|
||||||
RecyclerView.AdapterDataObserver() {
|
RecyclerView.AdapterDataObserver() {
|
||||||
override fun onChanged() {
|
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||||
if (watchHistoryAdapter.itemCount == 0) {
|
if (watchHistoryAdapter.itemCount == 0) {
|
||||||
binding.watchHistoryRecView.visibility = View.GONE
|
binding.watchHistoryRecView.visibility = View.GONE
|
||||||
binding.historyEmpty.visibility = View.VISIBLE
|
binding.historyEmpty.visibility = View.VISIBLE
|
||||||
|
@ -19,4 +19,6 @@ interface PlayerGestureOptions {
|
|||||||
fun onZoom()
|
fun onZoom()
|
||||||
|
|
||||||
fun onMinimize()
|
fun onMinimize()
|
||||||
|
|
||||||
|
fun onFullscreenChange(isFullscreen: Boolean)
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,18 @@
|
|||||||
package com.github.libretube.ui.preferences
|
package com.github.libretube.ui.preferences
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
|
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
import com.github.libretube.obj.BackupFile
|
|
||||||
import com.github.libretube.ui.activities.SettingsActivity
|
import com.github.libretube.ui.activities.SettingsActivity
|
||||||
import com.github.libretube.ui.base.BasePreferenceFragment
|
import com.github.libretube.ui.base.BasePreferenceFragment
|
||||||
import com.github.libretube.ui.dialogs.BackupDialog
|
|
||||||
import com.github.libretube.util.BackupHelper
|
|
||||||
import com.github.libretube.util.ImageHelper
|
import com.github.libretube.util.ImageHelper
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.util.PreferenceHelper
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.time.LocalDate
|
|
||||||
import java.time.LocalTime
|
|
||||||
|
|
||||||
class AdvancedSettings : BasePreferenceFragment() {
|
class AdvancedSettings : BasePreferenceFragment() {
|
||||||
|
|
||||||
// backup and restore database
|
|
||||||
private lateinit var getBackupFile: ActivityResultLauncher<String>
|
|
||||||
private lateinit var createBackupFile: ActivityResultLauncher<String>
|
|
||||||
private var backupFile = BackupFile()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
getBackupFile =
|
|
||||||
registerForActivityResult(
|
|
||||||
ActivityResultContracts.GetContent()
|
|
||||||
) { uri: Uri? ->
|
|
||||||
BackupHelper(requireContext()).restoreAdvancedBackup(uri)
|
|
||||||
}
|
|
||||||
|
|
||||||
createBackupFile = registerForActivityResult(
|
|
||||||
CreateDocument("application/json")
|
|
||||||
) { uri: Uri? ->
|
|
||||||
BackupHelper(requireContext()).advancedBackup(uri, backupFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.advanced_settings, rootKey)
|
setPreferencesFromResource(R.xml.advanced_settings, rootKey)
|
||||||
|
|
||||||
@ -61,22 +30,6 @@ class AdvancedSettings : BasePreferenceFragment() {
|
|||||||
showResetDialog()
|
showResetDialog()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
val advancesBackup = findPreference<Preference>("backup")
|
|
||||||
advancesBackup?.setOnPreferenceClickListener {
|
|
||||||
BackupDialog {
|
|
||||||
backupFile = it
|
|
||||||
createBackupFile.launch(getBackupFileName())
|
|
||||||
}
|
|
||||||
.show(childFragmentManager, null)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
val restoreAdvancedBackup = findPreference<Preference>("restore")
|
|
||||||
restoreAdvancedBackup?.setOnPreferenceClickListener {
|
|
||||||
getBackupFile.launch("application/json")
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showResetDialog() {
|
private fun showResetDialog() {
|
||||||
@ -95,9 +48,4 @@ class AdvancedSettings : BasePreferenceFragment() {
|
|||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getBackupFileName(): String {
|
|
||||||
val time = LocalTime.now().toString().split(".").firstOrNull()
|
|
||||||
return "libretube-backup-${LocalDate.now()}-$time.json"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,124 @@
|
|||||||
|
package com.github.libretube.ui.preferences
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.obj.BackupFile
|
||||||
|
import com.github.libretube.ui.base.BasePreferenceFragment
|
||||||
|
import com.github.libretube.ui.dialogs.BackupDialog
|
||||||
|
import com.github.libretube.util.BackupHelper
|
||||||
|
import com.github.libretube.util.ImportHelper
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalTime
|
||||||
|
|
||||||
|
class BackupRestoreSettings : BasePreferenceFragment() {
|
||||||
|
|
||||||
|
// backup and restore database
|
||||||
|
private lateinit var getBackupFile: ActivityResultLauncher<String>
|
||||||
|
private lateinit var createBackupFile: ActivityResultLauncher<String>
|
||||||
|
private var backupFile = BackupFile()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* result listeners for importing and exporting subscriptions
|
||||||
|
*/
|
||||||
|
private lateinit var getSubscriptionsFile: ActivityResultLauncher<String>
|
||||||
|
private lateinit var createSubscriptionsFile: ActivityResultLauncher<String>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* result listeners for importing and exporting playlists
|
||||||
|
*/
|
||||||
|
private lateinit var getPlaylistsFile: ActivityResultLauncher<String>
|
||||||
|
private lateinit var createPlaylistsFile: ActivityResultLauncher<String>
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
getSubscriptionsFile =
|
||||||
|
registerForActivityResult(
|
||||||
|
ActivityResultContracts.GetContent()
|
||||||
|
) { uri ->
|
||||||
|
ImportHelper(requireActivity()).importSubscriptions(uri)
|
||||||
|
}
|
||||||
|
createSubscriptionsFile = registerForActivityResult(
|
||||||
|
CreateDocument("application/json")
|
||||||
|
) { uri ->
|
||||||
|
ImportHelper(requireActivity()).exportSubscriptions(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlaylistsFile = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||||
|
ImportHelper(requireActivity()).importPlaylists(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
createPlaylistsFile = registerForActivityResult(
|
||||||
|
CreateDocument("application/json")
|
||||||
|
) { uri ->
|
||||||
|
ImportHelper(requireActivity()).exportPlaylists(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
getBackupFile =
|
||||||
|
registerForActivityResult(
|
||||||
|
ActivityResultContracts.GetContent()
|
||||||
|
) { uri: Uri? ->
|
||||||
|
BackupHelper(requireContext()).restoreAdvancedBackup(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
createBackupFile = registerForActivityResult(
|
||||||
|
CreateDocument("application/json")
|
||||||
|
) { uri: Uri? ->
|
||||||
|
BackupHelper(requireContext()).advancedBackup(uri, backupFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.import_export_settings, rootKey)
|
||||||
|
|
||||||
|
val importSubscriptions = findPreference<Preference>("import_subscriptions")
|
||||||
|
importSubscriptions?.setOnPreferenceClickListener {
|
||||||
|
getSubscriptionsFile.launch("*/*")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
val exportSubscriptions = findPreference<Preference>("export_subscriptions")
|
||||||
|
exportSubscriptions?.setOnPreferenceClickListener {
|
||||||
|
createSubscriptionsFile.launch("subscriptions.json")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
val importPlaylists = findPreference<Preference>("import_playlists")
|
||||||
|
importPlaylists?.setOnPreferenceClickListener {
|
||||||
|
getPlaylistsFile.launch("*/*")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
val exportPlaylists = findPreference<Preference>("export_playlists")
|
||||||
|
exportPlaylists?.setOnPreferenceClickListener {
|
||||||
|
createPlaylistsFile.launch("playlists.json")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
val advancesBackup = findPreference<Preference>("backup")
|
||||||
|
advancesBackup?.setOnPreferenceClickListener {
|
||||||
|
BackupDialog {
|
||||||
|
backupFile = it
|
||||||
|
createBackupFile.launch(getBackupFileName())
|
||||||
|
}
|
||||||
|
.show(childFragmentManager, null)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
val restoreAdvancedBackup = findPreference<Preference>("restore")
|
||||||
|
restoreAdvancedBackup?.setOnPreferenceClickListener {
|
||||||
|
getBackupFile.launch("application/json")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBackupFileName(): String {
|
||||||
|
val time = LocalTime.now().toString().split(".").firstOrNull()
|
||||||
|
return "libretube-backup-${LocalDate.now()}-$time.json"
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,7 @@
|
|||||||
package com.github.libretube.ui.preferences
|
package com.github.libretube.ui.preferences
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
@ -22,33 +17,10 @@ import com.github.libretube.ui.dialogs.CustomInstanceDialog
|
|||||||
import com.github.libretube.ui.dialogs.DeleteAccountDialog
|
import com.github.libretube.ui.dialogs.DeleteAccountDialog
|
||||||
import com.github.libretube.ui.dialogs.LoginDialog
|
import com.github.libretube.ui.dialogs.LoginDialog
|
||||||
import com.github.libretube.ui.dialogs.LogoutDialog
|
import com.github.libretube.ui.dialogs.LogoutDialog
|
||||||
import com.github.libretube.util.ImportHelper
|
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.util.PreferenceHelper
|
||||||
|
|
||||||
class InstanceSettings : BasePreferenceFragment() {
|
class InstanceSettings : BasePreferenceFragment() {
|
||||||
|
|
||||||
/**
|
|
||||||
* result listeners for importing and exporting subscriptions
|
|
||||||
*/
|
|
||||||
private lateinit var getContent: ActivityResultLauncher<String>
|
|
||||||
private lateinit var createFile: ActivityResultLauncher<String>
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
getContent =
|
|
||||||
registerForActivityResult(
|
|
||||||
ActivityResultContracts.GetContent()
|
|
||||||
) { uri: Uri? ->
|
|
||||||
ImportHelper(requireActivity()).importSubscriptions(uri)
|
|
||||||
}
|
|
||||||
createFile = registerForActivityResult(
|
|
||||||
CreateDocument("application/json")
|
|
||||||
) { uri: Uri? ->
|
|
||||||
ImportHelper(requireActivity()).exportSubscriptions(uri)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.instance_settings, rootKey)
|
setPreferencesFromResource(R.xml.instance_settings, rootKey)
|
||||||
|
|
||||||
@ -138,19 +110,6 @@ class InstanceSettings : BasePreferenceFragment() {
|
|||||||
newFragment.show(childFragmentManager, DeleteAccountDialog::class.java.name)
|
newFragment.show(childFragmentManager, DeleteAccountDialog::class.java.name)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
val importSubscriptions = findPreference<Preference>(PreferenceKeys.IMPORT_SUBS)
|
|
||||||
importSubscriptions?.setOnPreferenceClickListener {
|
|
||||||
// check StorageAccess
|
|
||||||
getContent.launch("*/*")
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
val exportSubscriptions = findPreference<Preference>(PreferenceKeys.EXPORT_SUBS)
|
|
||||||
exportSubscriptions?.setOnPreferenceClickListener {
|
|
||||||
createFile.launch("subscriptions.json")
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initCustomInstances(instancePref: ListPreference) {
|
private fun initCustomInstances(instancePref: ListPreference) {
|
||||||
@ -201,10 +160,4 @@ class InstanceSettings : BasePreferenceFragment() {
|
|||||||
PreferenceHelper.setToken("")
|
PreferenceHelper.setToken("")
|
||||||
Toast.makeText(context, getString(R.string.loggedout), Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, getString(R.string.loggedout), Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Fragment?.runOnUiThread(action: () -> Unit) {
|
|
||||||
this ?: return
|
|
||||||
if (!isAdded) return // Fragment not attached to an Activity
|
|
||||||
activity?.runOnUiThread(action)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -23,65 +23,52 @@ class MainSettings : BasePreferenceFragment() {
|
|||||||
|
|
||||||
val general = findPreference<Preference>("general")
|
val general = findPreference<Preference>("general")
|
||||||
general?.setOnPreferenceClickListener {
|
general?.setOnPreferenceClickListener {
|
||||||
val newFragment = GeneralSettings()
|
navigateToSettingsFragment(GeneralSettings())
|
||||||
navigateToSettingsFragment(newFragment)
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val instance = findPreference<Preference>("instance")
|
val instance = findPreference<Preference>("instance")
|
||||||
instance?.setOnPreferenceClickListener {
|
instance?.setOnPreferenceClickListener {
|
||||||
val newFragment = InstanceSettings()
|
navigateToSettingsFragment(InstanceSettings())
|
||||||
navigateToSettingsFragment(newFragment)
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val appearance = findPreference<Preference>("appearance")
|
val appearance = findPreference<Preference>("appearance")
|
||||||
appearance?.setOnPreferenceClickListener {
|
appearance?.setOnPreferenceClickListener {
|
||||||
val newFragment = AppearanceSettings()
|
navigateToSettingsFragment(AppearanceSettings())
|
||||||
navigateToSettingsFragment(newFragment)
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val sponsorBlock = findPreference<Preference>("sponsorblock")
|
val sponsorBlock = findPreference<Preference>("sponsorblock")
|
||||||
sponsorBlock?.setOnPreferenceClickListener {
|
sponsorBlock?.setOnPreferenceClickListener {
|
||||||
val newFragment = SponsorBlockSettings()
|
navigateToSettingsFragment(SponsorBlockSettings())
|
||||||
navigateToSettingsFragment(newFragment)
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val player = findPreference<Preference>("player")
|
val player = findPreference<Preference>("player")
|
||||||
player?.setOnPreferenceClickListener {
|
player?.setOnPreferenceClickListener {
|
||||||
val newFragment = PlayerSettings()
|
navigateToSettingsFragment(PlayerSettings())
|
||||||
navigateToSettingsFragment(newFragment)
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val audioVideo = findPreference<Preference>("audio_video")
|
val audioVideo = findPreference<Preference>("audio_video")
|
||||||
audioVideo?.setOnPreferenceClickListener {
|
audioVideo?.setOnPreferenceClickListener {
|
||||||
val newFragment = AudioVideoSettings()
|
navigateToSettingsFragment(AudioVideoSettings())
|
||||||
navigateToSettingsFragment(newFragment)
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val history = findPreference<Preference>("history")
|
val history = findPreference<Preference>("history")
|
||||||
history?.setOnPreferenceClickListener {
|
history?.setOnPreferenceClickListener {
|
||||||
val newFragment = HistorySettings()
|
navigateToSettingsFragment(HistorySettings())
|
||||||
navigateToSettingsFragment(newFragment)
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val notifications = findPreference<Preference>("notifications")
|
val notifications = findPreference<Preference>("notifications")
|
||||||
notifications?.setOnPreferenceClickListener {
|
notifications?.setOnPreferenceClickListener {
|
||||||
val newFragment = NotificationSettings()
|
navigateToSettingsFragment(NotificationSettings())
|
||||||
navigateToSettingsFragment(newFragment)
|
}
|
||||||
true
|
|
||||||
|
val backupRestore = findPreference<Preference>("backup_restore")
|
||||||
|
backupRestore?.setOnPreferenceClickListener {
|
||||||
|
navigateToSettingsFragment(BackupRestoreSettings())
|
||||||
}
|
}
|
||||||
|
|
||||||
val advanced = findPreference<Preference>("advanced")
|
val advanced = findPreference<Preference>("advanced")
|
||||||
advanced?.setOnPreferenceClickListener {
|
advanced?.setOnPreferenceClickListener {
|
||||||
val newFragment = AdvancedSettings()
|
navigateToSettingsFragment(AdvancedSettings())
|
||||||
navigateToSettingsFragment(newFragment)
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val update = findPreference<Preference>("update")
|
val update = findPreference<Preference>("update")
|
||||||
@ -131,9 +118,10 @@ class MainSettings : BasePreferenceFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToSettingsFragment(newFragment: Fragment) {
|
private fun navigateToSettingsFragment(newFragment: Fragment): Boolean {
|
||||||
parentFragmentManager.beginTransaction()
|
parentFragmentManager.beginTransaction()
|
||||||
.replace(R.id.settings, newFragment)
|
.replace(R.id.settings, newFragment)
|
||||||
.commitNow()
|
.commitNow()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import kotlinx.coroutines.withContext
|
|||||||
|
|
||||||
class CommentsSheet(
|
class CommentsSheet(
|
||||||
private val videoId: String,
|
private val videoId: String,
|
||||||
private val comments: MutableList<Comment>,
|
private val comments: List<Comment>,
|
||||||
private var nextPage: String?,
|
private var nextPage: String?,
|
||||||
private val onMoreComments: (comments: List<Comment>, nextPage: String?) -> Unit
|
private val onMoreComments: (comments: List<Comment>, nextPage: String?) -> Unit
|
||||||
) : ExpandedBottomSheet() {
|
) : ExpandedBottomSheet() {
|
||||||
@ -45,7 +45,7 @@ class CommentsSheet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commentsAdapter = CommentsAdapter(videoId, comments) {
|
commentsAdapter = CommentsAdapter(videoId, comments.toMutableList()) {
|
||||||
dialog?.dismiss()
|
dialog?.dismiss()
|
||||||
}
|
}
|
||||||
binding.commentsRV.adapter = commentsAdapter
|
binding.commentsRV.adapter = commentsAdapter
|
||||||
@ -60,7 +60,6 @@ class CommentsSheet(
|
|||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.getComments(videoId)
|
RetrofitInstance.api.getComments(videoId)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG(), e.toString())
|
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
}
|
}
|
||||||
binding.progress.visibility = View.GONE
|
binding.progress.visibility = View.GONE
|
||||||
|
@ -1,23 +1,20 @@
|
|||||||
package com.github.libretube.ui.sheets
|
package com.github.libretube.ui.sheets
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.InputType
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.api.PlaylistsHelper
|
|
||||||
import com.github.libretube.api.RetrofitInstance
|
import com.github.libretube.api.RetrofitInstance
|
||||||
import com.github.libretube.api.obj.PlaylistId
|
import com.github.libretube.api.obj.PlaylistId
|
||||||
import com.github.libretube.databinding.DialogTextPreferenceBinding
|
|
||||||
import com.github.libretube.enums.PlaylistType
|
import com.github.libretube.enums.PlaylistType
|
||||||
import com.github.libretube.enums.ShareObjectType
|
import com.github.libretube.enums.ShareObjectType
|
||||||
import com.github.libretube.extensions.toID
|
import com.github.libretube.extensions.toID
|
||||||
import com.github.libretube.extensions.toastFromMainThread
|
import com.github.libretube.extensions.toastFromMainThread
|
||||||
import com.github.libretube.obj.ShareData
|
import com.github.libretube.obj.ShareData
|
||||||
import com.github.libretube.ui.dialogs.DeletePlaylistDialog
|
import com.github.libretube.ui.dialogs.DeletePlaylistDialog
|
||||||
|
import com.github.libretube.ui.dialogs.RenamePlaylistDialog
|
||||||
import com.github.libretube.ui.dialogs.ShareDialog
|
import com.github.libretube.ui.dialogs.ShareDialog
|
||||||
import com.github.libretube.util.BackgroundHelper
|
import com.github.libretube.util.BackgroundHelper
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.util.PreferenceHelper
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -27,7 +24,7 @@ import java.io.IOException
|
|||||||
|
|
||||||
class PlaylistOptionsBottomSheet(
|
class PlaylistOptionsBottomSheet(
|
||||||
private val playlistId: String,
|
private val playlistId: String,
|
||||||
playlistName: String,
|
private val playlistName: String,
|
||||||
private val playlistType: PlaylistType
|
private val playlistType: PlaylistType
|
||||||
) : BaseBottomSheet() {
|
) : BaseBottomSheet() {
|
||||||
private val shareData = ShareData(currentPlaylist = playlistName)
|
private val shareData = ShareData(currentPlaylist = playlistName)
|
||||||
@ -87,32 +84,8 @@ class PlaylistOptionsBottomSheet(
|
|||||||
.show(parentFragmentManager, null)
|
.show(parentFragmentManager, null)
|
||||||
}
|
}
|
||||||
context?.getString(R.string.renamePlaylist) -> {
|
context?.getString(R.string.renamePlaylist) -> {
|
||||||
val binding = DialogTextPreferenceBinding.inflate(layoutInflater)
|
RenamePlaylistDialog(playlistId, playlistName)
|
||||||
binding.input.hint = context?.getString(R.string.playlistName)
|
.show(parentFragmentManager, null)
|
||||||
binding.input.inputType = InputType.TYPE_CLASS_TEXT
|
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
|
||||||
.setTitle(R.string.renamePlaylist)
|
|
||||||
.setView(binding.root)
|
|
||||||
.setPositiveButton(R.string.okay) { _, _ ->
|
|
||||||
if (binding.input.text.toString() == "") {
|
|
||||||
Toast.makeText(
|
|
||||||
context,
|
|
||||||
R.string.emptyPlaylistName,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
return@setPositiveButton
|
|
||||||
}
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
try {
|
|
||||||
PlaylistsHelper.renamePlaylist(playlistId, binding.input.text.toString())
|
|
||||||
} catch (e: Exception) {
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.setNegativeButton(R.string.cancel, null)
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package com.github.libretube.ui.viewholders
|
package com.github.libretube.ui.viewholders
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.github.libretube.databinding.AllCaughtUpRowBinding
|
||||||
import com.github.libretube.databinding.TrendingRowBinding
|
import com.github.libretube.databinding.TrendingRowBinding
|
||||||
import com.github.libretube.databinding.VideoRowBinding
|
import com.github.libretube.databinding.VideoRowBinding
|
||||||
|
|
||||||
class VideosViewHolder : RecyclerView.ViewHolder {
|
class VideosViewHolder : RecyclerView.ViewHolder {
|
||||||
var trendingRowBinding: TrendingRowBinding? = null
|
var trendingRowBinding: TrendingRowBinding? = null
|
||||||
var videoRowBinding: VideoRowBinding? = null
|
var videoRowBinding: VideoRowBinding? = null
|
||||||
|
var allCaughtUpBinding: AllCaughtUpRowBinding? = null
|
||||||
|
|
||||||
constructor(binding: TrendingRowBinding) : super(binding.root) {
|
constructor(binding: TrendingRowBinding) : super(binding.root) {
|
||||||
trendingRowBinding = binding
|
trendingRowBinding = binding
|
||||||
@ -15,4 +17,8 @@ class VideosViewHolder : RecyclerView.ViewHolder {
|
|||||||
constructor(binding: VideoRowBinding) : super(binding.root) {
|
constructor(binding: VideoRowBinding) : super(binding.root) {
|
||||||
videoRowBinding = binding
|
videoRowBinding = binding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor(binding: AllCaughtUpRowBinding) : super(binding.root) {
|
||||||
|
allCaughtUpBinding = binding
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,14 +85,15 @@ internal class CustomExoPlayerView(
|
|||||||
this.doubleTapOverlayBinding = doubleTapOverlayBinding
|
this.doubleTapOverlayBinding = doubleTapOverlayBinding
|
||||||
this.trackSelector = trackSelector
|
this.trackSelector = trackSelector
|
||||||
this.gestureViewBinding = playerGestureControlsViewBinding
|
this.gestureViewBinding = playerGestureControlsViewBinding
|
||||||
this.playerGestureController = PlayerGestureController(context, this)
|
this.playerGestureController = PlayerGestureController(context as BaseActivity, this)
|
||||||
this.brightnessHelper = BrightnessHelper(context as Activity)
|
this.brightnessHelper = BrightnessHelper(context as Activity)
|
||||||
this.audioHelper = AudioHelper(context)
|
this.audioHelper = AudioHelper(context)
|
||||||
|
|
||||||
// Set touch listner for tap and swipe gestures.
|
// Set touch listener for tap and swipe gestures.
|
||||||
setOnTouchListener(playerGestureController)
|
setOnTouchListener(playerGestureController)
|
||||||
initializeGestureProgress()
|
initializeGestureProgress()
|
||||||
enableDoubleTapToSeek()
|
|
||||||
|
initRewindAndForward()
|
||||||
|
|
||||||
initializeAdvancedOptions(context)
|
initializeAdvancedOptions(context)
|
||||||
|
|
||||||
@ -138,6 +139,29 @@ internal class CustomExoPlayerView(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun initRewindAndForward() {
|
||||||
|
val seekIncrementText = (PlayerHelper.seekIncrement / 1000).toString()
|
||||||
|
listOf(
|
||||||
|
doubleTapOverlayBinding?.rewindTV,
|
||||||
|
doubleTapOverlayBinding?.forwardTV,
|
||||||
|
binding.forwardTV,
|
||||||
|
binding.rewindTV
|
||||||
|
).forEach {
|
||||||
|
it?.text = seekIncrementText
|
||||||
|
}
|
||||||
|
binding.forwardBTN.setOnClickListener {
|
||||||
|
player?.seekTo(player!!.currentPosition + PlayerHelper.seekIncrement)
|
||||||
|
}
|
||||||
|
binding.rewindBTN.setOnClickListener {
|
||||||
|
player?.seekTo(player!!.currentPosition - PlayerHelper.seekIncrement)
|
||||||
|
}
|
||||||
|
if (PlayerHelper.doubleTapToSeek) return
|
||||||
|
|
||||||
|
listOf(binding.forwardBTN, binding.rewindBTN).forEach {
|
||||||
|
it.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initializeAdvancedOptions(context: Context) {
|
private fun initializeAdvancedOptions(context: Context) {
|
||||||
binding.toggleOptions.setOnClickListener {
|
binding.toggleOptions.setOnClickListener {
|
||||||
val items = mutableListOf(
|
val items = mutableListOf(
|
||||||
@ -248,19 +272,12 @@ internal class CustomExoPlayerView(
|
|||||||
binding.exoBottomBar.visibility = visibility
|
binding.exoBottomBar.visibility = visibility
|
||||||
binding.closeImageButton.visibility = visibility
|
binding.closeImageButton.visibility = visibility
|
||||||
binding.exoTitle.visibility = visibility
|
binding.exoTitle.visibility = visibility
|
||||||
binding.exoPlayPause.visibility = visibility
|
binding.playPauseBTN.visibility = visibility
|
||||||
|
|
||||||
// disable tap and swipe gesture if the player is locked
|
// disable tap and swipe gesture if the player is locked
|
||||||
playerGestureController.isEnabled = isLocked
|
playerGestureController.isEnabled = isLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enableDoubleTapToSeek() {
|
|
||||||
// set seek increment text
|
|
||||||
val seekIncrementText = (PlayerHelper.seekIncrement / 1000).toString()
|
|
||||||
doubleTapOverlayBinding?.rewindTV?.text = seekIncrementText
|
|
||||||
doubleTapOverlayBinding?.forwardTV?.text = seekIncrementText
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun rewind() {
|
private fun rewind() {
|
||||||
player?.seekTo((player?.currentPosition ?: 0L) - PlayerHelper.seekIncrement)
|
player?.seekTo((player?.currentPosition ?: 0L) - PlayerHelper.seekIncrement)
|
||||||
|
|
||||||
@ -322,7 +339,8 @@ internal class CustomExoPlayerView(
|
|||||||
|
|
||||||
private fun initializeGestureProgress() {
|
private fun initializeGestureProgress() {
|
||||||
gestureViewBinding.brightnessProgressBar.let { bar ->
|
gestureViewBinding.brightnessProgressBar.let { bar ->
|
||||||
bar.progress = brightnessHelper.getBrightnessWithScale(bar.max.toFloat(), saved = true).toInt()
|
bar.progress =
|
||||||
|
brightnessHelper.getBrightnessWithScale(bar.max.toFloat(), saved = true).toInt()
|
||||||
}
|
}
|
||||||
gestureViewBinding.volumeProgressBar.let { bar ->
|
gestureViewBinding.volumeProgressBar.let { bar ->
|
||||||
bar.progress = audioHelper.getVolumeWithScale(bar.max)
|
bar.progress = audioHelper.getVolumeWithScale(bar.max)
|
||||||
@ -443,13 +461,6 @@ internal class CustomExoPlayerView(
|
|||||||
params.bottomMargin = offset.toInt()
|
params.bottomMargin = offset.toInt()
|
||||||
it.layoutParams = params
|
it.layoutParams = params
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PlayerHelper.swipeGestureEnabled && this::brightnessHelper.isInitialized) {
|
|
||||||
when (newConfig?.orientation) {
|
|
||||||
Configuration.ORIENTATION_LANDSCAPE -> brightnessHelper.restoreSavedBrightness()
|
|
||||||
else -> brightnessHelper.resetToSystemBrightness(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSingleTap() {
|
override fun onSingleTap() {
|
||||||
@ -469,22 +480,24 @@ internal class CustomExoPlayerView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDoubleTapLeftScreen() {
|
override fun onDoubleTapLeftScreen() {
|
||||||
|
if (!PlayerHelper.doubleTapToSeek) return
|
||||||
rewind()
|
rewind()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDoubleTapRightScreen() {
|
override fun onDoubleTapRightScreen() {
|
||||||
|
if (!PlayerHelper.doubleTapToSeek) return
|
||||||
forward()
|
forward()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSwipeLeftScreen(distanceY: Float) {
|
override fun onSwipeLeftScreen(distanceY: Float) {
|
||||||
if (!PlayerHelper.swipeGestureEnabled || resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) return
|
if (!PlayerHelper.swipeGestureEnabled) return
|
||||||
|
|
||||||
if (isControllerFullyVisible) hideController()
|
if (isControllerFullyVisible) hideController()
|
||||||
updateBrightness(distanceY)
|
updateBrightness(distanceY)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSwipeRightScreen(distanceY: Float) {
|
override fun onSwipeRightScreen(distanceY: Float) {
|
||||||
if (!PlayerHelper.swipeGestureEnabled || resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) return
|
if (!PlayerHelper.swipeGestureEnabled) return
|
||||||
|
|
||||||
if (isControllerFullyVisible) hideController()
|
if (isControllerFullyVisible) hideController()
|
||||||
updateVolume(distanceY)
|
updateVolume(distanceY)
|
||||||
@ -496,10 +509,22 @@ internal class CustomExoPlayerView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onZoom() {
|
override fun onZoom() {
|
||||||
|
if (!PlayerHelper.pinchGestureEnabled) return
|
||||||
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMinimize() {
|
override fun onMinimize() {
|
||||||
|
if (!PlayerHelper.pinchGestureEnabled) return
|
||||||
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
|
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onFullscreenChange(isFullscreen: Boolean) {
|
||||||
|
if (PlayerHelper.swipeGestureEnabled && this::brightnessHelper.isInitialized) {
|
||||||
|
if (isFullscreen) {
|
||||||
|
brightnessHelper.restoreSavedBrightness()
|
||||||
|
} else {
|
||||||
|
brightnessHelper.resetToSystemBrightness(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import android.util.AttributeSet
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import com.github.libretube.databinding.PlayerGestureControlsViewBinding
|
import com.github.libretube.databinding.PlayerGestureControlsViewBinding
|
||||||
|
import com.github.libretube.extensions.normalize
|
||||||
|
|
||||||
class PlayerGestureControlsView(
|
class PlayerGestureControlsView(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -20,7 +21,21 @@ class PlayerGestureControlsView(
|
|||||||
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
|
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
|
||||||
super.onSizeChanged(width, height, oldHeight, oldHeight)
|
super.onSizeChanged(width, height, oldHeight, oldHeight)
|
||||||
|
|
||||||
binding.brightnessProgressBar.max = (height * 0.7).toInt()
|
// Set new max value of progress bar corresponding to the new height and
|
||||||
binding.volumeProgressBar.max = (height * 0.7).toInt()
|
// make progress accordingly, store oldProgress before changing it to avoid
|
||||||
|
// inconsistency when old progress > new max
|
||||||
|
binding.brightnessProgressBar.apply {
|
||||||
|
val oldMax = max
|
||||||
|
val oldProgress = progress
|
||||||
|
max = (height * 0.7).toInt()
|
||||||
|
progress = oldProgress.normalize(0, oldMax, 0, max)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.volumeProgressBar.apply {
|
||||||
|
val oldMax = max
|
||||||
|
val oldProgress = progress
|
||||||
|
max = (height * 0.7).toInt()
|
||||||
|
progress = oldProgress.normalize(0, oldMax, 0, max)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.github.libretube.util
|
package com.github.libretube.util
|
||||||
|
|
||||||
|
import android.app.ActivityManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@ -10,6 +11,11 @@ import com.github.libretube.services.BackgroundMode
|
|||||||
* Helper for starting a new Instance of the [BackgroundMode]
|
* Helper for starting a new Instance of the [BackgroundMode]
|
||||||
*/
|
*/
|
||||||
object BackgroundHelper {
|
object BackgroundHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the foreground service [BackgroundMode] to play in background. [position]
|
||||||
|
* is seek to position specified in milliseconds in the current [videoId].
|
||||||
|
*/
|
||||||
fun playOnBackground(
|
fun playOnBackground(
|
||||||
context: Context,
|
context: Context,
|
||||||
videoId: String,
|
videoId: String,
|
||||||
@ -29,4 +35,29 @@ object BackgroundHelper {
|
|||||||
context.startService(intent)
|
context.startService(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the [BackgroundMode] service if it is running.
|
||||||
|
*/
|
||||||
|
fun stopBackgroundPlay(context: Context) {
|
||||||
|
if (!isServiceRunning(context, BackgroundMode::class.java)) return
|
||||||
|
|
||||||
|
// Intent to stop background mode service
|
||||||
|
val intent = Intent(context, BackgroundMode::class.java)
|
||||||
|
context.stopService(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given service as [serviceClass] is currently running.
|
||||||
|
*/
|
||||||
|
fun isServiceRunning(context: Context, serviceClass: Class<*>): Boolean {
|
||||||
|
val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
for (service in manager.getRunningServices(Int.MAX_VALUE)) {
|
||||||
|
if (serviceClass.name == service.service.className) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.github.libretube.util
|
package com.github.libretube.util
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.os.Build
|
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
import com.github.libretube.extensions.normalize
|
import com.github.libretube.extensions.normalize
|
||||||
@ -46,9 +45,6 @@ class BrightnessHelper(private val activity: Activity) {
|
|||||||
* Set current screen brightness to saved brightness value.
|
* Set current screen brightness to saved brightness value.
|
||||||
*/
|
*/
|
||||||
fun restoreSavedBrightness() {
|
fun restoreSavedBrightness() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && activity.isInPictureInPictureMode) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
brightness = savedBrightness
|
brightness = savedBrightness
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,10 +6,12 @@ import android.util.Log
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.api.PlaylistsHelper
|
||||||
import com.github.libretube.api.RetrofitInstance
|
import com.github.libretube.api.RetrofitInstance
|
||||||
import com.github.libretube.api.SubscriptionHelper
|
import com.github.libretube.api.SubscriptionHelper
|
||||||
import com.github.libretube.extensions.TAG
|
import com.github.libretube.extensions.TAG
|
||||||
import com.github.libretube.extensions.toastFromMainThread
|
import com.github.libretube.extensions.toastFromMainThread
|
||||||
|
import com.github.libretube.obj.ImportPlaylistFile
|
||||||
import com.github.libretube.obj.NewPipeSubscription
|
import com.github.libretube.obj.NewPipeSubscription
|
||||||
import com.github.libretube.obj.NewPipeSubscriptions
|
import com.github.libretube.obj.NewPipeSubscriptions
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -36,12 +38,10 @@ class ImportHelper(
|
|||||||
}
|
}
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
Log.e(TAG(), e.toString())
|
Log.e(TAG(), e.toString())
|
||||||
Toast.makeText(
|
activity.toastFromMainThread(
|
||||||
activity,
|
|
||||||
activity.getString(R.string.unsupported_file_format) +
|
activity.getString(R.string.unsupported_file_format) +
|
||||||
" (${activity.contentResolver.getType(uri)}",
|
" (${activity.contentResolver.getType(uri)}"
|
||||||
Toast.LENGTH_SHORT
|
)
|
||||||
).show()
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG(), e.toString())
|
Log.e(TAG(), e.toString())
|
||||||
Toast.makeText(activity, e.localizedMessage, Toast.LENGTH_SHORT).show()
|
Toast.makeText(activity, e.localizedMessage, Toast.LENGTH_SHORT).show()
|
||||||
@ -55,12 +55,7 @@ class ImportHelper(
|
|||||||
return when (val fileType = activity.contentResolver.getType(uri)) {
|
return when (val fileType = activity.contentResolver.getType(uri)) {
|
||||||
"application/json", "application/octet-stream" -> {
|
"application/json", "application/octet-stream" -> {
|
||||||
// NewPipe subscriptions format
|
// NewPipe subscriptions format
|
||||||
val mapper = ObjectMapper()
|
val subscriptions = ObjectMapper().readValue(uri.readText(), NewPipeSubscriptions::class.java)
|
||||||
val json = activity.contentResolver.openInputStream(uri)?.use {
|
|
||||||
it.bufferedReader().use { reader -> reader.readText() }
|
|
||||||
}.orEmpty()
|
|
||||||
|
|
||||||
val subscriptions = mapper.readValue(json, NewPipeSubscriptions::class.java)
|
|
||||||
subscriptions.subscriptions.orEmpty().map {
|
subscriptions.subscriptions.orEmpty().map {
|
||||||
it.url!!.replace("https://www.youtube.com/channel/", "")
|
it.url!!.replace("https://www.youtube.com/channel/", "")
|
||||||
}
|
}
|
||||||
@ -84,12 +79,9 @@ class ImportHelper(
|
|||||||
*/
|
*/
|
||||||
fun exportSubscriptions(uri: Uri?) {
|
fun exportSubscriptions(uri: Uri?) {
|
||||||
if (uri == null) return
|
if (uri == null) return
|
||||||
try {
|
|
||||||
val mapper = ObjectMapper()
|
|
||||||
val token = PreferenceHelper.getToken()
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val subs = if (token != "") {
|
val subs = if (PreferenceHelper.getToken() != "") {
|
||||||
RetrofitInstance.authApi.subscriptions(token)
|
RetrofitInstance.authApi.subscriptions(PreferenceHelper.getToken())
|
||||||
} else {
|
} else {
|
||||||
RetrofitInstance.authApi.unauthenticatedSubscriptions(
|
RetrofitInstance.authApi.unauthenticatedSubscriptions(
|
||||||
SubscriptionHelper.getFormattedLocalSubscriptions()
|
SubscriptionHelper.getFormattedLocalSubscriptions()
|
||||||
@ -108,16 +100,62 @@ class ImportHelper(
|
|||||||
subscriptions = newPipeChannels
|
subscriptions = newPipeChannels
|
||||||
)
|
)
|
||||||
|
|
||||||
val data = mapper.writeValueAsBytes(newPipeSubscriptions)
|
uri.write(newPipeSubscriptions)
|
||||||
|
|
||||||
activity.contentResolver.openFileDescriptor(uri, "w")?.use {
|
activity.toastFromMainThread(R.string.exportsuccess)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import Playlists
|
||||||
|
*/
|
||||||
|
fun importPlaylists(uri: Uri?) {
|
||||||
|
if (uri == null) return
|
||||||
|
|
||||||
|
val playlistFile = ObjectMapper().readValue(uri.readText(), ImportPlaylistFile::class.java)
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
playlistFile.playlists?.let {
|
||||||
|
PlaylistsHelper.importPlaylists(activity, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.toastFromMainThread(R.string.success)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export Playlists
|
||||||
|
*/
|
||||||
|
fun exportPlaylists(uri: Uri?) {
|
||||||
|
if (uri == null) return
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
val playlists = PlaylistsHelper.exportPlaylists()
|
||||||
|
val playlistFile = ImportPlaylistFile(
|
||||||
|
format = "Piped",
|
||||||
|
version = 1,
|
||||||
|
playlists = playlists
|
||||||
|
)
|
||||||
|
|
||||||
|
uri.write(playlistFile)
|
||||||
|
|
||||||
|
activity.toastFromMainThread(R.string.exportsuccess)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Uri.readText(): String {
|
||||||
|
return activity.contentResolver.openInputStream(this)?.use {
|
||||||
|
it.bufferedReader().use { reader -> reader.readText() }
|
||||||
|
}.orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Uri.write(text: Any) {
|
||||||
|
activity.contentResolver.openFileDescriptor(this, "w")?.use {
|
||||||
FileOutputStream(it.fileDescriptor).use { fileOutputStream ->
|
FileOutputStream(it.fileDescriptor).use { fileOutputStream ->
|
||||||
fileOutputStream.write(data)
|
fileOutputStream.write(
|
||||||
|
ObjectMapper().writeValueAsBytes(text)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package com.github.libretube.util
|
package com.github.libretube.util
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
@ -11,27 +9,43 @@ import android.view.GestureDetector
|
|||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.ScaleGestureDetector
|
import android.view.ScaleGestureDetector
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import com.github.libretube.ui.base.BaseActivity
|
||||||
import com.github.libretube.ui.interfaces.PlayerGestureOptions
|
import com.github.libretube.ui.interfaces.PlayerGestureOptions
|
||||||
|
import com.github.libretube.ui.models.PlayerViewModel
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
class PlayerGestureController(context: Context, private val listener: PlayerGestureOptions) :
|
class PlayerGestureController(activity: BaseActivity, private val listener: PlayerGestureOptions) :
|
||||||
View.OnTouchListener {
|
View.OnTouchListener {
|
||||||
|
|
||||||
// width and height should be obtained each time using getter to adopt layout size changes.
|
// width and height should be obtained each time using getter to adopt layout
|
||||||
|
// size changes.
|
||||||
private val width get() = Resources.getSystem().displayMetrics.widthPixels
|
private val width get() = Resources.getSystem().displayMetrics.widthPixels
|
||||||
private val height get() = Resources.getSystem().displayMetrics.heightPixels
|
private val height get() = Resources.getSystem().displayMetrics.heightPixels
|
||||||
private val orientation get() = Resources.getSystem().configuration.orientation
|
|
||||||
private val elapsedTime get() = SystemClock.elapsedRealtime()
|
private val elapsedTime get() = SystemClock.elapsedRealtime()
|
||||||
|
|
||||||
|
private val playerViewModel: PlayerViewModel by activity.viewModels()
|
||||||
private val handler: Handler = Handler(Looper.getMainLooper())
|
private val handler: Handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
private val gestureDetector: GestureDetector
|
private val gestureDetector: GestureDetector
|
||||||
private val scaleGestureDetector: ScaleGestureDetector
|
private val scaleGestureDetector: ScaleGestureDetector
|
||||||
|
|
||||||
|
private var isFullscreen = false
|
||||||
private var isMoving = false
|
private var isMoving = false
|
||||||
var isEnabled = true
|
var isEnabled = true
|
||||||
|
|
||||||
|
// Indicates last touch event was for click or other gesture, used to avoid single click
|
||||||
|
// by runnable when scroll or pinch gesture already completed.
|
||||||
|
var wasClick = true
|
||||||
|
|
||||||
init {
|
init {
|
||||||
gestureDetector = GestureDetector(context, GestureListener(), handler)
|
gestureDetector = GestureDetector(activity, GestureListener(), handler)
|
||||||
scaleGestureDetector = ScaleGestureDetector(context, ScaleGestureListener(), handler)
|
scaleGestureDetector = ScaleGestureDetector(activity, ScaleGestureListener(), handler)
|
||||||
|
|
||||||
|
playerViewModel.isFullscreen.observe(activity) {
|
||||||
|
isFullscreen = it
|
||||||
|
listener.onFullscreenChange(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@ -47,14 +61,16 @@ class PlayerGestureController(context: Context, private val listener: PlayerGest
|
|||||||
gestureDetector.onTouchEvent(event)
|
gestureDetector.onTouchEvent(event)
|
||||||
} catch (_: Exception) { }
|
} catch (_: Exception) { }
|
||||||
|
|
||||||
// If orientation is landscape then allow `onScroll` to consume event and return true.
|
// If video is playing in full-screen mode, then allow `onScroll` to consume
|
||||||
return orientation == Configuration.ORIENTATION_LANDSCAPE
|
// event and return true.
|
||||||
|
return isFullscreen
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ScaleGestureListener : ScaleGestureDetector.OnScaleGestureListener {
|
private inner class ScaleGestureListener : ScaleGestureDetector.OnScaleGestureListener {
|
||||||
var scaleFactor: Float = 1f
|
var scaleFactor: Float = 1f
|
||||||
|
|
||||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||||
|
wasClick = false
|
||||||
scaleFactor *= detector.scaleFactor
|
scaleFactor *= detector.scaleFactor
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -78,6 +94,9 @@ class PlayerGestureController(context: Context, private val listener: PlayerGest
|
|||||||
private var xPos = 0.0F
|
private var xPos = 0.0F
|
||||||
|
|
||||||
override fun onDown(e: MotionEvent): Boolean {
|
override fun onDown(e: MotionEvent): Boolean {
|
||||||
|
// Initially assume this event is for click
|
||||||
|
wasClick = true
|
||||||
|
|
||||||
if (isMoving || scaleGestureDetector.isInProgress) return false
|
if (isMoving || scaleGestureDetector.isInProgress) return false
|
||||||
|
|
||||||
if (isEnabled && isSecondClick()) {
|
if (isEnabled && isSecondClick()) {
|
||||||
@ -124,6 +143,7 @@ class PlayerGestureController(context: Context, private val listener: PlayerGest
|
|||||||
}
|
}
|
||||||
|
|
||||||
isMoving = true
|
isMoving = true
|
||||||
|
wasClick = false
|
||||||
|
|
||||||
when {
|
when {
|
||||||
width * 0.5 > e1.x -> listener.onSwipeLeftScreen(distanceY)
|
width * 0.5 > e1.x -> listener.onSwipeLeftScreen(distanceY)
|
||||||
@ -133,8 +153,8 @@ class PlayerGestureController(context: Context, private val listener: PlayerGest
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val runnable = Runnable {
|
private val runnable = Runnable {
|
||||||
// If user is scrolling then avoid single tap call
|
// If the last event was for scroll or pinch then avoid single tap call
|
||||||
if (isMoving || isSecondClick()) return@Runnable
|
if (!wasClick || isSecondClick()) return@Runnable
|
||||||
listener.onSingleTap()
|
listener.onSingleTap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,6 +300,24 @@ object PlayerHelper {
|
|||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val pinchGestureEnabled: Boolean
|
||||||
|
get() = PreferenceHelper.getBoolean(
|
||||||
|
PreferenceKeys.PLAYER_PINCH_CONTROL,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
val captionsTextSize: Float
|
||||||
|
get() = PreferenceHelper.getString(
|
||||||
|
PreferenceKeys.CAPTIONS_SIZE,
|
||||||
|
"18"
|
||||||
|
).toFloat()
|
||||||
|
|
||||||
|
val doubleTapToSeek: Boolean
|
||||||
|
get() = PreferenceHelper.getBoolean(
|
||||||
|
PreferenceKeys.DOUBLE_TAP_TO_SEEK,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
fun getDefaultResolution(context: Context): String {
|
fun getDefaultResolution(context: Context): String {
|
||||||
return if (NetworkHelper.isNetworkMobile(context)) {
|
return if (NetworkHelper.isNetworkMobile(context)) {
|
||||||
PreferenceHelper.getString(
|
PreferenceHelper.getString(
|
||||||
|
@ -110,8 +110,8 @@ object PlayingQueue {
|
|||||||
fun insertPlaylist(playlistId: String, newCurrentStream: StreamItem) {
|
fun insertPlaylist(playlistId: String, newCurrentStream: StreamItem) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
try {
|
try {
|
||||||
val playlistType = PlaylistsHelper.getPrivateType(playlistId)
|
val playlistType = PlaylistsHelper.getPrivatePlaylistType(playlistId)
|
||||||
val playlist = PlaylistsHelper.getPlaylist(playlistType, playlistId)
|
val playlist = PlaylistsHelper.getPlaylist(playlistId)
|
||||||
add(
|
add(
|
||||||
*playlist.relatedStreams
|
*playlist.relatedStreams
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
object PreferenceHelper {
|
object PreferenceHelper {
|
||||||
/**
|
/**
|
||||||
@ -45,6 +46,10 @@ object PreferenceHelper {
|
|||||||
editor.putFloat(key, value).commit()
|
editor.putFloat(key, value).commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun putLong(key: String, value: Long) {
|
||||||
|
editor.putLong(key, value).commit()
|
||||||
|
}
|
||||||
|
|
||||||
fun getString(key: String?, defValue: String): String {
|
fun getString(key: String?, defValue: String): String {
|
||||||
return settings.getString(key, defValue) ?: defValue
|
return settings.getString(key, defValue) ?: defValue
|
||||||
}
|
}
|
||||||
@ -57,6 +62,10 @@ object PreferenceHelper {
|
|||||||
return settings.getInt(key, defValue)
|
return settings.getInt(key, defValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getLong(key: String?, defValue: Long): Long {
|
||||||
|
return settings.getLong(key, defValue)
|
||||||
|
}
|
||||||
|
|
||||||
fun getFloat(key: String?, defValue: Float): Float {
|
fun getFloat(key: String?, defValue: Float): Float {
|
||||||
return settings.getFloat(key, defValue)
|
return settings.getFloat(key, defValue)
|
||||||
}
|
}
|
||||||
@ -89,6 +98,14 @@ object PreferenceHelper {
|
|||||||
return getString(PreferenceKeys.LAST_STREAM_VIDEO_ID, "")
|
return getString(PreferenceKeys.LAST_STREAM_VIDEO_ID, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateLastFeedWatchedTime() {
|
||||||
|
putLong(PreferenceKeys.LAST_WATCHED_FEED_TIME, Instant.now().epochSecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLastCheckedFeedTime(): Long {
|
||||||
|
return getLong(PreferenceKeys.LAST_WATCHED_FEED_TIME, 0)
|
||||||
|
}
|
||||||
|
|
||||||
fun saveErrorLog(log: String) {
|
fun saveErrorLog(log: String) {
|
||||||
editor.putString(PreferenceKeys.ERROR_LOG, log).commit()
|
editor.putString(PreferenceKeys.ERROR_LOG, log).commit()
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,11 @@ object TextUtils {
|
|||||||
*/
|
*/
|
||||||
const val SEPARATOR = " • "
|
const val SEPARATOR = " • "
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regex to check for e-mails
|
||||||
|
*/
|
||||||
|
const val EMAIL_REGEX = "^[A-Za-z](.*)([@]{1})(.{1,})(\\.)(.{1,})"
|
||||||
|
|
||||||
fun toTwoDecimalsString(num: Int): String {
|
fun toTwoDecimalsString(num: Int): String {
|
||||||
return if (num >= 10) num.toString() else "0$num"
|
return if (num >= 10) num.toString() else "0$num"
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import com.github.libretube.extensions.toID
|
|||||||
import com.github.libretube.ui.activities.MainActivity
|
import com.github.libretube.ui.activities.MainActivity
|
||||||
import com.github.libretube.ui.views.TimePickerPreference
|
import com.github.libretube.ui.views.TimePickerPreference
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.util.PreferenceHelper
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
|
|
||||||
@ -82,12 +81,9 @@ class NotificationWorker(appContext: Context, parameters: WorkerParameters) :
|
|||||||
var success = true
|
var success = true
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val task = async {
|
|
||||||
SubscriptionHelper.getFeed()
|
|
||||||
}
|
|
||||||
// fetch the users feed
|
// fetch the users feed
|
||||||
val videoFeed = try {
|
val videoFeed = try {
|
||||||
task.await()
|
SubscriptionHelper.getFeed()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
success = false
|
success = false
|
||||||
return@runBlocking
|
return@runBlocking
|
||||||
|
18
app/src/main/res/drawable/ic_done.xml
Normal file
18
app/src/main/res/drawable/ic_done.xml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="400"
|
||||||
|
android:viewportHeight="400">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M200,15.89C98.47,15.89 15.89,98.48 15.89,200 15.89,301.52 98.47,384.11 200,384.11 301.52,384.11 384.11,301.52 384.11,200 384.11,98.48 301.52,15.89 200,15.89ZM200,359.97C111.79,359.97 40.03,288.2 40.03,200c0,-88.21 71.76,-159.96 159.96,-159.96 88.21,0 159.97,71.76 159.97,159.96 0,88.2 -71.76,159.97 -159.97,159.97z"
|
||||||
|
android:strokeWidth="27"
|
||||||
|
android:strokeColor="#000000" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="m266.65,139.73 l-94.94,94.93 -38.38,-38.37c-4.72,-4.71 -12.36,-4.71 -17.07,0 -4.72,4.72 -4.72,12.36 0,17.07l46.91,46.91c2.36,2.35 5.45,3.53 8.53,3.53 3.09,0 6.19,-1.18 8.54,-3.54 0.01,-0.01 0.01,-0.02 0.02,-0.03L283.73,156.8c4.72,-4.71 4.72,-12.36 0,-17.07 -4.72,-4.72 -12.36,-4.72 -17.07,0z"
|
||||||
|
android:strokeWidth="27"
|
||||||
|
android:strokeColor="#000000" />
|
||||||
|
</vector>
|
13
app/src/main/res/drawable/ic_pinch_gesture.xml
Normal file
13
app/src/main/res/drawable/ic_pinch_gesture.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="238.06"
|
||||||
|
android:viewportHeight="238.06">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M237.1,120.17c-3.77,-22.44 -19.64,-25.95 -27,-26.34c-2.27,-0.12 -4.39,-1.27 -5.63,-3.18c-5.37,-8.25 -12.58,-10.43 -18.71,-10.43c-2.74,0 -5.27,0.44 -7.31,0.96c-0.62,0.16 -1.26,0.24 -1.88,0.24c-2.28,0 -4.47,-1.04 -5.85,-2.93c-5.29,-7.25 -12.17,-9.21 -18.07,-9.21c-1.46,0 -2.87,0.12 -4.17,0.31c-0.37,0.05 -0.77,0.08 -1.13,0.08c-3.96,0 -7.35,-3.18 -7.35,-7.27V19.56c0,-10.98 -8.26,-16.47 -16.5,-16.47S107,8.57 107,19.56v105.36c0,8.24 -6.69,14.19 -14.11,14.19c-2.2,0 -4.42,-0.52 -6.57,-1.66c-10.06,-5.31 -20.86,-9.97 -32.11,-14.38c-2.51,-0.98 -4.93,-1.43 -7.21,-1.43c-15.43,-0 -24.11,20.33 -7.04,31.32c19.59,12.61 44.43,19.51 54.09,33.77c5.97,8.81 18.53,34.14 23.4,44.02c1.23,2.49 3.76,4.22 6.53,4.22h77c3.65,0 6.74,-2.87 7.22,-6.49l4.06,-30.63c0.14,-1.09 0.54,-2.17 1.14,-3.09C234.6,162.43 240.67,141.43 237.1,120.17z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M48.33,84.97c0,-3.31 -2.69,-6 -6,-6H21.43L75,24.96v20.9c0,3.31 2.69,6 6,6s6,-2.69 6,-6v-36c0,-3.31 -2.35,-5.89 -5.67,-5.89h-35c-3.31,0 -6,2.69 -6,6s2.69,6 6,6h21.13L12,70.99v-21.13c0,-3.31 -2.69,-6 -6,-6s-6,2.69 -6,6v35c0,3.31 3.02,6.11 6.33,6.11h36C45.65,90.97 48.33,88.29 48.33,84.97z" />
|
||||||
|
</vector>
|
13
app/src/main/res/drawable/ic_sb_disabled.xml
Normal file
13
app/src/main/res/drawable/ic_sb_disabled.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="@android:color/white"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,22.7994C11.55,22.7994 11.1,22.7094 10.74,22.4394 4.89,18.8394 1.29,12.6294 1.2,5.7894 1.2,4.8894 1.65,3.9894 2.46,3.5394 8.4,0.3894 15.6,0.3894 21.54,3.6294 22.35,3.9894 22.8,4.8894 22.8,5.7894 22.71,12.6294 19.11,18.8394 13.35,22.4394 12.9,22.7094 12.45,22.7994 12,22.7994ZM12,1.9194c-3.15,0 -6.3,0.81 -9.18,2.34 -0.54,0.27 -0.9,0.9 -0.9,1.53 0.09,6.57 3.51,12.51 9.18,16.02 0.54,0.36 1.26,0.36 1.8,0C18.57,18.3894 21.9,12.3594 22.08,5.7894 22.08,5.1594 21.72,4.5294 21.18,4.2594 18.3,2.7294 15.15,1.9194 12,1.9194Z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M20.73,4.9794C15.24,2.0994 8.76,2.0994 3.27,4.9794 3,5.1594 2.82,5.4294 2.82,5.7894c0.09,6.48 3.51,12.06 8.73,15.3 0.27,0.18 0.63,0.18 0.9,0 5.13,-3.15 8.64,-8.82 8.73,-15.3C21.18,5.4294 21,5.1594 20.73,4.9794ZM12,15.4194c0,0 -4.5,-3.6 -4.5,-5.94 0,-1.53 0.99,-2.43 2.25,-2.43 1.08,0 2.25,1.17 2.25,1.17 0,0 1.08,-1.17 2.25,-1.17 1.26,0 2.25,0.81 2.25,2.43 0,2.34 -4.5,5.94 -4.5,5.94z"/>
|
||||||
|
</vector>
|
19
app/src/main/res/drawable/ic_sb_enabled.xml
Normal file
19
app/src/main/res/drawable/ic_sb_enabled.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="@android:color/white"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,22.7994C11.55,22.7994 11.1,22.7094 10.74,22.4394 4.89,18.8394 1.29,12.6294 1.2,5.7894 1.2,4.8894 1.65,3.9894 2.46,3.5394 8.4,0.3894 15.6,0.3894 21.54,3.6294 22.35,3.9894 22.8,4.8894 22.8,5.7894 22.71,12.6294 19.11,18.8394 13.35,22.4394 12.9,22.7094 12.45,22.7994 12,22.7994ZM12,1.9194c-3.15,0 -6.3,0.81 -9.18,2.34 -0.54,0.27 -0.9,0.9 -0.9,1.53 0.09,6.57 3.51,12.51 9.18,16.02 0.54,0.36 1.26,0.36 1.8,0C18.57,18.3894 21.9,12.3594 22.08,5.7894 22.08,5.1594 21.72,4.5294 21.18,4.2594 18.3,2.7294 15.15,1.9194 12,1.9194Z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M20.73,4.9794C15.24,2.0994 8.76,2.0994 3.27,4.9794 3,5.1594 2.82,5.4294 2.82,5.7894c0.09,6.48 3.51,12.06 8.73,15.3 0.27,0.18 0.63,0.18 0.9,0 5.13,-3.15 8.64,-8.82 8.73,-15.3C21.18,5.4294 21,5.1594 20.73,4.9794ZM12,15.8694c-2.79,0 -4.95,-2.25 -4.95,-4.95 0,-2.7 2.25,-4.95 4.95,-4.95 2.7,0 4.95,2.25 4.95,4.95 0,2.79 -2.16,4.95 -4.95,4.95z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="m15.15,13.4394c0.54,-0.72 0.9,-1.53 0.9,-2.52 0,-2.25 -1.8,-4.05 -4.05,-4.05 -0.9,0 -1.8,0.36 -2.52,0.9z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="m8.85,8.4894c-0.54,0.72 -0.9,1.53 -0.9,2.52 0,2.25 1.8,4.05 4.05,4.05 0.9,0 1.8,-0.36 2.52,-0.9z"/>
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_text.xml
Normal file
10
app/src/main/res/drawable/ic_text.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M2.5,4v3h5v12h3V7h5V4H2.5zM21.5,9h-9v3h3v7h3v-7h3V9z" />
|
||||||
|
</vector>
|
35
app/src/main/res/layout/all_caught_up_row.xml
Normal file
35
app/src/main/res/layout/all_caught_up_row.xml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingVertical="50dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
|
||||||
|
android:src="@drawable/ic_done"
|
||||||
|
android:tint="?attr/colorPrimary"
|
||||||
|
tools:ignore="UseAppTint" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="@string/all_caught_up"
|
||||||
|
android:textColor="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:text="@string/all_caught_up_summary"
|
||||||
|
android:textColor="?attr/colorControlNormal" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -67,6 +67,14 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layoutDirection="ltr">
|
android:layoutDirection="ltr">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/sb_toggle"
|
||||||
|
style="@style/PlayerControlTop"
|
||||||
|
android:layout_marginEnd="2dp"
|
||||||
|
android:src="@drawable/ic_sb_enabled"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/queue_toggle"
|
android:id="@+id/queue_toggle"
|
||||||
style="@style/PlayerControlTop"
|
style="@style/PlayerControlTop"
|
||||||
@ -229,7 +237,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:clipToPadding="false"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:padding="20dp">
|
android:padding="20dp">
|
||||||
|
|
||||||
@ -241,12 +248,69 @@
|
|||||||
android:visibility="invisible"
|
android:visibility="invisible"
|
||||||
app:tint="@android:color/white" />
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/rewindBTN"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginHorizontal="10dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:scaleX="0.8"
|
||||||
|
android:scaleY="0.8"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_rewind"
|
||||||
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/rewindTV"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@id/exo_play_pause"
|
android:id="@+id/playPauseBTN"
|
||||||
style="@style/ExoStyledControls.Button.Center.PlayPause"
|
style="@style/ExoStyledControls.Button.Center.PlayPause"
|
||||||
|
android:layout_marginHorizontal="10dp"
|
||||||
android:background="?android:selectableItemBackgroundBorderless"
|
android:background="?android:selectableItemBackgroundBorderless"
|
||||||
app:tint="@android:color/white" />
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/forwardBTN"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginHorizontal="10dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:scaleX="0.8"
|
||||||
|
android:scaleY="0.8"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_forward"
|
||||||
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/forwardTV"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/skip_next"
|
android:id="@+id/skip_next"
|
||||||
style="@style/PlayerControlCenter"
|
style="@style/PlayerControlCenter"
|
||||||
|
@ -402,4 +402,19 @@
|
|||||||
<string name="defaults">الإفتراضي</string>
|
<string name="defaults">الإفتراضي</string>
|
||||||
<string name="pop_up">نافذة منبثقة</string>
|
<string name="pop_up">نافذة منبثقة</string>
|
||||||
<string name="swipe_controls_summary">استخدم إيماءة السحب لضبط السطوع ومستوى الصوت.</string>
|
<string name="swipe_controls_summary">استخدم إيماءة السحب لضبط السطوع ومستوى الصوت.</string>
|
||||||
|
<string name="no_comments_available">لا يحتوي هذا الفيديو على تعليقات متوفرة.</string>
|
||||||
|
<string name="comments_disabled">التعليقات معطلة من قبل القائم بالتحميل.</string>
|
||||||
|
<string name="double_tap_seek_summary">اضغط مرتين على اليسار أو اليمين لترجيع موضع المشغل أو إعادة تقديمه.</string>
|
||||||
|
<string name="all_caught_up">لقد وصلت الى النهاية</string>
|
||||||
|
<string name="captions_size">حجم التسميات التوضيحية</string>
|
||||||
|
<string name="double_tap_seek">انقر نقرا مزدوجا للسعي</string>
|
||||||
|
<string name="all_caught_up_summary">لقد شاهدت جميع مقاطع الفيديو الجديدة</string>
|
||||||
|
<string name="import_playlists">استيراد قوائم التشغيل</string>
|
||||||
|
<string name="export_playlists">تصدير قوائم التشغيل</string>
|
||||||
|
<string name="app_backup">النسخ الاحتياطي للتطبيق</string>
|
||||||
|
<string name="backup_restore_summary">استيراد وتصدير الاشتراكات وقوائم التشغيل …</string>
|
||||||
|
<string name="proceed">استمر</string>
|
||||||
|
<string name="privacy_alert">تنبيه الخصوصية</string>
|
||||||
|
<string name="exportsuccess">تم تصديرها بنجاح!</string>
|
||||||
|
<string name="username_email">قمت بإدراج عنوان بريد إلكتروني كاسم مستخدم غير مستحسن. هل أنت متأكد من أنك تريد المتابعة؟</string>
|
||||||
</resources>
|
</resources>
|
@ -58,7 +58,7 @@
|
|||||||
<string name="segment_skipped">Seqment ötürüldü</string>
|
<string name="segment_skipped">Seqment ötürüldü</string>
|
||||||
<string name="sponsorblock_state">Açıq</string>
|
<string name="sponsorblock_state">Açıq</string>
|
||||||
<string name="category_sponsor_description">Ödənişli reklam, tövsiyələr və birbaşa reklamlar.Öz reklamı və ya səbəblərə, yaradıcılara, veb saytlara və məhsullara pulsuz orijinal elanlar üçün deyil.</string>
|
<string name="category_sponsor_description">Ödənişli reklam, tövsiyələr və birbaşa reklamlar.Öz reklamı və ya səbəblərə, yaradıcılara, veb saytlara və məhsullara pulsuz orijinal elanlar üçün deyil.</string>
|
||||||
<string name="category_interaction">İnteraksiya xatırlatma(bəyən və abunə ol)</string>
|
<string name="category_interaction">İnteraksiya xatırladıcı(bəyən və abunə ol)</string>
|
||||||
<string name="category_intro">Fasilə/Giriş Animasiyası</string>
|
<string name="category_intro">Fasilə/Giriş Animasiyası</string>
|
||||||
<string name="login_register">Daxil ol/qeydiyyatdan keç</string>
|
<string name="login_register">Daxil ol/qeydiyyatdan keç</string>
|
||||||
<string name="please_login">Zəhmət olmasa, əvvəlcə tənzimləmələrdə daxil ol və ya qeydiyyatdan keç.</string>
|
<string name="please_login">Zəhmət olmasa, əvvəlcə tənzimləmələrdə daxil ol və ya qeydiyyatdan keç.</string>
|
||||||
@ -179,7 +179,7 @@
|
|||||||
<string name="playerVideoFormat">Oynadıcı üçün video formatı</string>
|
<string name="playerVideoFormat">Oynadıcı üçün video formatı</string>
|
||||||
<string name="buffering_goal_summary">Buferlənəcək video saniyələrin maksimum miqdarı.</string>
|
<string name="buffering_goal_summary">Buferlənəcək video saniyələrin maksimum miqdarı.</string>
|
||||||
<string name="buffering_goal">Ön yükləmə</string>
|
<string name="buffering_goal">Ön yükləmə</string>
|
||||||
<string name="player_autoplay">Avtomatik oynatma</string>
|
<string name="player_autoplay">Avtomatik oynat</string>
|
||||||
<string name="no_video">Video yoxdur</string>
|
<string name="no_video">Video yoxdur</string>
|
||||||
<string name="downloading">Endirilir…</string>
|
<string name="downloading">Endirilir…</string>
|
||||||
<string name="video">Video</string>
|
<string name="video">Video</string>
|
||||||
@ -212,7 +212,7 @@
|
|||||||
<string name="audio_video">Səs və video</string>
|
<string name="audio_video">Səs və video</string>
|
||||||
<string name="fullscreen_orientation">Tam ekran istiqaməti</string>
|
<string name="fullscreen_orientation">Tam ekran istiqaməti</string>
|
||||||
<string name="aspect_ratio">Video görünüş nisbəti</string>
|
<string name="aspect_ratio">Video görünüş nisbəti</string>
|
||||||
<string name="auto_rotation">Avtomatik fırlanma</string>
|
<string name="auto_rotation">Avtomatik fırlanmaq</string>
|
||||||
<string name="portrait">Portret</string>
|
<string name="portrait">Portret</string>
|
||||||
<string name="landscape">Landşaft</string>
|
<string name="landscape">Landşaft</string>
|
||||||
<string name="community">İctimaiyyət</string>
|
<string name="community">İctimaiyyət</string>
|
||||||
@ -402,4 +402,21 @@
|
|||||||
<string name="swipe_controls">Sürüşdürmə nəzarətləri</string>
|
<string name="swipe_controls">Sürüşdürmə nəzarətləri</string>
|
||||||
<string name="defaults">Defolt</string>
|
<string name="defaults">Defolt</string>
|
||||||
<string name="pop_up">Ani pəncərə</string>
|
<string name="pop_up">Ani pəncərə</string>
|
||||||
|
<string name="no_comments_available">Bu videoda şərhlər əlçatan deyil.</string>
|
||||||
|
<string name="comments_disabled">Şərhlər yükləyici tərəfindən deaktiv edilib.</string>
|
||||||
|
<string name="double_tap_seek">Axtarmaq üçün iki dəfə kliklə</string>
|
||||||
|
<string name="double_tap_seek_summary">Oynadıcı mövqeyini geri və ya irəli çəkmək üçün sola yaxud sağa iki dəfə kliklə.</string>
|
||||||
|
<string name="all_caught_up">Hamınız tutulmusunuz</string>
|
||||||
|
<string name="all_caught_up_summary">Bütün yeni videolara baxmısınız</string>
|
||||||
|
<string name="captions_size">Altyazı ölçüsü</string>
|
||||||
|
<string name="import_playlists">Pleylistləri idxal et</string>
|
||||||
|
<string name="export_playlists">Pleylistləri ixrac et</string>
|
||||||
|
<string name="app_backup">Tətbiq Nüsxələməsi</string>
|
||||||
|
<string name="backup_restore_summary">Abunəlikləri, pleylistləri idxal & ixrac et …</string>
|
||||||
|
<string name="exportsuccess">İxrac edildi.</string>
|
||||||
|
<string name="privacy_alert">Məxfilik xəbərdarlığı</string>
|
||||||
|
<string name="username_email">Tövsiyə olunmayan e-poçt ünvanı ilə davam edilsin\?</string>
|
||||||
|
<string name="proceed">Davam et</string>
|
||||||
|
<string name="pinch_control">Çimdik nəzarəti</string>
|
||||||
|
<string name="pinch_control_summary">Böyütmək/kiçiltmək üçün çimdik jesti istifadə et.</string>
|
||||||
</resources>
|
</resources>
|
@ -56,7 +56,7 @@
|
|||||||
<string name="videos">Videa</string>
|
<string name="videos">Videa</string>
|
||||||
<string name="darkTheme">Tmavý</string>
|
<string name="darkTheme">Tmavý</string>
|
||||||
<string name="lightTheme">Světlý</string>
|
<string name="lightTheme">Světlý</string>
|
||||||
<string name="startpage">Trendy</string>
|
<string name="startpage">Domů</string>
|
||||||
<string name="library">Knihovna</string>
|
<string name="library">Knihovna</string>
|
||||||
<string name="subscriptions">Odběry</string>
|
<string name="subscriptions">Odběry</string>
|
||||||
<string name="systemDefault">Systém</string>
|
<string name="systemDefault">Systém</string>
|
||||||
@ -283,7 +283,7 @@
|
|||||||
<string name="error_occurred">Chyba</string>
|
<string name="error_occurred">Chyba</string>
|
||||||
<string name="copied">Zkopírováno</string>
|
<string name="copied">Zkopírováno</string>
|
||||||
<string name="downloadsucceeded">Stahování bylo úspěšné</string>
|
<string name="downloadsucceeded">Stahování bylo úspěšné</string>
|
||||||
<string name="share_with_time">Sdílet s časovou značkou</string>
|
<string name="share_with_time">Sdílet s časem</string>
|
||||||
<string name="export_subscriptions">Exportovat odběry</string>
|
<string name="export_subscriptions">Exportovat odběry</string>
|
||||||
<string name="skip_buttons">Tlačítka přeskočení</string>
|
<string name="skip_buttons">Tlačítka přeskočení</string>
|
||||||
<string name="history_size">Maximální velikost historie</string>
|
<string name="history_size">Maximální velikost historie</string>
|
||||||
@ -292,11 +292,11 @@
|
|||||||
<string name="unlimited">Neomezená</string>
|
<string name="unlimited">Neomezená</string>
|
||||||
<string name="add_to_queue">Přidat do fronty</string>
|
<string name="add_to_queue">Přidat do fronty</string>
|
||||||
<string name="misc">Různé</string>
|
<string name="misc">Různé</string>
|
||||||
<string name="break_reminder">Připominka přestávky</string>
|
<string name="break_reminder">Připomínka přestávky</string>
|
||||||
<string name="take_a_break">Čas na přestávku</string>
|
<string name="take_a_break">Čas na přestávku</string>
|
||||||
<string name="already_spent_time">V aplikaci jste strávili již %1$s minut, dejte si chvíli pauzu.</string>
|
<string name="already_spent_time">V aplikaci jste strávili již %1$s minut, dejte si chvíli pauzu.</string>
|
||||||
<string name="yt_shorts">Shorts</string>
|
<string name="yt_shorts">Shorts</string>
|
||||||
<string name="repeat_mode_current">Současný</string>
|
<string name="repeat_mode_current">Aktuální</string>
|
||||||
<string name="repeat_mode_none">Žádný</string>
|
<string name="repeat_mode_none">Žádný</string>
|
||||||
<string name="resize_mode_fit">Vejít se</string>
|
<string name="resize_mode_fit">Vejít se</string>
|
||||||
<string name="no_subtitles_available">Nejsou dostupné žádné titulky</string>
|
<string name="no_subtitles_available">Nejsou dostupné žádné titulky</string>
|
||||||
@ -327,7 +327,7 @@
|
|||||||
<string name="push_channel_name">Pracovník oznámení</string>
|
<string name="push_channel_name">Pracovník oznámení</string>
|
||||||
<string name="download_channel_name">Služba stahování</string>
|
<string name="download_channel_name">Služba stahování</string>
|
||||||
<string name="skip_segment">Přeskočit segment</string>
|
<string name="skip_segment">Přeskočit segment</string>
|
||||||
<string name="sb_skip_manual_summary">Nepřeskakovat segmenty automaticky, vždy se předtím zeptat.</string>
|
<string name="sb_skip_manual_summary">Nepřeskakovat segmenty automaticky, vždy se nejprve zeptat.</string>
|
||||||
<string name="download_channel_description">Zobrazí oznámení při stahování médií.</string>
|
<string name="download_channel_description">Zobrazí oznámení při stahování médií.</string>
|
||||||
<string name="push_channel_description">Zobrazí oznámení, když jsou k dispozici nové videa.</string>
|
<string name="push_channel_description">Zobrazí oznámení, když jsou k dispozici nové videa.</string>
|
||||||
<string name="local_subscriptions">Lokální odběry</string>
|
<string name="local_subscriptions">Lokální odběry</string>
|
||||||
@ -351,7 +351,7 @@
|
|||||||
<string name="playlistNameReversed">Název playlistu (obráceně)</string>
|
<string name="playlistNameReversed">Název playlistu (obráceně)</string>
|
||||||
<string name="recentlyUpdated">Nedávno aktualizované</string>
|
<string name="recentlyUpdated">Nedávno aktualizované</string>
|
||||||
<string name="recentlyUpdatedReversed">Nedávno aktualizované (obráceně)</string>
|
<string name="recentlyUpdatedReversed">Nedávno aktualizované (obráceně)</string>
|
||||||
<string name="time_code">Časové razítko (sekundy)</string>
|
<string name="time_code">Čas (sekundy)</string>
|
||||||
<string name="show_more">Zobrazit více</string>
|
<string name="show_more">Zobrazit více</string>
|
||||||
<string name="added_to_playlist">Přidáno do playlistu</string>
|
<string name="added_to_playlist">Přidáno do playlistu</string>
|
||||||
<string name="queue">Fronta</string>
|
<string name="queue">Fronta</string>
|
||||||
@ -359,7 +359,7 @@
|
|||||||
<string name="livestreams">Živé přenosy</string>
|
<string name="livestreams">Živé přenosy</string>
|
||||||
<string name="alternative_videos_layout">Alternativní rozvržení videí</string>
|
<string name="alternative_videos_layout">Alternativní rozvržení videí</string>
|
||||||
<string name="sb_markers">Značky</string>
|
<string name="sb_markers">Značky</string>
|
||||||
<string name="sb_markers_summary">Označte si úseky na časové liště.</string>
|
<string name="sb_markers_summary">Označit úseky na časové liště.</string>
|
||||||
<string name="defaultIconLight">Výchozí světlá</string>
|
<string name="defaultIconLight">Výchozí světlá</string>
|
||||||
<string name="confirm_unsubscribing_summary">Zobrazit potvrzovací dialog před zrušením odběru.</string>
|
<string name="confirm_unsubscribing_summary">Zobrazit potvrzovací dialog před zrušením odběru.</string>
|
||||||
<string name="confirm_unsubscribe">Opravdu chcete zrušit odběr %1$s\?</string>
|
<string name="confirm_unsubscribe">Opravdu chcete zrušit odběr %1$s\?</string>
|
||||||
@ -402,4 +402,19 @@
|
|||||||
<string name="volume">Hlasitost</string>
|
<string name="volume">Hlasitost</string>
|
||||||
<string name="swipe_controls_summary">Pomocí gesta přejetí prstem upravit jas a hlasitost.</string>
|
<string name="swipe_controls_summary">Pomocí gesta přejetí prstem upravit jas a hlasitost.</string>
|
||||||
<string name="pop_up">Vyskakovací okno</string>
|
<string name="pop_up">Vyskakovací okno</string>
|
||||||
|
<string name="no_comments_available">Toto video nemá žádné komentáře.</string>
|
||||||
|
<string name="double_tap_seek">Dvojitým klepnutím vyhledáte</string>
|
||||||
|
<string name="captions_size">Velikost titulků</string>
|
||||||
|
<string name="comments_disabled">Komentáře jsou zakázány autorem videa.</string>
|
||||||
|
<string name="double_tap_seek_summary">Klepnutím dvakrát vlevo nebo vpravo přetočíte pozici přehrávače zpět nebo dopředu.</string>
|
||||||
|
<string name="all_caught_up">Už jste v obraze</string>
|
||||||
|
<string name="all_caught_up_summary">Viděli jste všechna nová videa</string>
|
||||||
|
<string name="app_backup">Záloha aplikace</string>
|
||||||
|
<string name="backup_restore_summary">Import a export odběrů, playlistů, …</string>
|
||||||
|
<string name="exportsuccess">Exportováno.</string>
|
||||||
|
<string name="privacy_alert">Upozornění na soukromí</string>
|
||||||
|
<string name="username_email">Pokračovat s e-mailovou adresou\? (není doporučeno)</string>
|
||||||
|
<string name="proceed">Pokračovat</string>
|
||||||
|
<string name="export_playlists">Exportovat playlisty</string>
|
||||||
|
<string name="import_playlists">Importovat playlisty</string>
|
||||||
</resources>
|
</resources>
|
@ -393,4 +393,28 @@
|
|||||||
<string name="bookmark">Lesezeichen</string>
|
<string name="bookmark">Lesezeichen</string>
|
||||||
<string name="clear_bookmarks">Lesezeichen löschen</string>
|
<string name="clear_bookmarks">Lesezeichen löschen</string>
|
||||||
<string name="bookmarks_empty">Noch keine Lesezeichen vorhanden!</string>
|
<string name="bookmarks_empty">Noch keine Lesezeichen vorhanden!</string>
|
||||||
|
<string name="brightness">Helligkeit</string>
|
||||||
|
<string name="auto">Automatisch</string>
|
||||||
|
<string name="volume">Lautstärke</string>
|
||||||
|
<string name="not_enabled">Menüelement nicht aktiviert!</string>
|
||||||
|
<string name="select_other_start_tab">Bitte wähle zuerst einen anderen Start-Tab!</string>
|
||||||
|
<string name="swipe_controls">Wischgesten-Steuerung</string>
|
||||||
|
<string name="defaults">Standardwerte</string>
|
||||||
|
<string name="pop_up">Pop-Up</string>
|
||||||
|
<string name="no_comments_available">Zu diesem Video gibt es keine Kommentare.</string>
|
||||||
|
<string name="comments_disabled">Kommentare sind durch den Uploader deaktiviert.</string>
|
||||||
|
<string name="all_caught_up_summary">Du hast alle neuen Videos gesehen</string>
|
||||||
|
<string name="swipe_controls_summary">Verwende eine Wischgeste, um Helligkeit und Lautstärke anzupassen.</string>
|
||||||
|
<string name="captions_size">Untertitelgröße</string>
|
||||||
|
<string name="double_tap_seek">Doppeltippen zum Suchen</string>
|
||||||
|
<string name="all_caught_up">Du bist auf dem neuesten Stand</string>
|
||||||
|
<string name="double_tap_seek_summary">Tippen Sie zweimal links oder rechts, um die Position des Players zurück- oder vorzuspulen.</string>
|
||||||
|
<string name="import_playlists">Playlists importieren</string>
|
||||||
|
<string name="export_playlists">Playlists exportieren</string>
|
||||||
|
<string name="app_backup">App-Backup</string>
|
||||||
|
<string name="backup_restore_summary">Import & Export von Abonnements, Playlists, …</string>
|
||||||
|
<string name="privacy_alert">Datenschutz-Warnung</string>
|
||||||
|
<string name="proceed">Fortfahren</string>
|
||||||
|
<string name="username_email">Mit einer nicht empfohlenen E-Mail-Adresse fortfahren\?</string>
|
||||||
|
<string name="exportsuccess">Exportiert.</string>
|
||||||
</resources>
|
</resources>
|
@ -110,7 +110,7 @@
|
|||||||
<string name="oledTheme">Negro</string>
|
<string name="oledTheme">Negro</string>
|
||||||
<string name="material_you">Tu Material</string>
|
<string name="material_you">Tu Material</string>
|
||||||
<string name="app_icon">Icono</string>
|
<string name="app_icon">Icono</string>
|
||||||
<string name="piped">Hilo</string>
|
<string name="piped">Piped</string>
|
||||||
<string name="youtube">Google YouTube</string>
|
<string name="youtube">Google YouTube</string>
|
||||||
<string name="sponsorblock_notifications">Notificaciones</string>
|
<string name="sponsorblock_notifications">Notificaciones</string>
|
||||||
<string name="enabled">Activado</string>
|
<string name="enabled">Activado</string>
|
||||||
@ -219,7 +219,7 @@
|
|||||||
<string name="telegram">Telegram</string>
|
<string name="telegram">Telegram</string>
|
||||||
<string name="reddit">Reddit</string>
|
<string name="reddit">Reddit</string>
|
||||||
<string name="community">Comunidad</string>
|
<string name="community">Comunidad</string>
|
||||||
<string name="discord">Discord (Servicio de mensajería instantánea)</string>
|
<string name="discord">Discord</string>
|
||||||
<string name="twitter">Twitter</string>
|
<string name="twitter">Twitter</string>
|
||||||
<string name="turnInternetOn">Por favor, active la WiFi o los datos móviles para conectarse a Internet.</string>
|
<string name="turnInternetOn">Por favor, active la WiFi o los datos móviles para conectarse a Internet.</string>
|
||||||
<string name="open">Abrir…</string>
|
<string name="open">Abrir…</string>
|
||||||
@ -267,7 +267,7 @@
|
|||||||
<string name="worst_quality">Peor calidad</string>
|
<string name="worst_quality">Peor calidad</string>
|
||||||
<string name="default_subtitle_language">Idioma subtítulo</string>
|
<string name="default_subtitle_language">Idioma subtítulo</string>
|
||||||
<string name="notify_new_streams">Notificaciones para nuevos directos</string>
|
<string name="notify_new_streams">Notificaciones para nuevos directos</string>
|
||||||
<string name="checking_frequency">Comprobando todo …</string>
|
<string name="checking_frequency">Comprobar cada…</string>
|
||||||
<string name="new_streams_count">%1$s nuevo directo disponible</string>
|
<string name="new_streams_count">%1$s nuevo directo disponible</string>
|
||||||
<string name="most_recent">Novedades</string>
|
<string name="most_recent">Novedades</string>
|
||||||
<string name="least_recent">Antiguo</string>
|
<string name="least_recent">Antiguo</string>
|
||||||
@ -289,7 +289,7 @@
|
|||||||
<string name="repeat_mode_current">Actual</string>
|
<string name="repeat_mode_current">Actual</string>
|
||||||
<string name="backup_restore">Copia de seguridad y restauración</string>
|
<string name="backup_restore">Copia de seguridad y restauración</string>
|
||||||
<string name="backup">Copia de seguridad</string>
|
<string name="backup">Copia de seguridad</string>
|
||||||
<string name="picture_in_picture">Imagen en imagen</string>
|
<string name="picture_in_picture">Reproductor emergente</string>
|
||||||
<string name="break_reminder_time">Minutos antes de ser recordado</string>
|
<string name="break_reminder_time">Minutos antes de ser recordado</string>
|
||||||
<string name="legacy_subscriptions">Vista de las suscripciones antiguas</string>
|
<string name="legacy_subscriptions">Vista de las suscripciones antiguas</string>
|
||||||
<string name="device_info">Información del dispositivo</string>
|
<string name="device_info">Información del dispositivo</string>
|
||||||
@ -306,7 +306,7 @@
|
|||||||
<string name="background_mode">Modo de segundo plano</string>
|
<string name="background_mode">Modo de segundo plano</string>
|
||||||
<string name="add_to_queue">Añadir a la cola</string>
|
<string name="add_to_queue">Añadir a la cola</string>
|
||||||
<string name="misc">Miscelánea</string>
|
<string name="misc">Miscelánea</string>
|
||||||
<string name="break_reminder">Recordatorio de tomate un descanso</string>
|
<string name="break_reminder">Recordatorio de descanso</string>
|
||||||
<string name="yt_shorts">Vídeos Cortos</string>
|
<string name="yt_shorts">Vídeos Cortos</string>
|
||||||
<string name="no_subtitles_available">No hay subtítulos disponibles</string>
|
<string name="no_subtitles_available">No hay subtítulos disponibles</string>
|
||||||
<string name="player_resize_mode">Modo de redimensión</string>
|
<string name="player_resize_mode">Modo de redimensión</string>
|
||||||
@ -324,11 +324,11 @@
|
|||||||
<string name="sb_skip_manual">Salto manual</string>
|
<string name="sb_skip_manual">Salto manual</string>
|
||||||
<string name="download_channel_name">Servicio de descarga</string>
|
<string name="download_channel_name">Servicio de descarga</string>
|
||||||
<string name="skip_segment">Saltar segmento</string>
|
<string name="skip_segment">Saltar segmento</string>
|
||||||
<string name="download_channel_description">Muestra una notificación cuando se descargan medios.</string>
|
<string name="download_channel_description">Muestra una notificación al descargar medios.</string>
|
||||||
<string name="background_channel_name">Modo de fondo</string>
|
<string name="background_channel_name">Modo de fondo</string>
|
||||||
<string name="push_channel_name">Trabajador de notificación</string>
|
<string name="push_channel_name">Trabajador de notificación</string>
|
||||||
<string name="sb_skip_manual_summary">No omita segmentos automáticamente, siempre pregunte antes.</string>
|
<string name="sb_skip_manual_summary">No omita segmentos automáticamente, siempre pregunte antes.</string>
|
||||||
<string name="push_channel_description">Muestra una notificación cuando haya nuevos flujos disponibles.</string>
|
<string name="push_channel_description">Muestra una notificación cuando hay nuevas transmisiones disponibles.</string>
|
||||||
<string name="background_channel_description">Mostrar una notificación con botones para controlar el reproductor de audio.</string>
|
<string name="background_channel_description">Mostrar una notificación con botones para controlar el reproductor de audio.</string>
|
||||||
<string name="local_subscriptions">Suscripciones locales</string>
|
<string name="local_subscriptions">Suscripciones locales</string>
|
||||||
<string name="preferences">Preferencias</string>
|
<string name="preferences">Preferencias</string>
|
||||||
@ -371,7 +371,7 @@
|
|||||||
<string name="end_time">Hora de finalización</string>
|
<string name="end_time">Hora de finalización</string>
|
||||||
<string name="notification_time">Hora de la notificación</string>
|
<string name="notification_time">Hora de la notificación</string>
|
||||||
<string name="notification_time_summary">Periodo de tiempo en el que se permite mostrar las notificaciones.</string>
|
<string name="notification_time_summary">Periodo de tiempo en el que se permite mostrar las notificaciones.</string>
|
||||||
<string name="alternative_player_layout_summary">Muestre los videos relacionados como una fila encima de los comentarios en lugar de debajo.</string>
|
<string name="alternative_player_layout_summary">Muestra los videos relacionados como una fila encima de los comentarios en lugar de debajo.</string>
|
||||||
<string name="alternative_trending_layout">Diseño de tendencias alternativas</string>
|
<string name="alternative_trending_layout">Diseño de tendencias alternativas</string>
|
||||||
<string name="alternative_player_layout">Diseño alternativo del reproductor</string>
|
<string name="alternative_player_layout">Diseño alternativo del reproductor</string>
|
||||||
<string name="navbar_order">Ordenar</string>
|
<string name="navbar_order">Ordenar</string>
|
||||||
@ -401,7 +401,12 @@
|
|||||||
<string name="brightness">Brillo</string>
|
<string name="brightness">Brillo</string>
|
||||||
<string name="swipe_controls_summary">Use el gesto de deslizar para ajustar el brillo y el volumen.</string>
|
<string name="swipe_controls_summary">Use el gesto de deslizar para ajustar el brillo y el volumen.</string>
|
||||||
<string name="defaults">Por defecto</string>
|
<string name="defaults">Por defecto</string>
|
||||||
<string name="pop_up">Ventana emergente</string>
|
<string name="pop_up">Emergente</string>
|
||||||
<string name="no_comments_available">Este vídeo no tiene comentarios disponibles.</string>
|
<string name="no_comments_available">Este vídeo no tiene comentarios disponibles.</string>
|
||||||
<string name="comments_disabled">Los comentarios están desactivados por el autor.</string>
|
<string name="comments_disabled">Los comentarios están desactivados por el creador del contenido.</string>
|
||||||
|
<string name="captions_size">Tamaño de los subtítulos</string>
|
||||||
|
<string name="all_caught_up">Estás al día</string>
|
||||||
|
<string name="double_tap_seek_summary">Toque dos veces a la izquierda o a la derecha para rebobinar o avanzar la posición del reproductor.</string>
|
||||||
|
<string name="double_tap_seek">Toca dos veces para buscar</string>
|
||||||
|
<string name="all_caught_up_summary">Has visto todos los vídeos nuevos</string>
|
||||||
</resources>
|
</resources>
|
@ -402,4 +402,19 @@
|
|||||||
<string name="best_quality">श्रेष्ठ</string>
|
<string name="best_quality">श्रेष्ठ</string>
|
||||||
<string name="worst_quality">न्यूनतम</string>
|
<string name="worst_quality">न्यूनतम</string>
|
||||||
<string name="pop_up">पॉप अप</string>
|
<string name="pop_up">पॉप अप</string>
|
||||||
|
<string name="no_comments_available">इस वीडियो में कोई टिप्पणी उपलब्ध नहीं है।</string>
|
||||||
|
<string name="captions_size">कैप्शन का आकार</string>
|
||||||
|
<string name="double_tap_seek">तलाश के लिए दो बार टैप करें</string>
|
||||||
|
<string name="double_tap_seek_summary">प्लेयर की स्थिति को रिवाइंड या फॉरवर्ड करने के लिए बाईं या दाईं ओर दो बार टैप करें।</string>
|
||||||
|
<string name="all_caught_up_summary">आपने सभी नए वीडियो देख लिए हैं</string>
|
||||||
|
<string name="all_caught_up">आप सभी काफी अधिक रुचि ले चुके हैं</string>
|
||||||
|
<string name="import_playlists">प्लेलिस्ट आयात करें</string>
|
||||||
|
<string name="export_playlists">प्लेलिस्ट निर्यात करें</string>
|
||||||
|
<string name="app_backup">ऐप बैकअप</string>
|
||||||
|
<string name="backup_restore_summary">आयात और निर्यात सब्सक्रिप्शनस, प्लेलिस्ट, …</string>
|
||||||
|
<string name="privacy_alert">गोपनीयता चेतावनी</string>
|
||||||
|
<string name="username_email">एक ऐसे ई-मेल पते के साथ आगे बढ़ें जो अनुशंसित नहीं है\?</string>
|
||||||
|
<string name="proceed">आगे बढ़ें</string>
|
||||||
|
<string name="exportsuccess">निर्यात किया गया।</string>
|
||||||
|
<string name="comments_disabled">अपलोडर द्वारा टिप्पणियां अक्षम कर दी गई हैं।</string>
|
||||||
</resources>
|
</resources>
|
@ -395,4 +395,28 @@
|
|||||||
<string name="trends">Felkapottak</string>
|
<string name="trends">Felkapottak</string>
|
||||||
<string name="select_other_start_tab">Válasszon egy másik kezdőlapot először!</string>
|
<string name="select_other_start_tab">Válasszon egy másik kezdőlapot először!</string>
|
||||||
<string name="not_enabled">Menüelem nincs engedélyezve!</string>
|
<string name="not_enabled">Menüelem nincs engedélyezve!</string>
|
||||||
|
<string name="volume">Hangerő</string>
|
||||||
|
<string name="auto">Automatikus</string>
|
||||||
|
<string name="brightness">Fényerő</string>
|
||||||
|
<string name="swipe_controls_summary">Húzási gesztusok használata a hangerő és a fényerő beállításához.</string>
|
||||||
|
<string name="swipe_controls">Húzási vezérlők</string>
|
||||||
|
<string name="defaults">Alapértelmezett</string>
|
||||||
|
<string name="pop_up">Felugró</string>
|
||||||
|
<string name="no_comments_available">Nincsenek elérhető hozzászólások a videóhoz.</string>
|
||||||
|
<string name="comments_disabled">A hozzászólásokat a feltöltő letiltotta.</string>
|
||||||
|
<string name="double_tap_seek_summary">Érintse meg kétszer bal- vagy jobboldalt, hogy megváltoztassa a lejátszó helyzetét.</string>
|
||||||
|
<string name="captions_size">Feliratok mérete</string>
|
||||||
|
<string name="double_tap_seek">Dupla érintéses keresés</string>
|
||||||
|
<string name="exportsuccess">Exportálva.</string>
|
||||||
|
<string name="import_playlists">Lejátszási listák importálása</string>
|
||||||
|
<string name="backup_restore_summary">Feliratkozások, lejátszási listák importálása és exportálása…</string>
|
||||||
|
<string name="app_backup">Alkalmazás biztonsági mentése</string>
|
||||||
|
<string name="export_playlists">Lejátszási listák exportálása</string>
|
||||||
|
<string name="proceed">Folytatás</string>
|
||||||
|
<string name="username_email">Folytatja email címmel, akkor is, ha nem ajánlott\?</string>
|
||||||
|
<string name="privacy_alert">Adatvédelmi figyelmeztetés</string>
|
||||||
|
<string name="pinch_control">Csípésvezérlés</string>
|
||||||
|
<string name="pinch_control_summary">Használjon csípés gesztust, a nagyításhoz/kicsinyítéshez.</string>
|
||||||
|
<string name="all_caught_up_summary">Már látta az összes új videót</string>
|
||||||
|
<string name="all_caught_up">Teljesen felzárkózott</string>
|
||||||
</resources>
|
</resources>
|
@ -404,4 +404,19 @@
|
|||||||
<string name="pop_up">Hamparan</string>
|
<string name="pop_up">Hamparan</string>
|
||||||
<string name="no_comments_available">Video ini tidak ada komentar yang tersedia.</string>
|
<string name="no_comments_available">Video ini tidak ada komentar yang tersedia.</string>
|
||||||
<string name="comments_disabled">Komentar dinonaktifkan oleh pengunggah.</string>
|
<string name="comments_disabled">Komentar dinonaktifkan oleh pengunggah.</string>
|
||||||
|
<string name="captions_size">Ukuran keterangan</string>
|
||||||
|
<string name="double_tap_seek">Ketuk dua kali untuk seek</string>
|
||||||
|
<string name="double_tap_seek_summary">Ketuk dua kali pada sisi kiri atau kanan untuk mundur atau maju posisi pemain.</string>
|
||||||
|
<string name="all_caught_up_summary">Anda telah melihat semua video baru</string>
|
||||||
|
<string name="all_caught_up">Anda telah melihat semuanya</string>
|
||||||
|
<string name="import_playlists">Impor daftar putar</string>
|
||||||
|
<string name="export_playlists">Ekspor daftar putar</string>
|
||||||
|
<string name="app_backup">Cadangan Aplikasi</string>
|
||||||
|
<string name="exportsuccess">Diekspor.</string>
|
||||||
|
<string name="privacy_alert">Peringatan privasi</string>
|
||||||
|
<string name="username_email">Lanjutkan dengan alamat surel yang tidak disarankan\?</string>
|
||||||
|
<string name="proceed">Lanjutkan</string>
|
||||||
|
<string name="backup_restore_summary">Impor & ekspor langganan, daftar putar, …</string>
|
||||||
|
<string name="pinch_control">Kendali cubit</string>
|
||||||
|
<string name="pinch_control_summary">Gunakan gestur cubit untuk perbesar/perkecil.</string>
|
||||||
</resources>
|
</resources>
|
@ -65,7 +65,7 @@
|
|||||||
<string name="darkTheme">Scuro</string>
|
<string name="darkTheme">Scuro</string>
|
||||||
<string name="comments">Commenti</string>
|
<string name="comments">Commenti</string>
|
||||||
<string name="videoCount">%1$s video</string>
|
<string name="videoCount">%1$s video</string>
|
||||||
<string name="noInternet">Connettiti ad internet prima.</string>
|
<string name="noInternet">Prima connettiti ad internet.</string>
|
||||||
<string name="retry">Riprova</string>
|
<string name="retry">Riprova</string>
|
||||||
<string name="instance">Istanza</string>
|
<string name="instance">Istanza</string>
|
||||||
<string name="settings">Impostazioni</string>
|
<string name="settings">Impostazioni</string>
|
||||||
@ -251,10 +251,10 @@
|
|||||||
<string name="yt_shorts">Short</string>
|
<string name="yt_shorts">Short</string>
|
||||||
<string name="add_to_queue">Aggiungi alla coda</string>
|
<string name="add_to_queue">Aggiungi alla coda</string>
|
||||||
<string name="notifications">Notifiche</string>
|
<string name="notifications">Notifiche</string>
|
||||||
<string name="notify_new_streams">Notifiche per nuove stream</string>
|
<string name="notify_new_streams">Notifiche per nuovi video</string>
|
||||||
<string name="notify_new_streams_summary">Notifiche su nuovi contenuti dai creatore che segui.</string>
|
<string name="notify_new_streams_summary">Notifiche su nuovi contenuti dai creatore che segui.</string>
|
||||||
<string name="checking_frequency">Controlla ogni…</string>
|
<string name="checking_frequency">Controlla ogni…</string>
|
||||||
<string name="new_streams_count">%1$s nuove stream disponibili</string>
|
<string name="new_streams_count">%1$s nuovi video disponibili</string>
|
||||||
<string name="new_streams_by">Nuova stream di %1$s…</string>
|
<string name="new_streams_by">Nuova stream di %1$s…</string>
|
||||||
<string name="most_views">Più visti</string>
|
<string name="most_views">Più visti</string>
|
||||||
<string name="least_views">Meno visti</string>
|
<string name="least_views">Meno visti</string>
|
||||||
@ -300,7 +300,7 @@
|
|||||||
<string name="repeat_mode_current">Attuale</string>
|
<string name="repeat_mode_current">Attuale</string>
|
||||||
<string name="picture_in_picture">Picture in Picture</string>
|
<string name="picture_in_picture">Picture in Picture</string>
|
||||||
<string name="player_resize_mode">Modalità ridimensionamento</string>
|
<string name="player_resize_mode">Modalità ridimensionamento</string>
|
||||||
<string name="maximum_image_cache">Dimensione massima della cache delle immagini</string>
|
<string name="maximum_image_cache">Dimensione massima cache immagini</string>
|
||||||
<string name="open_copied">Apri</string>
|
<string name="open_copied">Apri</string>
|
||||||
<string name="reset_watch_positions">Ripristina</string>
|
<string name="reset_watch_positions">Ripristina</string>
|
||||||
<string name="system_caption_style">Stile dei sottotitoli di sistema</string>
|
<string name="system_caption_style">Stile dei sottotitoli di sistema</string>
|
||||||
@ -317,7 +317,7 @@
|
|||||||
<string name="trending_layout">Layout tendenze alternativo</string>
|
<string name="trending_layout">Layout tendenze alternativo</string>
|
||||||
<string name="renamePlaylist">Rinomina la playlist</string>
|
<string name="renamePlaylist">Rinomina la playlist</string>
|
||||||
<string name="new_videos_badge">Indicatore per i nuovi video</string>
|
<string name="new_videos_badge">Indicatore per i nuovi video</string>
|
||||||
<string name="new_videos_badge_summary">Mostra un badge con il numero dei nuovi video, se ce ne sono.</string>
|
<string name="new_videos_badge_summary">Mostra una notifica con il numero dei nuovi video, se ce ne sono.</string>
|
||||||
<string name="wifi">Wi-Fi</string>
|
<string name="wifi">Wi-Fi</string>
|
||||||
<string name="mobile_data">Dati mobili</string>
|
<string name="mobile_data">Dati mobili</string>
|
||||||
<string name="skip_segment">Salta il segmento</string>
|
<string name="skip_segment">Salta il segmento</string>
|
||||||
@ -332,7 +332,7 @@
|
|||||||
<string name="background_channel_name">Modalità in sottofondo</string>
|
<string name="background_channel_name">Modalità in sottofondo</string>
|
||||||
<string name="download_channel_name">Servizio di Download</string>
|
<string name="download_channel_name">Servizio di Download</string>
|
||||||
<string name="push_channel_name">Operatore di notifica</string>
|
<string name="push_channel_name">Operatore di notifica</string>
|
||||||
<string name="push_channel_description">Mostra una notifica quando sono disponibili nuove stream.</string>
|
<string name="push_channel_description">Mostra una notifica quando sono disponibili nuovi video.</string>
|
||||||
<string name="save_feed">Carica flusso in secondo piano</string>
|
<string name="save_feed">Carica flusso in secondo piano</string>
|
||||||
<string name="save_feed_summary">Carica il flusso di iscrizioni in secondo piano e impedisci che venga aggiornato automaticamente.</string>
|
<string name="save_feed_summary">Carica il flusso di iscrizioni in secondo piano e impedisci che venga aggiornato automaticamente.</string>
|
||||||
<string name="legacy_subscriptions">Vista delle iscrizioni obsoleta</string>
|
<string name="legacy_subscriptions">Vista delle iscrizioni obsoleta</string>
|
||||||
@ -363,14 +363,14 @@
|
|||||||
<string name="time_code">Codice temporale (secondi)</string>
|
<string name="time_code">Codice temporale (secondi)</string>
|
||||||
<string name="playlistCloned">Playlist clonata</string>
|
<string name="playlistCloned">Playlist clonata</string>
|
||||||
<string name="confirm_unsubscribe">Sei sicuro di voler annullare l\'iscrizione a %1$s\?</string>
|
<string name="confirm_unsubscribe">Sei sicuro di voler annullare l\'iscrizione a %1$s\?</string>
|
||||||
<string name="confirm_unsubscribing">Conferma l\'annullamento dell\'iscrizione</string>
|
<string name="confirm_unsubscribing">Conferma la disiscrizione</string>
|
||||||
<string name="play_all">Riproduci tutti</string>
|
<string name="play_all">Riproduci tutti</string>
|
||||||
<string name="confirm_unsubscribing_summary">Mostra una finestra di conferma prima di annullare l\'iscrizione.</string>
|
<string name="confirm_unsubscribing_summary">Mostra una finestra di conferma prima di annullare l\'iscrizione.</string>
|
||||||
<string name="start_time">Ora di inizio</string>
|
<string name="start_time">Ora di inizio</string>
|
||||||
<string name="notification_time">Tempo di notifica</string>
|
<string name="notification_time">Orari di notifica</string>
|
||||||
<string name="time">Tempo</string>
|
<string name="time">Tempo</string>
|
||||||
<string name="end_time">Ora di fine</string>
|
<string name="end_time">Ora di fine</string>
|
||||||
<string name="notification_time_summary">L\'intervallo di tempo in cui le notifiche possono essere visualizzate.</string>
|
<string name="notification_time_summary">L\'intervallo di ore in cui le notifiche possono essere mostrate.</string>
|
||||||
<string name="local_playlists">Playlist locali</string>
|
<string name="local_playlists">Playlist locali</string>
|
||||||
<string name="not_enabled">Elemento del menu non attivo!</string>
|
<string name="not_enabled">Elemento del menu non attivo!</string>
|
||||||
<string name="brightness">Luminosità</string>
|
<string name="brightness">Luminosità</string>
|
||||||
@ -402,4 +402,11 @@
|
|||||||
<string name="clear_bookmarks">Cancella segnalibri</string>
|
<string name="clear_bookmarks">Cancella segnalibri</string>
|
||||||
<string name="bookmarks_empty">Ancora nessun segnalibro!</string>
|
<string name="bookmarks_empty">Ancora nessun segnalibro!</string>
|
||||||
<string name="pop_up">Pop-up</string>
|
<string name="pop_up">Pop-up</string>
|
||||||
|
<string name="no_comments_available">Questo video non ha commenti disponibili.</string>
|
||||||
|
<string name="comments_disabled">I commenti sono disattivati dall\'autore del video.</string>
|
||||||
|
<string name="captions_size">Dimensione sottotitoli</string>
|
||||||
|
<string name="double_tap_seek">Doppio tocco per cercare</string>
|
||||||
|
<string name="double_tap_seek_summary">Tocca due volte a sinistra o a destra per riavvolgere o avanzare la posizione del lettore.</string>
|
||||||
|
<string name="all_caught_up">Tutto a posto</string>
|
||||||
|
<string name="all_caught_up_summary">Hai visto tutti i video nuovi</string>
|
||||||
</resources>
|
</resources>
|
@ -402,4 +402,11 @@
|
|||||||
<string name="defaults">ברירות מחדל</string>
|
<string name="defaults">ברירות מחדל</string>
|
||||||
<string name="pop_up">צף</string>
|
<string name="pop_up">צף</string>
|
||||||
<string name="swipe_controls_summary">להשתמש במחוות החלקה כדי לכוון את הבהירות ואת עצמת השמע.</string>
|
<string name="swipe_controls_summary">להשתמש במחוות החלקה כדי לכוון את הבהירות ואת עצמת השמע.</string>
|
||||||
|
<string name="no_comments_available">התגובות לא זמינות לסרטון הזה.</string>
|
||||||
|
<string name="comments_disabled">הערות הושבתו על ידי מעלה הסרטון.</string>
|
||||||
|
<string name="double_tap_seek">נגיעה כפולה לדילוג</string>
|
||||||
|
<string name="all_caught_up">אין יותר הצעות</string>
|
||||||
|
<string name="captions_size">גודל כתוביות</string>
|
||||||
|
<string name="double_tap_seek_summary">נגיעה כפולה שמאלה או ימינה מחזירה אחורה מקדמת קדימה את מיקום הנגינה.</string>
|
||||||
|
<string name="all_caught_up_summary">ראית את כל הסרטונים החדשים</string>
|
||||||
</resources>
|
</resources>
|
@ -402,4 +402,19 @@
|
|||||||
<string name="swipe_controls_summary">Braukimo gestu sureguliuokite ryškumą ir garsumą.</string>
|
<string name="swipe_controls_summary">Braukimo gestu sureguliuokite ryškumą ir garsumą.</string>
|
||||||
<string name="defaults">Numatyti</string>
|
<string name="defaults">Numatyti</string>
|
||||||
<string name="pop_up">Iššokantis langas</string>
|
<string name="pop_up">Iššokantis langas</string>
|
||||||
|
<string name="no_comments_available">Šis vaizdo įrašas neturi prieinamų komentarų.</string>
|
||||||
|
<string name="comments_disabled">Įkėlėjas išjungė komentarus.</string>
|
||||||
|
<string name="double_tap_seek">Dukart bakstelėti, kad prasukti</string>
|
||||||
|
<string name="all_caught_up">Jūs viską pasivijote</string>
|
||||||
|
<string name="all_caught_up_summary">Peržiūrėjote visus naujus vaizdo įrašus</string>
|
||||||
|
<string name="captions_size">Subtitrų dydis</string>
|
||||||
|
<string name="double_tap_seek_summary">Bakstelėkite du kartus kairėje arba dešinėje, kad prasuktumėte grotuvo padėtį atgal arba pirmyn.</string>
|
||||||
|
<string name="import_playlists">Importuoti grojaraščius</string>
|
||||||
|
<string name="export_playlists">Eksportuoti grojaraščius</string>
|
||||||
|
<string name="app_backup">Programėlės atsarginė kopija</string>
|
||||||
|
<string name="backup_restore_summary">Importuokite ir eksportuokite prenumeratas, grojaraščius, …</string>
|
||||||
|
<string name="exportsuccess">Eksportuota.</string>
|
||||||
|
<string name="proceed">Tęsti</string>
|
||||||
|
<string name="privacy_alert">Įspėjimas apie privatumą</string>
|
||||||
|
<string name="username_email">Tęsti naudojant el. pašto adresą, nors tai ir nėra rekomenduojama\?</string>
|
||||||
</resources>
|
</resources>
|
@ -401,4 +401,20 @@
|
|||||||
<string name="brightness">Lysstyrke</string>
|
<string name="brightness">Lysstyrke</string>
|
||||||
<string name="volume">Lydstyrke</string>
|
<string name="volume">Lydstyrke</string>
|
||||||
<string name="auto">Auto</string>
|
<string name="auto">Auto</string>
|
||||||
|
<string name="pop_up">Oppsprett</string>
|
||||||
|
<string name="no_comments_available">Videoen har ingen tilgjengelige kommentarer.</string>
|
||||||
|
<string name="comments_disabled">Kommentarer er avskrudd av opplasteren.</string>
|
||||||
|
<string name="double_tap_seek">Dobbelttrykk for å blafre</string>
|
||||||
|
<string name="double_tap_seek_summary">Trykk to ganger til venstre eller høyre for å spole frem eller tilbake avspillingen.</string>
|
||||||
|
<string name="all_caught_up">Du er oppdatert</string>
|
||||||
|
<string name="all_caught_up_summary">Du har sett alle nye videoer</string>
|
||||||
|
<string name="import_playlists">Importer spillelister</string>
|
||||||
|
<string name="export_playlists">Eksporter spillelister</string>
|
||||||
|
<string name="app_backup">Legg til sikkerhetskopi</string>
|
||||||
|
<string name="backup_restore_summary">Importer og eksporter abonnementer, spillelister …</string>
|
||||||
|
<string name="exportsuccess">Eksportert.</string>
|
||||||
|
<string name="privacy_alert">Personvernsvarsel</string>
|
||||||
|
<string name="proceed">Fortsett</string>
|
||||||
|
<string name="captions_size">Undertekststørrelse</string>
|
||||||
|
<string name="username_email">Fortsett med en e-postadresse som ikke er anbefalt\?</string>
|
||||||
</resources>
|
</resources>
|
@ -402,4 +402,19 @@
|
|||||||
<string name="swipe_controls_summary">ଉଜ୍ଜ୍ୱଳତା ଏବଂ ଭଲ୍ୟୁମ୍ ସଜାଡିବା ପାଇଁ ସ୍ୱାଇପ୍ ଅଙ୍ଗଭଙ୍ଗୀ ବ୍ୟବହାର କରନ୍ତୁ ।</string>
|
<string name="swipe_controls_summary">ଉଜ୍ଜ୍ୱଳତା ଏବଂ ଭଲ୍ୟୁମ୍ ସଜାଡିବା ପାଇଁ ସ୍ୱାଇପ୍ ଅଙ୍ଗଭଙ୍ଗୀ ବ୍ୟବହାର କରନ୍ତୁ ।</string>
|
||||||
<string name="defaults">ଡିଫଲ୍ଟ ଗୁଡ଼ିକ</string>
|
<string name="defaults">ଡିଫଲ୍ଟ ଗୁଡ଼ିକ</string>
|
||||||
<string name="pop_up">ପପ୍-ଅପ୍</string>
|
<string name="pop_up">ପପ୍-ଅପ୍</string>
|
||||||
|
<string name="no_comments_available">ଏହି ଭିଡିଓରେ କୌଣସି ମନ୍ତବ୍ୟ ଉପଲବ୍ଧ ନାହିଁ ।</string>
|
||||||
|
<string name="comments_disabled">ମନ୍ତବ୍ୟଗୁଡିକ ଅପଲୋଡର୍ ଙ୍କ ଦ୍ୱାରା ଅକ୍ଷମ ହୋଇଛି ।</string>
|
||||||
|
<string name="captions_size">କ୍ୟାପସନ୍ ର ଆକାର</string>
|
||||||
|
<string name="double_tap_seek">ଖୋଜିବାକୁ ଦୁଇଥର ଟ୍ୟାପ୍ କରନ୍ତୁ</string>
|
||||||
|
<string name="double_tap_seek_summary">ପ୍ଲେୟାର୍ ସ୍ଥିତିକୁ ରିଭାଇଣ୍ଡ୍ କିମ୍ବା ଅଗ୍ରଗାମୀ କରିବାକୁ ବାମ କିମ୍ବା ଡାହାଣରେ ଦୁଇଥର ଟ୍ୟାପ୍ କରନ୍ତୁ ।</string>
|
||||||
|
<string name="all_caught_up_summary">ଆପଣ ସମସ୍ତ ନୂତନ ଭିଡିଓ ଦେଖିସାରିଛନ୍ତି</string>
|
||||||
|
<string name="all_caught_up">ସମସ୍ତ ନୂତନ ପୋଷ୍ଟ ପାଇସାରିଛ</string>
|
||||||
|
<string name="export_playlists">ପ୍ଲେଲିଷ୍ଟ ରପ୍ତାନି କରନ୍ତୁ</string>
|
||||||
|
<string name="import_playlists">ପ୍ଲେଲିଷ୍ଟ ଆମଦାନୀ କରନ୍ତୁ</string>
|
||||||
|
<string name="app_backup">ଆପ୍ ବ୍ୟାକଅପ୍</string>
|
||||||
|
<string name="backup_restore_summary">ସଦସ୍ୟତା, ପ୍ଲେଲିଷ୍ଟ ଆମଦାନି ଏବଂ ରପ୍ତାନି କରନ୍ତୁ,…</string>
|
||||||
|
<string name="exportsuccess">ରପ୍ତାନି ହୋଇଛି ।</string>
|
||||||
|
<string name="privacy_alert">ଗୋପନୀୟତା ସତର୍କତା</string>
|
||||||
|
<string name="username_email">ଏକ ଇ-ମେଲ୍ ଠିକଣା ସହିତ ଅଗ୍ରଗତି କରନ୍ତୁ ଯାହା ସୁପାରିଶ କରାଯାଏ ନାହିଁ\?</string>
|
||||||
|
<string name="proceed">ଅଗ୍ରଗତି କର</string>
|
||||||
</resources>
|
</resources>
|
@ -112,7 +112,7 @@
|
|||||||
<string name="music_videos">YT Music (Teledyski)</string>
|
<string name="music_videos">YT Music (Teledyski)</string>
|
||||||
<string name="music_albums">YT Music (Albumy)</string>
|
<string name="music_albums">YT Music (Albumy)</string>
|
||||||
<string name="music_playlists">YT Music (Playlisty)</string>
|
<string name="music_playlists">YT Music (Playlisty)</string>
|
||||||
<string name="playOnBackground">Odtwarzanie w tle</string>
|
<string name="playOnBackground">Odtwórz w tle</string>
|
||||||
<string name="piped">Piped</string>
|
<string name="piped">Piped</string>
|
||||||
<string name="enabled">Włączono</string>
|
<string name="enabled">Włączono</string>
|
||||||
<string name="disabled">Wyłączono</string>
|
<string name="disabled">Wyłączono</string>
|
||||||
@ -196,7 +196,7 @@
|
|||||||
<string name="legacy_subscriptions">Starszy widok subskrybcji</string>
|
<string name="legacy_subscriptions">Starszy widok subskrybcji</string>
|
||||||
<string name="device_info">Informacje o urządzeniu</string>
|
<string name="device_info">Informacje o urządzeniu</string>
|
||||||
<string name="downloading">Pobieranie…</string>
|
<string name="downloading">Pobieranie…</string>
|
||||||
<string name="autoplay_summary">Odtwórz kolejny filmik po obejrzeniu bieżącego.</string>
|
<string name="autoplay_summary">Automatycznie odtwórz kolejny filmik po obejrzeniu bieżącego.</string>
|
||||||
<string name="seekbar_preview">Podgląd wideo</string>
|
<string name="seekbar_preview">Podgląd wideo</string>
|
||||||
<string name="seekbar_preview_summary">Pokaż ujęcie podczas przesuwania wskaźnika odtwarzania.</string>
|
<string name="seekbar_preview_summary">Pokaż ujęcie podczas przesuwania wskaźnika odtwarzania.</string>
|
||||||
<string name="playingOnBackground">Odtwarzanie w tle…</string>
|
<string name="playingOnBackground">Odtwarzanie w tle…</string>
|
||||||
@ -280,7 +280,7 @@
|
|||||||
<string name="repeat_mode_current">Aktualny</string>
|
<string name="repeat_mode_current">Aktualny</string>
|
||||||
<string name="backup_restore">Kopia zapasowa i przywracanie</string>
|
<string name="backup_restore">Kopia zapasowa i przywracanie</string>
|
||||||
<string name="backup">Wykonaj kopię zapasową</string>
|
<string name="backup">Wykonaj kopię zapasową</string>
|
||||||
<string name="picture_in_picture">Obraz w obrazie</string>
|
<string name="picture_in_picture">Tryb PIP (obraz w obrazie)</string>
|
||||||
<string name="player_resize_mode">Rozmiar obrazu</string>
|
<string name="player_resize_mode">Rozmiar obrazu</string>
|
||||||
<string name="maximum_image_cache">Maks. rozmiar pamięci podręcznej obrazów</string>
|
<string name="maximum_image_cache">Maks. rozmiar pamięci podręcznej obrazów</string>
|
||||||
<string name="copied_to_clipboard">Skopiowano do schowka</string>
|
<string name="copied_to_clipboard">Skopiowano do schowka</string>
|
||||||
@ -401,5 +401,20 @@
|
|||||||
<string name="swipe_controls">Sterowanie gestami</string>
|
<string name="swipe_controls">Sterowanie gestami</string>
|
||||||
<string name="swipe_controls_summary">Steruj jasnością i głośnością za pomocą gestów.</string>
|
<string name="swipe_controls_summary">Steruj jasnością i głośnością za pomocą gestów.</string>
|
||||||
<string name="defaults">Domyślne</string>
|
<string name="defaults">Domyślne</string>
|
||||||
<string name="pop_up">Dymek</string>
|
<string name="pop_up">Tryb PIP</string>
|
||||||
|
<string name="no_comments_available">Nie skomentowano jeszcze tego materiału.</string>
|
||||||
|
<string name="comments_disabled">Komentarze zostały wyłączone przez twórcę.</string>
|
||||||
|
<string name="captions_size">Rozmiar napisów</string>
|
||||||
|
<string name="double_tap_seek">Gest przewijania</string>
|
||||||
|
<string name="all_caught_up">To już jest koniec</string>
|
||||||
|
<string name="double_tap_seek_summary">Stuknij dwukrotnie po lewej lub prawej, aby przewinąć materiał do tyłu lub do przodu.</string>
|
||||||
|
<string name="all_caught_up_summary">Obejrzano wszystkie nowe materiały</string>
|
||||||
|
<string name="import_playlists">Importuj playlisty</string>
|
||||||
|
<string name="export_playlists">Eksportuj playlisty</string>
|
||||||
|
<string name="app_backup">Kopia ustawień aplikacji</string>
|
||||||
|
<string name="backup_restore_summary">Importuj lub eksportuj subskrypcje, playlisty, …</string>
|
||||||
|
<string name="exportsuccess">Pomyślnie wyeksportowano!</string>
|
||||||
|
<string name="proceed">Kontynuuj</string>
|
||||||
|
<string name="privacy_alert">Ostrzeżenie o prywatności</string>
|
||||||
|
<string name="username_email">Jako nazwę użytkownika wstawiłeś/aś adres e-mail, lecz nie jest to zalecane. Czy na pewno chcesz kontynuować\?</string>
|
||||||
</resources>
|
</resources>
|
@ -400,4 +400,21 @@
|
|||||||
<string name="select_other_start_tab">Selecione outra aba inicial primeiro!</string>
|
<string name="select_other_start_tab">Selecione outra aba inicial primeiro!</string>
|
||||||
<string name="brightness">Brilho</string>
|
<string name="brightness">Brilho</string>
|
||||||
<string name="volume">Volume</string>
|
<string name="volume">Volume</string>
|
||||||
|
<string name="defaults">Padrões</string>
|
||||||
|
<string name="pop_up">Pop-Up</string>
|
||||||
|
<string name="no_comments_available">Este vídeo não tem comentários disponíveis.</string>
|
||||||
|
<string name="comments_disabled">Os comentários são desativados pelo criador.</string>
|
||||||
|
<string name="captions_size">Tamanho das legendas</string>
|
||||||
|
<string name="double_tap_seek">Toque duas vezes para buscar</string>
|
||||||
|
<string name="all_caught_up">Você está atualizado</string>
|
||||||
|
<string name="all_caught_up_summary">Você viu todos os vídeos novos</string>
|
||||||
|
<string name="double_tap_seek_summary">Toque duas vezes à esquerda ou à direita para retroceder ou avançar a posição do player.</string>
|
||||||
|
<string name="import_playlists">Importar playlists</string>
|
||||||
|
<string name="export_playlists">Exportar playlists</string>
|
||||||
|
<string name="app_backup">Backup do Aplicativo</string>
|
||||||
|
<string name="backup_restore_summary">Importar e exportar inscrições, playlists, …</string>
|
||||||
|
<string name="exportsuccess">Exportado com sucesso!</string>
|
||||||
|
<string name="privacy_alert">Alerta de privacidade</string>
|
||||||
|
<string name="proceed">Continuar</string>
|
||||||
|
<string name="username_email">Você inseriu um endereço de e-mail como nome de usuário, o que não é recomendado. Tem certeza de que deseja continuar\?</string>
|
||||||
</resources>
|
</resources>
|
@ -401,4 +401,11 @@
|
|||||||
<string name="brightness">Parlaklık</string>
|
<string name="brightness">Parlaklık</string>
|
||||||
<string name="swipe_controls_summary">Parlaklığı ve sesi ayarlamak için kaydırma hareketini kullan.</string>
|
<string name="swipe_controls_summary">Parlaklığı ve sesi ayarlamak için kaydırma hareketini kullan.</string>
|
||||||
<string name="defaults">Varsayılanlar</string>
|
<string name="defaults">Varsayılanlar</string>
|
||||||
|
<string name="pop_up">Açılır pencere</string>
|
||||||
|
<string name="comments_disabled">Yorumlar yükleyici tarafından devre dışı bırakıldı.</string>
|
||||||
|
<string name="no_comments_available">Bu videoda yorum yok.</string>
|
||||||
|
<string name="all_caught_up_summary">Tüm yeni videoları gördünüz</string>
|
||||||
|
<string name="captions_size">Altyazı boyutu</string>
|
||||||
|
<string name="double_tap_seek">Aramak için iki kez dokun</string>
|
||||||
|
<string name="double_tap_seek_summary">Oynatıcı konumunu ileri veya geri sarmak için sola veya sağa iki kez dokun.</string>
|
||||||
</resources>
|
</resources>
|
@ -402,4 +402,19 @@
|
|||||||
<string name="volume">Гучність</string>
|
<string name="volume">Гучність</string>
|
||||||
<string name="defaults">Типові</string>
|
<string name="defaults">Типові</string>
|
||||||
<string name="pop_up">Спливне вікно</string>
|
<string name="pop_up">Спливне вікно</string>
|
||||||
|
<string name="double_tap_seek">Двічі торкніться, щоб шукати</string>
|
||||||
|
<string name="double_tap_seek_summary">Двічі торкніться ліворуч або праворуч, щоб перемотати назад або вперед.</string>
|
||||||
|
<string name="no_comments_available">Це відео не має коментарів.</string>
|
||||||
|
<string name="comments_disabled">Коментарі вимкнені завантажувачем.</string>
|
||||||
|
<string name="captions_size">Розмір підписів</string>
|
||||||
|
<string name="all_caught_up">Ви все переглянули</string>
|
||||||
|
<string name="all_caught_up_summary">Ви переглянули всі нові відео</string>
|
||||||
|
<string name="app_backup">Резервне копіювання застосунку</string>
|
||||||
|
<string name="privacy_alert">Оповіщення про приватність</string>
|
||||||
|
<string name="username_email">Ви ввели адресу електронної пошти як ім\'я користувача, що не рекомендується. Ви впевнені, що хочете продовжити\?</string>
|
||||||
|
<string name="import_playlists">Імпорт добірок</string>
|
||||||
|
<string name="backup_restore_summary">Імпорт та експорт підписок, добірок, …</string>
|
||||||
|
<string name="exportsuccess">Успішно експортовано!</string>
|
||||||
|
<string name="proceed">Продовжити</string>
|
||||||
|
<string name="export_playlists">Експорт добірок</string>
|
||||||
</resources>
|
</resources>
|
@ -404,4 +404,19 @@
|
|||||||
<string name="pop_up">弹窗</string>
|
<string name="pop_up">弹窗</string>
|
||||||
<string name="comments_disabled">上传者禁止评论。</string>
|
<string name="comments_disabled">上传者禁止评论。</string>
|
||||||
<string name="no_comments_available">此视频无评论。</string>
|
<string name="no_comments_available">此视频无评论。</string>
|
||||||
|
<string name="double_tap_seek">轻按两次查找</string>
|
||||||
|
<string name="double_tap_seek_summary">在左侧或右侧轻按两次倒退或前进播放器位置。</string>
|
||||||
|
<string name="all_caught_up_summary">你看过了所有新视频</string>
|
||||||
|
<string name="all_caught_up">没什么可看的了</string>
|
||||||
|
<string name="captions_size">字幕尺寸</string>
|
||||||
|
<string name="import_playlists">导入播放列表</string>
|
||||||
|
<string name="export_playlists">导出播放列表</string>
|
||||||
|
<string name="app_backup">应用备份</string>
|
||||||
|
<string name="backup_restore_summary">导入和导出订阅、播放列表…</string>
|
||||||
|
<string name="exportsuccess">成功导出。</string>
|
||||||
|
<string name="privacy_alert">隐私警告</string>
|
||||||
|
<string name="username_email">你插入了一个电子邮件地址作为用户名,我们不推荐这样做。 你确定要继续吗?</string>
|
||||||
|
<string name="proceed">继续</string>
|
||||||
|
<string name="pinch_control_summary">使用伸缩手势进行缩小/放大操作。</string>
|
||||||
|
<string name="pinch_control">手指伸缩控制</string>
|
||||||
</resources>
|
</resources>
|
@ -1,8 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!-- Replaces the drawables of the default exoplayer controls (see https://github.com/google/ExoPlayer/blob/release-v2/library/ui/src/main/res/values/drawables.xml) -->
|
<?xml version="1.0" encoding="utf-8"?><!-- Replaces the drawables of the default exoplayer controls (see https://github.com/google/ExoPlayer/blob/release-v2/library/ui/src/main/res/values/drawables.xml) -->
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<drawable name="exo_styled_controls_play">@drawable/ic_play</drawable>
|
|
||||||
<drawable name="exo_styled_controls_pause">@drawable/ic_pause</drawable>
|
|
||||||
<drawable name="exo_notification_small_icon">@drawable/ic_launcher_lockscreen</drawable>
|
<drawable name="exo_notification_small_icon">@drawable/ic_launcher_lockscreen</drawable>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -394,11 +394,26 @@
|
|||||||
<string name="auto">Auto</string>
|
<string name="auto">Auto</string>
|
||||||
<string name="swipe_controls">Swipe controls</string>
|
<string name="swipe_controls">Swipe controls</string>
|
||||||
<string name="swipe_controls_summary">Use swipe gesture to adjust the brightness and volume.</string>
|
<string name="swipe_controls_summary">Use swipe gesture to adjust the brightness and volume.</string>
|
||||||
|
<string name="pinch_control">Pinch control</string>
|
||||||
|
<string name="pinch_control_summary">Use pinch gesture to zoom in/out.</string>
|
||||||
<string name="defaults">Defaults</string>
|
<string name="defaults">Defaults</string>
|
||||||
<string name="pop_up">Pop-Up</string>
|
<string name="pop_up">Pop-Up</string>
|
||||||
<string name="comments_disabled">Comments are disabled by the uploader.</string>
|
<string name="comments_disabled">Comments are disabled by the uploader.</string>
|
||||||
<string name="no_comments_available">This video has no comments available.</string>
|
<string name="no_comments_available">This video has no comments available.</string>
|
||||||
<string name="theme_monochrome">Minimalistic Monochrome</string>
|
<string name="theme_monochrome">Minimalistic Monochrome</string>
|
||||||
|
<string name="captions_size">Captions size</string>
|
||||||
|
<string name="double_tap_seek">Double tap to seek</string>
|
||||||
|
<string name="double_tap_seek_summary">Tap twice at the left or right to rewind or forward the player position.</string>
|
||||||
|
<string name="all_caught_up">You\'re all caught up</string>
|
||||||
|
<string name="all_caught_up_summary">You\'ve seen all new videos</string>
|
||||||
|
<string name="import_playlists">Import playlists</string>
|
||||||
|
<string name="export_playlists">Export playlists</string>
|
||||||
|
<string name="app_backup">App Backup</string>
|
||||||
|
<string name="backup_restore_summary">Import & export subscriptions, playlists, …</string>
|
||||||
|
<string name="exportsuccess">Exported.</string>
|
||||||
|
<string name="privacy_alert">Privacy alert</string>
|
||||||
|
<string name="username_email">Proceed with an e-mail address that isn\'t recommended\?</string>
|
||||||
|
<string name="proceed">Proceed</string>
|
||||||
|
|
||||||
<!-- Notification channel strings -->
|
<!-- Notification channel strings -->
|
||||||
<string name="download_channel_name">Download Service</string>
|
<string name="download_channel_name">Download Service</string>
|
||||||
|
@ -39,20 +39,6 @@
|
|||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory app:title="@string/backup_restore">
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:icon="@drawable/ic_backup"
|
|
||||||
app:key="backup"
|
|
||||||
app:title="@string/backup" />
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:icon="@drawable/ic_restore"
|
|
||||||
app:key="restore"
|
|
||||||
app:title="@string/restore" />
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory app:title="@string/misc">
|
<PreferenceCategory app:title="@string/misc">
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
|
48
app/src/main/res/xml/import_export_settings.xml
Normal file
48
app/src/main/res/xml/import_export_settings.xml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<PreferenceCategory app:title="@string/subscriptions">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:icon="@drawable/ic_download_filled"
|
||||||
|
android:summary="@string/import_from_yt_summary"
|
||||||
|
app:key="import_subscriptions"
|
||||||
|
app:title="@string/import_from_yt" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:icon="@drawable/ic_upload"
|
||||||
|
app:key="export_subscriptions"
|
||||||
|
app:title="@string/export_subscriptions" />
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory app:title="@string/playlists">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:icon="@drawable/ic_download_filled"
|
||||||
|
app:key="import_playlists"
|
||||||
|
app:title="@string/import_playlists" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:icon="@drawable/ic_upload"
|
||||||
|
app:key="export_playlists"
|
||||||
|
app:title="@string/export_playlists" />
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory app:title="@string/app_backup">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:icon="@drawable/ic_backup"
|
||||||
|
app:key="backup"
|
||||||
|
app:title="@string/backup" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:icon="@drawable/ic_restore"
|
||||||
|
app:key="restore"
|
||||||
|
app:title="@string/restore" />
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
@ -11,6 +11,13 @@
|
|||||||
app:key="player_swipe_controls"
|
app:key="player_swipe_controls"
|
||||||
app:title="@string/swipe_controls" />
|
app:title="@string/swipe_controls" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:icon="@drawable/ic_pinch_gesture"
|
||||||
|
android:summary="@string/pinch_control_summary"
|
||||||
|
app:key="player_pinch_control"
|
||||||
|
app:title="@string/pinch_control" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:icon="@drawable/ic_next"
|
android:icon="@drawable/ic_next"
|
||||||
@ -18,6 +25,13 @@
|
|||||||
app:key="skip_buttons"
|
app:key="skip_buttons"
|
||||||
app:title="@string/skip_buttons" />
|
app:title="@string/skip_buttons" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:icon="@drawable/ic_skip"
|
||||||
|
android:summary="@string/double_tap_seek_summary"
|
||||||
|
app:key="double_tap_seek"
|
||||||
|
app:title="@string/double_tap_seek" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:icon="@drawable/ic_window"
|
android:icon="@drawable/ic_window"
|
||||||
@ -27,7 +41,7 @@
|
|||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:icon="@drawable/ic_rotating_circle"
|
android:icon="@drawable/ic_rotating_circle"
|
||||||
android:summary="@string/autoRotatePlayer_summary"
|
android:summary="@string/autoRotatePlayer_summary"
|
||||||
app:defaultValue="false"
|
app:defaultValue="true"
|
||||||
app:key="auto_fullscreen"
|
app:key="auto_fullscreen"
|
||||||
app:title="@string/autoRotatePlayer" />
|
app:title="@string/autoRotatePlayer" />
|
||||||
|
|
||||||
@ -62,6 +76,15 @@
|
|||||||
app:summary="@string/buffering_goal_summary"
|
app:summary="@string/buffering_goal_summary"
|
||||||
app:title="@string/buffering_goal" />
|
app:title="@string/buffering_goal" />
|
||||||
|
|
||||||
|
<com.github.libretube.ui.views.SliderPreference
|
||||||
|
android:icon="@drawable/ic_text"
|
||||||
|
android:key="captions_size"
|
||||||
|
android:title="@string/captions_size"
|
||||||
|
app:defValue="18"
|
||||||
|
app:stepSize="1"
|
||||||
|
app:valueFrom="8"
|
||||||
|
app:valueTo="30" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory app:title="@string/appearance">
|
<PreferenceCategory app:title="@string/appearance">
|
||||||
|
@ -50,6 +50,12 @@
|
|||||||
app:key="notifications"
|
app:key="notifications"
|
||||||
app:title="@string/notifications" />
|
app:title="@string/notifications" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:icon="@drawable/ic_backup"
|
||||||
|
app:key="backup_restore"
|
||||||
|
app:summary="@string/backup_restore_summary"
|
||||||
|
app:title="@string/backup_restore" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:icon="@drawable/ic_list"
|
android:icon="@drawable/ic_list"
|
||||||
app:key="advanced"
|
app:key="advanced"
|
||||||
|
@ -11,7 +11,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.21'
|
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.22'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
Loading…
x
Reference in New Issue
Block a user