From 2484406cc70e6dc973a3b707042d6fa9f7f26fb9 Mon Sep 17 00:00:00 2001 From: Ivan Agosto Date: Tue, 16 Apr 2024 03:55:53 +0000 Subject: [PATCH] Feature/threads --- .../org/libre/agosto/p2play/LoginActivity.kt | 2 +- .../org/libre/agosto/p2play/MainActivity.kt | 1 + .../agosto/p2play/ReproductorActivity.kt | 10 +- .../p2play/adapters/CommentariesAdapter.kt | 43 +++++- .../org/libre/agosto/p2play/ajax/Comments.kt | 65 ++++++++- .../agosto/p2play/dialogs/ThreadDialog.kt | 136 ++++++++++++++++++ .../agosto/p2play/models/CommentaryModel.kt | 5 +- .../res/drawable/baseline_arrow_back_24.xml | 5 + .../main/res/layout/activity_reproductor.xml | 55 +------ app/src/main/res/layout/comment_component.xml | 46 ++++++ app/src/main/res/layout/dialog_thread.xml | 50 +++++++ app/src/main/res/layout/view_commentary.xml | 9 ++ app/src/main/res/layout/view_video.xml | 2 +- app/src/main/res/values/strings.xml | 3 + 14 files changed, 376 insertions(+), 56 deletions(-) create mode 100644 app/src/main/java/org/libre/agosto/p2play/dialogs/ThreadDialog.kt create mode 100644 app/src/main/res/drawable/baseline_arrow_back_24.xml create mode 100644 app/src/main/res/layout/comment_component.xml create mode 100644 app/src/main/res/layout/dialog_thread.xml diff --git a/app/src/main/java/org/libre/agosto/p2play/LoginActivity.kt b/app/src/main/java/org/libre/agosto/p2play/LoginActivity.kt index 119996b..6c2e07b 100644 --- a/app/src/main/java/org/libre/agosto/p2play/LoginActivity.kt +++ b/app/src/main/java/org/libre/agosto/p2play/LoginActivity.kt @@ -69,7 +69,7 @@ class LoginActivity : AppCompatActivity() { "-2" -> { // TODO: Start 2FA modal runOnUiThread { - val builder = AlertDialog.Builder(this) + val builder = AlertDialog.Builder(this, R.style.Widget_Material3_MaterialCalendar_Fullscreen) val dialog = layoutInflater.inflate(R.layout.two_factor_dialog, null) val inputTwoFactor = dialog.findViewById(R.id.twoFactorText) diff --git a/app/src/main/java/org/libre/agosto/p2play/MainActivity.kt b/app/src/main/java/org/libre/agosto/p2play/MainActivity.kt index ba68b37..3fc5aac 100644 --- a/app/src/main/java/org/libre/agosto/p2play/MainActivity.kt +++ b/app/src/main/java/org/libre/agosto/p2play/MainActivity.kt @@ -368,6 +368,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte mini_player_title.text = PlaybackSingleton.video!!.name mini_player_author.text = PlaybackSingleton.video!!.username Picasso.get().load("https://${ManagerSingleton.url}${PlaybackSingleton.video!!.thumbUrl}").into(mini_player_image) + mini_play_pause.setImageResource(R.drawable.ic_pause_24) mini.visibility = View.VISIBLE } else { mini.visibility = View.GONE diff --git a/app/src/main/java/org/libre/agosto/p2play/ReproductorActivity.kt b/app/src/main/java/org/libre/agosto/p2play/ReproductorActivity.kt index 1a45138..0735b8f 100644 --- a/app/src/main/java/org/libre/agosto/p2play/ReproductorActivity.kt +++ b/app/src/main/java/org/libre/agosto/p2play/ReproductorActivity.kt @@ -28,6 +28,10 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.squareup.picasso.Picasso import kotlinx.android.synthetic.main.activity_reproductor.* +import kotlinx.android.synthetic.main.comment_component.commentaryBtn +import kotlinx.android.synthetic.main.comment_component.commentaryLayout +import kotlinx.android.synthetic.main.comment_component.commentaryText +import kotlinx.android.synthetic.main.comment_component.userImgCom import org.libre.agosto.p2play.adapters.CommentariesAdapter import org.libre.agosto.p2play.ajax.Actions import org.libre.agosto.p2play.ajax.Comments @@ -275,7 +279,7 @@ class ReproductorActivity : AppCompatActivity() { private fun setDataComments(data: ArrayList) { // Set data for RecyclerView - viewAdapter = CommentariesAdapter(data) + viewAdapter = CommentariesAdapter(data).setFragmentManager(supportFragmentManager) recyclerView = findViewById(R.id.listCommentaries).apply { // use this setting to improve performance if you know that changes @@ -330,10 +334,12 @@ class ReproductorActivity : AppCompatActivity() { if (ManagerSingleton.user.avatar != "") { Picasso.get().load("https://" + ManagerSingleton.url + ManagerSingleton.user.avatar).into(userImgCom) } + } else { + commentaryLayout.visibility = View.GONE } } - fun getDescription() { + private fun getDescription() { AsyncTask.execute { val fullDescription = this.videos.fullDescription(this.video.id) runOnUiThread { diff --git a/app/src/main/java/org/libre/agosto/p2play/adapters/CommentariesAdapter.kt b/app/src/main/java/org/libre/agosto/p2play/adapters/CommentariesAdapter.kt index bdea1a2..7d0d976 100644 --- a/app/src/main/java/org/libre/agosto/p2play/adapters/CommentariesAdapter.kt +++ b/app/src/main/java/org/libre/agosto/p2play/adapters/CommentariesAdapter.kt @@ -1,22 +1,35 @@ package org.libre.agosto.p2play.adapters import android.content.Context +import android.content.Intent +import android.os.Bundle import android.text.Html import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentTransaction import androidx.recyclerview.widget.RecyclerView import com.squareup.picasso.Picasso import org.libre.agosto.p2play.ManagerSingleton import org.libre.agosto.p2play.R +import org.libre.agosto.p2play.ajax.Comments +import org.libre.agosto.p2play.dialogs.ThreadDialog import org.libre.agosto.p2play.models.CommentaryModel +import java.io.Serializable -@Suppress("DEPRECATION") class CommentariesAdapter(private val myDataset: ArrayList) : RecyclerView.Adapter() { + private lateinit var fragmentManager: FragmentManager + + fun setFragmentManager (manager: FragmentManager): CommentariesAdapter { + this.fragmentManager = manager + return this + } + // Provide a reference to the views for each data item // Complex data items may need more than one view per item, and // you provide access to all the views for a data item in a view holder. @@ -25,6 +38,7 @@ class CommentariesAdapter(private val myDataset: ArrayList) : val userImg: ImageView val username: TextView val commentary: TextView + val replyBtn: TextView val context: Context init { @@ -32,6 +46,7 @@ class CommentariesAdapter(private val myDataset: ArrayList) : username = view.findViewById(R.id.userTxt) commentary = view.findViewById(R.id.userCommentary) userImg = view.findViewById(R.id.userCommentImg) + replyBtn = view.findViewById(R.id.replyBtn) context = view.context } } @@ -40,7 +55,7 @@ class CommentariesAdapter(private val myDataset: ArrayList) : override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, - ): CommentariesAdapter.ViewHolder { + ): ViewHolder { // create a new view val view = LayoutInflater.from(parent.context) .inflate(R.layout.view_commentary, parent, false) as View @@ -66,6 +81,11 @@ class CommentariesAdapter(private val myDataset: ArrayList) : } holder.commentary.text = Html.fromHtml(myDataset[position].commentary) + holder.replyBtn.setOnClickListener { this.initRepliesDialog(myDataset[position]) } + + if (myDataset[position].replies > 0) { + holder.replyBtn.text = holder.itemView.context.getString(R.string.see_replies, myDataset[position].replies) + } // TODO: Support for view and account (is different than a video channel) // holder.userImg.setOnClickListener { @@ -77,4 +97,23 @@ class CommentariesAdapter(private val myDataset: ArrayList) : // Return the size of your dataset (invoked by the layout manager) override fun getItemCount() = myDataset.size + + private fun initRepliesDialog (commentData: CommentaryModel) { + val dialog = ThreadDialog() + val bundle = Bundle() + bundle.putSerializable("comment", commentData as Serializable) + + dialog.arguments = bundle + dialog.fragmentManager2 = this.fragmentManager + val transaction = fragmentManager.beginTransaction() + // For a polished look, specify a transition animation. + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) + // To make it fullscreen, use the 'content' root view as the container + // for the fragment, which is always the root view for the activity. + transaction + .add(android.R.id.content, dialog) + .addToBackStack("comments") + .commit() + + } } diff --git a/app/src/main/java/org/libre/agosto/p2play/ajax/Comments.kt b/app/src/main/java/org/libre/agosto/p2play/ajax/Comments.kt index 71a7d2c..f8a279a 100644 --- a/app/src/main/java/org/libre/agosto/p2play/ajax/Comments.kt +++ b/app/src/main/java/org/libre/agosto/p2play/ajax/Comments.kt @@ -6,7 +6,6 @@ import org.libre.agosto.p2play.models.CommentaryModel import java.io.InputStreamReader class Comments : Client() { - private fun parseCommentaries(data: JsonReader): ArrayList { val commentaries = arrayListOf() data.beginObject() @@ -70,4 +69,68 @@ class Comments : Client() { con.disconnect() return response } + + fun getCommentariesThread(videoId: Int, threadId: Int): ArrayList { + var commentaries = arrayListOf() + val con = this.newCon("videos/$videoId/comment-threads/${threadId}", "GET") + + try { + if (con.responseCode == 200) { + val response = InputStreamReader(con.inputStream) + val data = JsonReader(response) + data.beginObject() + while (data.hasNext()) { + when (data.nextName()) { + "children" -> { + data.beginArray() + while (data.hasNext()) { + data.beginObject() + while (data.hasNext()) { + when(data.nextName()) { + "comment" -> { + val comment = CommentaryModel() + comment.parseCommentary(data) + commentaries.add(comment) + } + else -> data.skipValue() + } + } + data.endObject() + } + data.endArray() + } + else -> data.skipValue() + } + } + data.endObject() + data.close() + } + } catch (err: Exception) { + err.printStackTrace() + } + con.disconnect() + return commentaries + } + + fun replyThread(token: String, videoId: Int, threadId: Int , text: String): Boolean { + val con = this.newCon("videos/$videoId/comments/${threadId}", "POST", token) + val params = "text=$text" + con.outputStream.write(params.toByteArray()) + + val response: Boolean = try { + if (con.responseCode == 200) { + con.disconnect() + true + } else { + Log.d("Status", con.responseMessage) + false + } + } catch (err: Exception) { + err.printStackTrace() + false + } + + con.disconnect() + return response + } } diff --git a/app/src/main/java/org/libre/agosto/p2play/dialogs/ThreadDialog.kt b/app/src/main/java/org/libre/agosto/p2play/dialogs/ThreadDialog.kt new file mode 100644 index 0000000..e7b0b5e --- /dev/null +++ b/app/src/main/java/org/libre/agosto/p2play/dialogs/ThreadDialog.kt @@ -0,0 +1,136 @@ +package org.libre.agosto.p2play.dialogs + +import android.app.Dialog +import android.content.AsyncQueryHandler +import android.content.Context +import android.os.AsyncTask +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.Window +import android.view.inputmethod.InputMethodManager +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.FragmentManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.squareup.picasso.Picasso +import kotlinx.android.synthetic.main.comment_component.commentaryLayout +import kotlinx.android.synthetic.main.comment_component.commentaryText +import kotlinx.android.synthetic.main.comment_component.userImgCom +import kotlinx.android.synthetic.main.comment_component.view.commentaryBtn +import kotlinx.android.synthetic.main.comment_component.view.commentaryLayout +import kotlinx.android.synthetic.main.comment_component.view.commentaryText +import kotlinx.android.synthetic.main.comment_component.view.userImgCom +import kotlinx.android.synthetic.main.dialog_thread.view.materialToolbar +import kotlinx.android.synthetic.main.two_factor_dialog.twoFactorText +import kotlinx.android.synthetic.main.view_commentary.view.replyBtn +import kotlinx.android.synthetic.main.view_commentary.view.userCommentImg +import kotlinx.android.synthetic.main.view_commentary.view.userCommentary +import kotlinx.android.synthetic.main.view_commentary.view.userTxt +import org.libre.agosto.p2play.ManagerSingleton +import org.libre.agosto.p2play.R +import org.libre.agosto.p2play.adapters.CommentariesAdapter +import org.libre.agosto.p2play.ajax.Comments +import org.libre.agosto.p2play.models.CommentaryModel + +class ThreadDialog : DialogFragment() { + private lateinit var comment: CommentaryModel + lateinit var fragmentManager2: FragmentManager + private val client: Comments = Comments() + // The system calls this to get the DialogFragment's layout, regardless of + // whether it's being displayed as a dialog or an embedded fragment. + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + // Inflate the layout to use as a dialog or embedded fragment. + val view = inflater.inflate(R.layout.dialog_thread, container, false) + comment = arguments?.getSerializable("comment") as CommentaryModel + + view.userTxt.text = comment.username + view.userCommentary.text = comment.commentary + Picasso.get().load("https://${ManagerSingleton.url}${comment.userImageUrl}").into(view.userCommentImg) + view.replyBtn.visibility = View.GONE + + if (ManagerSingleton.user.status == 1) { + view.commentaryText.setText("${comment.username}@${comment.userHost} ") + if (ManagerSingleton.user.avatar != "") { + Picasso.get().load("https://${ManagerSingleton.url}${ManagerSingleton.user.avatar}").into(view.userImgCom) + } + + view.commentaryText.requestFocus() + view.commentaryBtn.setOnClickListener { this.replyThread() } + } else { + view.commentaryLayout.visibility = View.GONE + } + + view.materialToolbar.setTitle("Thread") + view.materialToolbar.setNavigationIcon(R.drawable.baseline_arrow_back_24) + view.materialToolbar.setNavigationOnClickListener { + dismiss() + this.fragmentManager2.popBackStack() + } + + this.getComments() + + return view + } + + // The system calls this only when creating the layout in a dialog. + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val dialog = super.onCreateDialog(savedInstanceState) + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) + return dialog + } + + private fun getComments() { + AsyncTask.execute { + val data = + this.client.getCommentariesThread(this.comment.videoId, this.comment.id) + activity?.runOnUiThread { + this.setDataComments(data) + } + } + } + + private fun setDataComments(data: ArrayList) { + // Set data for RecyclerView + val viewAdapter = CommentariesAdapter(data).setFragmentManager(this.fragmentManager2) + + view?.findViewById(R.id.listCommentaries)?.let { + // use this setting to improve performance if you know that changes + // in content do not change the layout size of the RecyclerView + it.setHasFixedSize(true) + // use a linear layout manager + it.layoutManager = LinearLayoutManager(activity) + + // specify an viewAdapter (see also next example) + it.adapter = viewAdapter + } + } + + private fun replyThread() { + val commentary = view?.commentaryText?.text.toString() + + if (commentary == "") { + ManagerSingleton.toast(getString(R.string.emptyCommentaryMsg), requireActivity()) + return + } + + AsyncTask.execute { + val res = this.client.replyThread(ManagerSingleton.token.token, this.comment.videoId, this.comment.id, commentary) + activity?.runOnUiThread { + if (res) { + ManagerSingleton.toast(getString(R.string.makedCommentaryMsg), requireActivity()) + commentaryText.text?.clear() + this.getComments() + } else { + ManagerSingleton.toast(getString(R.string.errorCommentaryMsg), requireActivity()) + } + } + } + } +} diff --git a/app/src/main/java/org/libre/agosto/p2play/models/CommentaryModel.kt b/app/src/main/java/org/libre/agosto/p2play/models/CommentaryModel.kt index 9fed5c2..15891c2 100644 --- a/app/src/main/java/org/libre/agosto/p2play/models/CommentaryModel.kt +++ b/app/src/main/java/org/libre/agosto/p2play/models/CommentaryModel.kt @@ -2,6 +2,7 @@ package org.libre.agosto.p2play.models import android.util.JsonReader import android.util.JsonToken +import java.io.Serializable class CommentaryModel( var id: Int = 0, @@ -13,7 +14,8 @@ class CommentaryModel( var userHost: String = "", var replies: Int = 0, var nameChannel: String = "", -) { + var videoId: Int = 0 +): Serializable { fun parseCommentary(data: JsonReader) { data.beginObject() while (data.hasNext()) { @@ -23,6 +25,7 @@ class CommentaryModel( "threadId" -> this.threadId = data.nextInt() "text" -> this.commentary = data.nextString() "totalReplies" -> this.replies = data.nextInt() + "videoId" -> this.videoId = data.nextInt() "account" -> { data.beginObject() while (data.hasNext()) { diff --git a/app/src/main/res/drawable/baseline_arrow_back_24.xml b/app/src/main/res/drawable/baseline_arrow_back_24.xml new file mode 100644 index 0000000..075e95d --- /dev/null +++ b/app/src/main/res/drawable/baseline_arrow_back_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/activity_reproductor.xml b/app/src/main/res/layout/activity_reproductor.xml index 38b99c8..ad370a8 100644 --- a/app/src/main/res/layout/activity_reproductor.xml +++ b/app/src/main/res/layout/activity_reproductor.xml @@ -220,15 +220,16 @@ android:layout_width="match_parent" android:layout_height="60dp" android:layout_weight="1" + android:gravity="center|center_vertical" android:orientation="horizontal"> @@ -244,6 +245,7 @@ android:id="@+id/userTxt" android:layout_width="match_parent" android:layout_height="wrap_content" + android:maxWidth="300dp" android:textAppearance="@android:style/TextAppearance.Material.Large" android:textSize="18sp" android:textStyle="bold" /> @@ -323,50 +325,7 @@ android:layout_width="match_parent" android:layout_height="20dp" /> - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_thread.xml b/app/src/main/res/layout/dialog_thread.xml new file mode 100644 index 0000000..bd5c913 --- /dev/null +++ b/app/src/main/res/layout/dialog_thread.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_commentary.xml b/app/src/main/res/layout/view_commentary.xml index c15dc9f..a9eca7d 100644 --- a/app/src/main/res/layout/view_commentary.xml +++ b/app/src/main/res/layout/view_commentary.xml @@ -7,6 +7,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_video.xml b/app/src/main/res/layout/view_video.xml index 8397c04..af1a271 100644 --- a/app/src/main/res/layout/view_video.xml +++ b/app/src/main/res/layout/view_video.xml @@ -73,7 +73,7 @@ android:layout_width="120dp" android:layout_height="match_parent" android:layout_weight="1" - android:adjustViewBounds="false" + android:adjustViewBounds="true" android:clickable="false" android:cropToPadding="false" android:padding="5dp" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 79da459..847d4db 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -95,6 +95,9 @@ Unsubscribe Comment Show more + + Reply + See replies (%1$d) You have subscribed to this channel You have rated the video