Merge pull request #2890 from faisalcodes/master

Fixes #2878 : Using multi-level comments display.
This commit is contained in:
Bnyro 2023-01-28 19:54:18 +01:00 committed by GitHub
commit d9985e2567
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 373 additions and 187 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

@ -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

@ -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>