Skip to content

Instantly share code, notes, and snippets.

@VIkash2601
Created November 30, 2023 12:54
Show Gist options
  • Save VIkash2601/30d62fdefc09df656c96b08f49be708f to your computer and use it in GitHub Desktop.
Save VIkash2601/30d62fdefc09df656c96b08f49be708f to your computer and use it in GitHub Desktop.
class FragSquadChat : Fragment() {
companion object {
var squadName: String = ""
var squadId: String = ""
var squad: SquadImpl? = null
var showBlur = false
var target: ImageView? = null
var source: View? = null
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 positionListener: CompleteListener? = null
var event = false
var fromEventDetail = false
var lastVisible: DocumentSnapshot? = null
var idChange: String = ""
}
lateinit var binding: FragSquadChatBinding
private lateinit var ctx: ActHome
private var subscribeDetail: ListenerRegistration? = null
private var subscribe: ListenerRegistration? = null
private lateinit var repository: SquadMessageFlow
private lateinit var chatViewModel: ChatViewModel
private var placeholderItem: SquadMessageModel? = null
private var msgId: String = ""
private var loader: Loader? = null
private var from: Int = 1
private var loaded = false
private var filesUploadedCount = 0
private var uploadedFilesReference = ""
private val LOUVRE_REQUEST_CODE = 1020
private var mSelection: MutableList<Uri?> = mutableListOf()
private lateinit var giphyDialog: GiphyDialogFragment
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
if (loaded) return binding.root
loaded = true
binding = FragSquadChatBinding.inflate(inflater, container, false)
ctx = requireContext() as ActHome
Giphy.configure(ctx, "bj87yysRtVcTfGw0bfmSX7kh4PvS6HyK")
ctx.changeUi(this)
positionListener = CompleteListener {
position =
(binding.rv.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
}
setup()
requireActivity().window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
val settings = GPHSettings()
settings.mediaTypeConfig =
arrayOf(GPHContentType.gif)
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
binding.spinny.load(Constant.SPINNY_LOADING, enableCache = true)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecyclerView()
getData()
}
private fun setupRecyclerView() {
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.itemAnimator = null
binding.rv.setHasFixedSize(true)
binding.rv.setItemViewCacheSize(100)
(binding.rv.layoutManager as LinearLayoutManager).apply {
stackFromEnd = true
}
squadChatAdapter = SquadChatAdapter(ctx, this, data, replyListsner, scrpolklListener)
binding.rv.adapter = squadChatAdapter
}
private lateinit var squadChatAdapter: SquadChatAdapter
private var tagged: SquadMemberDetail? = null
private fun setup() {
if (arguments == null) {
ctx.backPress()
return
}
val eventValues = HashMap<String, Any>()
eventValues["customer_user_id"] =
Preference.preferenceInstance.readString(USERNAME)
eventValues["datetime"] = Calendar.getInstance().time.toString()
AppsFlyerLib.getInstance().logEvent(ctx, "Squad_Chat", eventValues)
loader = showLoader()
squadId = requireArguments().getString("id") ?: ""
squadName = requireArguments().getString("name") ?: ""
binding.title.text = squadName
binding.back.setOnClickListener(click)
binding.info.setOnClickListener(click)
binding.gallery.setOnClickListener(click)
binding.recordVideo.setOnClickListener(click)
binding.recordAudio.setOnClickListener(click)
binding.gif.setOnClickListener(click)
binding.send.setOnClickListener(click)
binding.photos.setOnClickListener {
FragPhoto.squadId = squadId
FragPhoto.title = squadName
FragPhoto.type = if (event) 2 else 1
ctx.navigateTo(FragPhoto(), true)
}
if (arguments?.getBoolean("forSpinny") == true) {
binding.input.setText("@spinny ")
binding.input.setSelection("@spinny ".length)
binding.input.requestFocus()
postDelayed({
binding.input.post {
(ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager)
.showSoftInput(binding.input, 0)
}
}, 1000)
}
binding.input.addTextChangedListener {
val words = it.toString().split(" ")
val memberrs = squad?.membersDetail?.filter { it1 -> it1.status == "accepted" }?.toMutableList() ?: mutableListOf()
if (preferenceInstance.readString(USERNAME) == "spinnrsupport" || preferenceInstance.readString(USERNAME) == squad?.username)
memberrs.add(SquadMemberDetail("", "everyone", "", ""))
memberrs.add(SquadMemberDetail("c1e77c0d-02e7-400f-a761-eb55e3978d51", "spinny", "", ""))
val members = mutableListOf<SquadMemberDetail>()
var targetWord = ""
for (word in words) {
members.clear()
val regex = Regex("^.*${Regex.escape(word)}", RegexOption.IGNORE_CASE)
if (word.startsWith("@") && word.count { a -> a == '@' } == 1 && (regex.matches(binding.input.text) || memberrs.filter { a -> "@${a.username}" == word.lowercase() }.size != 1)) {
targetWord = word
members.addAll(memberrs.filter { it1 -> it1.username.contains(word.replace("@", ""), true)}.toMutableList())
break
}
}
members.sortBy { member -> member.username }
binding.users.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE
binding.users.adapter = TagUserAdapter(ctx, members) { it1 ->
val tag = it1.tag as SquadMemberDetail
var text = ""
for (a in words.indices) {
text += if (targetWord == words[a])
"@${tag.username} "
else
words[a] + " "
}
binding.input.setText(text.trim() + " ")
binding.input.setSelection(binding.input.text.length)
}
}
source = binding.ll
target = binding.iv
}
override fun onPause() {
ANotification.squadSquadId = ""
super.onPause()
}
private var utcTime: Long = 0L
override fun onResume() {
Thread {
utcTime = App.getUtcTime() - System.currentTimeMillis()
}.start()
ANotification.squadSquadId = squadId
Blur.init(ctx)
super.onResume()
}
private var position = -1;
private val data = mutableListOf<SquadMessageModel>()
private var mediaFiles = mutableListOf<MediaFile?>()
private var isLoadMore = false
private var isLastMessageReached = false
private lateinit var linearLayoutManager: LinearLayoutManager
private var lastItemPosition = -1
private fun getData() {
repository = SquadMessageFlow(
"${
if (event) "spinnr-events-chat" else
"spinnr-squads"
}/${squadId}/messages/"
)
chatViewModel = ChatViewModel(repository)
lifecycleScope.launch(Dispatchers.IO) {
chatViewModel.retrieveMessages().collect { querySnapshot ->
if (binding.input.isKeyboardVisible && binding.input.isFocused)
showSoftKeyboard(binding.input, ctx)
if (querySnapshot == null) return@collect
var newData = mutableListOf<SquadMessageModel>()
querySnapshot.forEach {
try {
val message = it.toObject(SquadMessageModel::class.java)
if (message.createdAt.toString().length == 10) {
message.createdAt *= 1000
}
newData.add(message)
} catch (e: Exception) {
try {
Firebase.firestore.document("${
if (event) "spinnr-events-chat" else
"spinnr-squads"
}/${squadId}/messages/${it.id}")
.delete()
} catch (e: Exception) {
Log.e("423u5", "onScrolled: $e")
}
Log.i("423u5", ">>> ${it.data}")
e.printStackTrace()
}
}
newData.sortBy { msg -> msg.createdAt }
try {
if (lastVisible == null)
throw NullPointerException()
val index = newData.indexOfFirst { a -> a.documentID == lastVisible?.id }
newData = newData.subList(index, newData.size)
} catch (e: Exception) {
try {
newData = newData.subList(min(newData.size - 1, newData.size - 25), newData.size)
} catch (e:Exception){}
}
try {
if (newData.isNotEmpty())
Firebase.firestore.document("${if (event) "spinnr-events-chat" else "spinnr-squads"}/${squadId}/")
.update("lastMessage", newData.last())
} catch (e: Exception) {
Log.w("TAG", "getData: $e")
}
withContext(Dispatchers.Main) {
try{
for (a in 0 until data.size) {
val item = newData.find { z -> z.documentID == data[a].documentID }
if (item == null) {
data.removeAt(a)
squadChatAdapter.notifyItemRemoved(a)
break
}
}
for (a in 0 until newData.size) {
val item = data.find { z -> z.documentID == newData[a].documentID }
if (item == null) {
data.add(a, newData[a])
squadChatAdapter.notifyItemInserted(a)
binding.rv.post { binding.rv.scrollToPosition(data.size - 1) }
} else if (item != newData[a]) {
data[a] = newData[a]
squadChatAdapter.notifyItemChanged(a)
}
}
} catch (e: Exception){}}
if (querySnapshot.size() > 0)
lastVisible = querySnapshot.documents[querySnapshot.size() - 1]
}
}
binding.rv.addOnScrollListener(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) {
isLoadMore = true
lifecycleScope.launch(Dispatchers.IO) {
chatViewModel.retrieveMessages(lastVisible)
.collectLatest { newQuerySnapshot ->
if (newQuerySnapshot == null)
return@collectLatest
if (newQuerySnapshot.documents.isNotEmpty()) {
if (newQuerySnapshot.documents[newQuerySnapshot.size() - 1].id != lastVisible?.id) {
val newDatam = mutableListOf<SquadMessageModel>()
newQuerySnapshot.forEach { item ->
newDatam.add(
0,
item.toObject(SquadMessageModel::class.java)
) // Add new messages at the beginning
}
newDatam.forEach { msg -> if (msg.createdAt.toString().length == 10) msg.createdAt *= 1000 }
withContext(Dispatchers.Main) {
data.addAll(newDatam)
data.sortBy { msg -> msg.createdAt }
squadChatAdapter.notifyItemRangeInserted(0, newQuerySnapshot.documents.size)
}
isLoadMore = false
if (newQuerySnapshot.documents.isNotEmpty())
lastVisible =
newQuerySnapshot.documents[newQuerySnapshot.size() - 1]
if (newQuerySnapshot.documents.size < 25) {
isLastMessageReached = true
}
}
}
}
}
}
}
}
})
}
override fun onDestroy() {
event = false
Blur.destroy()
subscribeDetail?.remove()
subscribe?.remove()
super.onDestroy()
}
var linkTitle: String? = null
var linkDescription: String? = null
var linkThumb: String? = null
var editMessage: SquadMessageModel? = null
private fun sendMessage(
value: String,
type: String,
thumb: String = "",
spinnyMessage: SquadMessageModel? = null
) {
if (App.netNotConnected()) {
showSnackBar(getStr(R.string.no_internet))
return
}
if (editMessage != null) {
if (type == "text")
editMessage?.let {
it.isEdited = true;it.text = value;
it.linkTitle = linkTitle
it.linkDescription = linkDescription
it.linkThumb = linkThumb
linkTitle = null
linkDescription = null
linkThumb = null
idChange = it.documentID
Firebase.firestore.document("${if (event) "spinnr-events-chat" else "spinnr-squads"}/$squadId/messages/${it.documentID}")
.set(it)
.addOnFailureListener { e -> showSnackBar("${e.localizedMessage}") }
}
editMessage = null
binding.input.setText("")
return
}
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 = spinnyMessage ?: SquadMessageModel(
msgId,
preferenceInstance.readString(GUID),
preferenceInstance.readString(USERNAME),
type,
time1 + utcTime,
tag = null
)
message.documentID = msgId
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(if (event) "Event_Chat_Reply" else "Squad_Reply")
}
when (message.type) {
"text" -> {
message.text = value
if (data.size == 0)
App.appsFlyerEvent(if (event) "Event_Chat" else "Squad_Chat")
}
"video" -> {
message.content = value
message.thumb = thumb
App.appsFlyerEvent(if (event) "Event_Chat_Video" else "Squad_Video")
}
"gif" -> {
message.text = value
linkThumb = value
message.linkThumb = value
}
"photo" -> {
message.content = value
App.appsFlyerEvent(if (event) "Event_Chat_Photo" else "Squad_Photo")
}
"groupMedia" -> {
message.mediaFiles = mediaFiles
filesUploadedCount = 0
App.appsFlyerEvent("Chat_Media")
}
"audio" -> {
message.content = value
App.appsFlyerEvent(if (event) "Event_Chat_Audio" else "Squad_Audio")
}
}
message.linkTitle = linkTitle
message.linkDescription = linkDescription
message.linkThumb = linkThumb
linkTitle = null
linkDescription = null
linkThumb = null
binding.input.text.clear()
replyMsg = null
binding.replyClose.performClick()
if (type == "video") {
placeholderItem = message
}
lifecycleScope.launch {
idChange = msgId
chatViewModel.sendMessage(
"${
if (event) "spinnr-events-chat" else
"spinnr-squads"
}/$squadId/messages/$msgId",
message,
"${if (event) "spinnr-events-chat" else "spinnr-squads"}/$squadId/"
)
}
squad?.membersList?.chunked(500)?.forEach { items ->
val db = Firebase.firestore
val batch = db.batch()
val documentRef =
db.document("${if (event) "spinnr-events-chat" else "spinnr-squads"}/$squadId/")
var count = 0
items.forEach { user ->
batch.update(documentRef, "unreadCount.$user", FieldValue.increment(1))
}
batch.commit()
.addOnSuccessListener {
Log.d(
"TAG",
"sendMessage: No PendingWrites ${System.currentTimeMillis()}"
)
}
}
if (tagged == null)
sendTopicPush(
"/topics/${if(event) "event" else "squad"}-$squadId",
if (event) 25 else 23,
"${squad?.detail?.name}",
"New chat message from ${preferenceInstance.readString(USERNAME)}!${
when (message.type) {
"audio" -> "\uD83D\uDD08"
"video" -> "\uD83C\uDFA5"
"photo" -> "\uD83D\uDCF8"
"groupMedia" -> "\uD83C\uDFA5"
else -> "\uD83D\uDCAC"
}
}",
squadId
)
if (type == "text")
sendTaggedNotification(value, message)
if (replyMsg != null && event)
sendTopicPush(
"/topics/event-${squadId}",
25,
"${squad?.detail?.name}",
"${preferenceInstance.readString(USERNAME)} replied to your message! ↪ ",
squadId
)
}
private var click = View.OnClickListener {
it.isEnabled = false
postDelayed({ it.isEnabled = true }, 250)
when (it) {
binding.back -> ctx.backPress(
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 ->
postDelayed({
loader = showLoader()
msgId = Firebase
.firestore
.collection("${if (event) "spinnr-events-chat" else "spinnr-squads"}/$squadId/messages/")
.document().id
val name = System.currentTimeMillis().toString()
uploadThumb(p, name)
}, 1000)
}.apply {
arguments = Bundle().apply {
putString("category", "chat")
}
}
ctx.navigateTo(frag)
} else if (a0 == 1) {
from = 4
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
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
launcher.launch(Intent(Intent.ACTION_GET_CONTENT).apply {
type = "video/*"
})
}
}
}.show(childFragmentManager, FragSquadChat::class.java.simpleName)
}
}
}
private val handler = Handler(Looper.getMainLooper())
private val launcher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it == null || it.resultCode == Activity.RESULT_CANCELED) return@registerForActivityResult
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(MediaMetadataRetriever.METADATA_KEY_DURATION)
?: return@Thread
retriever.release()
val frag = FragTrimVideo { a1, a2 ->
if (a1 == "result") {
postDelayed({
loader = showLoader()
msgId = Firebase
.firestore
.collection("${if (event) "spinnr-events-chat" else "spinnr-squads"}/$squadId/messages/")
.document().id
val name = System.currentTimeMillis().toString()
uploadThumb(a2, name)
}, 1000)
}
}
frag.arguments = Bundle().apply {
putString("category", "chat")
putString("path", path)
putLong("length", length.toLong())
}
handler.post {
loader?.dismiss()
ctx.navigateTo(frag, true)
}
}.start()
}
}
private fun uploadFile(path: String, type: String, name: String) {
val ref = "${preferenceInstance.readString(USERNAME)}/$name.${path.substringAfterLast(".")}"
Firebase
.storage
.getReference(ref)
.putStream(FileInputStream(path))
.addOnSuccessListener {
Firebase
.storage
.getReference(ref)
.downloadUrl.addOnSuccessListener { uri ->
Log.d("vi26", "uploadFile: upload successful")
if (type == "video") {
Firebase
.firestore
.document("${
if (event) "spinnr-events-chat" else
"spinnr-squads"
}/$squadId/messages/${placeholderItem?.documentID}")
.update("content", "$uri")
.addOnFailureListener { Log.e("vi26", "sendMessage: File ${it.message}") }
} else
sendMessage("$uri", type)
}
}
.addOnCompleteListener {
if (type != "video") loader?.dismiss()
}
.addOnProgressListener { taskSnapshot ->
val progress: Double = if (taskSnapshot.totalByteCount > 0) {
100.0 * taskSnapshot.bytesTransferred / taskSnapshot.totalByteCount
} else {
10.0 * (taskSnapshot.bytesTransferred / (1024 * 1024)) // Assuming 10% for each 1 MB
}
Log.d("vi26", "Upload is ${progress.toInt()}% done, $type <====> , ${taskSnapshot.bytesTransferred} <====> ${taskSnapshot.totalByteCount}")
}
}
private fun uploadThumb(path: String, thumbName: String) {
thread {
uploadFile(path, "video", thumbName)
}
val thumb = MediaMetadataRetriever().apply { setDataSource(path) }
val bitmap = thumb.getFrameAtTime(1, MediaMetadataRetriever.OPTION_CLOSEST)
val ref = "${preferenceInstance.readString(USERNAME)}/${thumbName}_VideoThumb.jpeg"
if (bitmap == null) {
sendMessage("", "video")
loader?.dismiss()
return
}
val thumbPath = "${ctx.filesDir}/temp.jpeg"
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, FileOutputStream(thumbPath))
sendMessage("", "video", thumb = "")
Firebase
.storage
.getReference(ref)
.putStream(FileInputStream(thumbPath))
.addOnSuccessListener {
Firebase
.storage
.getReference(ref)
.downloadUrl.addOnSuccessListener { uri ->
Firebase.firestore.document("${
if (event) "spinnr-events-chat" else
"spinnr-squads"
}/$squadId/messages/${placeholderItem?.documentID}")
.update("thumb", "$uri")
.addOnFailureListener { Log.e("vi26", "sendMessage: Thumb ${it.message}") }
}
}
.addOnCompleteListener {
loader?.dismiss()
}
}
private var updateData = true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment