mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-13 22:00:30 +05:30
Merge branch 'master' into LocalSubscriptionDao_suspend
# Conflicts: # app/src/main/java/com/github/libretube/util/BackupHelper.kt
This commit is contained in:
commit
e238154217
@ -14,8 +14,8 @@ android {
|
||||
applicationId 'com.github.libretube'
|
||||
minSdk 21
|
||||
targetSdk 33
|
||||
versionCode 27
|
||||
versionName '0.11.0'
|
||||
versionCode 28
|
||||
versionName '0.11.1'
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
resValue "string", "app_name", "LibreTube"
|
||||
|
||||
|
@ -26,7 +26,7 @@ object DatabaseHelper {
|
||||
query {
|
||||
Database.watchHistoryDao().insertAll(watchHistoryItem)
|
||||
val maxHistorySize =
|
||||
PreferenceHelper.getString(PreferenceKeys.WATCH_HISTORY_SIZE, "unlimited")
|
||||
PreferenceHelper.getString(PreferenceKeys.WATCH_HISTORY_SIZE, "100")
|
||||
if (maxHistorySize == "unlimited") return@query
|
||||
|
||||
// delete the first watch history entry if the limit is reached
|
||||
|
@ -3,8 +3,8 @@ package com.github.libretube.extensions
|
||||
import android.content.res.Resources
|
||||
|
||||
/**
|
||||
* Convert DP to pixels
|
||||
* Convert dp to pixels
|
||||
*/
|
||||
fun Int.toPixel(): Float {
|
||||
fun Int.dpToPx(): Float {
|
||||
return this * Resources.getSystem().displayMetrics.density + 0.5f
|
||||
}
|
@ -11,12 +11,12 @@ import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class BackupFile(
|
||||
var watchHistory: List<WatchHistoryItem> = emptyList(),
|
||||
var watchPositions: List<WatchPosition> = emptyList(),
|
||||
var searchHistory: List<SearchHistoryItem> = emptyList(),
|
||||
var localSubscriptions: List<LocalSubscription> = emptyList(),
|
||||
var customInstances: List<CustomInstance> = emptyList(),
|
||||
var playlistBookmarks: List<PlaylistBookmark> = emptyList(),
|
||||
var localPlaylists: List<LocalPlaylistWithVideos> = emptyList(),
|
||||
var preferences: List<PreferenceItem> = emptyList()
|
||||
var watchHistory: List<WatchHistoryItem>? = emptyList(),
|
||||
var watchPositions: List<WatchPosition>? = emptyList(),
|
||||
var searchHistory: List<SearchHistoryItem>? = emptyList(),
|
||||
var localSubscriptions: List<LocalSubscription>? = emptyList(),
|
||||
var customInstances: List<CustomInstance>? = emptyList(),
|
||||
var playlistBookmarks: List<PlaylistBookmark>? = emptyList(),
|
||||
var localPlaylists: List<LocalPlaylistWithVideos>? = emptyList(),
|
||||
var preferences: List<PreferenceItem>? = emptyList()
|
||||
)
|
||||
|
@ -16,9 +16,9 @@ import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.databinding.AllCaughtUpRowBinding
|
||||
import com.github.libretube.databinding.TrendingRowBinding
|
||||
import com.github.libretube.databinding.VideoRowBinding
|
||||
import com.github.libretube.extensions.dpToPx
|
||||
import com.github.libretube.extensions.formatShort
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.extensions.toPixel
|
||||
import com.github.libretube.ui.base.BaseActivity
|
||||
import com.github.libretube.ui.extensions.setFormattedDuration
|
||||
import com.github.libretube.ui.extensions.setWatchProgressLength
|
||||
@ -119,8 +119,8 @@ class VideosAdapter(
|
||||
// set a fixed width for better visuals
|
||||
val params = root.layoutParams
|
||||
when (forceMode) {
|
||||
ForceMode.RELATED -> params.width = (210).toPixel().toInt()
|
||||
ForceMode.HOME -> params.width = (250).toPixel().toInt()
|
||||
ForceMode.RELATED -> params.width = (210).dpToPx().toInt()
|
||||
ForceMode.HOME -> params.width = (250).dpToPx().toInt()
|
||||
else -> {}
|
||||
}
|
||||
root.layoutParams = params
|
||||
|
@ -21,6 +21,10 @@ class WatchHistoryAdapter(
|
||||
) :
|
||||
RecyclerView.Adapter<WatchHistoryViewHolder>() {
|
||||
|
||||
var visibleCount = minOf(10, watchHistory.size)
|
||||
|
||||
override fun getItemCount(): Int = visibleCount
|
||||
|
||||
fun removeFromWatchHistory(position: Int) {
|
||||
val history = watchHistory[position]
|
||||
query {
|
||||
@ -31,6 +35,13 @@ class WatchHistoryAdapter(
|
||||
notifyItemRangeChanged(position, itemCount)
|
||||
}
|
||||
|
||||
fun showMoreItems() {
|
||||
val oldSize = visibleCount
|
||||
visibleCount += minOf(10, watchHistory.size - oldSize)
|
||||
if (visibleCount == oldSize) return
|
||||
notifyItemRangeInserted(oldSize, visibleCount)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WatchHistoryViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val binding = VideoRowBinding.inflate(layoutInflater, parent, false)
|
||||
@ -71,8 +82,4 @@ class WatchHistoryAdapter(
|
||||
watchProgress.setWatchProgressLength(video.videoId, video.duration)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return watchHistory.size
|
||||
}
|
||||
}
|
||||
|
@ -91,9 +91,7 @@ class ChannelFragment : BaseFragment() {
|
||||
|
||||
binding.channelScrollView.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (binding.channelScrollView.getChildAt(0).bottom
|
||||
== (binding.channelScrollView.height + binding.channelScrollView.scrollY)
|
||||
) {
|
||||
if (!binding.channelScrollView.canScrollVertically(1)) {
|
||||
try {
|
||||
onScrollEnd.invoke()
|
||||
} catch (e: Exception) {
|
||||
|
@ -18,7 +18,7 @@ import com.github.libretube.databinding.FragmentLibraryBinding
|
||||
import com.github.libretube.db.DatabaseHolder
|
||||
import com.github.libretube.extensions.TAG
|
||||
import com.github.libretube.extensions.awaitQuery
|
||||
import com.github.libretube.extensions.toPixel
|
||||
import com.github.libretube.extensions.dpToPx
|
||||
import com.github.libretube.ui.adapters.PlaylistBookmarkAdapter
|
||||
import com.github.libretube.ui.adapters.PlaylistsAdapter
|
||||
import com.github.libretube.ui.base.BaseFragment
|
||||
@ -103,7 +103,7 @@ class LibraryFragment : BaseFragment() {
|
||||
// optimize CreatePlaylistFab bottom margin if miniPlayer active
|
||||
val bottomMargin = if (isMiniPlayerVisible) 64 else 16
|
||||
val layoutParams = binding.createPlaylist.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParams.bottomMargin = bottomMargin.toPixel().toInt()
|
||||
layoutParams.bottomMargin = bottomMargin.dpToPx().toInt()
|
||||
binding.createPlaylist.layoutParams = layoutParams
|
||||
}
|
||||
|
||||
|
@ -184,6 +184,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
private var sponsorBlockEnabled = PlayerHelper.sponsorBlockEnabled
|
||||
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
private val mainActivity get() = activity as MainActivity
|
||||
|
||||
/**
|
||||
* Receiver for all actions in the PiP mode
|
||||
@ -283,7 +284,6 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun initializeTransitionLayout() {
|
||||
val mainActivity = activity as MainActivity
|
||||
mainActivity.binding.container.visibility = View.VISIBLE
|
||||
val mainMotionLayout = mainActivity.binding.mainMotionLayout
|
||||
|
||||
@ -360,16 +360,12 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
playerBinding.autoPlay.visibility = View.VISIBLE
|
||||
|
||||
binding.playImageView.setOnClickListener {
|
||||
if (!exoPlayer.isPlaying) {
|
||||
// start or go on playing
|
||||
if (exoPlayer.playbackState == Player.STATE_ENDED) {
|
||||
// restart video if finished
|
||||
when {
|
||||
!exoPlayer.isPlaying && exoPlayer.playbackState == Player.STATE_ENDED -> {
|
||||
exoPlayer.seekTo(0)
|
||||
}
|
||||
exoPlayer.play()
|
||||
} else {
|
||||
// pause the video
|
||||
exoPlayer.pause()
|
||||
!exoPlayer.isPlaying -> exoPlayer.play()
|
||||
else -> exoPlayer.pause()
|
||||
}
|
||||
}
|
||||
|
||||
@ -498,7 +494,6 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen_exit)
|
||||
playerBinding.exoTitle.visibility = View.VISIBLE
|
||||
|
||||
val mainActivity = activity as MainActivity
|
||||
if (!PlayerHelper.autoRotationEnabled) {
|
||||
// different orientations of the video are only available when auto rotation is disabled
|
||||
val orientation = PlayerHelper.getOrientation(exoPlayer.videoSize)
|
||||
@ -523,7 +518,6 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
|
||||
if (!PlayerHelper.autoRotationEnabled) {
|
||||
// switch back to portrait mode if auto rotation disabled
|
||||
val mainActivity = activity as MainActivity
|
||||
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
}
|
||||
|
||||
@ -550,7 +544,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
binding.playerViewsInfo.text = viewInfo
|
||||
|
||||
if (this::chapters.isInitialized && chapters.isNotEmpty()) {
|
||||
val chapterIndex = getCurrentChapterIndex()
|
||||
val chapterIndex = getCurrentChapterIndex() ?: return
|
||||
// scroll to the current chapter in the chapterRecView in the description
|
||||
val layoutManager = binding.chaptersRecView.layoutManager as LinearLayoutManager
|
||||
layoutManager.scrollToPositionWithOffset(chapterIndex, 0)
|
||||
@ -1165,19 +1159,14 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
"(${chapter.start?.let { DateUtils.formatElapsedTime(it) }}) ${chapter.title}"
|
||||
}
|
||||
playerBinding.chapterLL.setOnClickListener {
|
||||
if (viewModel.isFullscreen.value!!) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.chapters)
|
||||
.setItems(titles.toTypedArray()) { _, index ->
|
||||
exoPlayer.seekTo(
|
||||
chapters[index].start!! * 1000
|
||||
)
|
||||
}
|
||||
.show()
|
||||
} else {
|
||||
toggleDescription()
|
||||
}
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.chapters)
|
||||
.setItems(titles.toTypedArray()) { _, index ->
|
||||
exoPlayer.seekTo(chapters[index].start!! * 1000)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
setCurrentChapterName()
|
||||
}
|
||||
|
||||
@ -1189,7 +1178,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
// call the function again in 100ms
|
||||
exoPlayerView.postDelayed(this::setCurrentChapterName, 100)
|
||||
|
||||
val chapterIndex = getCurrentChapterIndex()
|
||||
val chapterIndex = getCurrentChapterIndex() ?: return
|
||||
val chapterName = chapters[chapterIndex].title?.trim()
|
||||
|
||||
// change the chapter name textView text to the chapterName
|
||||
@ -1204,18 +1193,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
/**
|
||||
* Get the name of the currently played chapter
|
||||
*/
|
||||
private fun getCurrentChapterIndex(): Int {
|
||||
val currentPosition = exoPlayer.currentPosition
|
||||
var chapterIndex = 0
|
||||
|
||||
chapters.forEachIndexed { index, chapter ->
|
||||
// check whether the chapter start is greater than the current player position
|
||||
if (currentPosition >= chapter.start!! * 1000) {
|
||||
// save chapter title if found
|
||||
chapterIndex = index
|
||||
}
|
||||
}
|
||||
return chapterIndex
|
||||
private fun getCurrentChapterIndex(): Int? {
|
||||
val currentPosition = exoPlayer.currentPosition / 1000
|
||||
return chapters.indexOfFirst { currentPosition >= it.start!! }.takeIf { it >= 0 }
|
||||
}
|
||||
|
||||
private fun setMediaSource(uri: Uri, mimeType: String) {
|
||||
@ -1356,7 +1336,6 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
*/
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
private fun changeOrientationMode() {
|
||||
val mainActivity = activity as MainActivity
|
||||
if (PlayerHelper.autoRotationEnabled) {
|
||||
// enable auto rotation
|
||||
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
|
||||
@ -1527,7 +1506,6 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
private fun killPlayerFragment() {
|
||||
viewModel.isFullscreen.value = false
|
||||
binding.playerMotionLayout.transitionToEnd()
|
||||
val mainActivity = activity as MainActivity
|
||||
mainActivity.supportFragmentManager.beginTransaction()
|
||||
.remove(this)
|
||||
.commit()
|
||||
|
@ -22,9 +22,9 @@ import com.github.libretube.db.DatabaseHolder
|
||||
import com.github.libretube.enums.PlaylistType
|
||||
import com.github.libretube.extensions.TAG
|
||||
import com.github.libretube.extensions.awaitQuery
|
||||
import com.github.libretube.extensions.dpToPx
|
||||
import com.github.libretube.extensions.query
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.extensions.toPixel
|
||||
import com.github.libretube.extensions.toPlaylistBookmark
|
||||
import com.github.libretube.ui.adapters.PlaylistAdapter
|
||||
import com.github.libretube.ui.base.BaseFragment
|
||||
@ -86,7 +86,7 @@ class PlaylistFragment : BaseFragment() {
|
||||
|
||||
playerViewModel.isMiniPlayerVisible.observe(viewLifecycleOwner) {
|
||||
binding.playlistRecView.updatePadding(
|
||||
bottom = if (it) (64).toPixel().toInt() else 0
|
||||
bottom = if (it) (64).dpToPx().toInt() else 0
|
||||
)
|
||||
}
|
||||
|
||||
@ -212,9 +212,7 @@ class PlaylistFragment : BaseFragment() {
|
||||
binding.playlistRecView.adapter = playlistAdapter
|
||||
binding.playlistScrollview.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (binding.playlistScrollview.getChildAt(0).bottom
|
||||
== (binding.playlistScrollview.height + binding.playlistScrollview.scrollY)
|
||||
) {
|
||||
if (!binding.playlistScrollview.canScrollVertically(1)) {
|
||||
if (isLoading) return@addOnScrollChangedListener
|
||||
|
||||
// append more playlists to the recycler view
|
||||
|
@ -137,9 +137,7 @@ class SubscriptionsFragment : BaseFragment() {
|
||||
|
||||
binding.scrollviewSub.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (binding.scrollviewSub.getChildAt(0).bottom
|
||||
== (binding.scrollviewSub.height + binding.scrollviewSub.scrollY)
|
||||
) {
|
||||
if (!binding.scrollviewSub.canScrollVertically(1)) {
|
||||
// scroll view is at bottom
|
||||
if (viewModel.videoFeed.value == null) return@addOnScrollChangedListener
|
||||
binding.subRefresh.isRefreshing = true
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.github.libretube.ui.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@ -14,8 +16,8 @@ import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.databinding.FragmentWatchHistoryBinding
|
||||
import com.github.libretube.db.DatabaseHolder.Companion.Database
|
||||
import com.github.libretube.extensions.awaitQuery
|
||||
import com.github.libretube.extensions.dpToPx
|
||||
import com.github.libretube.extensions.query
|
||||
import com.github.libretube.extensions.toPixel
|
||||
import com.github.libretube.ui.adapters.WatchHistoryAdapter
|
||||
import com.github.libretube.ui.base.BaseFragment
|
||||
import com.github.libretube.ui.models.PlayerViewModel
|
||||
@ -28,6 +30,7 @@ class WatchHistoryFragment : BaseFragment() {
|
||||
private lateinit var binding: FragmentWatchHistoryBinding
|
||||
|
||||
private val playerViewModel: PlayerViewModel by activityViewModels()
|
||||
private var isLoading = false
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
@ -43,13 +46,13 @@ class WatchHistoryFragment : BaseFragment() {
|
||||
|
||||
playerViewModel.isMiniPlayerVisible.observe(viewLifecycleOwner) {
|
||||
binding.watchHistoryRecView.updatePadding(
|
||||
bottom = if (it) (64).toPixel().toInt() else 0
|
||||
bottom = if (it) (64).dpToPx().toInt() else 0
|
||||
)
|
||||
}
|
||||
|
||||
val watchHistory = awaitQuery {
|
||||
Database.watchHistoryDao().getAll()
|
||||
}
|
||||
}.reversed()
|
||||
|
||||
if (watchHistory.isEmpty()) return
|
||||
|
||||
@ -96,16 +99,15 @@ class WatchHistoryFragment : BaseFragment() {
|
||||
)
|
||||
}
|
||||
|
||||
// reversed order
|
||||
binding.watchHistoryRecView.layoutManager = LinearLayoutManager(requireContext()).apply {
|
||||
reverseLayout = true
|
||||
stackFromEnd = true
|
||||
}
|
||||
|
||||
val watchHistoryAdapter = WatchHistoryAdapter(
|
||||
watchHistory.toMutableList()
|
||||
)
|
||||
|
||||
binding.watchHistoryRecView.layoutManager = LinearLayoutManager(context)
|
||||
binding.watchHistoryRecView.adapter = watchHistoryAdapter
|
||||
binding.historyEmpty.visibility = View.GONE
|
||||
binding.historyScrollView.visibility = View.VISIBLE
|
||||
|
||||
val itemTouchCallback = object : ItemTouchHelper.SimpleCallback(
|
||||
0,
|
||||
ItemTouchHelper.LEFT
|
||||
@ -130,7 +132,7 @@ class WatchHistoryFragment : BaseFragment() {
|
||||
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
|
||||
itemTouchHelper.attachToRecyclerView(binding.watchHistoryRecView)
|
||||
|
||||
// observe changes
|
||||
// observe changes to indicate if the history is empty
|
||||
watchHistoryAdapter.registerAdapterDataObserver(object :
|
||||
RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
@ -141,8 +143,15 @@ class WatchHistoryFragment : BaseFragment() {
|
||||
}
|
||||
})
|
||||
|
||||
binding.watchHistoryRecView.adapter = watchHistoryAdapter
|
||||
binding.historyEmpty.visibility = View.GONE
|
||||
binding.historyScrollView.visibility = View.VISIBLE
|
||||
// add a listener for scroll end, delay needed to prevent loading new ones the first time
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
binding.historyScrollView.viewTreeObserver.addOnScrollChangedListener {
|
||||
if (!binding.historyScrollView.canScrollVertically(1) && !isLoading) {
|
||||
isLoading = true
|
||||
watchHistoryAdapter.showMoreItems()
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import androidx.fragment.app.activityViewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.CommentsSheetBinding
|
||||
import com.github.libretube.extensions.toPixel
|
||||
import com.github.libretube.extensions.dpToPx
|
||||
import com.github.libretube.ui.adapters.CommentsAdapter
|
||||
import com.github.libretube.ui.models.CommentsViewModel
|
||||
|
||||
@ -37,7 +37,7 @@ class CommentsSheet : ExpandedBottomSheet() {
|
||||
binding.dragHandle.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
// limit the recyclerview height to not cover the video
|
||||
binding.commentsRV.layoutParams = binding.commentsRV.layoutParams.apply {
|
||||
height = viewModel.maxHeight - (binding.dragHandle.height + (20).toPixel().toInt())
|
||||
height = viewModel.maxHeight - (binding.dragHandle.height + (20).dpToPx().toInt())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -19,8 +19,8 @@ import com.github.libretube.R
|
||||
import com.github.libretube.databinding.DoubleTapOverlayBinding
|
||||
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||
import com.github.libretube.databinding.PlayerGestureControlsViewBinding
|
||||
import com.github.libretube.extensions.dpToPx
|
||||
import com.github.libretube.extensions.normalize
|
||||
import com.github.libretube.extensions.toPixel
|
||||
import com.github.libretube.obj.BottomSheetItem
|
||||
import com.github.libretube.ui.activities.MainActivity
|
||||
import com.github.libretube.ui.base.BaseActivity
|
||||
@ -573,8 +573,8 @@ internal class CustomExoPlayerView(
|
||||
|
||||
// add a larger bottom margin to the time bar in landscape mode
|
||||
val offset = when (newConfig?.orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> 20.toPixel()
|
||||
else -> 10.toPixel()
|
||||
Configuration.ORIENTATION_LANDSCAPE -> 20.dpToPx()
|
||||
else -> 10.dpToPx()
|
||||
}
|
||||
|
||||
binding.progressBar.let {
|
||||
@ -624,7 +624,7 @@ internal class CustomExoPlayerView(
|
||||
playerViewModel?.isFullscreen?.value == true
|
||||
binding.topBar.let {
|
||||
it.layoutParams = (it.layoutParams as MarginLayoutParams).apply {
|
||||
topMargin = (if (isFullscreen) 25 else 5).toPixel().toInt()
|
||||
topMargin = (if (isFullscreen) 10 else 0).dpToPx().toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -719,6 +719,6 @@ internal class CustomExoPlayerView(
|
||||
private const val SUBTITLE_BOTTOM_PADDING_FRACTION = 0.158f
|
||||
private const val ANIMATION_DURATION = 100L
|
||||
private const val AUTO_HIDE_CONTROLLER_DELAY = 2000L
|
||||
private val LANDSCAPE_MARGIN_HORIZONTAL = (20).toPixel().toInt()
|
||||
private val LANDSCAPE_MARGIN_HORIZONTAL = (20).dpToPx().toInt()
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import androidx.core.view.marginLeft
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.obj.Segment
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.extensions.toPixel
|
||||
import com.github.libretube.extensions.dpToPx
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.exoplayer2.Player
|
||||
@ -28,7 +28,7 @@ class MarkableTimeBar(
|
||||
private var player: Player? = null
|
||||
private var length: Int = 0
|
||||
|
||||
private val progressBarHeight = (2).toPixel().toInt()
|
||||
private val progressBarHeight = (2).dpToPx().toInt()
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
@ -51,23 +51,23 @@ class BackupHelper(private val context: Context) {
|
||||
|
||||
runBlocking(Dispatchers.IO) {
|
||||
Database.watchHistoryDao().insertAll(
|
||||
*backupFile.watchHistory.toTypedArray()
|
||||
*backupFile.watchHistory.orEmpty().toTypedArray()
|
||||
)
|
||||
Database.searchHistoryDao().insertAll(
|
||||
*backupFile.searchHistory.toTypedArray()
|
||||
*backupFile.searchHistory.orEmpty().toTypedArray()
|
||||
)
|
||||
Database.watchPositionDao().insertAll(
|
||||
*backupFile.watchPositions.toTypedArray()
|
||||
*backupFile.watchPositions.orEmpty().toTypedArray()
|
||||
)
|
||||
Database.localSubscriptionDao().insertAll(backupFile.localSubscriptions)
|
||||
Database.localSubscriptionDao().insertAll(backupFile.localSubscriptions.orEmpty())
|
||||
Database.customInstanceDao().insertAll(
|
||||
*backupFile.customInstances.toTypedArray()
|
||||
*backupFile.customInstances.orEmpty().toTypedArray()
|
||||
)
|
||||
Database.playlistBookmarkDao().insertAll(
|
||||
*backupFile.playlistBookmarks.toTypedArray()
|
||||
*backupFile.playlistBookmarks.orEmpty().toTypedArray()
|
||||
)
|
||||
|
||||
backupFile.localPlaylists.forEach {
|
||||
backupFile.localPlaylists.orEmpty().forEach {
|
||||
Database.localPlaylistsDao().createPlaylist(it.playlist)
|
||||
val playlistId = Database.localPlaylistsDao().getAll().last().playlist.id
|
||||
it.videos.forEach {
|
||||
|
@ -8,8 +8,8 @@
|
||||
<string name="login">ورود</string>
|
||||
<string name="logout">خروج</string>
|
||||
<string name="cancel">لغو</string>
|
||||
<string name="loggedIn">با موفقیت وارد شدید!</string>
|
||||
<string name="loggedout">با موفقیت خارج شدید!</string>
|
||||
<string name="loggedIn">وارد شدید.</string>
|
||||
<string name="loggedout">خارج شدید.</string>
|
||||
<string name="yes">بله</string>
|
||||
<string name="unsubscribe">لغو اشتراک</string>
|
||||
<string name="search_hint">جستجو</string>
|
||||
@ -17,31 +17,31 @@
|
||||
<string name="download">دانلود</string>
|
||||
<string name="register">ثبت نام</string>
|
||||
<string name="customInstance">سفارشی</string>
|
||||
<string name="please_login">لطفا ابتدا در تنظیمات وارد شوید یا ثبت نام کنید!</string>
|
||||
<string name="importsuccess">با موفقیت مشترک شدید!</string>
|
||||
<string name="please_login">لطفا ابتدا در بخش تنظیمات وارد حساب خود شده یا ثبت نام کنید.</string>
|
||||
<string name="importsuccess">مشترک شدید.</string>
|
||||
<string name="subscribeIsEmpty">ابتدا در برخی از کانالها مشترک شوید!</string>
|
||||
<string name="dlcomplete">دانلود کامل شد!</string>
|
||||
<string name="downloadfailed">دانلود شکست خورد!</string>
|
||||
<string name="downloadfailed">دانلود ناموفق.</string>
|
||||
<string name="vlc">باز کردن در VLC</string>
|
||||
<string name="unknown_error">خطای شبکه!</string>
|
||||
<string name="instances">انتخاب کنید …</string>
|
||||
<string name="login_first">لطفا وارد شوید و دوباره امتحان کنید!</string>
|
||||
<string name="login_first">لطفا وارد شوید و دوباره امتحان کنید.</string>
|
||||
<string name="region">منطقه</string>
|
||||
<string name="login_register">ورود/ثبتنام</string>
|
||||
<string name="app_theme">پوسته</string>
|
||||
<string name="import_from_yt">وارد کردن اشتراکها</string>
|
||||
<string name="registered">ثبت نام با موفقیت انجام شد! حالا میتوانید چنل مورد نظر خودتان را دنبال کنید.</string>
|
||||
<string name="already_logged_in">شما در حال حاضر وارد حسابتان شدید، میتوانید از حسابتان خارج شوید.</string>
|
||||
<string name="dlisinprogress">دانلود دیگری در جریان است، لطفا صبر کنید تا دانلود کامل شود!</string>
|
||||
<string name="cannotDownload">این ویدئو را نمیتوان دانلود کرد!</string>
|
||||
<string name="registered">ثبت نام انجام شد. حالا میتوانید کانالهای مورد نظر خودتان را دنبال کنید.</string>
|
||||
<string name="already_logged_in">در حال حاضر وارد حسابتان هستید، ممکن است از حسابتان خارج شوید.</string>
|
||||
<string name="dlisinprogress">دانلود دیگری در جریان است، لطفا صبر کنید تا دانلود تمام شود.</string>
|
||||
<string name="cannotDownload">این ویدئو را نمیتوان دانلود کرد.</string>
|
||||
<string name="server_error">سرور دچار مشکل شده. شاید بهترست نمونه دیگری را امتحان کنید؟</string>
|
||||
<string name="error">مشکلی پیش آمده است!</string>
|
||||
<string name="notgmail">این مربوط به حساب پایپ است</string>
|
||||
<string name="defres">ابعاد ویدئو</string>
|
||||
<string name="empty">نامکاربری و رمزعبور نمیتواند خالی باشد!</string>
|
||||
<string name="emptyList">هیچی اینجا نیست!</string>
|
||||
<string name="vlcerror">نمی توانید در VLC ویدئو را ببینید. لطفا مطمئن شوید که برنامه VLC را نصب کرده باشید</string>
|
||||
<string name="grid">انتخاب تعداد ستون ها</string>
|
||||
<string name="vlcerror">نمی توانید در VLC ویدئو را ببینید. ممکن است برنامه VLC نصب نباشد.</string>
|
||||
<string name="grid">انتخاب تعداد ستونها</string>
|
||||
<string name="deletePlaylist">حذف لیست پخش</string>
|
||||
<string name="areYouSure">لیست پخش حذف شود؟</string>
|
||||
<string name="createPlaylist">ساخت لیست پخش</string>
|
||||
@ -49,18 +49,18 @@
|
||||
<string name="playlistName">نام فهرست پخش</string>
|
||||
<string name="addToPlaylist">افزودن به فهرست پخش</string>
|
||||
<string name="success">انجام شد.</string>
|
||||
<string name="fail">ناموفق :(</string>
|
||||
<string name="fail">ناموفق بود :(</string>
|
||||
<string name="about">درباره ما</string>
|
||||
<string name="emptyPlaylistName">نام فهرست پخش نمیتواند خالی باشه</string>
|
||||
<string name="emptyPlaylistName">نام لیست پخش نمیتواند خالی باشد</string>
|
||||
<string name="import_from_yt_summary">از یوتوب یا نیوپایپ</string>
|
||||
<string name="startpage">خانه</string>
|
||||
<string name="library">کتابخانه</string>
|
||||
<string name="changeLanguage">زبان</string>
|
||||
<string name="systemDefault">پیشفرض سیستم</string>
|
||||
<string name="systemDefault">سیستم</string>
|
||||
<string name="lightTheme">پوسته روشن</string>
|
||||
<string name="darkTheme">پوسته تیره</string>
|
||||
<string name="subscribers">%1$s مشترکین</string>
|
||||
<string name="videos">ویدیو ها</string>
|
||||
<string name="videos">ویدیوها</string>
|
||||
<string name="subscriptions">اشتراکها</string>
|
||||
<string name="systemLanguage">زبان سیستم</string>
|
||||
<string name="history">تاریخچه</string>
|
||||
@ -73,11 +73,11 @@
|
||||
<string name="instance">نمونه</string>
|
||||
<string name="customization">تنظیمات</string>
|
||||
<string name="website">وبسایت</string>
|
||||
<string name="videoCount">%1$s ویدیوها</string>
|
||||
<string name="videoCount">%1$s ویدیو</string>
|
||||
<string name="noInternet">ابتدا به اینترنت متصل شوید</string>
|
||||
<string name="retry">تلاش مجدد</string>
|
||||
<string name="retry">تلاش دوباره</string>
|
||||
<string name="comments">نظرات</string>
|
||||
<string name="defaultTab">زبانه پیش فرض</string>
|
||||
<string name="defaultTab">زبانه پیشفرض</string>
|
||||
<string name="sponsorblock">اسپانسربلاک</string>
|
||||
<string name="sponsorblock_state">فعال</string>
|
||||
<string name="category_sponsor">اسپانسر</string>
|
||||
@ -92,7 +92,7 @@
|
||||
<string name="category_interaction">یادآوری تعامل (لایک و اشتراک)</string>
|
||||
<string name="color_accent">لهجه</string>
|
||||
<string name="color_blue">ابی شاد</string>
|
||||
<string name="color_green">سبز</string>
|
||||
<string name="color_green">سبز درهها</string>
|
||||
<string name="color_yellow">زرد</string>
|
||||
<string name="color_purple">بنفش</string>
|
||||
<string name="oledTheme">سیاه</string>
|
||||
@ -101,11 +101,11 @@
|
||||
<string name="disabled">خاموش</string>
|
||||
<string name="appearance">ظاهر</string>
|
||||
<string name="app_behavior">رفتار</string>
|
||||
<string name="download_directory">دانلود در...</string>
|
||||
<string name="download_directory">دانلود در</string>
|
||||
<string name="download_directory_summary">جایی که فایل دانلود شده ذخیره میشود.</string>
|
||||
<string name="update_available">نسخه %1$s موجود است</string>
|
||||
<string name="downloads">دانلودها</string>
|
||||
<string name="video_format">فرمت ویدئو</string>
|
||||
<string name="video_format">فرمت ویدیو</string>
|
||||
<string name="contributing">کمک کردن</string>
|
||||
<string name="choose_filter">انتخاب فیلتر جستجو</string>
|
||||
<string name="music_songs">آهنگ های موسیقی یوتیوب</string>
|
||||
@ -126,4 +126,9 @@
|
||||
<string name="advanced">پیشرفته</string>
|
||||
<string name="player">پخش کننده</string>
|
||||
<string name="live">زنده</string>
|
||||
<string name="sponsorblock_notifications">آگاهسازها</string>
|
||||
<string name="category_selfpromo">بدون حقوق/تبلیغ خود</string>
|
||||
<string name="app_icon">آیکون</string>
|
||||
<string name="no_update_available">شما در حال اجرای جدیدترین نسخه هستید.</string>
|
||||
<string name="update_summary">بررسی برای بروزرسانی</string>
|
||||
</resources>
|
@ -437,4 +437,17 @@
|
||||
<string name="alternative_pip_controls">Contrôles alternatifs de la PiP</string>
|
||||
<string name="alternative_pip_controls_summary">Afficher uniquement l\'audio et les commandes de saut dans PiP au lieu de l\'avance et du retour en arrière</string>
|
||||
<string name="audio_player">Lecteur audio</string>
|
||||
<string name="faq">FAQ</string>
|
||||
<string name="concurrent_downloads">Maximum de téléchargements simultanés</string>
|
||||
<string name="skip_silence">Saute les silences</string>
|
||||
<string name="concurrent_downloads_limit_reached">Limite maximum de téléchargements simultanés atteinte.</string>
|
||||
<string name="unknown">Inconnu</string>
|
||||
<string name="resume">Reprendre</string>
|
||||
<string name="help">Aide</string>
|
||||
<string name="audio_only_mode">Mode audio seulement</string>
|
||||
<string name="audio_only_mode_summary">Transforme LibreTube en lecteur de musique.</string>
|
||||
<string name="no_subtitle">Pas de sous-titre</string>
|
||||
<string name="download_paused">Téléchargement en pause</string>
|
||||
<string name="download_completed">Téléchargement terminé</string>
|
||||
<string name="sleep_timer">Minuteur de sommeil</string>
|
||||
</resources>
|
@ -346,4 +346,7 @@
|
||||
<string name="save_feed">Ielādēt plūsmu fonā</string>
|
||||
<string name="save_feed_summary">Ielādē abonementu plūsmu fonā un neļauj tai automātiski atjaunoties.</string>
|
||||
<string name="play_next">Atskaņot nākamo</string>
|
||||
<string name="no_subtitle">Bez subtitriem</string>
|
||||
<string name="download_paused">Lejupielāde iepauzēta</string>
|
||||
<string name="download_completed">Lejupielāde pabeigta</string>
|
||||
</resources>
|
@ -177,7 +177,7 @@
|
||||
<string name="advanced_summary">Transferências e redefinição</string>
|
||||
<string name="player_summary">Padrões e comportamento</string>
|
||||
<string name="hide_chapters">Ocultar capítulos</string>
|
||||
<string name="related_streams_summary">Exibir transmissões semelhantes ao que você assiste.</string>
|
||||
<string name="related_streams_summary">Exibir conteúdo semelhante ao que você assiste.</string>
|
||||
<string name="open">Abrir…</string>
|
||||
<string name="reset">Restaurar para os padrões</string>
|
||||
<string name="default_subtitle_language">Idioma de legendas</string>
|
||||
@ -207,7 +207,7 @@
|
||||
<string name="watch_history_summary">Acompanhar os vídeos assistidos localmente</string>
|
||||
<string name="reset_watch_positions">Redefinir</string>
|
||||
<string name="autoRotatePlayer">Tela cheia automática</string>
|
||||
<string name="notify_new_streams">Notificações de novas transmissões</string>
|
||||
<string name="notify_new_streams">Notificações de novos vídeos</string>
|
||||
<string name="most_views">Mais visualizações</string>
|
||||
<string name="network_wifi">Só no Wi-Fi</string>
|
||||
<string name="translate">Tradução</string>
|
||||
@ -435,18 +435,18 @@
|
||||
<string name="forward">Avançar</string>
|
||||
<string name="pause">Pausar</string>
|
||||
<string name="alternative_pip_controls">Controles PiP alternativos</string>
|
||||
<string name="alternative_pip_controls_summary">Mostrar controles de apenas áudio e pular no PiP em vez de avançar e retroceder</string>
|
||||
<string name="audio_player">Player de Áudio</string>
|
||||
<string name="no_subtitle">Sem legenda</string>
|
||||
<string name="download_paused">Download pausado</string>
|
||||
<string name="download_completed">Download concluído</string>
|
||||
<string name="concurrent_downloads">Máximo de downloads simultâneos</string>
|
||||
<string name="concurrent_downloads_limit_reached">Limite máximo de downloads simultâneos atingido.</string>
|
||||
<string name="alternative_pip_controls_summary">Mostrar opções de apenas áudio e pular no PiP em vez de avançar e retroceder</string>
|
||||
<string name="audio_player">Reprodutor de áudio</string>
|
||||
<string name="no_subtitle">Sem legendas</string>
|
||||
<string name="download_paused">Transferência pausada</string>
|
||||
<string name="download_completed">Transferência concluída</string>
|
||||
<string name="concurrent_downloads">Número de transferências simultâneas</string>
|
||||
<string name="concurrent_downloads_limit_reached">Limite máximo de transferências simultâneas atingido.</string>
|
||||
<string name="unknown">Desconhecido</string>
|
||||
<string name="resume">Retomar</string>
|
||||
<string name="audio_only_mode_summary">Transforme o LibreTube em um reprodutor de música.</string>
|
||||
<string name="audio_only_mode">Modo somente áudio</string>
|
||||
<string name="sleep_timer">Temporizador</string>
|
||||
<string name="audio_only_mode">Modo apenas áudio</string>
|
||||
<string name="sleep_timer">Temporizador de sono</string>
|
||||
<string name="skip_silence">Pular silêncio</string>
|
||||
<string name="help">Ajuda</string>
|
||||
<string name="faq">FAQ</string>
|
||||
|
@ -28,7 +28,7 @@
|
||||
app:title="@string/watch_history" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="unlimited"
|
||||
android:defaultValue="100"
|
||||
android:entries="@array/historySize"
|
||||
android:entryValues="@array/historySizeValues"
|
||||
android:icon="@drawable/ic_list"
|
||||
|
8
fastlane/metadata/android/en-US/changelogs/28.txt
Normal file
8
fastlane/metadata/android/en-US/changelogs/28.txt
Normal file
@ -0,0 +1,8 @@
|
||||
* Fix channel crashes
|
||||
* Fix backup backwards compatibility
|
||||
* Fix unresponsiveness when opening watch history
|
||||
* Fix that clicking description links plays next video
|
||||
* Keep the screen on while playing in the offline player
|
||||
* [Audio mode] Show video options when clicking thumbnail
|
||||
* Reduce the player top bar margin
|
||||
* Minor player view improvements
|
Loading…
Reference in New Issue
Block a user