Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- Add `markMessagesPending` flag to the `Config` model indicating whether pending messages are enabled for the channel. [#5784](https://github.com/GetStream/stream-chat-android/pull/5784)

### ⚠️ Changed
- Deprecate `AttachmentType.LINK` because it is not officially supported as a separate attachment type. [#5848](https://github.com/GetStream/stream-chat-android/pull/5848)
- Deprecate `Attachment.isLink()` because it is not officially supported as a separate attachment type. [#5848](https://github.com/GetStream/stream-chat-android/pull/5848)

### ❌ Removed

Expand Down Expand Up @@ -61,6 +63,7 @@
## stream-chat-android-ui-components
### 🐞 Fixed
- Filter out Poll Options differing only on whitespaces. [#5913](https://github.com/GetStream/stream-chat-android/pull/5913)
- Fix `MessageReplyStyle.linkBackgroundColorMine`, `MessageReplyStyle.linkBackgroundColorTheirs`, `MessageReplyStyle.linkStyleMine` and `MessageReplyStyle.linkStyleTheirs` customizations not applied to the `MessageReplyView`. [#5848](https://github.com/GetStream/stream-chat-android/pull/5848)

### ⬆️ Improved

Expand Down
2 changes: 2 additions & 0 deletions DEPRECATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ This document lists deprecated constructs in the SDK, with their expected time

| API / Feature | Deprecated (warning) | Deprecated (error) | Removed | Notes |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------|-----------------------|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `AttachmentType.LINK` constant | 2025.09.08 ⌛ | | | This property has been deprecated. The attachment of type 'LINK' is not officially supported, and Attachment.type can never have a value equal to "link". |
| `Attachment.isLink()` method | 2025.09.08 ⌛ | | | This method has been deprecated. The attachment of type 'LINK' is not officially supported, and Attachment.type can never have a value equal to "link". |
| `StreamColors.linkBackground` property | 2025.08.12 ⌛ | | | This property has been deprecated. Please use `MessageTheme.linkBackgroundColor` instead. |
| `SendReactionListener.onSendReactionPrecondition(currentUser: User?, reaction: Reaction)` method | 2025.07.30 ⌛ | | | This method has been deprecated. Please use `SendReactionListener.onSendReactionPrecondition(cid: String?, currentUser: User?, reaction: Reaction) instead`. |
| `NotificationConfig.ignorePushMessagesWhenUserOnline` property | 2025.07.11 ⌛ | | | This property has been deprecated. Please use `NotificationConfig.ignorePushMessageWhenUserOnline(type: String) instead`. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public fun Attachment.isImgur(): Boolean = type == AttachmentType.IMGUR
/**
* @return If the attachment type is link.
*/
@Deprecated(
message = "The attachment of type 'LINK' is not officially supported, and the Attachment.type can never have " +
"a value == 'link'. This method will always return false.",
level = DeprecationLevel.WARNING,
)
public fun Attachment.isLink(): Boolean = type == AttachmentType.LINK

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,41 @@ package io.getstream.chat.android.models
* Represents types of attachments.
*/
public object AttachmentType {
/** Image attachment type. */
public const val IMAGE: String = "image"

/** Imgur attachment type. */
public const val IMGUR: String = "imgur"

/** Giphy attachment type. */
public const val GIPHY: String = "giphy"

/** Video attachment type. */
public const val VIDEO: String = "video"

/** Audio attachment type. */
public const val AUDIO: String = "audio"

/** Product attachment type. */
public const val PRODUCT: String = "product"

/** File attachment type. */
public const val FILE: String = "file"

/**
* @deprecated The attachment of type 'LINK' is not officially supported, and the Attachment.type can never have a
* value == 'link'.
*/
@Deprecated(
message = "The attachment of type 'LINK' is not officially supported, and the Attachment.type can never have " +
"a value == 'link'",
level = DeprecationLevel.WARNING,
)
public const val LINK: String = "link"

/** Audio recording (voice message) attachment type. */
public const val AUDIO_RECORDING: String = "voiceRecording"

/** Unknown attachment type. */
public const val UNKNOWN: String = "unknown"
}
Original file line number Diff line number Diff line change
Expand Up @@ -521,9 +521,14 @@ public abstract class io/getstream/chat/android/ui/feature/channels/list/adapter
}

public final class io/getstream/chat/android/ui/feature/gallery/AttachmentActivity : androidx/appcompat/app/AppCompatActivity {
public static final field Companion Lio/getstream/chat/android/ui/feature/gallery/AttachmentActivity$Companion;
public fun <init> ()V
}

public final class io/getstream/chat/android/ui/feature/gallery/AttachmentActivity$Companion {
public final fun createIntent (Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
}

public final class io/getstream/chat/android/ui/feature/gallery/AttachmentGalleryActivity : androidx/appcompat/app/AppCompatActivity {
public static final field Companion Lio/getstream/chat/android/ui/feature/gallery/AttachmentGalleryActivity$Companion;
public fun <init> ()V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package io.getstream.chat.android.ui.feature.gallery

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
Expand Down Expand Up @@ -58,11 +60,15 @@ public class AttachmentActivity : AppCompatActivity() {
setupEdgeToEdge()
configUIs()

val type = intent.getStringExtra("type")
val url = intent.getStringExtra("url")
val type = intent.getStringExtra(EXTRA_TYPE)
val url = intent.getStringExtra(EXTRA_URL)
if (type.isNullOrEmpty() || url.isNullOrEmpty()) {
logger.e { "This file can't be displayed. TYPE or URL is missing." }
Toast.makeText(this, getString(R.string.stream_ui_message_list_attachment_display_error), Toast.LENGTH_SHORT).show()
Toast.makeText(
this,
getString(R.string.stream_ui_message_list_attachment_display_error),
Toast.LENGTH_SHORT,
).show()
return
}
showAttachment(type, url)
Expand Down Expand Up @@ -153,4 +159,24 @@ public class AttachmentActivity : AppCompatActivity() {
Toast.makeText(this@AttachmentActivity, error.toString(), Toast.LENGTH_SHORT).show()
}
}

public companion object {
private const val EXTRA_TYPE = "type"
private const val EXTRA_URL = "url"

/**
* Creates an Intent to start the AttachmentActivity.
*
* @param context The context to use for creating the Intent.
* @param type The type of the attachment (e.g., "giphy", etc.).
* @param url The URL of the attachment to display.
* @return An Intent to start the AttachmentActivity.
*/
public fun createIntent(context: Context, type: String?, url: String): Intent {
return Intent(context, AttachmentActivity::class.java).apply {
putExtra(EXTRA_TYPE, type)
putExtra(EXTRA_URL, url)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,21 @@

package io.getstream.chat.android.ui.feature.messages.list.adapter.view

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Paint
import android.util.AttributeSet
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.util.PatternsCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import com.google.android.material.shape.MaterialShapeDrawable
import io.getstream.chat.android.client.ChatClient
import io.getstream.chat.android.client.extensions.durationInMs
import io.getstream.chat.android.client.utils.attachment.isAudioRecording
import io.getstream.chat.android.client.utils.attachment.isLink
import io.getstream.chat.android.client.utils.attachment.isImage
import io.getstream.chat.android.client.utils.message.isDeleted
import io.getstream.chat.android.models.Attachment
import io.getstream.chat.android.models.Message
Expand Down Expand Up @@ -185,10 +187,25 @@ public class MessageReplyView : FrameLayout {
}
}

private fun isLink(message: Message) = message.attachments.run {
size == 1 && last().isLink()
/**
* Checks if the message contains a link, either as an enriched link (attachment) or as a plain text URL.
*/
private fun isLink(message: Message) = hasEnrichedLink(message) || hasLinkInText(message)

private fun hasEnrichedLink(message: Message) = message.attachments.run {
// Messages with enriched links have an image attachment with title_link or og_scrape_url set.
if (size == 1) {
val lastAttachment = last()
lastAttachment.isImage() && (lastAttachment.titleLink != null || lastAttachment.ogUrl != null)
} else {
false
}
}

@SuppressLint("RestrictedApi")
private fun hasLinkInText(message: Message) =
PatternsCompat.AUTOLINK_WEB_URL.matcher(message.text).matches()

private fun setAttachmentImage(message: Message) {
if (ChatUI.quotedAttachmentFactoryManager.canHandle(message)) {
binding.attachmentContainer.isVisible = true
Expand Down Expand Up @@ -227,9 +244,7 @@ public class MessageReplyView : FrameLayout {
displayedText
}
} else {
if (attachment.isLink()) {
attachment.titleLink ?: attachment.ogUrl
} else if (attachment.isAudioRecording()) {
if (attachment.isAudioRecording()) {
context.getString(R.string.stream_ui_message_audio_reply_info)
} else {
attachment.title ?: attachment.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ public class UnsupportedAttachmentFactory : AttachmentFactory {
AttachmentType.VIDEO,
AttachmentType.AUDIO,
AttachmentType.FILE,
AttachmentType.LINK,
AttachmentType.AUDIO_RECORDING,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,7 @@ public open class AttachmentDestination(
return
}

val intent = Intent(context, AttachmentActivity::class.java).apply {
putExtra("type", type)
putExtra("url", url)
}
val intent = AttachmentActivity.createIntent(context, type, url)
start(intent)
}

Expand Down
Loading