Merge branch 'libre-tube:master' into master

This commit is contained in:
Praveen Rajput 2023-01-29 11:11:12 +05:30 committed by GitHub
commit 275f304472
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 665 additions and 225 deletions

View File

@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable
data class Comment( data class Comment(
val author: String, val author: String,
val commentId: String, val commentId: String,
val commentText: String, val commentText: String?,
val commentedTime: String, val commentedTime: String,
val commentorUrl: String, val commentorUrl: String,
val repliesPage: String? = null, val repliesPage: String? = null,

View File

@ -18,4 +18,5 @@ object IntentData {
const val downloading = "downloading" const val downloading = "downloading"
const val openAudioPlayer = "openAudioPlayer" const val openAudioPlayer = "openAudioPlayer"
const val fragmentToOpen = "fragmentToOpen" const val fragmentToOpen = "fragmentToOpen"
const val replyPage = "replyPage"
} }

View File

@ -1,42 +1,33 @@
package com.github.libretube.ui.adapters package com.github.libretube.ui.adapters
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.util.Log import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast import android.widget.Toast
import androidx.core.text.parseAsHtml import androidx.core.text.parseAsHtml
import androidx.recyclerview.widget.LinearLayoutManager import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.Comment import com.github.libretube.api.obj.Comment
import com.github.libretube.api.obj.CommentsPage import com.github.libretube.constants.IntentData
import com.github.libretube.databinding.CommentsRowBinding import com.github.libretube.databinding.CommentsRowBinding
import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.formatShort import com.github.libretube.extensions.formatShort
import com.github.libretube.ui.fragments.CommentsRepliesFragment
import com.github.libretube.ui.viewholders.CommentsViewHolder import com.github.libretube.ui.viewholders.CommentsViewHolder
import com.github.libretube.util.ClipboardHelper import com.github.libretube.util.ClipboardHelper
import com.github.libretube.util.ImageHelper import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class CommentsAdapter( class CommentsAdapter(
private val fragment: Fragment?,
private val videoId: String, private val videoId: String,
private val comments: MutableList<Comment>, private val comments: MutableList<Comment>,
private val isRepliesAdapter: Boolean = false, private val isRepliesAdapter: Boolean = false,
private val dismiss: () -> Unit private val dismiss: () -> Unit
) : RecyclerView.Adapter<CommentsViewHolder>() { ) : RecyclerView.Adapter<CommentsViewHolder>() {
private var isLoading = false
private lateinit var repliesPage: CommentsPage
fun clear() { fun clear() {
val size: Int = comments.size val size: Int = comments.size
comments.clear() comments.clear()
@ -59,15 +50,8 @@ class CommentsAdapter(
override fun onBindViewHolder(holder: CommentsViewHolder, position: Int) { override fun onBindViewHolder(holder: CommentsViewHolder, position: Int) {
val comment = comments[position] val comment = comments[position]
holder.binding.apply { holder.binding.apply {
if (isRepliesAdapter) {
root.scaleX = REPLIES_ADAPTER_SCALE
root.scaleY = REPLIES_ADAPTER_SCALE
commentorImage.scaleX = REPLIES_ADAPTER_SCALE
commentorImage.scaleY = REPLIES_ADAPTER_SCALE
}
commentInfos.text = comment.author + TextUtils.SEPARATOR + comment.commentedTime commentInfos.text = comment.author + TextUtils.SEPARATOR + comment.commentedTime
commentText.text = comment.commentText.parseAsHtml() commentText.text = comment.commentText?.parseAsHtml()
ImageHelper.loadImage(comment.thumbnail, commentorImage) ImageHelper.loadImage(comment.thumbnail, commentorImage)
likesTextView.text = comment.likeCount.formatShort() likesTextView.text = comment.likeCount.formatShort()
@ -85,79 +69,31 @@ class CommentsAdapter(
dismiss.invoke() dismiss.invoke()
} }
repliesRecView.layoutManager = LinearLayoutManager(root.context)
val repliesAdapter = CommentsAdapter(videoId, mutableListOf(), true, dismiss)
repliesRecView.adapter = repliesAdapter
if (!isRepliesAdapter && comment.repliesPage != null) { if (!isRepliesAdapter && comment.repliesPage != null) {
val repliesFragment = CommentsRepliesFragment().apply {
arguments = Bundle().apply {
putString(IntentData.videoId, videoId)
putString(IntentData.replyPage, comment.repliesPage)
}
}
root.setOnClickListener { root.setOnClickListener {
showMoreReplies(comment.repliesPage, showMore, repliesAdapter) fragment!!.parentFragmentManager
.beginTransaction()
.replace(R.id.commentFragContainer, repliesFragment)
.addToBackStack(null)
.commit()
} }
} }
root.setOnLongClickListener { root.setOnLongClickListener {
ClipboardHelper(root.context).save(comment.commentText) ClipboardHelper(root.context).save(comment.commentText ?: "")
Toast.makeText(root.context, R.string.copied, Toast.LENGTH_SHORT).show() Toast.makeText(root.context, R.string.copied, Toast.LENGTH_SHORT).show()
true true
} }
} }
} }
private fun showMoreReplies(
nextPage: String,
showMoreBtn: Button,
repliesAdapter: CommentsAdapter
) {
when (repliesAdapter.itemCount) {
0 -> {
fetchReplies(nextPage) {
repliesAdapter.updateItems(it.comments)
if (repliesPage.nextpage == null) {
showMoreBtn.visibility = View.GONE
return@fetchReplies
}
showMoreBtn.visibility = View.VISIBLE
showMoreBtn.setOnClickListener { view ->
if (repliesPage.nextpage == null) {
view.visibility = View.GONE
return@setOnClickListener
}
fetchReplies(
repliesPage.nextpage!!
) {
repliesAdapter.updateItems(repliesPage.comments)
}
}
}
}
else -> {
repliesAdapter.clear()
showMoreBtn.visibility = View.GONE
}
}
}
override fun getItemCount(): Int { override fun getItemCount(): Int {
return comments.size return comments.size
} }
private fun fetchReplies(nextPage: String, onFinished: (CommentsPage) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
if (isLoading) return@launch
isLoading = true
repliesPage = try {
RetrofitInstance.api.getCommentsNextPage(videoId, nextPage)
} catch (e: Exception) {
Log.e(TAG(), "IOException, you might not have internet connection")
return@launch
}
withContext(Dispatchers.Main) {
onFinished.invoke(repliesPage)
}
isLoading = false
}
}
companion object {
private const val REPLIES_ADAPTER_SCALE = 0.9f
}
} }

View File

@ -1,6 +1,7 @@
package com.github.libretube.ui.dialogs package com.github.libretube.ui.dialogs
import android.app.Dialog import android.app.Dialog
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
@ -24,11 +25,12 @@ class CreatePlaylistDialog(
binding.clonePlaylist.setOnClickListener { binding.clonePlaylist.setOnClickListener {
val playlistUrl = binding.playlistUrl.text.toString() val playlistUrl = binding.playlistUrl.text.toString()
if (!TextUtils.validateUrl(playlistUrl)) { val playlistId = Uri.parse(playlistUrl).getQueryParameter("list")
if (!TextUtils.validateUrl(playlistUrl) || playlistId == null) {
Toast.makeText(context, R.string.invalid_url, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.invalid_url, Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
PlaylistsHelper.clonePlaylist(requireContext().applicationContext, playlistUrl) PlaylistsHelper.clonePlaylist(requireContext().applicationContext, playlistId)
dismiss() dismiss()
} }

View File

@ -0,0 +1,76 @@
package com.github.libretube.ui.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R
import com.github.libretube.databinding.FragmentCommentsBinding
import com.github.libretube.ui.adapters.CommentsAdapter
import com.github.libretube.ui.models.CommentsViewModel
class CommentsMainFragment : Fragment() {
private lateinit var binding: FragmentCommentsBinding
private lateinit var commentsAdapter: CommentsAdapter
private val viewModel: CommentsViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentCommentsBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.commentsRV.layoutManager = LinearLayoutManager(requireContext())
binding.commentsRV.setItemViewCacheSize(20)
binding.commentsRV.viewTreeObserver
.addOnScrollChangedListener {
if (!binding.commentsRV.canScrollVertically(1)) {
viewModel.fetchNextComments()
}
}
commentsAdapter = CommentsAdapter(
this,
viewModel.videoId!!,
viewModel.commentsPage.value?.comments.orEmpty().toMutableList()
) {
viewModel.commentsSheetDismiss?.invoke()
}
binding.commentsRV.adapter = commentsAdapter
if (viewModel.commentsPage.value?.comments.orEmpty().isEmpty()) {
binding.progress.visibility = View.VISIBLE
viewModel.fetchComments()
}
// listen for new comments to be loaded
viewModel.commentsPage.observe(viewLifecycleOwner) {
it ?: return@observe
binding.progress.visibility = View.GONE
if (it.disabled == true) {
binding.errorTV.visibility = View.VISIBLE
return@observe
}
if (it.comments.isEmpty()) {
binding.errorTV.text = getString(R.string.no_comments_available)
binding.errorTV.visibility = View.VISIBLE
return@observe
}
commentsAdapter.updateItems(
// only add the new comments to the recycler view
it.comments.subList(commentsAdapter.itemCount, it.comments.size)
)
}
}
}

View File

@ -0,0 +1,106 @@
package com.github.libretube.ui.fragments
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.CommentsPage
import com.github.libretube.constants.IntentData
import com.github.libretube.databinding.FragmentCommentsBinding
import com.github.libretube.extensions.TAG
import com.github.libretube.ui.adapters.CommentsAdapter
import com.github.libretube.ui.models.CommentsViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class CommentsRepliesFragment : Fragment() {
private lateinit var binding: FragmentCommentsBinding
private lateinit var repliesPage: CommentsPage
private lateinit var repliesAdapter: CommentsAdapter
private val viewModel: CommentsViewModel by activityViewModels()
private var isLoading = false
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentCommentsBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val videoId = arguments?.getString(IntentData.videoId) ?: ""
val nextPage = arguments?.getString(IntentData.replyPage) ?: ""
repliesAdapter = CommentsAdapter(null, videoId, mutableListOf(), true) {
viewModel.commentsSheetDismiss?.invoke()
}
binding.commentsRV.layoutManager = LinearLayoutManager(view.context)
binding.commentsRV.adapter = repliesAdapter
binding.commentsRV.viewTreeObserver
.addOnScrollChangedListener {
if (!binding.commentsRV.canScrollVertically(1) &&
::repliesPage.isInitialized &&
repliesPage.nextpage != null
) {
fetchReplies(videoId, repliesPage.nextpage!!) {
repliesAdapter.updateItems(repliesPage.comments)
}
}
}
loadInitialReplies(videoId, nextPage, repliesAdapter)
}
private fun loadInitialReplies(
videoId: String,
nextPage: String,
repliesAdapter: CommentsAdapter
) {
when (repliesAdapter.itemCount) {
0 -> {
binding.progress.visibility = View.VISIBLE
fetchReplies(videoId, nextPage) {
repliesAdapter.updateItems(it.comments)
binding.progress.visibility = View.GONE
}
}
else -> {
repliesAdapter.clear()
}
}
}
private fun fetchReplies(
videoId: String,
nextPage: String,
onFinished: (CommentsPage) -> Unit
) {
CoroutineScope(Dispatchers.IO).launch {
if (isLoading) return@launch
isLoading = true
repliesPage = try {
RetrofitInstance.api.getCommentsNextPage(videoId, nextPage)
} catch (e: Exception) {
Log.e(TAG(), "IOException, you might not have internet connection")
return@launch
}
withContext(Dispatchers.Main) {
onFinished.invoke(repliesPage)
}
isLoading = false
}
}
}

View File

@ -20,6 +20,7 @@ class CommentsViewModel : ViewModel() {
var videoId: String? = null var videoId: String? = null
var maxHeight: Int = 0 var maxHeight: Int = 0
var commentsSheetDismiss: (() -> Unit)? = null
fun fetchComments() { fun fetchComments() {
videoId ?: return videoId ?: return

View File

@ -1,25 +1,24 @@
package com.github.libretube.ui.sheets package com.github.libretube.ui.sheets
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import androidx.core.view.updateLayoutParams import android.view.WindowManager
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.databinding.CommentsSheetBinding import com.github.libretube.databinding.CommentsSheetBinding
import com.github.libretube.extensions.dpToPx import com.github.libretube.ui.fragments.CommentsMainFragment
import com.github.libretube.ui.adapters.CommentsAdapter import com.github.libretube.ui.fragments.CommentsRepliesFragment
import com.github.libretube.ui.models.CommentsViewModel import com.github.libretube.ui.models.CommentsViewModel
class CommentsSheet : ExpandedBottomSheet() { class CommentsSheet : ExpandedBottomSheet() {
private lateinit var binding: CommentsSheetBinding private lateinit var binding: CommentsSheetBinding
private val commentsViewModel: CommentsViewModel by activityViewModels()
private lateinit var commentsAdapter: CommentsAdapter
private val viewModel: CommentsViewModel by activityViewModels()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -33,56 +32,83 @@ class CommentsSheet : ExpandedBottomSheet() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.dragHandle.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { commentsViewModel.commentsSheetDismiss = this::dismiss
override fun onGlobalLayout() {
binding.dragHandle.viewTreeObserver.removeOnGlobalLayoutListener(this) binding.apply {
// limit the recyclerview height to not cover the video dragHandle.viewTreeObserver.addOnGlobalLayoutListener(object :
binding.commentsRV.updateLayoutParams { ViewTreeObserver.OnGlobalLayoutListener {
height = viewModel.maxHeight - (binding.dragHandle.height + 20.dpToPx().toInt()) override fun onGlobalLayout() {
dragHandle.viewTreeObserver.removeOnGlobalLayoutListener(this)
// limit the recyclerview height to not cover the video
binding.standardBottomSheet.layoutParams =
binding.commentFragContainer.layoutParams.apply {
height = commentsViewModel.maxHeight
}
} }
} })
})
binding.commentsRV.layoutManager = LinearLayoutManager(requireContext()) btnBack.setOnClickListener {
binding.commentsRV.setItemViewCacheSize(20) if (childFragmentManager.backStackEntryCount > 0) {
childFragmentManager.popBackStack()
binding.commentsRV.viewTreeObserver
.addOnScrollChangedListener {
if (!binding.commentsRV.canScrollVertically(1)) {
viewModel.fetchNextComments()
} }
} }
commentsAdapter = CommentsAdapter( btnClose.setOnClickListener { dismiss() }
viewModel.videoId!!,
viewModel.commentsPage.value?.comments.orEmpty().toMutableList()
) {
dialog?.dismiss()
}
binding.commentsRV.adapter = commentsAdapter
if (viewModel.commentsPage.value?.comments.orEmpty().isEmpty()) {
binding.progress.visibility = View.VISIBLE
viewModel.fetchComments()
} }
// listen for new comments to be loaded childFragmentManager.apply {
viewModel.commentsPage.observe(viewLifecycleOwner) { addOnBackStackChangedListener(this@CommentsSheet::onFragmentChanged)
it ?: return@observe
binding.progress.visibility = View.GONE beginTransaction()
if (it.disabled == true) { .replace(R.id.commentFragContainer, CommentsMainFragment())
binding.errorTV.visibility = View.VISIBLE .runOnCommit(this@CommentsSheet::onFragmentChanged)
return@observe .commit()
}
if (it.comments.isEmpty()) {
binding.errorTV.text = getString(R.string.no_comments_available)
binding.errorTV.visibility = View.VISIBLE
return@observe
}
commentsAdapter.updateItems(
// only add the new comments to the recycler view
it.comments.subList(commentsAdapter.itemCount, it.comments.size)
)
} }
} }
private fun onFragmentChanged() {
childFragmentManager.findFragmentById(R.id.commentFragContainer)?.let {
when (it) {
is CommentsRepliesFragment -> {
binding.btnBack.visibility = View.VISIBLE
binding.commentsTitle.text = getString(R.string.replies)
}
else -> {
binding.btnBack.visibility = View.GONE
binding.commentsTitle.text = getString(R.string.comments)
}
}
}
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
commentsViewModel.commentsSheetDismiss = null
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.apply {
setOnKeyListener { _, keyCode, _ ->
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (childFragmentManager.backStackEntryCount > 0) {
childFragmentManager.popBackStack()
return@setOnKeyListener true
}
}
return@setOnKeyListener false
}
window?.let {
it.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
}
setCanceledOnTouchOutside(false)
}
return dialog
}
} }

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/rounded_ripple"> android:background="?selectableItemBackground">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -25,9 +25,10 @@
<com.google.android.material.imageview.ShapeableImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/commentor_image" android:id="@+id/commentor_image"
android:layout_width="36dp" android:layout_width="32dp"
android:layout_height="36dp" android:layout_height="32dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:background="@android:color/darker_gray"
app:shapeAppearance="@style/CircleImageView" app:shapeAppearance="@style/CircleImageView"
app:srcCompat="@mipmap/ic_launcher" /> app:srcCompat="@mipmap/ic_launcher" />
@ -47,8 +48,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="2" android:maxLines="2"
android:textSize="15sp" android:textSize="14sp"
android:textStyle="bold" android:textColor="@color/text_color_secondary"
tools:text="Author and Time" /> tools:text="Author and Time" />
<ImageView <ImageView
@ -74,7 +75,9 @@
android:id="@+id/comment_text" android:id="@+id/comment_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="4dp" android:layout_marginTop="5dp"
android:layout_marginBottom="8dp"
android:textSize="15sp"
android:autoLink="web" android:autoLink="web"
tools:text="Comment Text" /> tools:text="Comment Text" />
@ -120,7 +123,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="6dp" android:layout_marginStart="6dp"
tools:text="LikeCount" /> tools:text="ReplyCount" />
</LinearLayout> </LinearLayout>
@ -128,26 +131,5 @@
</LinearLayout> </LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/replies_recView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:nestedScrollingEnabled="false" />
<com.google.android.material.button.MaterialButton
android:id="@+id/show_more"
style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="8dp"
android:stateListAnimator="@null"
android:text="@string/show_more"
android:textColor="?android:attr/textColorPrimary"
android:textSize="12sp"
android:visibility="gone"
app:cornerRadius="20dp" />
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -9,7 +10,6 @@
style="@style/Widget.Material3.BottomSheet" style="@style/Widget.Material3.BottomSheet"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingBottom="20dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<LinearLayout <LinearLayout
@ -20,35 +20,56 @@
<!-- Drag handle for accessibility --> <!-- Drag handle for accessibility -->
<com.google.android.material.bottomsheet.BottomSheetDragHandleView <com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:id="@+id/drag_handle" android:id="@+id/drag_handle"
android:layout_width="match_parent"
android:layout_height="20dp"
android:paddingTop="8dp"
android:paddingBottom="0dp"
app:tint="@color/drag_handle_color" />
<LinearLayout
android:layout_width="match_parent"
android:paddingHorizontal="10dp"
android:paddingBottom="7dp"
android:gravity="center_vertical"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/btnBack"
android:layout_width="35dp"
android:layout_height="35dp"
android:visibility="gone"
android:src="?homeAsUpIndicator"
android:padding="7dp"
android:background="?selectableItemBackgroundBorderless" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/commentsTitle"
style="@style/TextAppearance.Material3.ActionBar.Title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="10dp"
tools:text="Title" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/btnClose"
android:layout_width="35dp"
android:layout_height="35dp"
android:src="@drawable/ic_close"
android:padding="7dp"
android:background="?selectableItemBackgroundBorderless" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<FrameLayout <FrameLayout
android:id="@+id/commentFragContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="0dp"
android:layout_weight="1" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/commentsRV"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
<TextView
android:id="@+id/errorTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="5dp"
android:text="@string/comments_disabled"
android:visibility="gone" />
</FrameLayout>
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/commentsRV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="15dp"
android:clipToPadding="false" />
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
<TextView
android:id="@+id/errorTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="5dp"
android:text="@string/comments_disabled"
android:visibility="gone" />
</FrameLayout>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="text_color_secondary">#BFBFBF</color>
<color name="drag_handle_color">#3A3A3A</color>
</resources>

View File

@ -190,8 +190,8 @@
<string name="notifications">Notificações</string> <string name="notifications">Notificações</string>
<string name="notify_new_streams_summary">Notificações sobre novo conteúdo de criadores que você segue.</string> <string name="notify_new_streams_summary">Notificações sobre novo conteúdo de criadores que você segue.</string>
<string name="checking_frequency">Verificando a cada…</string> <string name="checking_frequency">Verificando a cada…</string>
<string name="new_streams_count">%1$s novas transmissões estão disponíveis</string> <string name="new_streams_count">%1$s novos vídeos estão disponíveis</string>
<string name="new_streams_by">Novas transmissôes por %1$s </string> <string name="new_streams_by">Novos vídeos de %1$s</string>
<string name="irreversible">Tem certeza\? Isto não pode ser desfeito!</string> <string name="irreversible">Tem certeza\? Isto não pode ser desfeito!</string>
<string name="least_views">Menos visualizações</string> <string name="least_views">Menos visualizações</string>
<string name="channel_name_az">Nome do canal (A-Z)</string> <string name="channel_name_az">Nome do canal (A-Z)</string>
@ -267,7 +267,7 @@
<string name="instance_frontend_url">URL para a interface da instância</string> <string name="instance_frontend_url">URL para a interface da instância</string>
<string name="quality">Qualidade</string> <string name="quality">Qualidade</string>
<string name="behavior">Comportamento</string> <string name="behavior">Comportamento</string>
<string name="seek_increment">Buscar incremento</string> <string name="seek_increment">Incremento de avanço/retrocesso</string>
<string name="pauseOnScreenOff">Pausa automática</string> <string name="pauseOnScreenOff">Pausa automática</string>
<string name="pauseOnScreenOff_summary">Pausar quando a tela é desligada.</string> <string name="pauseOnScreenOff_summary">Pausar quando a tela é desligada.</string>
<string name="autoplay_summary">Reproduzir o próximo vídeo assim que o atual termina.</string> <string name="autoplay_summary">Reproduzir o próximo vídeo assim que o atual termina.</string>
@ -307,7 +307,7 @@
<string name="take_a_break">Hora de tirar uma pausa</string> <string name="take_a_break">Hora de tirar uma pausa</string>
<string name="resize_mode_fill">Preencher</string> <string name="resize_mode_fill">Preencher</string>
<string name="repeat_mode_none">Nenhum</string> <string name="repeat_mode_none">Nenhum</string>
<string name="picture_in_picture">Imagem em imagem (PIP)</string> <string name="picture_in_picture">Imagem em imagem (PiP)</string>
<string name="break_reminder_time">Minutos antes de ser lembrado</string> <string name="break_reminder_time">Minutos antes de ser lembrado</string>
<string name="yt_shorts">Vídeos curtos</string> <string name="yt_shorts">Vídeos curtos</string>
<string name="no_subtitles_available">Legendas não disponíveis</string> <string name="no_subtitles_available">Legendas não disponíveis</string>
@ -315,7 +315,7 @@
<string name="backup">Cópia de segurança</string> <string name="backup">Cópia de segurança</string>
<string name="resize_mode_fit">Encaixar</string> <string name="resize_mode_fit">Encaixar</string>
<string name="repeat_mode_current">Atual</string> <string name="repeat_mode_current">Atual</string>
<string name="push_channel_description">Exibe uma notificação quando novas transmissões estão disponíveis.</string> <string name="push_channel_description">Exibe uma notificação quando novos vídeos estão disponíveis.</string>
<string name="push_channel_name">Serviço de notificações</string> <string name="push_channel_name">Serviço de notificações</string>
<string name="play_next">Reproduzir a seguir</string> <string name="play_next">Reproduzir a seguir</string>
<string name="background_channel_name">Modo de segundo plano</string> <string name="background_channel_name">Modo de segundo plano</string>
@ -405,7 +405,7 @@
<string name="no_comments_available">Este vídeo não tem comentários disponíveis.</string> <string name="no_comments_available">Este vídeo não tem comentários disponíveis.</string>
<string name="comments_disabled">O autor não permite comentários.</string> <string name="comments_disabled">O autor não permite comentários.</string>
<string name="captions_size">Tamanho das legendas</string> <string name="captions_size">Tamanho das legendas</string>
<string name="double_tap_seek">Dois toques para buscar</string> <string name="double_tap_seek">Dois toques para avançar/retrodeceder</string>
<string name="all_caught_up">Você está atualizado</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="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 reprodutor.</string> <string name="double_tap_seek_summary">Toque duas vezes à esquerda ou à direita para retroceder ou avançar a posição do reprodutor.</string>
@ -435,7 +435,7 @@
<string name="forward">Avançar</string> <string name="forward">Avançar</string>
<string name="pause">Pausar</string> <string name="pause">Pausar</string>
<string name="alternative_pip_controls">Controles PiP alternativos</string> <string name="alternative_pip_controls">Controles PiP alternativos</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="alternative_pip_controls_summary">Exibir 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="audio_player">Reprodutor de áudio</string>
<string name="no_subtitle">Sem legendas</string> <string name="no_subtitle">Sem legendas</string>
<string name="download_paused">Transferência pausada</string> <string name="download_paused">Transferência pausada</string>

View File

@ -36,7 +36,7 @@
<string name="empty">Морате да унесете корисничко име и лозинку.</string> <string name="empty">Морате да унесете корисничко име и лозинку.</string>
<string name="defres">Видео резолуција</string> <string name="defres">Видео резолуција</string>
<string name="grid">Мрежне колоне</string> <string name="grid">Мрежне колоне</string>
<string name="emptyList">Нема ништа овде.</string> <string name="emptyList">Нема ништа.</string>
<string name="addToPlaylist">Додај на листу</string> <string name="addToPlaylist">Додај на листу</string>
<string name="createPlaylist">Направи листу</string> <string name="createPlaylist">Направи листу</string>
<string name="library">Библиотека</string> <string name="library">Библиотека</string>
@ -88,7 +88,7 @@
<string name="color_red">Црвена</string> <string name="color_red">Црвена</string>
<string name="material_you">Системска</string> <string name="material_you">Системска</string>
<string name="sponsorblock_notifications">Обавештења</string> <string name="sponsorblock_notifications">Обавештења</string>
<string name="app_icon">Иконица</string> <string name="app_icon">Икона</string>
<string name="enabled">Укључено</string> <string name="enabled">Укључено</string>
<string name="disabled">Искључено</string> <string name="disabled">Искључено</string>
<string name="piped">Пајпед</string> <string name="piped">Пајпед</string>
@ -127,7 +127,7 @@
<string name="version">Верзија %1$s</string> <string name="version">Верзија %1$s</string>
<string name="about_summary">Упознајте LibreTube тим и како све то функцијонише.</string> <string name="about_summary">Упознајте LibreTube тим и како све то функцијонише.</string>
<string name="related_streams">Сличан садржај</string> <string name="related_streams">Сличан садржај</string>
<string name="related_streams_summary">Прикажите сличне садржаје испод оног што тренутно гледате.</string> <string name="related_streams_summary">Прикажите сличан садржај испод тренутног гледања.</string>
<string name="show_chapters">Прикажи поглавља</string> <string name="show_chapters">Прикажи поглавља</string>
<string name="hide_chapters">Сакриј поглавља</string> <string name="hide_chapters">Сакриј поглавља</string>
<string name="buffering_goal">Предучитавање</string> <string name="buffering_goal">Предучитавање</string>
@ -182,8 +182,8 @@
<string name="pure_theme_summary">Чиста бело/црна тема</string> <string name="pure_theme_summary">Чиста бело/црна тема</string>
<string name="no_player_found">Није пронађен екстерни плејер. Проверите да ли имате неки инсталиран.</string> <string name="no_player_found">Није пронађен екстерни плејер. Проверите да ли имате неки инсталиран.</string>
<string name="data_saver_mode">Режим уштеде података</string> <string name="data_saver_mode">Режим уштеде података</string>
<string name="search_history_summary">Запамти претраге</string> <string name="search_history_summary">Памти претраге</string>
<string name="watch_history_summary">Чувајте историју прегледа видеа локално</string> <string name="watch_history_summary">Чувајте историју прегледа снимака локално</string>
<string name="watch_positions_title">Запамћене позиције репродукције</string> <string name="watch_positions_title">Запамћене позиције репродукције</string>
<string name="reset_watch_positions">Ресетуј</string> <string name="reset_watch_positions">Ресетуј</string>
<string name="captions">Наслови</string> <string name="captions">Наслови</string>
@ -267,7 +267,7 @@
<string name="notify_new_streams">Обавештења о новим стримовима</string> <string name="notify_new_streams">Обавештења о новим стримовима</string>
<string name="checking_frequency">Провера на сваких …</string> <string name="checking_frequency">Провера на сваких …</string>
<string name="new_streams_count">%1$s нових стримова је доступно</string> <string name="new_streams_count">%1$s нових стримова је доступно</string>
<string name="notify_new_streams_summary">Обавештења када буду доступни нови стримови из ваших претплата.</string> <string name="notify_new_streams_summary">Обавештења када буду доступни нови стримови из ваших праћења.</string>
<string name="new_streams_by">Нови стримови од %1$s…</string> <string name="new_streams_by">Нови стримови од %1$s…</string>
<string name="irreversible">Да ли сте сигурни\? Ово не може бити опозвано!</string> <string name="irreversible">Да ли сте сигурни\? Ово не може бити опозвано!</string>
<string name="required_network">Потребна веза</string> <string name="required_network">Потребна веза</string>
@ -335,13 +335,13 @@
<string name="play_next">Пусти следеће</string> <string name="play_next">Пусти следеће</string>
<string name="playback_pitch">Висина тона</string> <string name="playback_pitch">Висина тона</string>
<string name="default_load_interval">Аутоматски</string> <string name="default_load_interval">Аутоматски</string>
<string name="break_reminder_time">Неколико минута пре подсећања</string> <string name="break_reminder_time">Минута пре подсећања</string>
<string name="change_region">Чини се да трендови нису доступни за тренутни регион. Молимо изаберите другу у подешавањима.</string> <string name="change_region">Чини се да трендови нису доступни за тренутни регион. Молимо изаберите другу у подешавањима.</string>
<string name="limit_hls">Ограничи HLS на 1080p</string> <string name="limit_hls">Ограничи HLS на 1080p</string>
<string name="filename">Назив фајла</string> <string name="filename">Назив фајла</string>
<string name="invalid_filename">Непостојеће име фајла!</string> <string name="invalid_filename">Непостојеће име фајла!</string>
<string name="playlists_order">Редослед плејлиста</string> <string name="playlists_order">Редослед листи</string>
<string name="playlistNameReversed">Назив плејлисте (обрнуто)</string> <string name="playlistNameReversed">Назив листе (обрнуто)</string>
<string name="recentlyUpdated">Недавно ажуриран</string> <string name="recentlyUpdated">Недавно ажуриран</string>
<string name="recentlyUpdatedReversed">Недавно ажурирано (обрнуто)</string> <string name="recentlyUpdatedReversed">Недавно ажурирано (обрнуто)</string>
<string name="download_channel_name">Преузми услугу</string> <string name="download_channel_name">Преузми услугу</string>
@ -380,7 +380,7 @@
<string name="defaultIconLight">Подразумевано светло</string> <string name="defaultIconLight">Подразумевано светло</string>
<string name="unsupported_file_format">Неподржан формат датотеке!</string> <string name="unsupported_file_format">Неподржан формат датотеке!</string>
<string name="hls_instead_of_dash">Користи HLS</string> <string name="hls_instead_of_dash">Користи HLS</string>
<string name="hls_instead_of_dash_summary">Користи HLS уместо DASH (биће спорије, не препоручује се)</string> <string name="hls_instead_of_dash_summary">Користи HLS уместо DASH (спорије, не препоручује се)</string>
<string name="auto_quality">Аутоматски</string> <string name="auto_quality">Аутоматски</string>
<string name="limit_to_runtime">Ограничење времена коришћења</string> <string name="limit_to_runtime">Ограничење времена коришћења</string>
<string name="open_queue_from_notification">Отвори редослед из обавештења</string> <string name="open_queue_from_notification">Отвори редослед из обавештења</string>
@ -432,7 +432,7 @@
<string name="forward">Премотај унапред</string> <string name="forward">Премотај унапред</string>
<string name="alternative_pip_controls">Алтернативне PiP контроле</string> <string name="alternative_pip_controls">Алтернативне PiP контроле</string>
<string name="alternative_pip_controls_summary">Приказивање само звука и прескакање контрола у PiP-у уместо унапред и уназад</string> <string name="alternative_pip_controls_summary">Приказивање само звука и прескакање контрола у PiP-у уместо унапред и уназад</string>
<string name="audio_only_mode">Режим само звука</string> <string name="audio_only_mode">Режим аудио плејера</string>
<string name="audio_only_mode_summary">Претвори LibreTube у музички плејер.</string> <string name="audio_only_mode_summary">Претвори LibreTube у музички плејер.</string>
<string name="concurrent_downloads_limit_reached">Максимално истовремених преузимања је достигнуто.</string> <string name="concurrent_downloads_limit_reached">Максимално истовремених преузимања је достигнуто.</string>
<string name="pinch_control_summary">Користите покрет штипања за увећавање/умањивање.</string> <string name="pinch_control_summary">Користите покрет штипања за увећавање/умањивање.</string>
@ -446,4 +446,8 @@
<string name="shuffle">Насумично</string> <string name="shuffle">Насумично</string>
<string name="remove_bookmark">Обриши обележивач</string> <string name="remove_bookmark">Обриши обележивач</string>
<string name="add_to_bookmarks">Додај у обележиваче</string> <string name="add_to_bookmarks">Додај у обележиваче</string>
<string name="sleep_timer">Аутоматско искључење</string>
<string name="skip_silence">Прескочи тишину</string>
<string name="help">Помоћ</string>
<string name="faq">Често постављена питања</string>
</resources> </resources>

View File

@ -401,4 +401,8 @@
<string name="swipe_controls">Điều khiển vuốt</string> <string name="swipe_controls">Điều khiển vuốt</string>
<string name="swipe_controls_summary">Sử dụng thao tác vuốt để điều chỉnh độ sáng và âm lượng.</string> <string name="swipe_controls_summary">Sử dụng thao tác vuốt để điều chỉnh độ sáng và âm lượng.</string>
<string name="bookmark">Đánh dấu trang</string> <string name="bookmark">Đánh dấu trang</string>
<string name="download_paused">Tạm dừng tải xuống</string>
<string name="download_completed">Tải xuống hoàn tất</string>
<string name="concurrent_downloads">Tải xuống đồng thời tối đa</string>
<string name="no_subtitle">Không có phụ đề</string>
</resources> </resources>

View File

@ -24,8 +24,8 @@
<string name="registered">註冊完成。現在你可以開始訂閱頻道了。</string> <string name="registered">註冊完成。現在你可以開始訂閱頻道了。</string>
<string name="already_logged_in">已登入。請登出現有的帳號。</string> <string name="already_logged_in">已登入。請登出現有的帳號。</string>
<string name="login_first">請登入並再試一次。</string> <string name="login_first">請登入並再試一次。</string>
<string name="instances">選擇實</string> <string name="instances">選擇實例…</string>
<string name="customInstance">自訂實</string> <string name="customInstance">自訂實</string>
<string name="region">地區</string> <string name="region">地區</string>
<string name="login_register">登入/註冊</string> <string name="login_register">登入/註冊</string>
<string name="please_login">請先登入或到設定裡註冊。</string> <string name="please_login">請先登入或到設定裡註冊。</string>
@ -55,7 +55,7 @@
<string name="darkTheme">深色主題</string> <string name="darkTheme">深色主題</string>
<string name="subscribers">%1$s個訂閱者</string> <string name="subscribers">%1$s個訂閱者</string>
<string name="settings">設定</string> <string name="settings">設定</string>
<string name="instance"></string> <string name="instance"></string>
<string name="customization">自訂</string> <string name="customization">自訂</string>
<string name="website">網站</string> <string name="website">網站</string>
<string name="videoCount">%1$s部影片</string> <string name="videoCount">%1$s部影片</string>
@ -93,12 +93,12 @@
<string name="license">授權</string> <string name="license">授權</string>
<string name="color_accent">強調色</string> <string name="color_accent">強調色</string>
<string name="color_red">放鬆紅</string> <string name="color_red">放鬆紅</string>
<string name="color_blue">像素</string> <string name="color_blue">幸福</string>
<string name="color_yellow">活力黃</string> <string name="color_yellow">活力黃</string>
<string name="color_green">時髦綠</string> <string name="color_green">時髦綠</string>
<string name="color_purple">愉快紫</string> <string name="color_purple">愉快紫</string>
<string name="oledTheme">黑色主題</string> <string name="oledTheme">黑色</string>
<string name="material_you">神秘質感3</string> <string name="material_you">Material You</string>
<string name="sponsorblock_notifications">通知</string> <string name="sponsorblock_notifications">通知</string>
<string name="app_icon">圖示</string> <string name="app_icon">圖示</string>
<string name="enabled">開啟</string> <string name="enabled">開啟</string>
@ -113,10 +113,10 @@
<string name="download_directory_summary">儲存下載影音的位置。</string> <string name="download_directory_summary">儲存下載影音的位置。</string>
<string name="contributing">貢獻</string> <string name="contributing">貢獻</string>
<string name="donate">捐贈</string> <string name="donate">捐贈</string>
<string name="update_summary">按這裡檢查APP是否為最新版本。</string> <string name="update_summary">按這裡檢查更新</string>
<string name="app_uptodate">現在是最新版本。</string> <string name="app_uptodate">現在是最新版本。</string>
<string name="playback_speed">預設播放速度</string> <string name="playback_speed">播放速度</string>
<string name="advanced_summary">播放器,下載,歷史</string> <string name="advanced_summary">下載和重設</string>
<string name="live">直播</string> <string name="live">直播</string>
<string name="no_replies">此留言無任何回覆。</string> <string name="no_replies">此留言無任何回覆。</string>
<string name="authors">作者</string> <string name="authors">作者</string>
@ -135,19 +135,19 @@
<string name="flameIcon">飛馳烈焰</string> <string name="flameIcon">飛馳烈焰</string>
<string name="birdIcon">加速鳥兒</string> <string name="birdIcon">加速鳥兒</string>
<string name="instance_summary">Piped登入訂閱</string> <string name="instance_summary">Piped登入訂閱</string>
<string name="customInstance_summary">加入自訂實體 (風險自負)</string> <string name="customInstance_summary">加入實例</string>
<string name="instance_name">名稱</string> <string name="instance_name">名稱</string>
<string name="instance_api_url">API網址</string> <string name="instance_api_url">API網址</string>
<string name="addInstance">新增實</string> <string name="addInstance">新增實</string>
<string name="empty_instance">請填寫名稱和API網址欄位。</string> <string name="empty_instance">請填寫名稱和API網址欄位。</string>
<string name="clear_customInstances">清除自訂實</string> <string name="clear_customInstances">清除自訂實</string>
<string name="invalid_url">請填入有效的網址</string> <string name="invalid_url">請填入有效的網址</string>
<string name="version">版本%1$s</string> <string name="version">版本%1$s</string>
<string name="about_summary">認識LibretTube團隊以及背後緣由。</string> <string name="about_summary">認識LibretTube團隊以及背後緣由。</string>
<string name="related_streams">相關串流</string> <string name="related_streams">相關內容</string>
<string name="related_streams_summary">在影片中顯示相關的串流。</string> <string name="related_streams_summary">在影片中顯示相關的串流。</string>
<string name="show_chapters">顯示章節</string> <string name="show_chapters">顯示章節</string>
<string name="buffering_goal">緩衝</string> <string name="buffering_goal">預先載入</string>
<string name="yes"></string> <string name="yes"></string>
<string name="subscriptions">訂閱</string> <string name="subscriptions">訂閱</string>
<string name="library">媒體庫</string> <string name="library">媒體庫</string>
@ -160,7 +160,7 @@
<string name="about">關於</string> <string name="about">關於</string>
<string name="changeLanguage">語言</string> <string name="changeLanguage">語言</string>
<string name="register">註冊</string> <string name="register">註冊</string>
<string name="server_error">伺服器出了點問題。換個實試試?</string> <string name="server_error">伺服器出了點問題。換個實試試?</string>
<string name="dlisinprogress">有其他下載正在進行,請等待所有下載完成…</string> <string name="dlisinprogress">有其他下載正在進行,請等待所有下載完成…</string>
<string name="systemLanguage">系統</string> <string name="systemLanguage">系統</string>
<string name="okay"></string> <string name="okay"></string>
@ -181,7 +181,7 @@
<string name="reset_message">是否重置所有設定並從實例登出?</string> <string name="reset_message">是否重置所有設定並從實例登出?</string>
<string name="deleteAccount">刪除帳號</string> <string name="deleteAccount">刪除帳號</string>
<string name="deleteAccount_summary">刪除您於piped實例中的帳號</string> <string name="deleteAccount_summary">刪除您於piped實例中的帳號</string>
<string name="playerVideoFormat">播放器所使用的視頻格式</string> <string name="playerVideoFormat">播放器所使用的影片格式</string>
<string name="audio">音頻</string> <string name="audio">音頻</string>
<string name="video">視訊</string> <string name="video">視訊</string>
<string name="downloading">下載中…</string> <string name="downloading">下載中…</string>
@ -206,4 +206,248 @@
<string name="no_video">無視訊</string> <string name="no_video">無視訊</string>
<string name="hls">HLS</string> <string name="hls">HLS</string>
<string name="clonePlaylist">拷貝播放清單</string> <string name="clonePlaylist">拷貝播放清單</string>
<string name="open">開啟…</string>
<string name="playlistCloned">已複製播放清單</string>
<string name="confirm_unsubscribing">確認取消訂閱</string>
<string name="default_subtitle_language">字幕語言</string>
<string name="local_playlists">本機播放清單</string>
<string name="color_violet">多彩紫</string>
<string name="push_channel_description">新串流上線時顯示通知。</string>
<string name="chapters">章節</string>
<string name="not_enabled">選單選項尚未啟用!</string>
<string name="select_other_start_tab">請先在另一個啟動頁籤選取!</string>
<string name="play_all">全部播放</string>
<string name="volume">音量</string>
<string name="brightness">亮度</string>
<string name="auto">自動</string>
<string name="swipe_controls">滑動控制</string>
<string name="swipe_controls_summary">使用滑動手勢調整亮度和音量。</string>
<string name="background_channel_description">在通知欄顯示附有按鈕的音訊播放器。</string>
<string name="history_empty">尚無歷史紀錄。</string>
<string name="notifications">通知</string>
<string name="notify_new_streams">新串流通知</string>
<string name="notify_new_streams_summary">您跟隨的創作者最新串流的通知。</string>
<string name="wifi">Wi-Fi</string>
<string name="pop_up">彈出</string>
<string name="no_comments_available">這部影片沒有留言。</string>
<string name="required_network">需要的連線</string>
<string name="network_all">全部</string>
<string name="pause">暫停</string>
<string name="rewind">後退</string>
<string name="forward">前進</string>
<string name="all_caught_up">沒有更多了</string>
<string name="repeat_mode">重複模式</string>
<string name="resize_mode_fill">填滿</string>
<string name="resize_mode_zoom">放大</string>
<string name="background_mode">背景模式</string>
<string name="add_to_queue">加入佇列</string>
<string name="skip_segment">跳過片段</string>
<string name="sb_skip_manual">手動跳過</string>
<string name="sb_skip_manual_summary">不要自動跳過片段,總是事先提醒。</string>
<string name="local_subscriptions">本機訂閱列表</string>
<string name="preferences">偏好設定</string>
<string name="playing_queue">播放佇列</string>
<string name="queue">佇列</string>
<string name="time">時長</string>
<string name="start_time">開始時間</string>
<string name="end_time">結束時間</string>
<string name="notification_time">通知時長</string>
<string name="notification_time_summary">通知顯示的時長。</string>
<string name="download_completed">下載完成</string>
<string name="failed_fetching_instances">無法取得可用實例。</string>
<string name="play_next">播放下一部</string>
<string name="community">社群</string>
<string name="require_restart">需要重新啟動APP</string>
<string name="auth_instance_summary">授權呼叫時使用不同的實例。</string>
<string name="views_placeholder">觀看次數</string>
<string name="concurrent_downloads">最大並行下載數</string>
<string name="concurrent_downloads_limit_reached">已達到最大並行下載數。</string>
<string name="unknown">未知</string>
<string name="resume">繼續</string>
<string name="pauseOnScreenOff">自動暫停</string>
<string name="portrait">肖像</string>
<string name="discord">Discord</string>
<string name="matrix">Matrix</string>
<string name="telegram">Telegram</string>
<string name="turnInternetOn">請開啟Wifi或行動數據連上網路。</string>
<string name="never">永不</string>
<string name="change_playback_speed">播放速度</string>
<string name="selected">已選取</string>
<string name="navLabelVisibility">標籤可見度</string>
<string name="always">總是</string>
<string name="autoRotatePlayer">自動全螢幕</string>
<string name="pure_theme">純粹主題</string>
<string name="autoRotatePlayer_summary">裝置旋轉後自動全螢幕。</string>
<string name="no_player_found">找不到外部播放器。請確認已安裝外部播放器。</string>
<string name="data_saver_mode_summary">跳過縮圖和其他圖片。</string>
<string name="data_saver_mode">數據節省模式</string>
<string name="search_history_summary">記住搜尋</string>
<string name="watch_history_summary">於本機儲存影片觀看歷史</string>
<string name="history_summary">觀看和搜尋歷史</string>
<string name="playerAudioQuality">音訊品質</string>
<string name="captions_size">說明文字大小</string>
<string name="sort">排序</string>
<string name="skip_buttons_summary">顯示按鈕,跳至上一部或下一部影片。</string>
<string name="take_a_break">是時候休息一下了</string>
<string name="maximum_image_cache">最大圖片快取大小</string>
<string name="device_info">裝置資訊</string>
<string name="delete">從下載刪除</string>
<string name="new_videos_badge">新影片的指示器</string>
<string name="backup_customInstances">自訂實例</string>
<string name="default_load_interval">預設</string>
<string name="save_feed">在背景載入訂閱列表</string>
<string name="limit_hls">將HLS限制為1080p</string>
<string name="save_feed_summary">在背景載入訂閱列表,並防止自動重新載入。</string>
<string name="progressive_load_interval">載入的進度間隔</string>
<string name="filename">檔案名稱</string>
<string name="recentlyUpdatedReversed">最近更新 (反序)</string>
<string name="added_to_playlist">新增至播放清單</string>
<string name="confirm_unsubscribe">您確定要取消訂閱 %1$s 嗎?</string>
<string name="misc">雜項</string>
<string name="player_resize_mode">重新調整大小模式</string>
<string name="break_reminder_time">幾分鐘前提醒</string>
<string name="break_reminder">休息提醒</string>
<string name="resize_mode_fit">適應</string>
<string name="copied_to_clipboard">複製到剪貼簿</string>
<string name="repeat_mode_none"></string>
<string name="new_videos_badge_summary">如果有新影片,顯示數量徽章。</string>
<string name="hide_watched_from_feed">從訂閱列表隱藏觀看過的影片</string>
<string name="hide_watched_from_feed_summary">不要在訂閱列表頁籤顯示觀看進度90%以上的影片。</string>
<string name="select_at_least_one">請至少選取一個項目</string>
<string name="download_channel_name">下載服務</string>
<string name="confirm_unsubscribing_summary">在取消訂閱前顯示確認對話框。</string>
<string name="livestreams">直播</string>
<string name="progressive_load_interval_summary">數值越低,影片初始載入可能就越慢。</string>
<string name="invalid_filename">檔案名稱無效!</string>
<string name="playback_pitch">音高</string>
<string name="playlists_order">播放清單順序</string>
<string name="playlistNameReversed">播放清單名稱 (反序)</string>
<string name="recentlyUpdated">最近更新</string>
<string name="pure_theme_summary">純粹白/黑主題</string>
<string name="watch_positions_title">記住播放位置</string>
<string name="reset_watch_positions">重設</string>
<string name="none"></string>
<string name="system_caption_style">系統說明文字樣式</string>
<string name="update_now">現在安裝新版本LibreTube嗎</string>
<string name="playingOnBackground">在背景播放…</string>
<string name="downloading_apk">下載APK…</string>
<string name="playerAudioFormat">播放器的音訊格式</string>
<string name="new_streams_count">新增 %1$s 個串流</string>
<string name="translate">翻譯</string>
<string name="history_size">最大歷史紀錄</string>
<string name="unlimited">無限</string>
<string name="already_spent_time">你已花費%1$s 在此APP上是時候休息一下了。</string>
<string name="yt_shorts">短影片</string>
<string name="repeat_mode_current">目前的</string>
<string name="backup_restore">備份 &amp; 還原</string>
<string name="backup">備份</string>
<string name="picture_in_picture">畫中畫</string>
<string name="trending_layout">另外一種熱門影片版面</string>
<string name="renamePlaylist">重新命名播放清單</string>
<string name="mobile_data">行動數據</string>
<string name="navigation_bar">導航列</string>
<string name="change_region">目前的區域似乎無法使用熱門影片。請在設定中選取其他地區。</string>
<string name="legacy_subscriptions">舊版訂閱列表</string>
<string name="player_summary">預設值與行為</string>
<string name="require_restart_message">重啟APP以套用變更。</string>
<string name="seekbar_preview">影片預覽</string>
<string name="seekbar_preview_summary">拖動播放指示器時顯示快照。</string>
<string name="downloadsucceeded">下載成功</string>
<string name="share_with_time">分享時包含時間戳</string>
<string name="pinch_control_summary">使用手勢放大縮小。</string>
<string name="defaults">預設</string>
<string name="general_summary">語言和地區</string>
<string name="worst_quality">最差</string>
<string name="caption_settings">說明文字</string>
<string name="least_recent">最舊</string>
<string name="network_metered">監測</string>
<string name="captions">說明文字</string>
<string name="network_wifi">僅限Wifi</string>
<string name="checking_frequency">檢查頻率…</string>
<string name="most_views">最多觀看</string>
<string name="channel_name_az">頻道名稱 (A-Z)</string>
<string name="most_recent">最新</string>
<string name="no_subtitles_available">無字幕可用</string>
<string name="channel_name_za">頻道名稱 (Z-A)</string>
<string name="new_streams_by">%1$s 的新串流…</string>
<string name="irreversible">您確定嗎?此操作無法撤銷!</string>
<string name="least_views">最少觀看</string>
<string name="audio_video_summary">品質與格式</string>
<string name="nothing_selected">沒有選取任何項目!</string>
<string name="double_tap_seek">按二下快轉</string>
<string name="backup_restore_summary">匯入&匯出訂閱列表,播放清單…</string>
<string name="export_playlists">匯出播放清單</string>
<string name="app_backup">應用程式備份</string>
<string name="exportsuccess">已匯出。</string>
<string name="privacy_alert">隱私警告</string>
<string name="username_email">使用不推薦的電子郵件繼續嗎?</string>
<string name="proceed">繼續</string>
<string name="sb_markers">標記</string>
<string name="sb_markers_summary">在時間軸上標記片段。</string>
<string name="fullscreen_orientation">全螢幕方向</string>
<string name="aspect_ratio">影片比例</string>
<string name="auto_rotation">自動旋轉</string>
<string name="landscape">風景</string>
<string name="reddit">Reddit</string>
<string name="twitter">Twitter</string>
<string name="pinch_control">調整音高</string>
<string name="play_latest_videos">播放最新影片</string>
<string name="audio_track">音訊曲目</string>
<string name="default_audio_track">預設</string>
<string name="export_subscriptions">匯出訂閱列表</string>
<string name="skip_buttons">跳過按鈕</string>
<string name="no_search_result">無結果。</string>
<string name="error_occurred">錯誤</string>
<string name="copied">已複製</string>
<string name="alternative_videos_layout">另一種影片版面</string>
<string name="defaultIconLight">預設亮色</string>
<string name="unsupported_file_format">檔案格式不支援!</string>
<string name="open_copied">開啟</string>
<string name="general">一般</string>
<string name="audio_player">音訊播放器</string>
<string name="playlistUrl">播放清單網址</string>
<string name="background_channel_name">背景模式</string>
<string name="download_channel_description">在下載媒體檔案時顯示通知。</string>
<string name="push_channel_name">通知工作器</string>
<string name="open_queue_from_notification">從通知開啟佇列</string>
<string name="show_more">顯示更多</string>
<string name="time_code">時間戳 (秒)</string>
<string name="trends">熱門影片</string>
<string name="featured">精選影片</string>
<string name="audio_only_mode">純音訊模式</string>
<string name="audio_only_mode_summary">將LibreTube變成音樂播放器。</string>
<string name="no_subtitle">無字幕</string>
<string name="download_paused">下載暫停</string>
<string name="alternative_trending_layout">另一種熱門影片版面</string>
<string name="navbar_order">排序</string>
<string name="layout">版面</string>
<string name="alternative_player_layout">另一種播放器版面</string>
<string name="alternative_player_layout_summary">在留言上方顯示一排相關影片,而非留言下面。</string>
<string name="hls_instead_of_dash">使用HLS</string>
<string name="hls_instead_of_dash_summary">使用HLS而非DASH (較慢,不推薦)</string>
<string name="auto_quality">自動</string>
<string name="trending">現正熱門</string>
<string name="comments_disabled">上傳者停用了留言功能。</string>
<string name="theme_monochrome">極簡單色</string>
<string name="pause_on_quit">退出後暫停</string>
<string name="queue_insert_related_videos">插入相關影片</string>
<string name="bookmarks">書籤</string>
<string name="bookmark">加入書籤</string>
<string name="clear_bookmarks">清除書籤</string>
<string name="bookmarks_empty">沒有書籤!</string>
<string name="shuffle">隨機</string>
<string name="add_to_bookmarks">新增至書籤</string>
<string name="remove_bookmark">移除書籤</string>
<string name="double_tap_seek_summary">按螢幕左邊或右邊二下快轉影片。</string>
<string name="limit_to_runtime">限制執行時間</string>
<string name="sleep_timer">睡眠定時器</string>
<string name="skip_silence">跳過無聲片段</string>
<string name="help">幫助</string>
<string name="faq">問答</string>
<string name="best_quality">最佳</string>
<string name="seek_increment">快轉幅度</string>
<string name="import_playlists">匯入播放清單</string>
<string name="alternative_pip_controls">另一種畫中畫控制模式</string>
<string name="alternative_pip_controls_summary">只顯示音訊,跳過畫中畫的前進後退控制</string>
<string name="all_caught_up_summary">您已經看完了所有影片</string>
</resources> </resources>

View File

@ -3,6 +3,8 @@
<color name="duration_background_color">#AA000000</color> <color name="duration_background_color">#AA000000</color>
<color name="duration_text_color">#EEFFFFFF</color> <color name="duration_text_color">#EEFFFFFF</color>
<color name="shortcut_color">#0061A6</color> <color name="shortcut_color">#0061A6</color>
<color name="text_color_secondary">#505050</color>
<color name="drag_handle_color">#CCCCCC</color>
<color name="blue_md_theme_light_primary">#0058CB</color> <color name="blue_md_theme_light_primary">#0058CB</color>
<color name="blue_md_theme_light_onPrimary">#FFFFFF</color> <color name="blue_md_theme_light_onPrimary">#FFFFFF</color>

View File

@ -72,6 +72,7 @@
<string name="noInternet">Connect to the Internet first.</string> <string name="noInternet">Connect to the Internet first.</string>
<string name="retry">Retry</string> <string name="retry">Retry</string>
<string name="comments">Comments</string> <string name="comments">Comments</string>
<string name="replies">Replies</string>
<string name="choose_filter">Choose search filter</string> <string name="choose_filter">Choose search filter</string>
<string name="channels">Channels</string> <string name="channels">Channels</string>
<string name="all">All</string> <string name="all">All</string>