Skip to content

Instantly share code, notes, and snippets.

@VIkash2601
Created October 11, 2023 06:30
Show Gist options
  • Save VIkash2601/4313bea0c239719624c91076744e86e2 to your computer and use it in GitHub Desktop.
Save VIkash2601/4313bea0c239719624c91076744e86e2 to your computer and use it in GitHub Desktop.
Frag Chat
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Dialog
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Rect
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.Html
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.PopupWindow
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import com.app.spinnr.R
import com.app.spinnr.act.ActHome
import com.app.spinnr.databinding.DialogChatOptionBinding
import com.app.spinnr.databinding.DialogReactionsBinding
import com.app.spinnr.databinding.ItemChatBinding
import com.app.spinnr.frag.ReactedUser
import com.app.spinnr.frag.chat.FragChat
import com.app.spinnr.frag.profile.FragProfileView
import com.app.spinnr.frag.squad.SquadRepo
import com.app.spinnr.helper.Constant
import com.app.spinnr.listener.ItemSelectListener
import com.app.spinnr.model.MessageModel
import com.app.spinnr.model.SquadImpl
import com.app.spinnr.util.*
import com.app.spinnr.util.Preference.Companion.preferenceInstance
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.google.firebase.firestore.Source
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import com.google.firebase.storage.ktx.storage
import dev.leonardpark.emoji.EmojiManager
import dev.leonardpark.emoji.EmojiPopup
import dev.leonardpark.emoji.emoji.Emoji
import dev.leonardpark.emoji.emojicompat.EmojiImageView
import dev.leonardpark.emoji.listeners.OnEmojiClickListener
import io.github.ponnamkarthik.richlinkpreview.MetaData
import io.github.ponnamkarthik.richlinkpreview.RichLinkListener
import io.github.ponnamkarthik.richlinkpreview.RichLinkView
import io.github.ponnamkarthik.richlinkpreview.ViewListener
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class ChatAdapter(
val ctx: Context,
val frag: Fragment,
val inflater: LayoutInflater,
val username: String,
val data: MutableList<MessageModel>,
val listener: ItemSelectListener,
val replyListener: ReplyListener,
var clickListener: ScrollListener
) : RecyclerView.Adapter<ChatAdapter.MyViewHolder>() {
inner class MyViewHolder(val binding: ItemChatBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: MessageModel) {
if (item.type == "missedCall") {
binding.right.visibility = View.GONE
binding.left.visibility = View.GONE
binding.llMissedCall.visibility = View.VISIBLE
binding.missedCallTime.text = item.chatMessageTimeFormat()
if (item.username == FragChat.USERNAME) {
binding.missedCallTitle.text = setCustomText(item.content, item.username == preferenceInstance.readString(Preference.USERNAME))
} else {
binding.missedCallTitle.text = setCustomText(item.content, item.username == preferenceInstance.readString(Preference.USERNAME))
}
} else {
binding.llMissedCall.visibility = View.GONE
if (item.username == FragChat.USERNAME) {
binding.left.visibility = View.GONE
binding.right.visibility = View.VISIBLE
if (item.type == "squadInvite") {
binding.rightMsg.text =
"You invited ${FragChat.username} to the\n${item.text} squad"
} else {
binding.rightMsg.text = item.text
}
binding.rightTime.text =
item.chatMessageTimeFormat() + if (item.isEdited) " (Edited)" else ""
binding.rightThumb1.visibility =
if (item.type == "video" || item.type == "photo") View.VISIBLE else View.GONE
binding.rightThumb2.visibility =
if (item.type == "video" || item.type == "photo") View.VISIBLE else View.GONE
binding.rightThumb2Container.visibility =
if (item.type == "video" || item.type == "photo") View.VISIBLE else View.GONE
binding.rightMediaContainer.visibility =
if (item.type == "groupMedia") View.VISIBLE else View.GONE
binding.rightVideoIcon.visibility =
if (item.type == "video") View.VISIBLE else View.GONE
binding.rightAudio.visibility =
if (item.type == "audio") View.VISIBLE else View.GONE
binding.rightGifContainer.visibility = View.GONE
binding.rightMsg.visibility = View.VISIBLE;
binding.rightLinkPreviewContainer.visibility = View.VISIBLE;
var urls = extractUrls(item.text);
if (urls != null) {
binding.rightLinkPreviewContainer.removeAllViews();
if (urls.size > 0) {
if (urls.size == 1) {
if (item.text.trim().length == urls[0].trim().length) {
binding.rightMsg.visibility = View.GONE;
}
}
for (urlsObj in urls) {
if (urlsObj.contains("giphy.com")) {
binding.rightThumb1.visibility = View.GONE
binding.rightLinkPreviewContainer.visibility = View.GONE
if (item.text.contains("&ratio=") && item.text.split("&ratio=").last().isNotBlank()) {
binding.rightGif.layoutParams.height = (binding.rightGif.layoutParams.width / item.text.split("&ratio=").last().toFloat()).toInt()
}
binding.rightGif.loadGif(item.text)
binding.rightGifLink.text = item.text
binding.rightGifContainer.visibility = View.VISIBLE
} else {
var richLinkView = RichLinkView(ctx);
if (item.linkThumb == null)
richLinkView.setLink(makeValidUrl(urlsObj), object :
ViewListener {
override fun onSuccess(status: Boolean) {}
override fun onError(e: Exception) {}
});
var metaData = MetaData()
metaData.originalUrl = makeValidUrl(urlsObj);
metaData.title = item.linkTitle ?: "";
metaData.description = item.linkDescription ?: "";
metaData.imageurl = item.linkThumb ?: "";
if (item.linkThumb != null)
richLinkView.setLinkFromMeta(metaData);
binding.rightLinkPreviewContainer.addView(richLinkView);
}
}
} else {
binding.rightLinkPreviewContainer.visibility = View.GONE;
}
}
if (item.type == "video") {
binding.rightThumb1.load(item.thumb.ifEmpty { item.content })
binding.rightThumb2.load(item.thumb.ifEmpty { item.content })
} else if (item.type == "photo") {
binding.rightThumb1.load(item.content)
binding.rightThumb2.load(item.content)
} else if (item.type == "groupMedia") {
if (item.mediaFiles.size > 2)
binding.rightGroupMediaCount.visibility = View.VISIBLE
else binding.rightGroupMediaCount.visibility = View.GONE
binding.rightGroupMediaCount.text =
String.format("+%s", (item.mediaFiles.size - 2).toString())
binding.rightThumbGroupMedia1.load(item.mediaFiles[0]?.content)
binding.rightThumbGroupMedia2.load(item.mediaFiles[1]?.content)
} else if (item.type == "gif") {
binding.rightThumb1.visibility = View.GONE
binding.rightLinkPreviewContainer.visibility = View.GONE
if (item.text.contains("&ratio=") && item.text.split("&ratio=").last().isNotBlank()) {
binding.rightGif.layoutParams.height = (binding.rightGif.layoutParams.width / item.text.split("&ratio=").last().toFloat()).toInt()
}
binding.rightGif.loadGif(item.text)
binding.rightGifLink.text = item.text
binding.rightGifContainer.visibility = View.VISIBLE
} else if (item.type == "squadInvite" || item.type == "eventInvite") {
binding.rightLinkPreviewContainer.visibility = View.VISIBLE;
binding.rightLinkPreviewContainer.removeAllViews();
var richLinkView = RichLinkView(ctx);
richLinkView.setDefaultClickListener(false)
richLinkView.setClickListener(object : RichLinkListener {
override fun onClicked(view: View?, meta: MetaData?) {
binding.rightFrame.performClick()
}
})
richLinkView.setLinkFromMeta(MetaData().apply {
var squadId = ""
var squadDetail: SquadImpl? = null
for (a in 0 until SquadRepo.all.size)
if (SquadRepo.all[a].detail.name == item.text) {
squadDetail = SquadRepo.all[a]
squadId = SquadRepo.all[a].squadid
}
imageurl =
if (item.type == "eventInvite") item.thumb else Constant.PROFILE_IMG_URL + squadId + ".jpeg"
title = item.text
squadDetail?.let { squadImpl ->
description = squadImpl.detail.description
}
})
binding.rightLinkPreviewContainer.addView(richLinkView)
}
binding.rightLike.visibility = View.GONE
binding.rightLove.visibility = View.GONE
binding.rightHappy.visibility = View.GONE
binding.rightWow.visibility = View.GONE
binding.rightSad.visibility = View.GONE
binding.rightAngry.visibility = View.GONE
binding.rightReactionText.visibility = View.GONE
binding.rightReaction.visibility = View.GONE
val values = item.reaction?.values?.filter { it.isNotEmpty() }?.toMutableList()
val set = values?.toMutableSet()?.sorted()
binding.rightReactionText.text = ""
set?.forEach { a ->
binding.rightReactionText.visibility = View.VISIBLE
binding.rightReactionText.append(a)
binding.rightReaction.visibility = View.VISIBLE
}
binding.rightReactionText.append(values?.size.toString() + " ")
/*values.forEachIndexed { i, s ->
*//*when(i) {
0 -> {
binding.rightLike.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.rightLike.setEmoji(it)
}
}
1 -> {
binding.rightLove.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.rightLove.setEmoji(it)
}
}
2 -> {
binding.rightHappy.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.rightHappy.setEmoji(it)
}
}
3 -> {
binding.rightWow.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.rightWow.setEmoji(it)
}
}
4-> {
binding.rightSad.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.rightSad.setEmoji(it)
}
}
5 -> {
binding.rightAngry.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.rightAngry.setEmoji(it)
}
}
}*//*
}*/
if (!values.isNullOrEmpty())
binding.rightReaction.setOnClickListener {
ReactedUser(
frag,
item.reaction ?: mutableMapOf(),
"spinnr-chat/${FragChat.collection}/messages/${item.documentID}/"
).show(
frag.childFragmentManager,
"ReactedUser"
)
}
if (!item.tag.isNullOrBlank()) {
Firebase
.firestore
.document("spinnr-chat/${FragChat.collection}/messages/${item.tag}/")
.get(Source.CACHE).addOnSuccessListener {
val tag =
it.toObject(MessageModel::class.java) ?: return@addOnSuccessListener
binding.rightReplyContainer.visibility = View.VISIBLE
binding.rightReplyOrigin.text =
if (tag.username == FragChat.USERNAME) "You" else tag.username
binding.rightReplyContent.text = when (tag.type) {
"gif" -> "\uD83C\uDFA5 GIF"
"video" -> "\uD83C\uDFA5 Video"
"photo" -> "\uD83D\uDCF7 Photo"
"groupMedia" -> "\uD83C\uDFA5 Media"
"audio" -> "\uD83C\uDFA4 Audio"
else -> tag.text
}
if (tag.type == "text" || tag.type == "audio") {
binding.rightReplyPreview.visibility = View.GONE
} else {
binding.rightReplyPreview.visibility = View.VISIBLE
when (tag.type) {
"video" -> binding.rightReplyPreview.load(tag.thumb, 10f)
"photo" -> binding.rightReplyPreview.load(tag.content, 10f)
"gif" -> binding.rightReplyPreview.load(
tag.linkThumb ?: tag.text, 10f
)
"groupMedia" -> binding.rightReplyPreview.load(
tag.mediaFiles[0]?.content,
10f
)
"eventInvite" -> binding.rightReplyPreview.load(tag.thumb, 10f)
"squadInvite" -> binding.rightReplyPreview.load("${Constant.PROFILE_IMG_URL}${tag.content}.jpeg", 10f)
}
}
}
} else if (item.repliedTo != null) {
binding.rightReplyContainer.visibility = View.VISIBLE
binding.rightReplyOrigin.text =
if (item.repliedTo?.username == FragChat.USERNAME) "You" else item.repliedTo?.username
binding.rightReplyContent.text = when (item.repliedTo!!.type) {
"gif" -> "\uD83C\uDFA5 GIF"
"video" -> "\uD83C\uDFA5 Video"
"photo" -> "\uD83D\uDCF7 Photo"
"groupMedia" -> "\uD83C\uDFA5 Media"
"audio" -> "\uD83C\uDFA4 Audio"
else -> item.repliedTo!!.type
}
if (item.repliedTo?.thumb == null) {
binding.rightReplyPreview.visibility = View.GONE
} else {
binding.rightReplyPreview.load(item.repliedTo?.thumb, 10f)
binding.rightReplyPreview.visibility = View.VISIBLE
}
} else
binding.rightReplyContainer.visibility = View.GONE
binding.rightReplyContainer.setOnClickListener {
clickListener.onScroll(item.tag ?: "")
}
} else {
binding.right.visibility = View.GONE
binding.left.visibility = View.VISIBLE
if (item.type == "squadInvite") {
binding.leftMsg.text =
"${FragChat.username} invited you to the\n${item.text} squad"
} else {
binding.leftMsg.text = item.text
}
binding.leftTime.text = item.chatMessageTimeFormat()
binding.leftThumb1.visibility =
if (item.type == "video" || item.type == "photo") View.VISIBLE else View.GONE
binding.leftThumb2.visibility =
if (item.type == "video" || item.type == "photo") View.VISIBLE else View.GONE
binding.leftThumb2Container.visibility =
if (item.type == "video" || item.type == "photo") View.VISIBLE else View.GONE
binding.leftVideoIcon.visibility =
if (item.type == "video") View.VISIBLE else View.GONE
binding.leftMediaContainer.visibility =
if (item.type == "groupMedia") View.VISIBLE else View.GONE
binding.leftAudio.visibility = if (item.type == "audio") View.VISIBLE else View.GONE
binding.profile.keyProfile(username)
binding.leftGifContainer.visibility = View.GONE
binding.leftMsg.visibility = View.VISIBLE;
binding.leftLinkPreviewContainer.visibility = View.VISIBLE;
var urls = extractUrls(item.text)
if (urls != null) {
binding.leftLinkPreviewContainer.removeAllViews();
if (urls.size > 0) {
if (urls.size == 1) {
if (item.text.trim().length == urls[0].trim().length) {
binding.leftMsg.visibility = View.GONE;
}
}
for (urlsObj in urls) {
if (urlsObj.contains("giphy.com")) {
binding.leftThumb1.visibility = View.GONE
binding.leftThumb2.visibility = View.GONE
binding.leftLinkPreviewContainer.visibility = View.GONE
if (item.text.contains("&ratio=") && item.text.split("&ratio=").last().isNotBlank()) {
binding.leftGif.layoutParams.height = (binding.leftGif.layoutParams.width / item.text.split("&ratio=").last().toFloat()).toInt()
}
binding.leftGif.loadGif(item.text)
binding.leftGifLink.text = item.text
binding.leftGifContainer.visibility = View.VISIBLE
} else {
var richLinkView = RichLinkView(ctx);
if (item.linkThumb == null)
richLinkView.setLink(makeValidUrl(urlsObj), object :
ViewListener {
override fun onSuccess(status: Boolean) {}
override fun onError(e: Exception) {}
});
var metaData = MetaData()
metaData.originalUrl = makeValidUrl(urlsObj);
metaData.title = item.linkTitle ?: "";
metaData.description = item.linkDescription ?: "";
metaData.imageurl = item.linkThumb ?: "";
if (item.linkThumb != null)
richLinkView.setLinkFromMeta(metaData);
binding.leftLinkPreviewContainer.addView(richLinkView);
}
}
} else {
binding.leftLinkPreviewContainer.visibility = View.GONE;
}
}
if (item.type == "video") {
binding.leftThumb1.load(item.thumb.ifEmpty { item.content })
binding.leftThumb2.load(item.thumb.ifEmpty { item.content })
} else if (item.type == "photo") {
binding.leftThumb1.load(item.content)
binding.leftThumb2.load(item.content)
} else if (item.type == "groupMedia") {
if (item.mediaFiles.size > 2)
binding.leftGroupMediaCount.visibility = View.VISIBLE
else binding.leftGroupMediaCount.visibility = View.GONE
binding.leftGroupMediaCount.text =
String.format("+%s", (item.mediaFiles.size - 2).toString())
binding.leftThumbGroupMedia1.load(item.mediaFiles[0]?.content)
binding.leftThumbGroupMedia2.load(item.mediaFiles[1]?.content)
} else if (item.type == "gif"/* && !item.text.contains(Patterns.WEB_URL.toRegex())*/) {
binding.leftThumb1.visibility = View.GONE
binding.leftThumb2.visibility = View.GONE
binding.leftLinkPreviewContainer.visibility = View.GONE
if (item.text.contains("&ratio=") && item.text.split("&ratio=").last().isNotBlank()) {
binding.leftGif.layoutParams.height = (binding.leftGif.layoutParams.width / item.text.split("&ratio=").last().toFloat()).toInt()
}
binding.leftGif.loadGif(item.text)
binding.leftGifLink.text = item.text
binding.leftGifContainer.visibility = View.VISIBLE
} else if (item.type == "squadInvite" || item.type == "eventInvite") {
binding.leftLinkPreviewContainer.visibility = View.VISIBLE;
binding.leftLinkPreviewContainer.removeAllViews();
var richLinkView = RichLinkView(ctx);
richLinkView.setDefaultClickListener(false)
richLinkView.setClickListener(object : RichLinkListener {
override fun onClicked(view: View?, meta: MetaData?) {
binding.leftFrame.performClick()
}
})
richLinkView.setLinkFromMeta(MetaData().apply {
var squadId = ""
var squadDetail: SquadImpl? = null
for (a in 0 until SquadRepo.all.size)
if (SquadRepo.all[a].detail.name == item.text) {
squadDetail = SquadRepo.all[a]
squadId = SquadRepo.all[a].squadid
}
imageurl =
if (item.type == "eventInvite") item.thumb else Constant.PROFILE_IMG_URL + squadId + ".jpeg"
title = item.text
squadDetail?.let { squadImpl ->
description = squadImpl.detail.description
}
})
binding.leftLinkPreviewContainer.addView(richLinkView)
}
binding.leftLike.visibility = View.GONE
binding.leftLove.visibility = View.GONE
binding.leftHappy.visibility = View.GONE
binding.leftWow.visibility = View.GONE
binding.leftSad.visibility = View.GONE
binding.leftAngry.visibility = View.GONE
binding.leftReactionText.visibility = View.GONE
binding.leftReaction.visibility = View.GONE
val values = item.reaction?.values?.filter { it.isNotEmpty() }?.toMutableList()
val set = values?.toMutableSet()?.sorted()
binding.leftReactionText.text = ""
set?.forEach { a ->
binding.leftReactionText.visibility = View.VISIBLE
binding.leftReaction.visibility = View.VISIBLE
binding.leftReactionText.append(a)
}
binding.leftReactionText.append(values?.size.toString() + " ")
/*values.forEachIndexed { i, s ->
*//*when(i) {
0 -> {
binding.leftLike.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.leftLike.setEmoji(it)
}
}
1 -> {
binding.leftLove.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.leftLove.setEmoji(it)
}
}
2 -> {
binding.leftHappy.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.leftHappy.setEmoji(it)
}
}
3 -> {
binding.leftWow.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.leftWow.setEmoji(it)
}
}
4-> {
binding.leftSad.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.leftSad.setEmoji(it)
}
}
5 -> {
binding.leftAngry.visibility = View.VISIBLE
EmojiManager.getInstance().findEmoji(s)?.let {
binding.leftAngry.setEmoji(it)
}
}
}*//*
}*/
if (!values.isNullOrEmpty())
binding.leftReaction.setOnClickListener {
ReactedUser(
frag,
item.reaction ?: mutableMapOf(),
"spinnr-chat/${FragChat.collection}/messages/${item.documentID}/"
).show(
frag.childFragmentManager,
"ReactedUser"
)
}
if (!item.tag.isNullOrBlank()) {
Firebase
.firestore
.document("spinnr-chat/${FragChat.collection}/messages/${item.tag}/")
.get().addOnSuccessListener {
val tag =
it.toObject(MessageModel::class.java) ?: return@addOnSuccessListener
binding.leftReplyContainer.visibility = View.VISIBLE
binding.leftReplyOrigin.text =
if (tag.username == FragChat.USERNAME) "You" else tag.username
binding.leftReplyContent.text = when (tag.type) {
"gif" -> "\uD83C\uDFA5 GIF"
"video" -> "\uD83C\uDFA5 Video"
"photo" -> "\uD83D\uDCF7 Photo"
"groupMedia" -> "\uD83C\uDFA5 Media"
"audio" -> "\uD83C\uDFA4 Audio"
else -> tag.text
}
if (tag.type == "text" || tag.type == "audio") {
binding.leftReplyPreview.visibility = View.GONE
} else {
binding.leftReplyPreview.visibility = View.VISIBLE
when (tag.type) {
"video" -> binding.leftReplyPreview.load(tag.thumb, 10f)
"photo" -> binding.leftReplyPreview.load(tag.content, 10f)
"gif" -> binding.leftReplyPreview.load(
tag.linkThumb ?: tag.text, 10f
)
"groupMedia" -> binding.leftReplyPreview.load(
tag.mediaFiles[0]?.content,
10f
)
"eventInvite" -> binding.leftReplyPreview.load(tag.thumb, 10f)
"squadInvite" -> binding.leftReplyPreview.load("${Constant.PROFILE_IMG_URL}${tag.content}.jpeg", 10f)
}
}
}
} else if (item.repliedTo != null) {
binding.leftReplyContainer.visibility = View.VISIBLE
binding.leftReplyOrigin.text =
if (item.repliedTo?.username == FragChat.USERNAME) "You" else item.repliedTo?.username
binding.leftReplyContent.text = when (item.repliedTo!!.type) {
"gif" -> "\uD83C\uDFA5 GIF"
"video" -> "\uD83C\uDFA5 Video"
"photo" -> "\uD83D\uDCF7 Photo"
"groupMedia" -> "\uD83C\uDFA5 Media"
"audio" -> "\uD83C\uDFA4 Audio"
else -> item.repliedTo!!.type
}
if (item.repliedTo?.thumb == null) {
binding.leftReplyPreview.visibility = View.GONE
} else {
binding.leftReplyPreview.load(item.repliedTo?.thumb, 10f)
binding.leftReplyPreview.visibility = View.VISIBLE
}
} else
binding.leftReplyContainer.visibility = View.GONE
binding.leftReplyContainer.setOnClickListener {
clickListener.onScroll(item.tag ?: "")
}
}
}
}
}
private fun setCustomText(content: String, isLoggedInUser: Boolean): SpannableString? {
val isVoice = content.equals("voice", true)
val string = SpannableString("${if (isLoggedInUser) username else "You"} missed a $content call from ${if (isLoggedInUser) "you" else username} ${if(isVoice) "☎️" else "\uD83C\uDFA5"}")
string.setSpan(object: ForegroundColorSpan(ctx.getColor(R.color.profile_username)){}, 0, if (isLoggedInUser) username.length else 3, 0)
return string
}
private fun ImageView.load(uri: String, enableCache: Boolean = true) {
try {
Glide
.with(ctx)
.asDrawable()
.load(uri)
.diskCacheStrategy(if (enableCache) DiskCacheStrategy.AUTOMATIC else DiskCacheStrategy.NONE)
.thumbnail(
Glide
.with(ctx)
.load(R.mipmap.gif_loader)
)
.into(this)
} catch (ignored: Exception) {
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = ItemChatBinding.inflate(inflater, parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
CoroutineScope(Dispatchers.Default).launch {
val item = data[holder.absoluteAdapterPosition]
withContext(Dispatchers.Main) {
holder.bind(item)
holder.binding.root.setOnLongClickListener {
if (item.type == "missedCall")
return@setOnLongClickListener false
popup(holder)
return@setOnLongClickListener true
}
holder.binding.rightGifContainer.setOnLongClickListener {
popup(holder)
return@setOnLongClickListener true
}
holder.binding.rightGif.setOnLongClickListener {
popup(holder)
return@setOnLongClickListener true
}
holder.binding.leftMediaContainer.setOnLongClickListener {
popup(holder)
return@setOnLongClickListener true
}
holder.binding.rightMediaContainer.setOnLongClickListener {
popup(holder)
return@setOnLongClickListener true
}
holder.binding.rightGif.setOnClickListener {
ctx.startActivity(Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse([email protected][holder.absoluteAdapterPosition].text)
})
}
holder.binding.leftGifContainer.setOnLongClickListener {
popup(holder)
return@setOnLongClickListener true
}
holder.binding.leftGif.setOnLongClickListener {
popup(holder)
return@setOnLongClickListener true
}
holder.binding.leftGif.setOnClickListener {
ctx.startActivity(Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse([email protected][holder.absoluteAdapterPosition].text)
})
}
holder.binding.leftFrame.setOnLongClickListener {
popup(holder)
return@setOnLongClickListener true
}
holder.binding.rightFrame.setOnLongClickListener {
popup(holder)
return@setOnLongClickListener true
}
holder.binding.profile.setOnClickListener {
if (FragChat.username == "spinny" || FragChat.username == "spinnr") return@setOnClickListener
val frag = FragProfileView()
frag.arguments = Bundle().apply {
putString("guid", data[holder.absoluteAdapterPosition].guid)
putString("username", data[holder.absoluteAdapterPosition].username)
}
(inflater.context as ActHome).navigateTo(frag)
}
holder.binding.leftFrame.setOnClickListener {
listener.onItemSelect(holder.absoluteAdapterPosition)
}
holder.binding.leftMediaContainer.setOnClickListener {
listener.onItemSelect(holder.absoluteAdapterPosition)
}
holder.binding.rightFrame.setOnClickListener {
listener.onItemSelect(holder.absoluteAdapterPosition)
}
holder.binding.rightMediaContainer.setOnClickListener {
listener.onItemSelect(holder.absoluteAdapterPosition)
}
}
}
}
private fun popup(holder: MyViewHolder) {
if (currentSeconds() > 0) {
options(holder)
return
}
(ctx as ActHome).hideKeyboard()
val item = data[holder.absoluteAdapterPosition]
val popupBinding = DialogChatOptionBinding.inflate(inflater)
if (item.username == FragChat.USERNAME) {
popupBinding.previewLeft.visibility = View.GONE
holder.binding.rightFrame.buildDrawingCache()
val bitmap = holder.binding.rightFrame.drawingCache
popupBinding.previewRight.setImageBitmap(bitmap)
} else {
holder.binding.leftFrame.buildDrawingCache()
val bitmap = holder.binding.leftFrame.drawingCache
popupBinding.previewLeft.setImageBitmap(bitmap)
popupBinding.divider.visibility = View.GONE
popupBinding.delete.visibility = View.GONE
}
val y = if (item.username == FragChat.USERNAME) {
holder.binding.root.y.toInt()
} else {
(holder.binding.root.y + ctx.toPx(35f)).toInt()
}
val x = if (item.username == FragChat.USERNAME) {
(ctx.width() * .45f - ctx.toPx(10f)).toInt()
} else {
ctx.toPx(50f).toInt()
}
FragChat.showBlur = true
FragChat.startBlur()
val popup = PopupWindow(
popupBinding.root,
(ctx.width() * .55f).toInt(),
LinearLayout.LayoutParams.WRAP_CONTENT,
true
)
popupBinding.delete.setOnClickListener {
Firebase
.firestore
.document("spinnr-chat/" + FragChat.collection + "/messages/${item.documentID}")
.delete().addOnSuccessListener {
try {
if (item.type != "text") {
item.fileRefPath?.let { it1 ->
Firebase
.storage
.getReference(it1)
.delete()
}
if (item.type == "video") {
item.thumbRefPath?.let { it1 ->
Firebase
.storage
.getReference(it1)
.delete()
}
}
Firebase
.firestore
.document("media-collection/${item.mediaDocumentPath}")
.delete()
}
} catch (e: java.lang.Exception) {
}
}
popup.dismiss()
}
popupBinding.copy.setOnClickListener {
val cm = ctx.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("423u5", item.text)
cm.setPrimaryClip(clip)
popup.dismiss()
if (item.text.isNotBlank())
(ctx as Activity).showSnackBar("Copied!")
}
popupBinding.root.setOnClickListener {
popup.dismiss()
}
popup.setOnDismissListener {
FragChat.showBlur = false
}
popup.showAtLocation(holder.binding.left, Gravity.NO_GRAVITY, x, y)
}
private var emojiPopup: EmojiPopup? = null
@SuppressLint("ClickableViewAccessibility")
private fun options(holder: MyViewHolder) {
val item = data[holder.absoluteAdapterPosition]
val rect = Rect()
val binding = DialogReactionsBinding.inflate(LayoutInflater.from(ctx))
val dialog = Dialog(ctx)
var bitmap1: Bitmap? = null
if (item.username == FragChat.USERNAME) {
//left
holder.binding.rightFrame2.getGlobalVisibleRect(rect)
holder.binding.rightFrame2.buildDrawingCache()
val bitmap = holder.binding.rightFrame2.drawingCache
bitmap1 = bitmap
binding.container1.setImageBitmap(bitmap)
binding.container1.setBackgroundResource(R.drawable.bg_chat_right)
binding.leftSupport.visibility = View.VISIBLE
(binding.container1.layoutParams as LinearLayout.LayoutParams).gravity = Gravity.END
if (item.type != "text" || item.text.contains("giphy.com")) {
binding.edit.visibility = View.GONE
binding.divider0.visibility = View.GONE
}
} else {
//right
holder.binding.leftFrame2.getGlobalVisibleRect(rect)
holder.binding.leftFrame2.buildDrawingCache()
val bitmap = holder.binding.leftFrame2.drawingCache
bitmap1 = bitmap
binding.container1.setImageBitmap(bitmap)
binding.container1.setBackgroundResource(R.drawable.bg_chat_left)
binding.rightSupport.visibility = View.VISIBLE
binding.divider.visibility = View.GONE
binding.delete.visibility = View.GONE
binding.divider0.visibility = View.GONE
binding.edit.visibility = View.GONE
}
if (preferenceInstance.readString("emoji_1").isBlank()) {
preferenceInstance.writeString("emoji_1", "\uD83D\uDC4D")
}
if (preferenceInstance.readString("emoji_2").isBlank()) {
preferenceInstance.writeString("emoji_2", "❤")
}
if (preferenceInstance.readString("emoji_3").isBlank()) {
preferenceInstance.writeString("emoji_3", "\uD83D\uDE06")
}
if (preferenceInstance.readString("emoji_4").isBlank()) {
preferenceInstance.writeString("emoji_4", "\uD83D\uDE32")
}
if (preferenceInstance.readString("emoji_5").isBlank()) {
preferenceInstance.writeString("emoji_5", "\uD83D\uDE25")
}
if (preferenceInstance.readString("emoji_6").isBlank()) {
preferenceInstance.writeString("emoji_6", "\uD83D\uDE21")
}
EmojiManager.getInstance()
.findEmoji(preferenceInstance.readString("emoji_1", "\uD83D\uDC4D"))?.let {
binding.like.setEmoji(it)
}
EmojiManager.getInstance().findEmoji(preferenceInstance.readString("emoji_2", "❤"))?.let {
binding.love.setEmoji(it)
}
EmojiManager.getInstance()
.findEmoji(preferenceInstance.readString("emoji_3", "\uD83D\uDE06"))?.let {
binding.happy.setEmoji(it)
}
EmojiManager.getInstance()
.findEmoji(preferenceInstance.readString("emoji_4", "\uD83D\uDE32"))?.let {
binding.wow.setEmoji(it)
}
EmojiManager.getInstance()
.findEmoji(preferenceInstance.readString("emoji_5", "\uD83D\uDE25"))?.let {
binding.sad.setEmoji(it)
}
EmojiManager.getInstance()
.findEmoji(preferenceInstance.readString("emoji_6", "\uD83D\uDE21"))?.let {
binding.angry.setEmoji(it)
}
binding.like.setOnClickListener {
dialog.dismiss()
react(
item.documentID,
preferenceInstance.readString("emoji_1"),
item.reaction?.get(FragChat.USERNAME) == preferenceInstance.readString("emoji_1")
)
}
binding.love.setOnClickListener {
dialog.dismiss()
react(
item.documentID,
preferenceInstance.readString("emoji_2"),
item.reaction?.get(FragChat.USERNAME) == preferenceInstance.readString("emoji_2")
)
}
binding.happy.setOnClickListener {
dialog.dismiss()
react(
item.documentID,
preferenceInstance.readString("emoji_3"),
item.reaction?.get(FragChat.USERNAME) == preferenceInstance.readString("emoji_3")
)
}
binding.wow.setOnClickListener {
dialog.dismiss()
react(
item.documentID,
preferenceInstance.readString("emoji_4"),
item.reaction?.get(FragChat.USERNAME) == preferenceInstance.readString("emoji_4")
)
}
binding.sad.setOnClickListener {
dialog.dismiss()
react(
item.documentID,
preferenceInstance.readString("emoji_5"),
item.reaction?.get(FragChat.USERNAME) == preferenceInstance.readString("emoji_5")
)
}
binding.angry.setOnClickListener {
dialog.dismiss()
react(
item.documentID,
preferenceInstance.readString("emoji_6"),
item.reaction?.get(FragChat.USERNAME) == preferenceInstance.readString("emoji_6")
)
}
binding.edit.setOnClickListener {
dialog.dismiss()
if (item.type == "text") {
(frag as FragChat).binding.input.setText(item.text)
(frag as FragChat).binding.input.setSelection(item.text.length)
(frag as FragChat).editMessage = item
}
}
binding.add.setOnClickListener {
dialog.dismiss()
FragChat.popup = EmojiPopup.Builder
.fromRootView(frag.requireView())
.setOnEmojiClickListener(object : OnEmojiClickListener {
override fun onEmojiClick(imageView: EmojiImageView, emoji: Emoji) {
swapWith(emoji.getUnicode())
react(
item.documentID,
emoji.getUnicode(),
item.reaction?.get(FragChat.USERNAME) == emoji.getUnicode()
)
emojiPopup?.dismiss()
FragChat.popup?.dismiss()
}
})
.build()
FragChat.popup?.showAtBottom()
}
binding.copy.setOnClickListener {
val cm = ctx.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("423u5", item.text)
cm.setPrimaryClip(clip)
dialog.dismiss()
if (item.text.isNotBlank())
(ctx as Activity).showSnackBar("Copied!")
}
binding.delete.setOnClickListener {
dialog.dismiss()
frag.showOptionDialog(
"Delete Message",
when (item.type) {
"video" -> "Are you sure want to delete this video?"
"audio" -> "Are you sure want to delete this audio?"
"photo" -> "Are you sure want to delete this image?"
else -> "Are you sure you want to delete this message?"
},
"Cancel", "Delete", true
) {
if (it == 1)
Firebase
.firestore
.document("spinnr-chat/" + FragChat.collection + "/messages/${item.documentID}")
.delete().addOnSuccessListener {
try {
if (item.type != "text") {
item.fileRefPath?.let { it1 ->
Firebase
.storage
.getReference(it1)
.delete()
}
if (item.type == "video") {
item.thumbRefPath?.let { it1 ->
Firebase
.storage
.getReference(it1)
.delete()
}
}
Firebase
.firestore
.document("media-collection/${item.mediaDocumentPath}")
.delete()
}
} catch (e: Exception) {
}
}
}
}
binding.reply.setOnClickListener {
replyListener.onReply(item)
dialog.dismiss()
}
/*binding.like.setBackgroundResource(if (item.reaction[FragChat.USERNAME] == "like") R.mipmap.img_reaction_select_bg else R.color.transparent)
binding.love.setBackgroundResource(if (item.reaction[FragChat.USERNAME] == "love") R.mipmap.img_reaction_select_bg else R.color.transparent)
binding.happy.setBackgroundResource(if (item.reaction[FragChat.USERNAME] == "happy") R.mipmap.img_reaction_select_bg else R.color.transparent)
binding.wow.setBackgroundResource(if (item.reaction[FragChat.USERNAME] == "surprise") R.mipmap.img_reaction_select_bg else R.color.transparent)
binding.sad.setBackgroundResource(if (item.reaction[FragChat.USERNAME] == "sad") R.mipmap.img_reaction_select_bg else R.color.transparent)
binding.angry.setBackgroundResource(if (item.reaction[FragChat.USERNAME] == "angry") R.mipmap.img_reaction_select_bg else R.color.transparent)*/
dialog.setContentView(binding.root)
dialog.window?.setBackgroundDrawable(null)
dialog.window?.setDimAmount(0f)
dialog.window?.attributes?.apply {
gravity = Gravity.TOP or Gravity.START
x = if (item.username == FragChat.USERNAME)
rect.right - binding.root.width
else
rect.left
y = rect.top - (ctx.toPx(56f) * 1.7f).toInt()
}
dialog.setOnDismissListener {
emojiPopup?.dismiss()
FragChat.showBlur = false
}
FragChat.showBlur = true
FragChat.startBlur()
dialog.show()
}
private fun swapWith(new: String) {
if (
preferenceInstance.readString("emoji_1") == new ||
preferenceInstance.readString("emoji_2") == new ||
preferenceInstance.readString("emoji_3") == new ||
preferenceInstance.readString("emoji_4") == new ||
preferenceInstance.readString("emoji_5") == new ||
preferenceInstance.readString("emoji_6") == new
) return
preferenceInstance.writeString("emoji_6", preferenceInstance.readString("emoji_5"))
preferenceInstance.writeString("emoji_5", preferenceInstance.readString("emoji_4"))
preferenceInstance.writeString("emoji_4", preferenceInstance.readString("emoji_3"))
preferenceInstance.writeString("emoji_3", preferenceInstance.readString("emoji_2"))
preferenceInstance.writeString("emoji_2", preferenceInstance.readString("emoji_1"))
preferenceInstance.writeString("emoji_1", new)
}
private fun react(id: String, reaction: String, remove: Boolean) {
FragChat.positionListener?.onComplete()
if (!remove)
(frag as FragChat).reactionNotification(reaction)
Firebase
.firestore
.document("spinnr-chat/${FragChat.collection}/messages/$id/")
.update("reaction.${FragChat.USERNAME}", /*if (remove) "" else*/ reaction)
}
override fun getItemCount() = data.size
}
fun interface ReplyListener {
fun onReply(bitmap: MessageModel)
}
fun interface ScrollListener {
fun onScroll(id: String)
}
<FrameLayout 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"
android:background="@color/primaryBackground"
android:orientation="vertical"
tools:context=".frag.chat.FragChat">
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/back"
android:layout_width="@dimen/dimen_50_dp"
android:layout_height="@dimen/dimen_50_dp"
android:layout_marginStart="@dimen/dimen_12_dp"
android:contentDescription="@string/app_name"
android:padding="@dimen/dimen_7_dp"
android:src="@mipmap/img_back"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/profile_username" />
<FrameLayout
android:id="@+id/fm"
android:layout_width="@dimen/dimen_50_dp"
android:layout_height="@dimen/dimen_50_dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/back"
app:layout_constraintTop_toTopOf="parent">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/profile"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/dimen_7_dp"
android:src="@mipmap/img_profile_placeholder"
app:civ_border_color="@color/purpleDark"
app:civ_border_width=".9dp" />
<View
android:id="@+id/online"
android:layout_width="@dimen/dimen_14_dp"
android:layout_height="@dimen/dimen_14_dp"
android:layout_gravity="end"
android:layout_margin="@dimen/dimen_5_dp"
android:background="@drawable/bg_online"
android:visibility="gone" />
</FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/clTitle"
android:layout_width="@dimen/dimen_0_dp"
android:layout_height="@dimen/dimen_50_dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/audioCall"
app:layout_constraintStart_toEndOf="@id/fm"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:fontFamily="@font/hellix_bold"
android:gravity="center_vertical"
android:maxLines="1"
android:paddingTop="@dimen/dimen_2_dp"
android:text="@string/app_name"
android:textColor="@color/profile_username"
app:layout_constraintBottom_toTopOf="@id/status"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/hellix_medium"
android:gravity="center_vertical"
android:paddingVertical="@dimen/dimen_2_dp"
android:text="@string/typing"
android:textColor="@color/primaryDark"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@+id/audioCall"
android:layout_width="@dimen/dimen_50_dp"
android:layout_height="@dimen/dimen_50_dp"
android:contentDescription="@string/app_name"
android:padding="@dimen/dimen_7_dp"
android:src="@mipmap/img_audio_call"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/videoCall"
app:layout_constraintStart_toEndOf="@id/clTitle"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/profile_username" />
<ImageView
android:id="@+id/videoCall"
android:layout_width="@dimen/dimen_50_dp"
android:layout_height="@dimen/dimen_50_dp"
android:contentDescription="@string/app_name"
android:padding="@dimen/dimen_7_dp"
android:src="@mipmap/img_video_call"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/option"
app:layout_constraintStart_toEndOf="@id/audioCall"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/profile_username" />
<ImageView
android:id="@+id/option"
android:layout_width="@dimen/dimen_50_dp"
android:layout_height="@dimen/dimen_50_dp"
android:contentDescription="@string/app_name"
android:padding="@dimen/dimen_7_dp"
android:src="@mipmap/img_more"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/videoCall"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/profile_username" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.app.spinnr.helper.CustomRecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:clipToPadding="false"
android:paddingTop="@dimen/dimen_10_dp"
android:scrollbars="vertical"
tools:listitem="@layout/item_chat" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/bottomParent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dimen_10_dp"
android:orientation="vertical">
<TextView
android:id="@+id/globalChatMsg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/hellix_regular"
android:gravity="center"
android:lineSpacingExtra="@dimen/dimen_5_dp"
android:paddingVertical="@dimen/dimen_15_dp"
android:text="@string/global_chat_msg"
android:textColor="@color/primaryDark"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/replyContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dimen_10_dp"
android:layout_marginTop="@dimen/dimen_10_dp"
android:background="@mipmap/img_left_reply_bg"
android:paddingStart="@dimen/dimen_10_dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/inputLL"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/replyOrigin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="You"
android:textColor="@color/primaryDark"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@id/replyContent"
app:layout_constraintEnd_toStartOf="@id/fm1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/replyContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_4_dp"
android:ellipsize="end"
android:maxLines="2"
android:textColor="@color/color_date_text"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/fm1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/replyOrigin" />
<FrameLayout
android:id="@+id/fm1"
android:layout_width="@dimen/dimen_47_dp"
android:layout_height="@dimen/dimen_47_dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/replyOrigin"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/replyPreview"
android:layout_width="@dimen/dimen_47_dp"
android:layout_height="@dimen/dimen_47_dp"
android:scaleType="centerCrop"
android:src="@mipmap/ic_launcher"
android:visibility="gone"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.DifferentCornerSize.PreviewImage" />
<View
android:layout_width="@dimen/dimen_9_dp"
android:layout_height="@dimen/dimen_9_dp"
android:layout_gravity="end"
android:background="@color/white" />
<ImageView
android:id="@+id/replyClose"
android:layout_width="@dimen/dimen_25_dp"
android:layout_height="@dimen/dimen_25_dp"
android:layout_gravity="end"
android:paddingStart="@dimen/dimen_7_dp"
android:paddingBottom="@dimen/dimen_7_dp"
android:src="@mipmap/img_close_bg" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/inputLL"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/dimen_10_dp"
app:layout_constraintBottom_toTopOf="@id/media"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/replyContainer">
<EditText
android:id="@+id/input"
android:layout_width="@dimen/dimen_0_dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_10_dp"
android:layout_marginEnd="@dimen/dimen_10_dp"
android:layout_weight="1"
android:background="@drawable/bg_chat_input"
android:gravity="center_vertical"
android:hint="@string/type_a_message"
android:importantForAutofill="no"
android:includeFontPadding="false"
android:inputType="textMultiLine|text|textNoSuggestions"
android:maxLines="6"
android:minHeight="@dimen/dimen_47_dp"
android:paddingHorizontal="@dimen/dimen_10_dp"
android:paddingVertical="@dimen/dimen_5_dp"
android:textSize="15sp"
android:verticalScrollbarPosition="defaultPosition"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/send"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/spinnyMessage"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="Please wait while Spinny is in progress!"
android:background="@drawable/bg_chat_input"
android:backgroundTint="@color/input_color"
android:gravity="center"
android:visibility="gone"
android:textColor="@color/profile_list_icon"
app:layout_constraintBottom_toBottomOf="@id/input"
app:layout_constraintEnd_toEndOf="@id/input"
app:layout_constraintStart_toStartOf="@id/input"
app:layout_constraintTop_toTopOf="@id/input" />
<ImageView
android:id="@+id/send"
android:layout_width="@dimen/dimen_47_dp"
android:layout_height="@dimen/dimen_47_dp"
android:layout_gravity="bottom"
android:layout_marginEnd="@dimen/dimen_10_dp"
android:background="@drawable/circle_white"
android:backgroundTint="@color/profile_username"
android:fontFamily="@font/hellix_regular"
android:gravity="center"
android:src="@mipmap/img_message_send"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/input"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/spinny"
android:layout_width="@dimen/dimen_47_dp"
android:layout_height="@dimen/dimen_47_dp"
android:layout_gravity="bottom"
android:layout_marginEnd="@dimen/dimen_10_dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/input"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:id="@+id/mediaSpace"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_10_dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/media"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/inputLL" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/media"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingHorizontal="@dimen/dimen_10_dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/mediaSpace"
tools:ignore="ContentDescription">
<ImageView
android:id="@+id/gallery"
android:layout_width="@dimen/dimen_38_dp"
android:layout_height="@dimen/dimen_38_dp"
android:padding="@dimen/dimen_10_dp"
android:src="@mipmap/img_gallery"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/recordVideo"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/profile_username" />
<ImageView
android:id="@+id/recordVideo"
android:layout_width="@dimen/dimen_38_dp"
android:layout_height="@dimen/dimen_38_dp"
android:padding="@dimen/dimen_10_dp"
android:src="@mipmap/img_video"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/recordAudio"
app:layout_constraintStart_toEndOf="@id/gallery"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/profile_username" />
<ImageView
android:id="@+id/recordAudio"
android:layout_width="@dimen/dimen_38_dp"
android:layout_height="@dimen/dimen_38_dp"
android:padding="@dimen/dimen_10_dp"
android:src="@mipmap/img_record_audio"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/gif"
app:layout_constraintStart_toEndOf="@id/recordVideo"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/profile_username" />
<ImageView
android:id="@+id/gif"
android:layout_width="@dimen/dimen_38_dp"
android:layout_height="@dimen/dimen_38_dp"
android:padding="@dimen/dimen_10_dp"
android:src="@mipmap/ic_gif"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/recordAudio"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/profile_username" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
<ImageView
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.Matrix
import android.media.MediaMetadataRetriever
import android.media.MediaMetadataRetriever.METADATA_KEY_DURATION
import android.net.Uri
import android.os.*
import android.provider.MediaStore
import android.provider.Settings
import android.text.Editable
import android.text.TextWatcher
import android.util.DisplayMetrics
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.exifinterface.media.ExifInterface
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView
import com.andremion.louvre.Louvre
import com.andremion.louvre.home.GalleryActivity
import com.app.spinnr.App
import com.app.spinnr.App.Companion.getStr
import com.app.spinnr.R
import com.app.spinnr.act.ActHome
import com.app.spinnr.act.ActHome.Companion.timeDx
import com.app.spinnr.act.ActVideoCall
import com.app.spinnr.act.ActVoiceCall
import com.app.spinnr.adapter.ChatAdapter
import com.app.spinnr.adapter.ReplyListener
import com.app.spinnr.adapter.ScrollListener
import com.app.spinnr.databinding.DialogInactiveUserChatBinding
import com.app.spinnr.databinding.FragChatBinding
import com.app.spinnr.frag.*
import com.app.spinnr.frag.chat.vik.flow.ChatRepository
import com.app.spinnr.frag.chat.vik.viewmodel.ChatViewModel
import com.app.spinnr.frag.profile.FragProfileView
import com.app.spinnr.frag.squad.FragSquadInfo
import com.app.spinnr.frag.squad.SquadRepo
import com.app.spinnr.frag.video.FragCreateVideo
import com.app.spinnr.frag.video.FragTrimVideo
import com.app.spinnr.helper.Constant
import com.app.spinnr.listener.CompleteListener
import com.app.spinnr.listener.ItemSelectListener
import com.app.spinnr.model.*
import com.app.spinnr.service.ANotification
import com.app.spinnr.service.ApiClient.Companion.getClient
import com.app.spinnr.service.ApiClient.Companion.getClient2
import com.app.spinnr.service.ResponseBean
import com.app.spinnr.service.ResponseListBean
import com.app.spinnr.util.*
import com.app.spinnr.util.Preference.Companion.FALSE
import com.app.spinnr.util.Preference.Companion.FTP_CAMERA
import com.app.spinnr.util.Preference.Companion.TRUE
import com.app.spinnr.util.Preference.Companion.preferenceInstance
import com.app.spinnr.util.imageUtil.Blur
import com.appsflyer.AppsFlyerLib
import com.giphy.sdk.core.models.Media
import com.giphy.sdk.core.models.enums.MediaType
import com.giphy.sdk.core.models.enums.RatingType
import com.giphy.sdk.core.models.enums.RenditionType
import com.giphy.sdk.ui.GPHContentType
import com.giphy.sdk.ui.GPHSettings
import com.giphy.sdk.ui.Giphy
import com.giphy.sdk.ui.pagination.GPHContent
import com.giphy.sdk.ui.utils.aspectRatio
import com.giphy.sdk.ui.utils.videoAspectRatio
import com.giphy.sdk.ui.views.GiphyDialogFragment
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FieldValue
import com.google.firebase.firestore.ListenerRegistration
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.firestore.ktx.getField
import com.google.firebase.ktx.Firebase
import com.google.firebase.storage.ktx.storage
import com.google.gson.JsonElement
import dev.leonardpark.emoji.EmojiPopup
import io.github.ponnamkarthik.richlinkpreview.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.json.JSONObject
import kotlinx.coroutines.withContext
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.lang.StringBuilder
import java.net.HttpURLConnection
import java.net.URL
import java.text.SimpleDateFormat
import java.util.*
@Suppress("DEPRECATION")
@SuppressLint("StaticFieldLeak")
class FragChat(
private val me: Boolean = true,
private val global: Boolean = false,
) : Fragment() {
companion object {
var showBlur = false
var target: ImageView? = null
var source: View? = null
var collection: String = ""
var USERNAME: String = "" // me
var GUID: String = "" // me
var username: String = "" // other
var guid: String = "" // other
fun startBlur() {
if (!showBlur) {
target?.setImageBitmap(null)
return
}
source?.buildDrawingCache()
Blur.doBlur(
source?.drawingCache, 14, Color.argb(10, 0, 0, 0)
) { bmp ->
Handler(Looper.getMainLooper()).post {
source?.destroyDrawingCache()
target?.setImageBitmap(bmp)
startBlur()
}
}
}
var callListener: CompleteListener? = null
var positionListener: CompleteListener? = null
var popup: EmojiPopup? = null
}
private var filesUploadedCount = 0
private var uploadedFilesReference = ""
private val LOUVRE_REQUEST_CODE = 1020
private var mSelection: MutableList<Uri?> = mutableListOf()
private lateinit var ctx: Context
lateinit var binding: FragChatBinding
private lateinit var adapter: ChatAdapter
private val data = mutableListOf<MessageModel>()
private var mediaFiles = mutableListOf<MediaFile?>()
//private var guid = ""
//private var username = ""
private var position = -1
private var unreadCount = 0
private var recordsToFetch = 25L
private lateinit var linearLayoutManager: LinearLayoutManager
private lateinit var lastVisible: DocumentSnapshot
private var isLastMessageReached = false
private var isLoadMore = false
private lateinit var repository: ChatRepository
private lateinit var chatViewModel: ChatViewModel
private var from = 1
private var loaded = false
private var detailSubscriber: ListenerRegistration? = null
private var typingSubscriber: ListenerRegistration? = null
private var subscriber: ListenerRegistration? = null
private var onlineStatusSubscriber: ListenerRegistration? = null
private var loader: Loader? = null
private var isResumed = false
private var typingTimer: CountDownTimer? = null
private val handler = Handler(Looper.getMainLooper())
private lateinit var giphyDialog: GiphyDialogFragment
override fun onDestroy() {
Blur.destroy()
callListener = null
typingSubscriber?.remove()
detailSubscriber?.remove()
subscriber?.remove()
onlineStatusSubscriber?.remove()
super.onDestroy()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
if (loaded) return binding.root
loaded = true
ctx = requireContext()
Giphy.configure(ctx, "bj87yysRtVcTfGw0bfmSX7kh4PvS6HyK")
binding = FragChatBinding.inflate(inflater, container, false)
(ctx as ActHome).changeUi(this)
postDelayed({ setup() }, 250)
/*if ((ctx as ActHome).systemDarkMode == AppCompatDelegate.MODE_NIGHT_YES)
binding.bottomParent.background = null*/
requireActivity().window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
callListener = CompleteListener {
val i = preferenceInstance.readInt("callStatus")
}
positionListener = CompleteListener {
position =
(binding.rv.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
Log.i("423u5-position", ">>> $position")
}
/*GIPHY SDK Start*/
/*GIPHY SDK End*/
return binding.root
}
override fun onResume() {
paused = false
Blur.init(ctx)
ANotification.chatUsername = username
super.onResume()
postDelayed({
if (collection.isNotBlank())
Firebase.firestore.document("spinnr-chat/$collection/").get().addOnSuccessListener {
if (it.toObject(MessageImpl::class.java) == null) binding.back.performClick()
}
}, 100)
}
override fun onPause() {
paused = true
ANotification.chatUsername = ""
updateTyping(false)
popup?.dismiss()
super.onPause()
}
private fun setup() {
target = binding.preview
source = binding.ll
guid = arguments?.getString("guid") ?: ""
username = arguments?.getString("username") ?: ""
if (me) {
USERNAME = preferenceInstance.readString(Preference.USERNAME)
GUID = preferenceInstance.readString(Preference.GUID)
}
ANotification.chatUsername = username
val a0 = arrayOf(USERNAME, username).sorted()
collection = "${a0.first()}-${a0.last()}"
if (global) collection = "spinnr-global"
binding.back.setOnClickListener(clickListener)
binding.audioCall.setOnClickListener(clickListener)
binding.videoCall.setOnClickListener(clickListener)
binding.option.setOnClickListener(clickListener)
binding.gallery.setOnClickListener(clickListener)
binding.recordVideo.setOnClickListener(clickListener)
binding.recordAudio.setOnClickListener(clickListener)
binding.gif.setOnClickListener(clickListener)
binding.send.setOnClickListener(clickListener)
binding.input.addTextChangedListener(textChangeListener)
binding.profile.setOnClickListener(clickListener)
binding.spinnyMessage.setOnClickListener { Log.i("423u5", "lol....") }
binding.title.text = username
adapter = ChatAdapter(
ctx,
this,
LayoutInflater.from(ctx),
username,
data,
messageClickListener,
replyListener,
lickListsner
)
linearLayoutManager = object : LinearLayoutManager(ctx) {
override fun smoothScrollToPosition(
recyclerView: RecyclerView,
state: RecyclerView.State,
position: Int
) {
val smoothScroller: LinearSmoothScroller = object : LinearSmoothScroller(ctx) {
private val SPEED = 300f // Change this value (default=25f)
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float {
return SPEED / displayMetrics.densityDpi
}
}
smoothScroller.targetPosition = position
startSmoothScroll(smoothScroller)
}
}
binding.rv.layoutManager = linearLayoutManager
binding.rv.setHasFixedSize(true)
binding.rv.itemAnimator = null
binding.rv.setItemViewCacheSize(100)
binding.rv.adapter = adapter
(binding.rv.layoutManager as LinearLayoutManager).apply {
stackFromEnd = true
}
if (username == "spinny") {
binding.mediaSpace.visibility = View.VISIBLE
binding.audioCall.visibility = View.GONE
binding.videoCall.visibility = View.GONE
binding.option.visibility = View.GONE
binding.media.visibility = View.GONE
binding.status.visibility = View.VISIBLE
binding.status.text = "Your personal A.I. friend to chat with!"
binding.spinny.load(Constant.SPINNY_LOADING, enableCache = true)
} else if (username == "spinnr") {
binding.audioCall.visibility = View.GONE
binding.videoCall.visibility = View.GONE
binding.option.visibility = View.GONE
binding.media.visibility = View.GONE
binding.inputLL.visibility = View.GONE
binding.globalChatMsg.visibility = View.VISIBLE
binding.status.visibility = View.VISIBLE
binding.status.text = "Global Chat"
} else {
binding.mediaSpace.visibility = View.GONE
binding.audioCall.visibility = View.VISIBLE
binding.videoCall.visibility = View.VISIBLE
binding.option.visibility = View.VISIBLE
binding.media.visibility = View.VISIBLE
}
binding.profile.keyProfile(username, update = true)
if (requireArguments().getBoolean("initChat")) initChat()
else chatData()
binding.root.setOnTouchListener { v, event ->
popup?.dismiss()
return@setOnTouchListener false
}
loader = showLoader()
getProfile()
}
private fun initChat() {
var sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf.timeZone = TimeZone.getTimeZone("UTC")
var time = sdf.format(Date())
var sdf1 = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf1.timeZone = TimeZone.getTimeZone("UTC")
var time1 = (sdf1.parse(time)!!.time / 1000)
val data = MessageImpl(
collection, time1, mutableListOf(USERNAME, username), mutableListOf(
MemberDetail(GUID, USERNAME),
MemberDetail(guid, username),
), mutableMapOf(
username to 0, USERNAME to 0
), MessageModel(
"",
preferenceInstance.readString(Preference.GUID),
preferenceInstance.readString(Preference.USERNAME),
"text",
System.currentTimeMillis() + timeDx,
text = "Let's chat!"
), false
)
Firebase.firestore.document("spinnr-chat/$collection/").set(data).addOnFailureListener {
showMessage("${it.message}") {
binding.back.performClick()
}
}
chatData()
val value = requireArguments().getString("value") ?: ""
sendMessage(
if (value.startsWith("http")) value else value.decodeMessage(),
if (value.startsWith("http")) "video" else "text",
me = false,
)
}
private var paused = false
private fun chatData() {
/*subscriber = Firebase.firestore.collection("spinnr-chat/$collection/messages/")
.orderBy("createdAt", Query.Direction.DESCENDING)
.limit(recordsToFetch)
.addSnapshotListener { value, _ ->
if (value == null) return@addSnapshotListener
data.clear()
isLastMessageReached = false
value.forEach {
try {
val item = it.toObject(MessageModel::class.java)
data.add(item)
data.forEach { msg -> if (msg.createdAt.toString().length == 10) msg.createdAt *= 1000 }
data.sortBy { a -> a.createdAt }
} catch (e: Exception) {
try {
Firebase.firestore.document("spinnr-chat/$collection/messages/${it.id}")
.delete()
} catch (e: Exception) {
Log.e("vi26", "chatData: $e")
}
Log.i("423u5", ">>> ${it.data}")
e.printStackTrace()
}
}
if (data.size != 0) Firebase.firestore.document("spinnr-chat/$collection/")
.update("lastMessage", data.last())
binding.rv.adapter = ChatAdapter(
ctx,
this,
LayoutInflater.from(ctx),
username,
data,
messageClickListener,
replyListener,
lickListsner
)
if (position != -1) {
binding.rv.post {
binding.rv.smoothScrollToPosition(position)
position = -1
}
}
if (data.isNotEmpty()) updateMessageCount(true, data.last().username)
if (value.size() > 0 && value.documents.isNotEmpty())
lastVisible = value.documents[value.size() - 1]
val onScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val firstVisibleItemPosition =
linearLayoutManager.findFirstVisibleItemPosition()
if (dy < 0 && firstVisibleItemPosition - 5 < 0 && !isLastMessageReached) {
val nextQuery = Firebase.firestore
.collection("spinnr-chat/$collection/messages/")
.orderBy("createdAt", Query.Direction.DESCENDING)
.startAfter(lastVisible)
.limit(recordsToFetch)
nextQuery.addSnapshotListener { nextValue, _ ->
if (nextValue != null) {
if (nextValue.size() > 0 && nextValue.documents.isNotEmpty())
if (nextValue.documents[nextValue.size() - 1].id != lastVisible.id) {
nextValue.forEach {
try {
val item = it.toObject(MessageModel::class.java)
data.add(0, item)
data.forEach { msg -> if (msg.createdAt.toString().length == 10) msg.createdAt *= 1000 }
data.sortBy { a -> a.createdAt }
} catch (e: Exception) {
try {
Firebase.firestore.document("spinnr-chat/$collection/messages/${it.id}")
.delete()
} catch (e: Exception) {
Log.e("vi26", "onScrolled: $e")
}
Log.i("423u5", ">>> ${it.data}")
e.printStackTrace()
}
}
}
adapter.notifyItemRangeInserted(0, nextValue.documents.size)
if (nextValue.documents.isNotEmpty())
lastVisible = nextValue.documents[nextValue.size() - 1]
if (nextValue.size() < recordsToFetch) {
isLastMessageReached = true
}
}
}
}
}
}
binding.rv.addOnScrollListener(onScrollListener)
}*/
repository = ChatRepository("spinnr-chat/$collection/messages/")
chatViewModel = ChatViewModel(repository)
lifecycleScope.launch(Dispatchers.IO) {
chatViewModel._retrieveMessages().collect { value ->
if (value == null || !updateData) {
updateData = true
return@collect
}
val items = mutableListOf<MessageModel>()
isLastMessageReached = false
var select = 0
value.forEach {
Log.e("423u5-aa", "${++select} >> ${it.data}")
try {
items.add(it.toObject(MessageModel::class.java))
} catch (e: Exception) {
e.printStackTrace()
}
}
items.forEach { msg -> if (msg.createdAt.toString().length == 10) msg.createdAt *= 1000 }
items.sortBy { a -> a.createdAt }
if (items.size != 0)
Firebase.firestore.document("spinnr-chat/$collection/").update("lastMessage", items.last())
withContext(Dispatchers.Main) {
data.clear()
data.addAll(items)
adapter.notifyDataSetChanged()
position = adapter.itemCount - 1
if (position != -1) {
binding.rv.post {
binding.rv.scrollToPosition(position)
position = -1
}
}
}
if (data.isNotEmpty()) updateMessageCount(true, data.last().username)
if (value.size() > 0 && value.documents.isNotEmpty())
lastVisible = value.documents[value.size() - 1]
}
}
val onScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (!isLastMessageReached) {
val firstVisibleItemPosition =
linearLayoutManager.findFirstVisibleItemPosition()
if (dy < 0 && firstVisibleItemPosition < 10 && !isLoadMore) {
// Load more data when the user scrolls to the top
isLoadMore = true
lifecycleScope.launch(Dispatchers.IO) {
chatViewModel._retrieveMessages(lastVisible)
.collectLatest { nextValue ->
Log.d("423u5-aa", "onScrolled: ${nextValue?.documents}")
if (nextValue == null) return@collectLatest
if (nextValue.size() > 0 && nextValue.documents.isNotEmpty()) {
val newDatam = mutableListOf<MessageModel>()
if (nextValue.documents[nextValue.size() - 1].id != lastVisible.id) {
nextValue.forEach {
try {
newDatam.add(it.toObject(MessageModel::class.java))
} catch (e: Exception) {
}
}
newDatam.forEach { msg -> if (msg.createdAt.toString().length == 10) msg.createdAt *= 1000 }
newDatam.sortBy { a -> a.createdAt }
}
withContext(Dispatchers.Main) {
val size = data.size
data.addAll(0, newDatam)
adapter.notifyItemRangeInserted(0, newDatam.size)
}
}
isLoadMore = false
if (nextValue.documents.isNotEmpty())
lastVisible = nextValue.documents[nextValue.size() - 1]
if (nextValue.documents.size < recordsToFetch) {
isLastMessageReached = true
}
}
}
}
}
}
}
binding.rv.addOnScrollListener(onScrollListener)
detailSubscriber = Firebase.firestore.document("spinnr-chat/$collection/")
.addSnapshotListener { value, _ ->
if (value == null || paused) return@addSnapshotListener
val raw = value.toObject(MessageImpl::class.java)
if (raw == null) {
binding.back.performClick()
return@addSnapshotListener
}
unreadCount = raw.unreadCount?.get(username) ?: 0
}
typingSubscriber = Firebase.firestore.collection("spinnr-chat/$collection/typing-info")
.addSnapshotListener { value, _ ->
if (value == null || username == "spinny" || username == "spinnr") return@addSnapshotListener
for (a in value)
if (a.id == username) {
binding.status.visibility = if (a.getBoolean("typing") == true) View.VISIBLE
else View.GONE
break
}
}
if (binding.title.text == "spinny" || binding.title.text == "spinnr") {
binding.online.visibility = View.VISIBLE
return
}
onlineStatusSubscriber = Firebase.firestore
.document("users/$username")
.addSnapshotListener { value, _ ->
Log.i("423u5-online-profile", value?.data.toString())
if (value == null || value.data == null || netNotConnected())
binding.online.visibility = View.GONE
else {
val onlineStatus = value.getField<ActHome.OnlineStatus>("onlineStatus")
val isOnline = onlineStatus?.status == true && onlineStatus.updatedAt.seconds > currentSeconds() / 1000
binding.online.visibility = if (isOnline) View.VISIBLE else View.GONE
}
}
}
var linkTitle: String? = null
var linkDescription: String? = null
var linkThumb: String? = null
fun decodeMessageLink(text: String) {
var urls = extractUrls(text)
if (urls != null) {
//binding.rightLinkPreviewContainer.removeAllViews();
if (urls.size > 0) {
if (urls.size == 1) {
if (text.trim().length == urls[0].trim().length) {
//binding.rightMsg.visibility = View.GONE;
}
}
loader = showLoader()
var ww = RichPreview(object : ResponseListener {
override fun onData(metaData: MetaData?) {
//TODO("Not yet implemented")
loader?.dismiss()
linkTitle = metaData?.title
linkDescription = metaData?.description
linkThumb = metaData?.imageurl
message = text
filterMessage(text) {
sendMessage(message, "text")
}
}
override fun onError(e: java.lang.Exception?) {
//TODO("Not yet implemented")
loader?.dismiss()
}
});
ww.getPreview(makeValidUrl(urls[0]))
for (urlsObj in urls) {
Log.i("here text", text);
Log.i("here urls", urlsObj);
var richLinkView = RichLinkView(ctx);
richLinkView.setLink(makeValidUrl(urlsObj), object : ViewListener {
override fun onSuccess(status: Boolean) {}
override fun onError(e: Exception) {}
});
//binding.rightLinkPreviewContainer.addView(richLinkView);
}
} else {
//binding.rightLinkPreviewContainer.visibility = View.GONE;
message = text
filterMessage(text) {
sendMessage(message, "text")
}
}
} else {
message = text
filterMessage(text) {
sendMessage(message, "text")
}
}
}
private var message: String = ""
private fun filterMessage(v: String, listner: CompleteListener) {
System.currentTimeMillis()
message = convertOffendString(v)
System.currentTimeMillis()
sendMessage(message, "text")
}
var editMessage: MessageModel? = null
private fun sendMessage(
value: String,
type: String,
me: Boolean = true,
thumb: String = "",
storageRef: String = "",
storageThumbRef: String = ""
) {
if (App.netNotConnected()) {
showSnackBar(getStr(R.string.no_internet))
return
}
if (editMessage != null) {
if (type == "text") editMessage?.let {
it.text = value
it.isEdited = true
it.linkTitle = linkTitle
it.linkDescription = linkDescription
it.linkThumb = linkThumb
linkTitle = null
linkDescription = null
linkThumb = null
Firebase.firestore.document("spinnr-chat/$collection/messages/${it.documentID}")
.set(it)
}
editMessage = null
binding.input.setText("")
return
}
val msgId = Firebase.firestore.collection("spinnr-chat/$collection/messages").document().id
var sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf.timeZone = TimeZone.getTimeZone("UTC")
var time = sdf.format(Date())
var sdf1 = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf1.timeZone = TimeZone.getTimeZone("UTC")
var time1 = (sdf1.parse(time)!!.time)
val message = MessageModel(
msgId,
if (me) GUID else guid,
if (me) USERNAME else username,
type,
time1 + timeDx,
tag = null
)
replyMsg?.let {
message.repliedTo =
RepliedTo(
it.documentID,
it.username,
it.guid,
it.type,
when (it.type) {
"text" -> it.text
else -> it.content
},
when (it.type) {
"photo" -> it.content
"video" -> it.thumb
"gif" -> it.linkThumb
"groupMedia" -> {
var athumb: String? = null
it.mediaFiles.first()?.let { m->
athumb = if (m.type == "photo")
m.content
else
m.reference
}
athumb
}
else -> null
}
)
}
replyMsg?.let {
App.appsFlyerEvent("Chat_Reply")
}
when (message.type) {
"missedCall" -> {
message.content = value
}
"text" -> {
message.text = value
val eventValues = HashMap<String, Any>()
eventValues["customer_user_id"] =
Preference.preferenceInstance.readString(Preference.USERNAME)
eventValues["datetime"] = Calendar.getInstance().time.toString()
if (data.size == 1 && binding.title.text != "spinny") AppsFlyerLib.getInstance()
.logEvent(ctx, "Chat_Message", eventValues)
if (data.size == 1 && binding.title.text != "spinny")
App.appsFlyerEvent("Chat_Message")
}
"gif" -> {
message.text = value
linkThumb = value
message.linkThumb = value
val eventValues = HashMap<String, Any>()
eventValues["customer_user_id"] =
Preference.preferenceInstance.readString(Preference.USERNAME)
eventValues["datetime"] = Calendar.getInstance().time.toString()
if (data.size == 1 && binding.title.text != "spinny") AppsFlyerLib.getInstance()
.logEvent(ctx, "Chat_Gif", eventValues)
App.appsFlyerEvent("Chat_GIF")
}
"video" -> {
message.content = value
message.thumb = thumb
App.appsFlyerEvent("Chat_Video")
}
"photo" -> {
message.content = value
App.appsFlyerEvent("Chat_Photo")
}
"groupMedia" -> {
message.mediaFiles = mediaFiles
filesUploadedCount = 0
App.appsFlyerEvent("Chat_Media")
}
"audio" -> {
message.content = value
App.appsFlyerEvent("Chat_Audio")
}
}
message.linkTitle = linkTitle
message.linkDescription = linkDescription
message.linkThumb = linkThumb
linkTitle = null
linkDescription = null
linkThumb = null
if (type != "text" && type != "gif") {
val mediaId = Firebase.firestore.collection("media-collection").document().id
val mediaDetail = MediaDetail(
mediaId,
msgId,
message.createdAt,
message.content,
message.thumb,
message.type,
USERNAME,
GUID,
username,
guid,
storageRef,
storageThumbRef,
mediaFiles
)
Firebase.firestore.document("media-collection/$mediaId").set(mediaDetail)
if (!message.type.equals("groupMedia", true))
message.fileRefPath = storageRef
message.thumbRefPath = storageThumbRef
message.mediaDocumentPath = mediaId
Log.i("423u5", ">> $mediaDetail")
}
if (me)
updateMessageCount()
binding.replyClose.performClick()
binding.input.text.clear()
//(ctx as ActHome).hideKeyboard()
Firebase.firestore.document("spinnr-chat/$collection/messages/$msgId").set(message)
.addOnFailureListener { showSnackBar("${it.message}") }
Firebase.firestore.document("spinnr-chat/$collection/").update("lastMessage", message)
if (me)
sendPush(
NEW_CHAT_MESSAGE, guid, 15, "New chat message ${
when (type) {
"audio" -> "\uD83D\uDD08"
"video" -> "\uD83C\uDFA5"
"photo" -> "\uD83D\uDCF8"
"groupMedia" -> "\uD83C\uDFA5"
else -> "\uD83D\uDCAC"
}
}", "You have a new chat message from $USERNAME!", collection
)
if (me && username == "spinny") {
ChatRepo.spinnyQuestions(message)
autoMessageV2(value)
}
}
private val messageClickListener = ItemSelectListener {
try {
when (data[it].type) {
"photo" -> ctx.showImage(data[it].content)
"groupMedia" -> ctx.showImages(data[it].mediaFiles)
"audio" -> {
val frag = FragVideoPreview()
frag.arguments = Bundle().apply {
putString("title", "Audio")
putString("path", data[it].content)
}
(ctx as ActHome).navigateTo(frag, true)
}
"video" -> {
val frag = FragVideoPreview()
frag.arguments = Bundle().apply {
putString("title", "Video")
putString("path", data[it].content)
}
(ctx as ActHome).navigateTo(frag, true)
}
"eventInvite" -> {
FragEventDetail.event = EventsImpl.EventsModel()
FragEventDetail.event.id = data[it].content
(ctx as ActHome).navigateTo(FragEventDetail(), true)
}
"squadInvite" -> {
var squadId = ""
for (a in 0 until SquadRepo.all.size)
if (SquadRepo.all[a].squadid == data[it].content)
squadId = SquadRepo.all[a].squadid
(ctx as ActHome).navigateTo(FragSquadInfo().apply {
arguments = Bundle().apply {
putString("id", squadId)
}
})
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun isCallAllow(audio: Boolean) {
loader = showLoader()
getClient().getUserNotificationSettings(mapOf("username" to username, "guid" to guid))
.enqueue(object : Callback<NotificationSettingImpl> {
override fun onResponse(call: Call<NotificationSettingImpl>, response: Response<NotificationSettingImpl>) {
loader?.dismiss()
var notAllow = true
if (response.body()?.code == "1") {
response.body()?.data?.forEach {
if (it.pushnotificationId == "10")
notAllow = false
}
}
if (notAllow) {
showSnackBar("$username has \"do not disturb\" turned on")
return
}
if (audio) {
App.appsFlyerEvent("Chat_Audio_Call")
ActVoiceCall.type = 33
ActVoiceCall.username = username
startActivityForResult(Intent(ctx, ActVoiceCall::class.java), 10920)
} else {
ActVideoCall.type = 33
ActVideoCall.username = username
App.appsFlyerEvent("Chat_Video_Call")
if (!preferenceInstance.readBoolean("accept_video_call"))
FragVideoCallAccept() {
startActivityForResult(Intent(ctx, ActVideoCall::class.java), 10920)
}.show(childFragmentManager, "anything")
else
startActivityForResult(Intent(ctx, ActVideoCall::class.java), 10920)
}
}
override fun onFailure(call: Call<NotificationSettingImpl>, t: Throwable) { loader?.dismiss() }
})
}
private val clickListener = View.OnClickListener {
it.isEnabled = false
postDelayed({
it.isEnabled = true
}, 300)
when (it) {
binding.back -> {
(ctx as ActHome).backPress()
}
binding.audioCall -> isCallAllow(true)
binding.videoCall -> isCallAllow(false)
binding.profile -> {
if (username == "spinny" || username == "spinnr") return@OnClickListener
val frag = FragProfileView()
frag.arguments = Bundle().apply {
putString("guid", guid)
putString("username", username)
}
(ctx as ActHome).navigateTo(frag)
}
binding.option -> {
BottomSheetDialog(
"Choose action", mutableListOf(
BottomSheetModel(
"Report Member", R.mipmap.img_report_member
), BottomSheetModel(
"Block Member", R.mipmap.img_block_member
)
)
) { i1 ->
if (i1 == 0)
showOptionDialog(
getString(R.string.report_person),
getString(R.string.report_person_desc)
) { i2 -> if (i2 == 1) reportUser() }
else if (i1 == 1)
showOptionDialog(
getString(R.string.block_person), getString(R.string.block_person_desc)
) { i2 -> if (i2 == 1) updateUserFlag("F") }
}.show(childFragmentManager, BottomSheetDialog::class.java.simpleName)
}
binding.gallery -> {
showBottomSheetOptionDialog(
mutableListOf(
getString(R.string.take_photo),
getString(R.string.photo_library),
)
) { a0 ->
if (a0 == 0) {
if (ContextCompat.checkSelfPermission(
ctx,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_DENIED
) {
if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) ||
preferenceInstance.readBoolean(FTP_CAMERA, TRUE)
) {
preferenceInstance.writeBoolean(FTP_CAMERA, FALSE)
(ctx as Activity).requestPermissions(
arrayOf(Manifest.permission.CAMERA),
3001
)
} else {
showOptionDialog(
getString(R.string.permission_alert),
getString(R.string.capture_permission),
getString(R.string.cancel),
getString(R.string.setting)
) { z ->
if (z == 1) {
val i = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", ctx.packageName, null)
i.data = uri
startActivity(i)
}
}
}
return@showBottomSheetOptionDialog
}
val file = File("${ctx.cacheDir}/temp.jpeg")
val uri = FileProvider.getUriForFile(ctx, "com.app.spinnr.provider", file)
val i = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
i.putExtra(MediaStore.EXTRA_OUTPUT, uri)
from = 1
launcher.launch(i)
} else if (a0 == 1) {
from = 2
/*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
ContextCompat.checkSelfPermission(
ctx, Manifest.permission.READ_MEDIA_IMAGES
) != PackageManager.PERMISSION_GRANTED
) {
showOptionDialog(
getString(R.string.permission_alert),
getString(R.string.capture_permission),
getString(R.string.cancel),
getString(R.string.setting)
) { z ->
if (z == 1) {
val i = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", ctx.packageName, null)
i.data = uri
startActivity(i)
}
}
} else {
Louvre.init(this).setMaxSelection(10)
.setMediaTypeFilter(Louvre.IMAGE_TYPE_JPEG, Louvre.IMAGE_TYPE_PNG)
.setRequestCode(LOUVRE_REQUEST_CODE).open()
}*/
Louvre.init(this).setMaxSelection(10)
.setMediaTypeFilter(Louvre.IMAGE_TYPE_JPEG, Louvre.IMAGE_TYPE_PNG)
.setRequestCode(LOUVRE_REQUEST_CODE).open()
}
}
}
binding.recordVideo -> {
BottomSheetDialog(
"Please select an option",
mutableListOf(
BottomSheetModel(
"Record Video",
R.mipmap.img_record_video,
),
BottomSheetModel(
"Video Library",
R.mipmap.img_choose_intro_video,
)
)
) { a0 ->
if (a0 == 0) {
val frag = FragCreateVideo { _, p ->
loader = showLoader()
uploadFile(p, "video")
}.apply {
arguments = Bundle().apply {
putString("category", "chat")
}
}
(ctx as ActHome).navigateTo(frag)
} else if (a0 == 1) {
from = 4
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
//fromCamera = false
launcher.launch(Intent(MediaStore.ACTION_PICK_IMAGES).apply {
type = "video/*"
})
} else {
if (ContextCompat.checkSelfPermission(
ctx,
Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
)
showOptionDialog(
getString(R.string.permission_alert),
getString(R.string.capture_permission),
getString(R.string.cancel),
getString(R.string.setting)
) { z ->
if (z == 1) {
val i = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", ctx.packageName, null)
i.data = uri
startActivity(i)
}
} else
//fromCamera = false
launcher.launch(Intent(Intent.ACTION_GET_CONTENT).apply {
type = "video/*"
})
}
}
}.show(childFragmentManager, BottomSheetDialog::class.java.simpleName)
}
binding.gif -> {
val settings = GPHSettings()
// Option to select only mentioned Content Types
settings.mediaTypeConfig = arrayOf(GPHContentType.gif)
// Option to select the default Content Type to open
settings.selectedContentType = GPHContentType.gif
settings.renditionType = RenditionType.downsizedSmall
settings.clipsPreviewRenditionType = RenditionType.downsizedLarge
settings.confirmationRenditionType = RenditionType.original
GPHContent.trending(MediaType.gif, RatingType.pg13)
giphyDialog =
GiphyDialogFragment.newInstance(settings.copy(selectedContentType = GPHContentType.gif))
giphyDialog.gifSelectionListener = this.gifSelectionListener
giphyDialog.show(childFragmentManager, "giphy_dialog")
}
binding.recordAudio -> {
val frag = FragAudio(username) { a1, a2 ->
if (a1 == "result") {
loader = showLoader()
uploadFile(a2, "audio")
}
}
frag.show(childFragmentManager, FragAudio::class.java.simpleName)
}
binding.send -> {
if (binding.input.text.toString().isBlank()) return@OnClickListener
decodeMessageLink(binding.input.text.toString())
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == LOUVRE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
mSelection.clear()
mediaFiles.clear()
GalleryActivity.getSelection(data).also { mSelection = it }
if (mSelection.size > 1) {
loader = showLoader()
mSelection.forEach { uri ->
if (uri != null && !uri.path.isNullOrEmpty()) uploadFiles(uri.path!!)
}
} else if (mSelection.size == 1) {
mSelection[0].let { uri ->
loader = showLoader()
Thread {
try {
var bitmap =
BitmapFactory.decodeStream(ctx.contentResolver.openInputStream(uri!!))
val cachePath = "${ctx.cacheDir}/temp.jpeg"
val path = UriHelper.getInstance(ctx).getPath(uri) ?: return@Thread
val exifOrientation = ExifInterface(path).getAttributeInt(
ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL
)
val orientation = when (exifOrientation) {
ExifInterface.ORIENTATION_ROTATE_270 -> 270f
ExifInterface.ORIENTATION_ROTATE_180 -> 180f
ExifInterface.ORIENTATION_ROTATE_90 -> 90f
else -> 0f
}
val mat = Matrix()
mat.postRotate(orientation)
bitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.width, bitmap.height, mat, false
)
handler.post {
val frag = FragImageEditor(bitmap, false) { _, bitmap ->
loader = showLoader()
val fos = FileOutputStream(cachePath)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
uploadFile(cachePath, "photo")
}
loader?.dismiss()
(ctx as ActHome).navigateTo(frag)
}
} catch (e: Exception) {
handler.post {
loader?.dismiss()
showSnackBar("${e.message}")
}
}
}.start()
}
}
return
}
if (requestCode == 10920 && resultCode == Activity.RESULT_OK) {
val userName = data?.getStringExtra(Preference.USERNAME)
val isDeclined = data?.getBooleanExtra("isDeclined", false)
val content = data?.getStringExtra("content")
sendMessage(content ?: "", "missedCall")
}
super.onActivityResult(requestCode, resultCode, data)
}
private val gifSelectionListener = object : GiphyDialogFragment.GifSelectionListener {
override fun didSearchTerm(term: String) {
}
override fun onDismissed(selectedContentType: GPHContentType) {
}
override fun onGifSelected(
media: Media,
searchTerm: String?,
selectedContentType: GPHContentType
) {
Log.i("TAG", "onGifSelected: ${media.images.downsizedLarge?.gifUrl}")
if (media.images.downsizedLarge?.gifUrl.isNullOrEmpty()) return
message = media.images.downsizedLarge?.gifUrl ?: ""
if (message.isEmpty()) return
message += "&ratio=${media.aspectRatio}"
linkThumb = message
sendMessage(message, "gif")
}
}
private val textChangeListener = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable?) {
if (s.toString().isNotEmpty())
updateTyping(true)
typingTimer?.cancel()
typingTimer = object : CountDownTimer(2000, 1000) {
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
updateTyping(false)
}
}.start()
}
}
private fun reportUser() {
inputDialog(
desc = getString(R.string.reporting_reason_desc),
hint = getString(R.string.reason),
noBtn = getString(R.string.cancel),
yesBtn = getString(R.string.send),
enableEmoji = false
) { a0, a1 ->
if (a0 == "yes" && a1.isBlank())
showOptionMessage(getString(R.string.reason_should_net_be_blank)) { reportUser() }
else if (a0 == "yes")
updateUserFlag("R", a1.trim())
}
}
private fun updateUserFlag(type: String, desc: String = "") {
if (netNotConnected()) {
showSnackBar(getString(R.string.no_internet))
return
}
loader = showLoader()
getClient()
.updateUserFlag(
GUID,
guid, type, desc
)
.enqueue(object : Callback<ResponseBean<ProfileModel>> {
override fun onResponse(
call: Call<ResponseBean<ProfileModel>>,
response: Response<ResponseBean<ProfileModel>>
) {
loader?.dismiss()
if (response.body()?.code == 0)
when (type) {
"R" -> {
subscriber?.remove()
detailSubscriber?.remove()
typingSubscriber?.remove()
Firebase
.firestore
.document("spinnr-chat/$collection/")
.update("isDeleted", true)
Firebase
.firestore
.collection("spinnr-chat/")
.document(collection)
.delete()
(ctx as ActHome).backPress()
App.appsFlyerEvent("Chat_Report")
showSnackBar(getString(R.string.successfully_reported_person))
}
"F" -> {
subscriber?.remove()
detailSubscriber?.remove()
typingSubscriber?.remove()
Firebase
.firestore
.document("spinnr-chat/$collection/")
.update("isDeleted", true)
Firebase
.firestore
.collection("spinnr-chat/")
.document(collection)
.delete()
App.appsFlyerEvent("Chat_Block")
showSnackBar(getString(R.string.successfully_blocked_person))
(ctx as ActHome).backPress()
}
}
else if (response.body()?.code == 1)
showSnackBar("${response.body()?.msg}")
}
override fun onFailure(call: Call<ResponseBean<ProfileModel>>, t: Throwable) {
loader?.dismiss()
}
})
}
private val lickListsner = ScrollListener { id ->
val x = data.indexOfFirst { it.documentID == id }
if (x > -1 && x < data.size)
binding.rv.scrollToPosition(x)
}
private var replyMsg: MessageModel? = null
private val replyListener = ReplyListener {
replyMsg = it
binding.replyContainer.visibility = View.VISIBLE
binding.replyContainer.setBackgroundResource(if (it.username == USERNAME) R.mipmap.img_right_reply_bg else R.mipmap.img_left_reply_bg)
binding.replyOrigin.text = if (it.username == USERNAME) "You" else it.username
binding.replyContent.text = when (it.type) {
"gif" -> "\uD83C\uDFA5 GIF"
"video" -> "\uD83C\uDFA5 Video"
"photo" -> "\uD83D\uDCF7 Photo"
"groupMedia" -> "\uD83C\uDFA5 Media"
"audio" -> "\uD83C\uDFA4 Audio"
else -> it.text
}
binding.replyClose.setOnClickListener {
replyMsg = null
binding.replyContainer.visibility = View.GONE
}
if (it.type == "text" || it.type == "audio") {
binding.replyPreview.visibility = View.GONE
return@ReplyListener
} else {
binding.replyPreview.visibility = View.VISIBLE
when (it.type) {
"video" -> binding.replyPreview.load(it.thumb, 10f)
"photo" -> binding.replyPreview.load(it.content, 10f)
"gif" -> binding.replyPreview.load(it.linkThumb ?: it.text, 10f)
"groupMedia" -> binding.replyPreview.load(
it.mediaFiles[0]?.content,
10f
)
"eventInvite" -> binding.replyPreview.load(it.thumb, 10f)
"squadInvite" -> binding.replyPreview.load(
"${Constant.PROFILE_IMG_URL}${it.content}.jpeg",
10f
)
}
}
}
private val launcher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it == null || it.resultCode == Activity.RESULT_CANCELED) return@registerForActivityResult
if (from == 1) {
loader = showLoader()
Thread {
try {
val path = "${ctx.cacheDir}/temp.jpeg"
var bitmap = BitmapFactory.decodeFile(path)
val exifOrientation = ExifInterface(path).getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL
)
val orientation = when (exifOrientation) {
ExifInterface.ORIENTATION_ROTATE_270 -> 270f
ExifInterface.ORIENTATION_ROTATE_180 -> 180f
ExifInterface.ORIENTATION_ROTATE_90 -> 90f
else -> 0f
}
val mat = Matrix()
mat.postRotate(orientation)
bitmap = Bitmap.createBitmap(
bitmap,
0,
0,
bitmap.width,
bitmap.height,
mat,
false
)
handler.post {
val frag = FragImageEditor(bitmap, false) { _, bitmap ->
loader = showLoader()
val fos = FileOutputStream(path)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
uploadFile(path, "photo")
}
loader?.dismiss()
(ctx as ActHome).navigateTo(frag)
}
} catch (e: Exception) {
handler.post {
loader?.dismiss()
showSnackBar("${e.message}")
}
}
}.start()
} else if (from == 2) {
it.data?.data?.let { uri ->
loader = showLoader()
Thread {
try {
var bitmap =
BitmapFactory.decodeStream(ctx.contentResolver.openInputStream(uri))
val cachePath = "${ctx.cacheDir}/temp.jpeg"
val path = UriHelper.getInstance(ctx).getPath(uri) ?: return@Thread
val exifOrientation = ExifInterface(path).getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL
)
val orientation = when (exifOrientation) {
ExifInterface.ORIENTATION_ROTATE_270 -> 270f
ExifInterface.ORIENTATION_ROTATE_180 -> 180f
ExifInterface.ORIENTATION_ROTATE_90 -> 90f
else -> 0f
}
val mat = Matrix()
mat.postRotate(orientation)
bitmap = Bitmap.createBitmap(
bitmap, 0, 0,
bitmap.width,
bitmap.height,
mat, false
)
handler.post {
val frag = FragImageEditor(bitmap, false) { _, bitmap ->
loader = showLoader()
val fos = FileOutputStream(cachePath)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
uploadFile(cachePath, "photo")
}
loader?.dismiss()
(ctx as ActHome).navigateTo(frag)
}
} catch (e: Exception) {
handler.post {
loader?.dismiss()
showSnackBar("${e.message}")
}
}
}.start()
}
} else if (from == 3 || from == 4) {
it.data?.data?.let { uri ->
Thread {
handler.post {
loader = showLoader()
}
val path = UriHelper.getInstance(ctx).getPath(uri) ?: return@Thread
val retriever = MediaMetadataRetriever()
retriever.setDataSource(path)
val length =
retriever.extractMetadata(METADATA_KEY_DURATION) ?: return@Thread
retriever.release()
val frag = FragTrimVideo { a1, a2 ->
if (a1 == "result") {
loader = showLoader()
uploadFile(a2, "video")
}
}
frag.arguments = Bundle().apply {
putString("category", "chat")
putString("path", path)
putLong("length", length.toLong())
}
handler.post {
loader?.dismiss()
(ctx as ActHome).navigateTo(frag, true)
}
}.start()
}
}
}
private fun updateTyping(isTyping: Boolean) {
val data = mutableMapOf(
"username" to USERNAME,
"typing" to isTyping
)
if (collection.isNotBlank())
Firebase
.firestore
.document("spinnr-chat/$collection/typing-info/$USERNAME")
.set(data)
}
private fun updateMessageCount(makeRead: Boolean = false, sender: String = "") {
if (sender == username && makeRead)
Firebase
.firestore
.document("spinnr-chat/$collection/")
.update("unreadCount.$USERNAME", 0)
else if (!makeRead)
Firebase
.firestore
.document("spinnr-chat/$collection/")
.update("unreadCount.${username}", unreadCount + 1)
}
private fun uploadFile(path: String, type: String) {
if (App.netNotConnected()) {
showSnackBar(getStr(R.string.no_internet))
loader?.dismiss()
return
}
val name = System.currentTimeMillis().toString()
val ref = "$USERNAME/$name.${path.substringAfterLast(".")}"
Firebase
.storage
.getReference(ref)
.putStream(FileInputStream(path))
.addOnSuccessListener {
Firebase
.storage
.getReference(ref)
.downloadUrl.addOnSuccessListener {
if (type == "video")
uploadThumb(path, name, "$it", ref)
else
sendMessage("$it", type, storageRef = ref)
}
}
.addOnCompleteListener {
if (type != "video") loader?.dismiss()
}
}
private fun uploadFiles(path: String) {
if (App.netNotConnected()) {
showSnackBar(getStr(R.string.no_internet))
loader?.dismiss()
return
}
val name = System.currentTimeMillis().toString()
uploadedFilesReference = "$USERNAME/$name.${path.substringAfterLast("/")}"
val storageRef = Firebase.storage.getReference(uploadedFilesReference)
val uploadTask = storageRef.putStream(FileInputStream(path))
uploadTask.addOnSuccessListener {
storageRef.downloadUrl.addOnSuccessListener { uri ->
Log.i("vi26", "uploadFiles: File $uri uploaded")
val mediaFile = MediaFile(
uploadedFilesReference,
"photo",
uri.toString()
)
synchronized(mediaFiles) { // Synchronize access to mediaFiles
mediaFiles.add(mediaFile) // Add at the correct index
}
filesUploadedCount++
if (filesUploadedCount == mSelection.size) {
sendMessage("", "groupMedia", storageRef = uploadedFilesReference)
loader?.dismiss()
}
}
}.addOnFailureListener { exception ->
// Handle the failure here
Log.w("vi26", "uploadFiles: File upload failed: ${exception.localizedMessage}")
}
}
private fun uploadThumb(path: String, thumbName: String, content: String, storageRef: String) {
val thumb = MediaMetadataRetriever().apply { setDataSource(path) }
val bitmap = thumb.getFrameAtTime(1, MediaMetadataRetriever.OPTION_CLOSEST)
val ref = "$USERNAME/${thumbName}_VideoThumb.jpeg"
if (bitmap == null) {
sendMessage(content, "video")
loader?.dismiss()
return
}
val thumbPath = "${ctx.cacheDir}/temp.jpeg"
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, FileOutputStream(thumbPath))
Firebase
.storage
.getReference(ref)
.putStream(FileInputStream(thumbPath))
.addOnSuccessListener {
Firebase
.storage
.getReference(ref)
.downloadUrl.addOnSuccessListener {
sendMessage(
content,
"video",
thumb = "$it",
storageRef = storageRef,
storageThumbRef = ref
)
}
}
.addOnCompleteListener {
loader?.dismiss()
}
}
private var updateData = true
private fun autoMessageV2(body: String) {
val handler = Handler(Looper.getMainLooper())
binding.send.isEnabled = false
binding.send.visibility = View.INVISIBLE
binding.spinnyMessage.visibility = View.VISIBLE
binding.spinny.visibility = View.VISIBLE
adapter.notifyItemChanged(data.size - 1)
Thread {
try {
val guid = guid
val username = username
val collection = collection
val connection = URL("https://spinnrweb.com:5555/api/ChatGPTWebAPIStream").openConnection() as HttpURLConnection
connection.requestMethod = "POST"
connection.setRequestProperty("Accept", "application/json")
connection.setRequestProperty("Content-Type", "application/json")
connection.doOutput = true
connection.outputStream.write("{\"question\": \"$body\"}".toByteArray())
handler.post { loader?.dismiss() }
if (connection.responseCode == HttpURLConnection.HTTP_OK) {
val bytes = ByteArray(256)
var length: Int
val string = StringBuilder()
handler.post {
data.add(MessageModel("", guid, username, "text", System.currentTimeMillis(), tag = null, text = ""))
adapter.notifyItemInserted(data.size - 1)
binding.rv.post { binding.rv.scrollBy(0, 20) }
}
while (connection.inputStream.read(bytes, 0, bytes.size).also { length = it } != -1) {
val obj = JSONObject(String(bytes, 0, length))
if (!obj.has("content")) continue
val value = obj.getString("content")
string.append(value)
handler.post {
try {
data[data.size - 1].text = string.toString()
adapter.notifyItemChanged(data.size - 1)
binding.rv.post {
val lastView = linearLayoutManager.findViewByPosition(data.size - 1)
if (lastView != null) {
val scrollY = lastView.bottom - binding.rv.height + lastView.height
binding.rv.scrollBy(0, scrollY)
}
}
}catch(e:Exception){}
}
}
handler.post {
if (context == null) {
val msgId = Firebase.firestore.collection("spinnr-chat/$collection/messages").document().id
val message = MessageModel(msgId, username, guid, "text", System.currentTimeMillis(), text = string.toString())
Firebase.firestore.document("spinnr-chat/$collection/").update("unreadCount.${username}", FieldValue.increment(1))
Firebase.firestore.document("spinnr-chat/$collection/messages/$msgId").set(message)
Firebase.firestore.document("spinnr-chat/$collection/").update("lastMessage", message)
} else {
updateData = false
binding.send.isEnabled = true
binding.send.visibility = View.VISIBLE
binding.spinnyMessage.visibility = View.GONE
binding.spinny.visibility = View.GONE
sendMessage(string.toString(), "text", false)
}
}
} else { handler.post {
showSnackBar(connection.responseMessage)
binding.send.isEnabled = true
binding.send.visibility = View.VISIBLE
binding.spinnyMessage.visibility = View.GONE
binding.spinny.visibility = View.GONE
loader?.dismiss() }
}
connection.disconnect()
} catch (e: Exception) { handler.post {
showSnackBar("${e.localizedMessage}")
binding.send.isEnabled = true
binding.send.visibility = View.VISIBLE
binding.spinnyMessage.visibility = View.GONE
binding.spinny.visibility = View.GONE
loader?.dismiss() }
}
}.start()
}
data class ChatGptQuestion(val question: String)
private fun autoMessageV1(body: String) {
loader = showLoader()
getClient()
.chatGpt(
"https://spinnrweb.com:5555/api/ChatGPTWebAPI",
ChatGptQuestion(body)
)
.enqueue(object : Callback<JsonElement> {
override fun onResponse(call: Call<JsonElement>, response: Response<JsonElement>) {
if (response.body()?.asJsonObject?.get("status")?.asString == "success")
sendMessage("${response.body()?.asJsonObject?.get("answer")?.asString}", "text", false)
else
showSnackBar("${response.body()?.asJsonObject?.get("reason")?.asString}")
loader?.dismiss()
}
override fun onFailure(call: Call<JsonElement>, t: Throwable) {
loader?.dismiss()
}
})
}
fun reactionNotification(reaction: String) {
App.appsFlyerEvent("Chat_Reaction")
sendPush(
"",
guid,
15,
"New chat reaction",
"You have a new chat reaction $reaction from $USERNAME!",
collection,
topic = "/topics/$username-member-reaction"
)
}
fun getProfile() {
if (username == "spinnr" || username == "spinny" || username.isBlank()) {
loader?.dismiss()
return
}
getClient2().getProfile(username)
.enqueue(object : Callback<ResponseListBean<ProfileStatusModel>> {
override fun onResponse(call: Call<ResponseListBean<ProfileStatusModel>>, response: Response<ResponseListBean<ProfileStatusModel>>) {
loader?.dismiss()
postDelayed({ getProfile() }, 1500)
if (response.body()?.data?.first()?.statusid != "2" && !paused)
inactiveUser()
else
activateDialog?.dismiss()
}
override fun onFailure(call: Call<ResponseListBean<ProfileStatusModel>>, t: Throwable) {
loader?.dismiss()
postDelayed({ getProfile() }, 1500)
if (netConnected())
(ctx as ActHome).backPress()
}
})
}
private var activateDialog: Dialog? = null
private fun inactiveUser() {
if (context == null || activateDialog?.isShowing == true) return
val binding = DialogInactiveUserChatBinding.inflate(layoutInflater, null, false)
activateDialog = Dialog(requireContext())
binding.noCollection.load(Constant.NO_COLLECTION_PICTURE, enableCache = true)
binding.close.setOnClickListener {
(ctx as ActHome).backPress()
activateDialog?.dismiss()
}
activateDialog?.setCancelable(false)
activateDialog?.setContentView(binding.root)
activateDialog?.window?.setLayout(-1, -1)
activateDialog?.window?.setBackgroundDrawable(null)
activateDialog?.show()
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="wrap_content"
android:paddingHorizontal="@dimen/dimen_10_dp"
android:paddingVertical="@dimen/dimen_6_dp"
tools:ignore="contentDescription">
<LinearLayout
android:id="@+id/llMissedCall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="@drawable/bg_missed_call"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="@dimen/dimen_12_dp"
android:paddingVertical="@dimen/dimen_5_dp">
<TextView
android:id="@+id/missedCallTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/hellix_medium"
android:layout_gravity="center_horizontal"
android:includeFontPadding="true"
android:layout_marginHorizontal="@dimen/dimen_5_dp"
android:textColor="@color/purpleDark"
android:textSize="14sp" />
<TextView
android:id="@+id/missedCallTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="true"
android:layout_gravity="center_horizontal"
android:layout_marginHorizontal="@dimen/dimen_5_dp"
android:fontFamily="@font/hellix_regular"
android:textColor="@color/color_date_text"
android:textSize="13sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/left"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/profile"
android:layout_width="@dimen/dimen_28_dp"
android:layout_height="@dimen/dimen_28_dp"
android:background="@drawable/circle_white"
android:backgroundTint="@color/grey" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/leftFrame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_10_dp"
android:layout_marginEnd="@dimen/dimen_50_dp">
<LinearLayout
android:id="@+id/leftFrame3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_chat_left"
android:minWidth="@dimen/dimen_80_dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/leftReplyContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dimen_5_dp"
android:layout_marginTop="@dimen/dimen_5_dp"
android:background="@mipmap/img_left_reply_bg"
android:orientation="horizontal"
android:paddingStart="@dimen/dimen_10_dp"
android:visibility="gone">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/leftReplyOrigin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/hellix_medium"
android:maxLines="1"
android:textColor="@color/primaryDark"
android:textSize="16sp" />
<TextView
android:id="@+id/leftReplyContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_2_dp"
android:ellipsize="end"
android:fontFamily="@font/hellix_regular"
android:maxLines="2"
android:textColor="#CCFFFFFF" />
</LinearLayout>
<FrameLayout
android:layout_width="@dimen/dimen_47_dp"
android:layout_height="@dimen/dimen_47_dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/leftReplyPreview"
android:layout_width="@dimen/dimen_47_dp"
android:layout_height="@dimen/dimen_47_dp"
android:scaleType="centerCrop"
android:visibility="gone"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.DifferentCornerSize.PreviewImage" />
<View
android:layout_width="@dimen/dimen_9_dp"
android:layout_height="@dimen/dimen_9_dp"
android:layout_gravity="end"
android:background="@color/white"
android:visibility="gone" />
<ImageView
android:id="@+id/leftReplyClose"
android:layout_width="@dimen/dimen_25_dp"
android:layout_height="@dimen/dimen_25_dp"
android:layout_gravity="end"
android:paddingStart="@dimen/dimen_7_dp"
android:paddingBottom="@dimen/dimen_7_dp"
android:src="@mipmap/img_close_bg"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/leftFrame2"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/leftGifContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<ImageView
android:id="@+id/ivLeftPoweredByGiphy"
android:layout_width="50dp"
android:layout_height="14dp"
android:layout_margin="@dimen/dimen_5_dp"
android:elevation="14dp"
android:src="@drawable/poweredbygiphy_100px"
app:layout_constraintEnd_toEndOf="@id/leftGif"
app:layout_constraintTop_toTopOf="@id/leftGif" />
<com.giphy.sdk.ui.views.GPHMediaView
android:id="@+id/leftGif"
android:layout_width="210dp"
android:layout_height="140dp"
android:layout_margin="@dimen/dimen_10_dp"
android:elevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/leftGifLink"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/pink2"
android:ellipsize="end"
android:fontFamily="@font/hellix_regular"
android:maxLines="1"
android:padding="@dimen/dimen_9_dp"
android:textColor="@color/white"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/leftGif"
app:layout_constraintEnd_toEndOf="@id/leftGif"
app:layout_constraintStart_toStartOf="@id/leftGif" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/leftMsg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/hellix_regular"
android:lineSpacingExtra="3.5dp"
android:padding="@dimen/dimen_9_dp"
android:text="@string/space"
android:textColor="#220b2a"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/leftLinkPreviewContainer"
android:layout_width="@dimen/dimen_250_dp"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/dimen_12_dp"
android:layout_marginStart="@dimen/dimen_12_dp"
android:layout_marginEnd="@dimen/dimen_8_dp"
android:gravity="center_vertical"
android:orientation="vertical"
android:visibility="gone" />
</LinearLayout>
<FrameLayout
android:id="@+id/leftThumb2Container"
android:layout_width="@dimen/dimen_135_dp"
android:layout_height="@dimen/dimen_100_dp"
android:layout_margin="@dimen/dimen_8_dp"
android:visibility="gone">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/leftThumb2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/dimen_10_dp"
android:layout_marginEnd="@dimen/dimen_10_dp"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.DifferentCornerSize.LeftImage" />
</FrameLayout>
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dimen_8_dp"
android:visibility="gone"
app:cardCornerRadius="@dimen/dimen_9_dp"
app:cardElevation="@dimen/dimen_0_dp">
<ImageView
android:id="@+id/leftThumb1"
android:layout_width="@dimen/dimen_135_dp"
android:layout_height="@dimen/dimen_100_dp"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/leftMediaContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dimen_8_dp"
android:layout_marginVertical="@dimen/dimen_4_dp"
android:visibility="gone">
<androidx.cardview.widget.CardView
android:id="@+id/cvLeftThumbGroupMedia1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dimen_4_dp"
app:cardCornerRadius="@dimen/dimen_9_dp"
app:cardElevation="@dimen/dimen_0_dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/cvLeftThumbGroupMedia2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/leftThumbGroupMedia1"
android:layout_width="@dimen/dimen_135_dp"
android:layout_height="@dimen/dimen_100_dp"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cvLeftThumbGroupMedia2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dimen_4_dp"
app:cardCornerRadius="@dimen/dimen_9_dp"
app:cardElevation="@dimen/dimen_0_dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/cvLeftThumbGroupMedia1"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/leftThumbGroupMedia2"
android:layout_width="@dimen/dimen_135_dp"
android:layout_height="@dimen/dimen_100_dp"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/leftGroupMediaCount"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/bg_media_count"
android:fontFamily="@font/hellix_bold"
android:gravity="center"
android:letterSpacing=".1"
android:textColor="@color/white"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@id/cvLeftThumbGroupMedia2"
app:layout_constraintEnd_toEndOf="@id/cvLeftThumbGroupMedia2"
app:layout_constraintStart_toStartOf="@id/cvLeftThumbGroupMedia2"
app:layout_constraintTop_toTopOf="@id/cvLeftThumbGroupMedia2" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@+id/leftVideoIcon"
android:layout_width="@dimen/dimen_30_dp"
android:layout_height="@dimen/dimen_30_dp"
android:layout_gravity="center"
android:src="@mipmap/img_play_hollow"
android:visibility="gone"
app:tint="@color/white" />
<LinearLayout
android:id="@+id/leftAudio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingVertical="@dimen/dimen_12_dp"
android:paddingStart="@dimen/dimen_12_dp"
android:paddingEnd="@dimen/dimen_8_dp"
android:visibility="gone">
<ImageView
android:layout_width="@dimen/dimen_22_dp"
android:layout_height="@dimen/dimen_22_dp"
android:layout_gravity="center_vertical"
android:src="@mipmap/img_play_hollow"
app:tint="@color/purpleDark" />
<View
android:layout_width="@dimen/dimen_22_dp"
android:layout_height="@dimen/dimen_22_dp"
android:layout_marginStart="@dimen/dimen_4_dp"
android:background="@drawable/circle_white"
android:backgroundTint="@color/purpleDark"
android:elevation="@dimen/dimen_1_dp" />
<View
android:layout_width="@dimen/dimen_115_dp"
android:layout_height="@dimen/dimen_4_dp"
android:layout_marginStart="-4dp"
android:background="@drawable/audio_line" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/leftReaction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="@dimen/dimen_2_dp"
android:paddingHorizontal="@dimen/dimen_10_dp"
android:paddingTop="@dimen/dimen_10_dp"
android:paddingBottom="@dimen/dimen_5_dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/leftFrame3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/leftFrame3">
<TextView
android:id="@+id/leftReactionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/reaction_bg"
android:fontFamily="@font/hellix_medium"
android:letterSpacing=".1"
android:padding="@dimen/dimen_2_dp"
android:textColor="@color/reaction_count_color"
android:textSize="14sp" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/leftLike"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_like" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/leftLove"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_heart" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/leftHappy"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_happy" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/leftWow"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_surprise" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/leftSad"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_sad" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/leftAngry"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_angry" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
<TextView
android:id="@+id/leftTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_38_dp"
android:layout_marginTop="@dimen/dimen_5_dp"
android:fontFamily="@font/hellix_regular"
android:textColor="@color/color_date_text"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:orientation="vertical"
android:visibility="visible"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/rightFrame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_55_dp">
<LinearLayout
android:id="@+id/rightFrame3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_chat_right"
android:minWidth="@dimen/dimen_80_dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/rightReplyContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dimen_5_dp"
android:layout_marginTop="@dimen/dimen_5_dp"
android:background="@mipmap/img_right_reply_bg"
android:paddingStart="@dimen/dimen_10_dp"
android:visibility="gone">
<LinearLayout
android:layout_width="@dimen/dimen_0_dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/rightReplyOrigin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/hellix_medium"
android:maxLines="1"
android:text="You"
android:textColor="@color/purpleDark"
android:textSize="16sp" />
<TextView
android:id="@+id/rightReplyContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_2_dp"
android:ellipsize="end"
android:fontFamily="@font/hellix_regular"
android:maxLines="2"
android:textColor="@color/white" />
</LinearLayout>
<FrameLayout
android:layout_width="@dimen/dimen_47_dp"
android:layout_height="@dimen/dimen_47_dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/rightReplyPreview"
android:layout_width="@dimen/dimen_47_dp"
android:layout_height="@dimen/dimen_47_dp"
android:scaleType="centerCrop"
android:visibility="gone"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.DifferentCornerSize.PreviewImage" />
<View
android:layout_width="@dimen/dimen_9_dp"
android:layout_height="@dimen/dimen_9_dp"
android:layout_gravity="end"
android:background="@color/white"
android:visibility="gone" />
<ImageView
android:id="@+id/rightReplyClose"
android:layout_width="@dimen/dimen_25_dp"
android:layout_height="@dimen/dimen_25_dp"
android:layout_gravity="end"
android:paddingStart="@dimen/dimen_7_dp"
android:paddingBottom="@dimen/dimen_7_dp"
android:src="@mipmap/img_close_bg"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/rightFrame2"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/rightGifContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<ImageView
android:id="@+id/ivRightPoweredByGiphy"
android:layout_width="50dp"
android:layout_height="14dp"
android:layout_margin="@dimen/dimen_5_dp"
android:elevation="14dp"
android:src="@drawable/poweredbygiphy_100px"
app:layout_constraintEnd_toEndOf="@id/rightGif"
app:layout_constraintTop_toTopOf="@id/rightGif" />
<com.giphy.sdk.ui.views.GPHMediaView
android:id="@+id/rightGif"
android:layout_width="210dp"
android:layout_height="140dp"
android:layout_margin="@dimen/dimen_10_dp"
android:elevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/rightGifLink"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/pink2"
android:elevation="14dp"
android:ellipsize="end"
android:fontFamily="@font/hellix_regular"
android:maxLines="1"
android:padding="@dimen/dimen_9_dp"
android:textColor="@color/white"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rightGif"
app:layout_constraintEnd_toEndOf="@id/rightGif"
app:layout_constraintStart_toStartOf="@id/rightGif" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/rightMsg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/hellix_regular"
android:lineSpacingExtra="3.5dp"
android:padding="@dimen/dimen_9_dp"
android:text="@string/space"
android:textColor="@color/white" />
<LinearLayout
android:id="@+id/rightLinkPreviewContainer"
android:layout_width="@dimen/dimen_230_dp"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/dimen_12_dp"
android:layout_marginStart="@dimen/dimen_8_dp"
android:layout_marginEnd="@dimen/dimen_12_dp"
android:gravity="center_vertical"
android:orientation="vertical"
android:visibility="gone" />
</LinearLayout>
<FrameLayout
android:id="@+id/rightThumb2Container"
android:layout_width="@dimen/dimen_135_dp"
android:layout_height="@dimen/dimen_100_dp"
android:layout_margin="@dimen/dimen_8_dp"
android:visibility="gone">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/rightThumb2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/dimen_10_dp"
android:layout_marginTop="@dimen/dimen_10_dp"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.DifferentCornerSize.RightImage" />
</FrameLayout>
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dimen_8_dp"
android:visibility="gone"
app:cardCornerRadius="@dimen/dimen_9_dp"
app:cardElevation="@dimen/dimen_0_dp">
<ImageView
android:id="@+id/rightThumb1"
android:layout_width="@dimen/dimen_135_dp"
android:layout_height="@dimen/dimen_100_dp"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/rightMediaContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dimen_8_dp"
android:layout_marginVertical="@dimen/dimen_4_dp"
android:visibility="gone">
<androidx.cardview.widget.CardView
android:id="@+id/cvRightThumbGroupMedia1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dimen_4_dp"
app:cardCornerRadius="@dimen/dimen_9_dp"
app:cardElevation="@dimen/dimen_0_dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/cvRightThumbGroupMedia2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/rightThumbGroupMedia1"
android:layout_width="@dimen/dimen_135_dp"
android:layout_height="@dimen/dimen_100_dp"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cvRightThumbGroupMedia2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dimen_4_dp"
app:cardCornerRadius="@dimen/dimen_9_dp"
app:cardElevation="@dimen/dimen_0_dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/cvRightThumbGroupMedia1"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/rightThumbGroupMedia2"
android:layout_width="@dimen/dimen_135_dp"
android:layout_height="@dimen/dimen_100_dp"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/rightGroupMediaCount"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/bg_media_count"
android:fontFamily="@font/hellix_bold"
android:gravity="center"
android:letterSpacing=".1"
android:textColor="@color/white"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@id/cvRightThumbGroupMedia2"
app:layout_constraintEnd_toEndOf="@id/cvRightThumbGroupMedia2"
app:layout_constraintStart_toStartOf="@id/cvRightThumbGroupMedia2"
app:layout_constraintTop_toTopOf="@id/cvRightThumbGroupMedia2" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@+id/rightVideoIcon"
android:layout_width="@dimen/dimen_30_dp"
android:layout_height="@dimen/dimen_30_dp"
android:layout_gravity="center"
android:src="@mipmap/img_play_hollow"
android:visibility="gone"
app:tint="@color/white" />
<LinearLayout
android:id="@+id/rightAudio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingVertical="@dimen/dimen_12_dp"
android:paddingStart="@dimen/dimen_12_dp"
android:paddingEnd="@dimen/dimen_8_dp"
android:visibility="gone">
<ImageView
android:layout_width="@dimen/dimen_22_dp"
android:layout_height="@dimen/dimen_22_dp"
android:layout_gravity="center_vertical"
android:src="@mipmap/img_play_hollow"
app:tint="@color/white" />
<View
android:layout_width="@dimen/dimen_22_dp"
android:layout_height="@dimen/dimen_22_dp"
android:layout_marginStart="@dimen/dimen_4_dp"
android:background="@drawable/circle_white"
android:elevation="@dimen/dimen_1_dp" />
<View
android:layout_width="@dimen/dimen_115_dp"
android:layout_height="@dimen/dimen_4_dp"
android:layout_marginStart="-4dp"
android:background="@drawable/audio_line"
android:backgroundTint="#B3FFFFFF" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/rightReaction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="@dimen/dimen_2_dp"
android:paddingHorizontal="@dimen/dimen_10_dp"
android:paddingTop="@dimen/dimen_10_dp"
android:paddingBottom="@dimen/dimen_5_dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rightFrame3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/rightFrame3">
<TextView
android:id="@+id/rightReactionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/reaction_bg"
android:fontFamily="@font/hellix_medium"
android:letterSpacing=".1"
android:padding="@dimen/dimen_2_dp"
android:textColor="@color/reaction_count_color"
android:textSize="14sp" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/rightLike"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_like" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/rightLove"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_heart" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/rightHappy"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_happy" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/rightWow"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_surprise" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/rightSad"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_sad" />
<dev.leonardpark.emoji.emojicompat.EmojiImageView
android:id="@+id/rightAngry"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginHorizontal="@dimen/dimen_1_dp"
android:src="@drawable/ic_angry" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/rightTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_5_dp"
android:fontFamily="@font/hellix_regular"
android:textColor="@color/color_date_text"
android:textSize="12sp" />
</LinearLayout>
</FrameLayout>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment