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
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ import com.nextcloud.talk.utils.FileViewerUtils
import com.nextcloud.talk.utils.FileViewerUtils.ProgressUi
import com.nextcloud.talk.utils.message.MessageUtils
import com.stfalcon.chatkit.messages.MessageHolders.IncomingImageMessageViewHolder
import coil.load
import com.nextcloud.talk.utils.ApiUtils
import io.reactivex.Single
import io.reactivex.SingleObserver
import io.reactivex.disposables.Disposable
Expand Down Expand Up @@ -100,6 +102,22 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
super.onBind(message)
image.minimumHeight = DisplayUtils.convertDpToPixel(MIN_IMAGE_HEIGHT, context!!).toInt()

// Reset state for view recycling
image.adjustViewBounds = false
messageText.visibility = View.VISIBLE

// Check if image is GIF and load animated image
if (message.imageUrl != null && message.shouldAutoplayGif()) {
image.adjustViewBounds = true
image.load(message.imageUrl) {
size(coil.size.Size.ORIGINAL)
addHeader(
"Authorization",
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
)
}
}

if (message.lastEditTimestamp != 0L && !message.isDeleted) {
time.text = dateUtils.getLocalTimeStringFromTimestamp(message.lastEditTimestamp!!)
messageEditIndicator.visibility = View.VISIBLE
Expand All @@ -110,14 +128,18 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :

viewThemeUtils!!.platform.colorCircularProgressBar(progressBar!!, ColorRole.PRIMARY)
clickView = image
messageText.visibility = View.VISIBLE
if (message.getCalculateMessageType() === ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE) {
val chatActivity = commonMessageInterface as ChatActivity
fileViewerUtils = FileViewerUtils(chatActivity, message.activeUser!!)
val fileName = message.selectedIndividualHashMap!![KEY_NAME]

messageText.text = fileName

// hide filename display for GIF images
if (message.shouldAutoplayGif()) {
messageText.visibility = View.INVISIBLE
}

if (message.activeUser != null &&
message.activeUser!!.username != null &&
message.activeUser!!.baseUrl != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.models.json.converters.EnumSystemMessageTypeConverter
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.Mimetype
import com.stfalcon.chatkit.commons.models.IUser
import com.stfalcon.chatkit.commons.models.MessageContentType
import java.security.MessageDigest
Expand Down Expand Up @@ -236,6 +237,22 @@ data class ChatMessage(
return false
}

/**
* @return true if message is a GIF file, and size is below max-gif-size in the config
*/
fun shouldAutoplayGif(): Boolean {
val mimetype = selectedIndividualHashMap?.get("mimetype")
val capabilities = activeUser?.capabilities?.spreedCapability
val fileSize = selectedIndividualHashMap?.get("size")?.toLongOrNull()

return if (mimetype != Mimetype.IMAGE_GIF || activeUser == null || capabilities == null) {
false
} else {
val maxGifSize = CapabilitiesUtil.getMaxGifSize(capabilities)
fileSize == null || fileSize in 1..maxGifSize
}
}

@Suppress("Detekt.NestedBlockDepth")
override fun getImageUrl(): String? {
if (messageParameters != null && messageParameters!!.size > 0) {
Expand All @@ -246,6 +263,16 @@ data class ChatMessage(
selectedIndividualHashMap = individualHashMap
if (!isVoiceMessage) {
if (activeUser != null && activeUser!!.baseUrl != null) {
val path = individualHashMap["path"]
if (path != null && activeUser!!.username != null) {
if (shouldAutoplayGif()) {
return ApiUtils.getUrlForFileDownload(
activeUser!!.baseUrl!!,
activeUser!!.username!!,
path
)
}
}
return ApiUtils.getUrlForFilePreviewWithFileId(
activeUser!!.baseUrl!!,
individualHashMap["id"]!!,
Expand Down
21 changes: 16 additions & 5 deletions app/src/main/java/com/nextcloud/talk/contacts/ImageRequest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@email.com>
* SPDX-FileCopyrightText: 2026 Jens Zalzala <anakin78z@gmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

Expand All @@ -26,13 +27,23 @@ fun loadImage(imageUri: String?, context: Context, errorPlaceholderImage: Int):
}

@Composable
fun load(imageUri: String?, context: Context, errorPlaceholderImage: Int): ImageRequest {
val imageRequest = ImageRequest.Builder(context)
fun load(
imageUri: String?,
context: Context,
errorPlaceholderImage: Int,
animated: Boolean = false,
authHeader: String? = null
): ImageRequest {
val builder = ImageRequest.Builder(context)
.data(imageUri)
.size(Size.ORIGINAL)
.transformations(RoundedCornersTransformation())
.error(errorPlaceholderImage)
.placeholder(errorPlaceholderImage)
.build()
return imageRequest
if (!animated) {
builder.transformations(RoundedCornersTransformation())
}
if (authHeader != null) {
builder.addHeader("Authorization", authHeader)
}
return builder.build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ fun ImageView.loadThumbnail(url: String, user: User): io.reactivex.disposables.D
requestBuilder.placeholder(LayerDrawable(layers))

if (url.startsWith(user.baseUrl!!) &&
(url.contains("index.php/core/preview") || url.contains("/avatar/"))
(url.contains("index.php/core/preview") || url.contains("/avatar/") || url.contains("remote.php/dav/"))
) {
requestBuilder.addHeader(
"Authorization",
Expand All @@ -259,7 +259,7 @@ fun ImageView.loadImage(url: String, user: User, placeholder: Drawable? = null):
.transformations(RoundedCornersTransformation(ROUNDING_PIXEL, ROUNDING_PIXEL, ROUNDING_PIXEL, ROUNDING_PIXEL))

if (url.startsWith(user.baseUrl!!) &&
(url.contains("index.php/core/preview") || url.contains("/avatar/"))
(url.contains("index.php/core/preview") || url.contains("/avatar/") || url.contains("remote.php/dav/"))
) {
requestBuilder.addHeader(
"Authorization",
Expand Down
15 changes: 14 additions & 1 deletion app/src/main/java/com/nextcloud/talk/ui/ComposeChatAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.models.json.opengraph.Reference
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.DrawableUtils.getDrawableResourceIdForMimeType
Expand Down Expand Up @@ -809,7 +810,19 @@ class ComposeChatAdapter(
val imageUri = message.imageUrl
val mimetype = message.selectedIndividualHashMap!![KEY_MIMETYPE]
val drawableResourceId = getDrawableResourceIdForMimeType(mimetype)
val loadedImage = load(imageUri, LocalContext.current, drawableResourceId)
val isGif = message.shouldAutoplayGif()
val authHeader = if (isGif) {
ApiUtils.getCredentials(currentUser.username, currentUser.token)
} else {
null
}
val loadedImage = load(
imageUri,
LocalContext.current,
drawableResourceId,
animated = isGif,
authHeader = authHeader
)

AsyncImage(
model = loadedImage,
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,21 @@ object CapabilitiesUtil {
return DEFAULT_CHAT_SIZE
}

fun getMaxGifSize(spreedCapabilities: SpreedCapability): Long {
if (spreedCapabilities.config?.containsKey("previews") == true) {
val previewsConfigHashMap = spreedCapabilities.config!!["previews"]
if (previewsConfigHashMap?.containsKey("max-gif-size") == true) {
val maxGifSize = previewsConfigHashMap["max-gif-size"]!!.toString().toLong()
return if (maxGifSize > 0) {
maxGifSize
} else {
DEFAULT_MAX_GIF_SIZE
}
}
}
return DEFAULT_MAX_GIF_SIZE
}

fun conversationDescriptionLength(spreedCapabilities: SpreedCapability): Int {
if (spreedCapabilities.config?.containsKey("conversations") == true) {
val map: Map<String, Any>? = spreedCapabilities.config!!["conversations"]
Expand Down Expand Up @@ -331,6 +346,7 @@ object CapabilitiesUtil {

private val TAG = CapabilitiesUtil::class.java.simpleName
const val DEFAULT_CHAT_SIZE = 1000
const val DEFAULT_MAX_GIF_SIZE = 3_145_728L
const val RECORDING_CONSENT_NOT_REQUIRED = 0
const val RECORDING_CONSENT_REQUIRED = 1
const val RECORDING_CONSENT_DEPEND_ON_CONVERSATION = 2
Expand Down
Loading