Skip to content

Commit c60210c

Browse files
committedFeb 14, 2024
Changes to message view
1 parent 173ab92 commit c60210c

File tree

6 files changed

+192
-24
lines changed

6 files changed

+192
-24
lines changed
 

‎app/build.gradle

+3
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,9 @@ dependencies {
307307
//swipe to refresh
308308
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01"
309309

310+
//link preview
311+
implementation 'org.jsoup:jsoup:1.17.2'
312+
310313

311314
implementation 'androidx.core:core-ktx:1.10.1'
312315
implementation 'androidx.appcompat:appcompat:1.6.1'

‎app/src/main/java/com/ncs/o2/UI/Tasks/TaskPage/Chat/Adapters/ChatAdapter.kt

+6-4
Original file line numberDiff line numberDiff line change
@@ -598,17 +598,19 @@ class ChatAdapter(
598598
}
599599

600600
fun convertLinksToHtml(text: String): String {
601-
val pattern = Regex("""\b(?:https?|ftp):\/\/\S+""")
601+
val pattern = Regex("""\b(?:https?|www)\:\/\/\S+|\b\S+\.\S+""")
602602
val replacedText = pattern.replace(text) { matchResult ->
603603
val url = matchResult.value
604-
"""<a href="$url" target="_blank">$url</a>"""
604+
if (url.startsWith("www.") || url.startsWith("http")) {
605+
"""<a href="$url" target="_blank">$url</a>"""
606+
} else {
607+
"""<a href="http://$url" target="_blank">$url</a>"""
608+
}
605609
}
606610
return replacedText
607611
}
608612

609613

610-
611-
612614
private fun setMessageReplyView(message: Message, binding: ChatMessageReplyItemBinding) {
613615
val links=convertLinksToHtml(message.content)
614616
val spannedMessage = processSpan(markwon.render(markwon.parse(links)))

‎app/src/main/java/com/ncs/o2/UI/Tasks/TaskPage/Chat/TaskChatFragment.kt

+115-14
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,17 @@ import android.graphics.Bitmap
1616
import android.graphics.BitmapFactory
1717
import android.net.Uri
1818
import android.os.Bundle
19-
import android.os.Environment
2019
import android.provider.MediaStore
2120
import android.text.Editable
2221
import android.text.TextWatcher
2322
import android.util.Log
24-
import android.util.Patterns
25-
import android.util.TypedValue
2623
import android.view.LayoutInflater
2724
import android.view.View
2825
import android.view.ViewGroup
2926
import android.view.inputmethod.InputMethodManager
3027
import android.widget.Toast
3128
import androidx.appcompat.app.AlertDialog
29+
import androidx.compose.ui.text.capitalize
3230
import androidx.core.app.ActivityCompat
3331
import androidx.core.content.ContextCompat
3432
import androidx.core.content.FileProvider
@@ -59,6 +57,7 @@ import com.ncs.o2.Domain.Utility.ExtensionsUtil.appendTextAtCursor
5957
import com.ncs.o2.Domain.Utility.ExtensionsUtil.appendTextAtCursorMiddleCursor
6058
import com.ncs.o2.Domain.Utility.ExtensionsUtil.gone
6159
import com.ncs.o2.Domain.Utility.ExtensionsUtil.isNull
60+
import com.ncs.o2.Domain.Utility.ExtensionsUtil.load
6261
import com.ncs.o2.Domain.Utility.ExtensionsUtil.performHapticFeedback
6362
import com.ncs.o2.Domain.Utility.ExtensionsUtil.setOnClickThrottleBounceListener
6463
import com.ncs.o2.Domain.Utility.ExtensionsUtil.slideDownAndGone
@@ -102,11 +101,12 @@ import kotlinx.coroutines.CoroutineScope
102101
import kotlinx.coroutines.Dispatchers
103102
import kotlinx.coroutines.launch
104103
import kotlinx.coroutines.withContext
104+
import org.jsoup.Jsoup
105105
import timber.log.Timber
106106
import java.io.File
107+
import java.io.IOException
107108
import java.io.InputStream
108109
import java.util.concurrent.Executors
109-
import java.util.regex.Matcher
110110
import javax.inject.Inject
111111

112112

@@ -144,6 +144,7 @@ class TaskChatFragment : Fragment(), ChatAdapter.onChatDoubleClickListner,
144144
private val CAMERA_PERMISSION_CODE = 101
145145
private var currentPhotoPath: String? = null
146146
private var replyingTo: String? = null
147+
private val data: Metadata?= null
147148

148149

149150
var contributors: MutableList<String> = mutableListOf()
@@ -214,6 +215,12 @@ class TaskChatFragment : Fragment(), ChatAdapter.onChatDoubleClickListner,
214215
override fun afterTextChanged(s: Editable?) {
215216
val input = s.toString()
216217

218+
// val links = extractLinks(input)
219+
// processLinks(links)
220+
// if (links.isEmpty() || links=="") {
221+
// processLinks("")
222+
// }
223+
217224
val lastAtSymbolIndex = input.lastIndexOf('@')
218225

219226
if (lastAtSymbolIndex != -1 && lastAtSymbolIndex == input.length - 1) {
@@ -320,22 +327,116 @@ class TaskChatFragment : Fragment(), ChatAdapter.onChatDoubleClickListner,
320327
"` `", type = 2
321328
)
322329
}
330+
331+
binding.inputBox.closeLinkPreview.setOnClickThrottleBounceListener {
332+
binding.inputBox.linkPreviewSender.gone()
333+
}
323334
}
335+
private fun extractLinks(input: String) : String{
336+
val urlPattern = "(https?|ftp)://[^\\s/$.?#].[^\\s]*".toRegex()
337+
val matches = urlPattern.findAll(input)
338+
val firstMatch = matches.firstOrNull()
339+
if (firstMatch != null) {
340+
val link = firstMatch.value
341+
return link
342+
} else {
343+
return ""
344+
}
345+
}
346+
private fun processLinks(link: String) {
347+
if (link.isNotEmpty() || link!="") {
348+
binding.inputBox.linkPreviewSender.visible()
349+
CoroutineScope(Dispatchers.IO).launch {
324350

351+
// "Title" to title,
352+
// "Description" to description,
353+
// "Open Graph Title" to ogTitle,
354+
// "Open Graph Description" to ogDescription,
355+
// "Open Graph Image" to ogImage
356+
357+
val metadata = fetchMetadata(link)
358+
withContext(Dispatchers.Main) {
359+
if (metadata.isNull) {
360+
binding.inputBox.linkPreviewSender.gone()
361+
} else {
362+
if (metadata?.getValue("Type")=="normal") {
363+
binding.inputBox.linkPreviewTitle.text = metadata?.getValue("Title")
364+
365+
binding.inputBox.linkPreviewDesc.text = if (metadata?.getValue("Open Graph Description").isNullOrEmpty()) link else metadata.getValue("Open Graph Description")
366+
367+
}
368+
else{
369+
binding.inputBox.linkPreviewTitle.text = metadata?.getValue("Title")
370+
binding.inputBox.linkPreviewDesc.text = link
371+
}
372+
}
373+
}
374+
}
325375

326-
private fun extractLinks(text: String): List<String> {
327-
val links = mutableListOf<String>()
328-
val pattern = Patterns.WEB_URL
329-
val matcher: Matcher = pattern.matcher(text)
330-
while (matcher.find()) {
331-
val url = matcher.group()
332-
links.add(url)
376+
} else {
377+
binding.inputBox.linkPreviewSender.gone()
333378
}
334-
return links
335379
}
336380

337-
private fun processLink(link: String) {
338-
381+
suspend fun fetchMetadata(url: String): Map<String, String>? {
382+
return try {
383+
val document = Jsoup.connect(url).get()
384+
385+
val title = document.title()
386+
val description = document.select("meta[name=description]").attr("content")
387+
val ogTitle = document.select("meta[property=og:title]").attr("content")
388+
val ogDescription = document.select("meta[property=og:description]").attr("content")
389+
val ogImage = document.select("meta[property=og:image]").attr("content")
390+
391+
mapOf(
392+
"Title" to title,
393+
"Description" to description,
394+
"Open Graph Title" to ogTitle,
395+
"Open Graph Description" to ogDescription,
396+
"Open Graph Image" to ogImage,
397+
"Type" to "normal"
398+
)
399+
} catch (e: IOException) {
400+
Log.d("metadatafetch",e.message.toString())
401+
val list=extractPartsFromUrl(e.message!!)
402+
if (list.isNullOrEmpty()) {
403+
util.showSnackbar(binding.root, "Unable to fetch the link details", 2000)
404+
null
405+
}
406+
else{
407+
if (list.size==4){
408+
mapOf(
409+
"Title" to "${list[0].capitalize()} #${list[3].dropLast(1)}-${list[1]}",
410+
"Description" to "",
411+
"Open Graph Title" to "",
412+
"Open Graph Description" to "",
413+
"Open Graph Image" to "",
414+
"Type" to "o2"
415+
)
416+
}
417+
else {
418+
mapOf(
419+
"Title" to "${list[0].capitalize()} - ${list[1].capitalize().dropLast(1)}",
420+
"Description" to "",
421+
"Open Graph Title" to "",
422+
"Open Graph Description" to "",
423+
"Open Graph Image" to "",
424+
"Type" to "o2"
425+
)
426+
}
427+
}
428+
}
429+
}
430+
fun extractPartsFromUrl(inputString: String): List<String> {
431+
val urlPattern = Regex("https://oxgn\\.page\\.link/([^/]+)/([^/]+)(?:/([^/]+)(?:/([^/\\]]+))?)?")
432+
val matchResult = urlPattern.find(inputString)
433+
return matchResult?.let {
434+
val (part1, part2, part3, part4) = matchResult.destructured
435+
when {
436+
part3.isEmpty() -> listOf(part1, part2)
437+
else -> listOf(part1, part2, part3, part4).filter { it.isNotEmpty() }
438+
}
439+
} ?: emptyList()
339440
}
340441

341442
private fun pasteFromClipboard() {

‎app/src/main/java/com/ncs/o2/UI/Teams/Chat/ChannelChatAdapter.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -505,10 +505,14 @@ class ChannelChatAdapter(
505505
}
506506

507507
fun convertLinksToHtml(text: String): String {
508-
val pattern = Regex("""\b(?:https?|ftp):\/\/\S+""")
508+
val pattern = Regex("""\b(?:https?|www)\:\/\/\S+|\b\S+\.\S+""")
509509
val replacedText = pattern.replace(text) { matchResult ->
510510
val url = matchResult.value
511-
"""<a href="$url" target="_blank">$url</a>"""
511+
if (url.startsWith("www.") || url.startsWith("http")) {
512+
"""<a href="$url" target="_blank">$url</a>"""
513+
} else {
514+
"""<a href="http://$url" target="_blank">$url</a>"""
515+
}
512516
}
513517
return replacedText
514518
}

‎app/src/main/res/layout/inputbox.xml

+62-3
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,70 @@
88
android:background="@color/primary_bg"
99
android:orientation="vertical">
1010

11-
<include
11+
<androidx.appcompat.widget.LinearLayoutCompat
12+
android:layout_width="match_parent"
13+
android:orientation="horizontal"
1214
android:id="@+id/linkPreviewSender"
15+
android:weightSum="10"
1316
android:visibility="gone"
14-
layout="@layout/link_preview_layout_sender"
15-
/>
17+
android:background="@color/primary_bg"
18+
android:layout_height="wrap_content">
19+
<androidx.cardview.widget.CardView
20+
android:layout_width="50dp"
21+
android:layout_gravity="center"
22+
app:cardBackgroundColor="@color/primary_bg"
23+
android:layout_height="wrap_content"
24+
app:cardCornerRadius="10dp">
25+
<ImageView
26+
android:layout_width="wrap_content"
27+
android:layout_gravity="center"
28+
android:id="@+id/linkPreviewImage"
29+
android:layout_height="wrap_content"
30+
android:src="@drawable/baseline_add_link_24"/>
31+
</androidx.cardview.widget.CardView>
32+
33+
<LinearLayout
34+
android:layout_width="match_parent"
35+
android:layout_weight="10"
36+
android:layout_height="wrap_content"
37+
android:orientation="vertical"
38+
>
39+
<TextView
40+
android:layout_width="match_parent"
41+
android:maxLines="1"
42+
android:layout_height="match_parent"
43+
android:layout_marginTop="10dp"
44+
android:ellipsize="end"
45+
android:id="@+id/linkPreviewTitle"
46+
android:layout_marginStart="10dp"
47+
android:textColor="@color/primary"
48+
android:fontFamily="@font/sf_pro_display_semibold"
49+
android:text="Getting link info..."/>
50+
51+
<TextView
52+
android:layout_width="wrap_content"
53+
android:layout_height="match_parent"
54+
android:ellipsize="end"
55+
android:maxLines="3"
56+
android:id="@+id/linkPreviewDesc"
57+
android:layout_marginBottom="10dp"
58+
android:layout_marginStart="10dp"
59+
android:textColor="@color/better_white"
60+
android:fontFamily="@font/sf_pro_display_light"
61+
android:text="Please wait..."/>
62+
63+
</LinearLayout>
64+
65+
<ImageView
66+
android:layout_width="wrap_content"
67+
android:layout_gravity="center"
68+
android:id="@+id/closeLinkPreview"
69+
android:layout_marginEnd="16dp"
70+
android:layout_marginStart="16dp"
71+
android:layout_height="wrap_content"
72+
android:src="@drawable/baseline_close_24"/>
73+
74+
</androidx.appcompat.widget.LinearLayoutCompat>
1675

1776

1877
<LinearLayout

‎gradle.properties

-1
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,3 @@ kotlin.code.style=official
2222
# thereby reducing the size of the R class for that library
2323
android.nonTransitiveRClass=true
2424
android.enableJetifier=true
25-
org.gradle.unsafe.configuration-cache=true

0 commit comments

Comments
 (0)
Please sign in to comment.