Merge branch 'fix_comments' into 'master'

Fix comments: open comments in activity.

See merge request pixeldroid/PixelDroid!311
This commit is contained in:
Matthieu 2021-03-14 16:45:28 +00:00
commit 681fdc5582
7 changed files with 255 additions and 283 deletions

View File

@ -1,26 +1,36 @@
package com.h.pixeldroid.posts
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
import android.widget.LinearLayout
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import com.h.pixeldroid.R
import com.h.pixeldroid.databinding.ActivityPostBinding
import com.h.pixeldroid.utils.api.objects.Status
import com.h.pixeldroid.utils.api.objects.Status.Companion.DISCOVER_TAG
import com.h.pixeldroid.utils.api.objects.Status.Companion.DOMAIN_TAG
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG
import com.h.pixeldroid.databinding.CommentBinding
import com.h.pixeldroid.utils.BaseActivity
import com.h.pixeldroid.utils.api.PixelfedAPI
import com.h.pixeldroid.utils.api.objects.Mention
import com.h.pixeldroid.utils.api.objects.Status
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_COMMENT_TAG
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG
import com.h.pixeldroid.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
import com.h.pixeldroid.utils.displayDimensionsInPx
import retrofit2.HttpException
import java.io.IOException
class PostActivity : BaseActivity() {
private lateinit var postFragment : PostFragment
lateinit var domain : String
private lateinit var accessToken : String
private lateinit var binding: ActivityPostBinding
private lateinit var status: Status
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPostBinding.inflate(layoutInflater)
@ -28,18 +38,41 @@ class PostActivity : BaseActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val status = intent.getSerializableExtra(POST_TAG) as Status?
status = intent.getSerializableExtra(POST_TAG) as Status
val viewComments: Boolean = (intent.getSerializableExtra(VIEW_COMMENTS_TAG) ?: false) as Boolean
val postComment: Boolean = (intent.getSerializableExtra(POST_COMMENT_TAG) ?: false) as Boolean
val user = db.userDao().getActiveUser()
domain = user?.instance_uri.orEmpty()
accessToken = user?.accessToken.orEmpty()
postFragment = PostFragment()
val arguments = Bundle()
arguments.putString(DOMAIN_TAG, domain)
initializeFragment(arguments, status)
supportActionBar?.title = getString(R.string.post_title).format(status.account?.getDisplayName())
val holder = StatusViewHolder(binding.postFragmentSingle)
holder.bind(status, apiHolder.api!!, db, lifecycleScope, displayDimensionsInPx(), isActivity = true)
val credential = "Bearer $accessToken"
activateCommenter(credential)
if(viewComments || postComment){
//Scroll already down as much as possible (since comments are not loaded yet)
binding.scrollview.requestChildFocus(binding.editComment, binding.editComment)
//Open keyboard if we want to post a comment
if(postComment && binding.editComment.requestFocus()) {
window.setSoftInputMode(SOFT_INPUT_STATE_VISIBLE)
binding.editComment.requestFocus()
}
// also retrieve comments if we're not posting the comment
if(!postComment) retrieveComments(apiHolder.api!!, credential)
}
binding.postFragmentSingle.viewComments.setOnClickListener {
retrieveComments(apiHolder.api!!, credential)
}
}
override fun onSupportNavigateUp(): Boolean {
@ -47,13 +80,109 @@ class PostActivity : BaseActivity() {
return true
}
private fun initializeFragment(arguments: Bundle, status: Status?){
supportActionBar?.title = getString(R.string.post_title).format(status!!.account?.getDisplayName())
arguments.putSerializable(POST_TAG, status)
postFragment.arguments = arguments
supportFragmentManager.isStateSaved
supportFragmentManager.beginTransaction()
.add(R.id.postFragmentSingle, postFragment).commit()
binding.postFragmentSingle.visibility = View.VISIBLE
private fun activateCommenter(credential: String) {
//Activate commenter
binding.submitComment.setOnClickListener {
val textIn = binding.editComment.text
//Open text input
if(textIn.isNullOrEmpty()) {
Toast.makeText(
binding.root.context,
binding.root.context.getString(R.string.empty_comment),
Toast.LENGTH_SHORT
).show()
} else {
//Post the comment
lifecycleScope.launchWhenCreated {
apiHolder.api?.let { it1 -> postComment(it1, credential) }
}
}
}
}
private fun addComment(context: Context, commentContainer: LinearLayout,
commentUsername: String, commentContent: String, mentions: List<Mention>,
credential: String) {
val itemBinding = CommentBinding.inflate(
LayoutInflater.from(context), commentContainer, true
)
itemBinding.user.text = commentUsername
itemBinding.commentText.text = parseHTMLText(
commentContent,
mentions,
apiHolder.api!!,
context,
credential,
lifecycleScope
)
}
private fun retrieveComments(api: PixelfedAPI, credential: String) {
lifecycleScope.launchWhenCreated {
status.id.let {
try {
val statuses = api.statusComments(it, credential).descendants
binding.commentContainer.removeAllViews()
//Create the new views for each comment
for (status in statuses) {
addComment(binding.root.context, binding.commentContainer, status.account!!.username!!,
status.content!!, status.mentions.orEmpty(), credential
)
}
binding.commentContainer.visibility = View.VISIBLE
//Focus the comments
binding.scrollview.requestChildFocus(binding.commentContainer, binding.commentContainer)
} catch (exception: IOException) {
Log.e("COMMENT FETCH ERROR", exception.toString())
} catch (exception: HttpException) {
Log.e("COMMENT ERROR", "${exception.code()} with body ${exception.response()?.errorBody()}")
}
}
}
}
private suspend fun postComment(
api: PixelfedAPI,
credential: String,
) {
val textIn = binding.editComment.text
val nonNullText = textIn.toString()
status.id.let {
try {
val response = api.postStatus(credential, nonNullText, it)
binding.commentIn.visibility = View.GONE
//Add the comment to the comment section
addComment(
binding.root.context, binding.commentContainer, response.account!!.username!!,
response.content!!, response.mentions.orEmpty(), credential
)
Toast.makeText(
binding.root.context,
binding.root.context.getString(R.string.comment_posted).format(textIn),
Toast.LENGTH_SHORT
).show()
} catch (exception: IOException) {
Log.e("COMMENT ERROR", exception.toString())
Toast.makeText(
binding.root.context, binding.root.context.getString(R.string.comment_error),
Toast.LENGTH_SHORT
).show()
} catch (exception: HttpException) {
Toast.makeText(
binding.root.context, binding.root.context.getString(R.string.comment_error),
Toast.LENGTH_SHORT
).show()
Log.e("ERROR_CODE", exception.code().toString())
}
}
}
}

View File

@ -1,52 +0,0 @@
package com.h.pixeldroid.posts
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import com.h.pixeldroid.databinding.PostFragmentBinding
import com.h.pixeldroid.utils.api.objects.Status
import com.h.pixeldroid.utils.api.objects.Status.Companion.DOMAIN_TAG
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG
import com.h.pixeldroid.utils.BaseFragment
import com.h.pixeldroid.utils.bindingLifecycleAware
import com.h.pixeldroid.utils.displayDimensionsInPx
class PostFragment : BaseFragment() {
private lateinit var statusDomain: String
private var currentStatus: Status? = null
var binding: PostFragmentBinding by bindingLifecycleAware()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
currentStatus = arguments?.getSerializable(POST_TAG) as Status?
statusDomain = arguments?.getString(DOMAIN_TAG)!!
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = PostFragmentBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
val holder = StatusViewHolder(binding)
holder.bind(currentStatus, api, db, lifecycleScope, requireContext().displayDimensionsInPx())
}
}

View File

@ -19,13 +19,15 @@ import com.bumptech.glide.RequestBuilder
import com.google.android.material.snackbar.Snackbar
import com.h.pixeldroid.R
import com.h.pixeldroid.databinding.AlbumImageViewBinding
import com.h.pixeldroid.databinding.CommentBinding
import com.h.pixeldroid.databinding.PostFragmentBinding
import com.h.pixeldroid.utils.BlurHashDecoder
import com.h.pixeldroid.utils.ImageConverter
import com.h.pixeldroid.utils.api.PixelfedAPI
import com.h.pixeldroid.utils.api.objects.Attachment
import com.h.pixeldroid.utils.api.objects.Status
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_COMMENT_TAG
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG
import com.h.pixeldroid.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
import com.h.pixeldroid.utils.db.AppDatabase
import com.karumi.dexter.Dexter
import com.karumi.dexter.listener.PermissionDeniedResponse
@ -44,7 +46,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
private var status: Status? = null
fun bind(status: Status?, pixelfedAPI: PixelfedAPI, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope, displayDimensionsInPx: Pair<Int, Int>) {
fun bind(status: Status?, pixelfedAPI: PixelfedAPI, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope, displayDimensionsInPx: Pair<Int, Int>, isActivity: Boolean = false) {
this.itemView.visibility = View.VISIBLE
this.status = status
@ -67,14 +69,13 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
}
//Setup the post layout
val picRequest = Glide.with(itemView)
.asDrawable().fitCenter()
val picRequest = Glide.with(itemView).asDrawable().fitCenter()
val user = db.userDao().getActiveUser()!!
setupPost(picRequest, user.instance_uri, false)
setupPost(picRequest, user.instance_uri, isActivity)
activateButtons(pixelfedAPI, db, lifecycleScope)
activateButtons(pixelfedAPI, db, lifecycleScope, isActivity)
}
@ -130,10 +131,6 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
binding.postPager.visibility = View.GONE
binding.postIndicator.visibility = View.GONE
}
//Set comment initial visibility
binding.commentIn.visibility = View.GONE
binding.commentContainer.visibility = View.GONE
}
private fun setupPostPics(
@ -200,8 +197,13 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
}
}
}
private fun activateButtons(api: PixelfedAPI, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope){
//region buttons
private fun activateButtons(
api: PixelfedAPI,
db: AppDatabase,
lifecycleScope: LifecycleCoroutineScope,
isActivity: Boolean
){
val user = db.userDao().getActiveUser()!!
val credential = "Bearer ${user.accessToken}"
@ -217,9 +219,23 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
api, credential, status?.reblogged ?: false,
lifecycleScope
)
activateCommenter(api, credential, lifecycleScope)
showComments(api, credential, lifecycleScope)
if(isActivity){
binding.commenter.visibility = View.INVISIBLE
}
else {
binding.commenter.setOnClickListener {
lifecycleScope.launchWhenCreated {
//Open status in activity
val intent = Intent(it.context, PostActivity::class.java)
intent.putExtra(POST_TAG, status)
intent.putExtra(POST_COMMENT_TAG, true)
it.context.startActivity(intent)
}
}
}
showComments(lifecycleScope, isActivity)
activateMoreButton(api, db, lifecycleScope)
}
@ -514,13 +530,13 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
}
}
}
//endregion
private fun showComments(
api: PixelfedAPI,
credential: String,
lifecycleScope: LifecycleCoroutineScope
lifecycleScope: LifecycleCoroutineScope,
isActivity: Boolean
) {
//Show all comments of a post
//Show number of comments on the post
if (status?.replies_count == 0) {
binding.viewComments.text = binding.root.context.getString(R.string.NoCommentsToShow)
} else {
@ -529,143 +545,22 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
status?.replies_count ?: 0,
status?.replies_count ?: 0
)
setOnClickListener {
visibility = View.GONE
lifecycleScope.launchWhenCreated {
//Retrieve the comments
retrieveComments(api, credential)
if(!isActivity) {
setOnClickListener {
lifecycleScope.launchWhenCreated {
//Open status in activity
val intent = Intent(context, PostActivity::class.java)
intent.putExtra(POST_TAG, status)
intent.putExtra(VIEW_COMMENTS_TAG, true)
context.startActivity(intent)
}
}
}
}
}
}
private fun activateCommenter(
api: PixelfedAPI,
credential: String,
lifecycleScope: LifecycleCoroutineScope
) {
//Toggle comment button
toggleCommentInput()
//Activate commenterpostPicture
binding.submitComment.setOnClickListener {
val textIn = binding.editComment.text
//Open text input
if(textIn.isNullOrEmpty()) {
Toast.makeText(
binding.root.context,
binding.root.context.getString(R.string.empty_comment),
Toast.LENGTH_SHORT
).show()
} else {
//Post the comment
lifecycleScope.launchWhenCreated {
postComment(api, credential)
}
}
}
}
private fun toggleCommentInput() {
//Toggle comment button
binding.commenter.setOnClickListener {
when(binding.commentIn.visibility) {
View.VISIBLE -> {
binding.commentIn.visibility = View.GONE
ImageConverter.setImageFromDrawable(
binding.root,
binding.commenter,
R.drawable.ic_comment_empty
)
}
View.GONE -> {
binding.commentIn.visibility = View.VISIBLE
ImageConverter.setImageFromDrawable(
binding.root,
binding.commenter,
R.drawable.ic_comment_blue
)
}
}
}
}
fun addComment(context: android.content.Context, commentContainer: LinearLayout, commentUsername: String, commentContent: String) {
val itemBinding = CommentBinding.inflate(
LayoutInflater.from(context), commentContainer, false
)
itemBinding.user.text = commentUsername
itemBinding.commentText.text = commentContent
}
private suspend fun retrieveComments(
api: PixelfedAPI,
credential: String,
) {
status?.id?.let {
try {
val statuses = api.statusComments(it, credential).descendants
binding.commentContainer.removeAllViews()
//Create the new views for each comment
for (status in statuses) {
addComment(binding.root.context, binding.commentContainer, status.account!!.username!!,
status.content!!
)
}
binding.commentContainer.visibility = View.VISIBLE
} catch (exception: IOException) {
Log.e("COMMENT FETCH ERROR", exception.toString())
} catch (exception: HttpException) {
Log.e("COMMENT ERROR", "${exception.code()} with body ${exception.response()?.errorBody()}")
}
}
}
private suspend fun postComment(
api: PixelfedAPI,
credential: String,
) {
val textIn = binding.editComment.text
val nonNullText = textIn.toString()
status?.id?.let {
try {
val response = api.postStatus(credential, nonNullText, it)
binding.commentIn.visibility = View.GONE
//Add the comment to the comment section
addComment(
binding.root.context, binding.commentContainer, response.account!!.username!!,
response.content!!
)
Toast.makeText(
binding.root.context,
binding.root.context.getString(R.string.comment_posted).format(textIn),
Toast.LENGTH_SHORT
).show()
} catch (exception: IOException) {
Log.e("COMMENT ERROR", exception.toString())
Toast.makeText(
binding.root.context, binding.root.context.getString(R.string.comment_error),
Toast.LENGTH_SHORT
).show()
} catch (exception: HttpException) {
Toast.makeText(
binding.root.context, binding.root.context.getString(R.string.comment_error),
Toast.LENGTH_SHORT
).show()
Log.e("ERROR_CODE", exception.code().toString())
}
}
}
companion object {

View File

@ -64,8 +64,8 @@ open class Status(
{
companion object {
const val POST_TAG = "postTag"
const val DOMAIN_TAG = "domainTag"
const val DISCOVER_TAG = "discoverTag"
const val VIEW_COMMENTS_TAG = "view_comments_tag"
const val POST_COMMENT_TAG = "post_comment_tag"
}
fun getPostUrl() : String? = media_attachments?.firstOrNull()?.url

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/ic_comment_empty"
android:state_pressed="false" />
<item
android:drawable="@drawable/ic_comment_blue"
android:state_pressed="true"/>
</selector>

View File

@ -1,26 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".posts.PostActivity">
tools:context=".posts.PostActivity"
android:id="@+id/scrollview">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/postFragmentSingle"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:context=".posts.PostFragment"
tools:visibility="visible"/>
</ScrollView>
android:layout_height="wrap_content">
</androidx.constraintlayout.widget.ConstraintLayout>
<include layout="@layout/post_fragment"
android:id="@+id/postFragmentSingle"/>
<LinearLayout
android:id="@+id/commentIn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@+id/postFragmentSingle"
tools:layout_editor_absoluteX="10dp">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="3">
<EditText
android:id="@+id/editComment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/comment"
android:inputType="text"
android:importantForAutofill="no" />
</com.google.android.material.textfield.TextInputLayout>
<ImageButton
android:id="@+id/submitComment"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end"
android:layout_weight="1"
android:contentDescription="@string/submit_comment"
android:src="@drawable/ic_send_blue" />
</LinearLayout>
<LinearLayout
android:id="@+id/commentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@+id/commentIn">
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -6,8 +6,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
xmlns:sparkbutton="http://schemas.android.com/apk/res-auto"
tools:context=".posts.PostFragment">
xmlns:sparkbutton="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@ -119,7 +118,7 @@
android:layout_width="30dp"
android:layout_height="30dp"
android:padding="4dp"
android:src="@drawable/ic_comment_empty"
android:src="@drawable/selector_commenter"
app:layout_constraintBottom_toBottomOf="@+id/liker"
app:layout_constraintEnd_toStartOf="@id/reblogger"
app:layout_constraintStart_toEndOf="@id/liker"
@ -227,49 +226,8 @@
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/commentIn"
tools:text="3 comments" />
<LinearLayout
android:id="@+id/commentIn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@+id/postDate"
tools:layout_editor_absoluteX="10dp">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="3">
<EditText
android:id="@+id/editComment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/comment"
android:inputType="text"
android:importantForAutofill="no" />
</com.google.android.material.textfield.TextInputLayout>
<ImageButton
android:id="@+id/submitComment"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end"
android:layout_weight="1"
android:contentDescription="@string/submit_comment"
android:src="@drawable/ic_send_blue" />
</LinearLayout>
<LinearLayout
android:id="@+id/commentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@+id/viewComments">
</LinearLayout>
tools:text="3 comments" />
</androidx.constraintlayout.widget.ConstraintLayout>