Selaa lähdekoodia

Merge pull request #3406 from nextcloud/issue-3290-file-caption

File Captions 🏷️
Marcel Hibbe 1 vuosi sitten
vanhempi
commit
f6f9f35901

+ 55 - 2
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java

@@ -24,6 +24,8 @@
 
 package com.nextcloud.talk.adapters.messages;
 
+import android.text.Spanned;
+import android.util.TypedValue;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.ProgressBar;
@@ -33,6 +35,10 @@ import com.nextcloud.talk.R;
 import com.nextcloud.talk.databinding.ItemCustomIncomingPreviewMessageBinding;
 import com.nextcloud.talk.databinding.ReactionsInsideMessageBinding;
 import com.nextcloud.talk.models.json.chat.ChatMessage;
+import com.nextcloud.talk.utils.TextMatchers;
+
+import java.util.HashMap;
+import java.util.Objects;
 
 import androidx.annotation.NonNull;
 import androidx.core.content.ContextCompat;
@@ -49,7 +55,49 @@ public class IncomingPreviewMessageViewHolder extends PreviewMessageViewHolder {
     @Override
     public void onBind(@NonNull ChatMessage message) {
         super.onBind(message);
-
+        if(!message.isVoiceMessage()
+            && !Objects.equals(message.getMessage(), "{file}")
+        ) {
+            Spanned processedMessageText = null;
+            binding.incomingPreviewMessageBubble.setBackgroundResource(R.drawable.shape_grouped_incoming_message);
+            binding.incomingPreviewMessageBubble.setOnClickListener(null);
+            if (viewThemeUtils != null ) {
+                processedMessageText = messageUtils.enrichChatMessageText(
+                    binding.messageCaption.getContext(),
+                    message,
+                    true,
+                    viewThemeUtils);
+                viewThemeUtils.talk.themeIncomingMessageBubble(binding.incomingPreviewMessageBubble, true, false);
+            }
+
+            if (processedMessageText != null) {
+                processedMessageText = messageUtils.processMessageParameters(
+                    binding.messageCaption.getContext(),
+                    viewThemeUtils,
+                    processedMessageText,
+                    message,
+                    itemView);
+            }
+
+            float textSize = 0;
+            if (context != null) {
+                textSize = context.getResources().getDimension(R.dimen.chat_text_size);
+            }
+            HashMap<String, HashMap<String, String>> messageParameters = message.getMessageParameters();
+            if (
+                (messageParameters == null || messageParameters.size() <= 0) &&
+                    TextMatchers.isMessageWithSingleEmoticonOnly(message.getText())
+            ) {
+                textSize = (float) (textSize * IncomingTextMessageViewHolder.TEXT_SIZE_MULTIPLIER);
+                itemView.setSelected(true);
+            }
+            binding.messageCaption.setVisibility(View.VISIBLE);
+            binding.messageCaption.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+            binding.messageCaption.setText(processedMessageText);
+        } else {
+            binding.incomingPreviewMessageBubble.setBackground(null);
+            binding.messageCaption.setVisibility(View.GONE);
+        }
         binding.messageAuthor.setText(message.getActorDisplayName());
         binding.messageText.setTextColor(ContextCompat.getColor(binding.messageText.getContext(),
                                                                 R.color.no_emphasis_text));
@@ -63,6 +111,12 @@ public class IncomingPreviewMessageViewHolder extends PreviewMessageViewHolder {
         return binding.messageText;
     }
 
+    @NonNull
+    @Override
+    public EmojiTextView getMessageCaption() {
+        return binding.messageCaption;
+    }
+
     @Override
     public ProgressBar getProgressBar() {
         return binding.progressBar;
@@ -99,5 +153,4 @@ public class IncomingPreviewMessageViewHolder extends PreviewMessageViewHolder {
 
     @Override
     public ReactionsInsideMessageBinding getReactionsBinding(){ return binding.reactions; }
-
 }

+ 61 - 1
app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java

@@ -22,6 +22,9 @@
 
 package com.nextcloud.talk.adapters.messages;
 
+import android.text.Spanned;
+import android.util.Log;
+import android.util.TypedValue;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.ProgressBar;
@@ -31,7 +34,12 @@ import com.nextcloud.talk.R;
 import com.nextcloud.talk.databinding.ItemCustomOutcomingPreviewMessageBinding;
 import com.nextcloud.talk.databinding.ReactionsInsideMessageBinding;
 import com.nextcloud.talk.models.json.chat.ChatMessage;
+import com.nextcloud.talk.utils.TextMatchers;
 
+import java.util.HashMap;
+import java.util.Objects;
+
+import androidx.annotation.NonNull;
 import androidx.core.content.ContextCompat;
 import androidx.emoji2.widget.EmojiTextView;
 
@@ -45,8 +53,51 @@ public class OutcomingPreviewMessageViewHolder extends PreviewMessageViewHolder
     }
 
     @Override
-    public void onBind(ChatMessage message) {
+    public void onBind(@NonNull ChatMessage message) {
         super.onBind(message);
+        if(!message.isVoiceMessage()
+            && !Objects.equals(message.getMessage(), "{file}")
+        ) {
+            Spanned processedMessageText = null;
+            binding.outgoingPreviewMessageBubble.setBackgroundResource(R.drawable.shape_grouped_outcoming_message);
+            binding.outgoingPreviewMessageBubble.setOnClickListener(null);
+            if (viewThemeUtils != null) {
+                processedMessageText = messageUtils.enrichChatMessageText(
+                    binding.messageCaption.getContext(),
+                    message,
+                    false,
+                    viewThemeUtils);
+                viewThemeUtils.talk.themeOutgoingMessageBubble(binding.outgoingPreviewMessageBubble, true, false);
+            }
+
+            if (processedMessageText != null) {
+                processedMessageText = messageUtils.processMessageParameters(
+                    binding.messageCaption.getContext(),
+                    viewThemeUtils,
+                    processedMessageText,
+                    message,
+                    itemView);
+            }
+
+            float textSize = 0;
+            if (context != null) {
+                textSize = context.getResources().getDimension(R.dimen.chat_text_size);
+            }
+            HashMap<String, HashMap<String, String>> messageParameters = message.getMessageParameters();
+            if (
+                (messageParameters == null || messageParameters.size() <= 0) &&
+                    TextMatchers.isMessageWithSingleEmoticonOnly(message.getText())
+            ) {
+                textSize = (float)(textSize * IncomingTextMessageViewHolder.TEXT_SIZE_MULTIPLIER);
+                itemView.setSelected(true);
+            }
+            binding.messageCaption.setVisibility(View.VISIBLE);
+            binding.messageCaption.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+            binding.messageCaption.setText(processedMessageText);
+        } else {
+            binding.outgoingPreviewMessageBubble.setBackground(null);
+            binding.messageCaption.setVisibility(View.GONE);
+        }
 
         binding.messageText.setTextColor(ContextCompat.getColor(binding.messageText.getContext(),
                                                                 R.color.no_emphasis_text));
@@ -54,6 +105,7 @@ public class OutcomingPreviewMessageViewHolder extends PreviewMessageViewHolder
                                                                 R.color.no_emphasis_text));
     }
 
+    @NonNull
     @Override
     public EmojiTextView getMessageText() {
         return binding.messageText;
@@ -64,21 +116,25 @@ public class OutcomingPreviewMessageViewHolder extends PreviewMessageViewHolder
         return binding.progressBar;
     }
 
+    @NonNull
     @Override
     public View getPreviewContainer() {
         return binding.previewContainer;
     }
 
+    @NonNull
     @Override
     public MaterialCardView getPreviewContactContainer() {
         return binding.contactContainer;
     }
 
+    @NonNull
     @Override
     public ImageView getPreviewContactPhoto() {
         return binding.contactPhoto;
     }
 
+    @NonNull
     @Override
     public EmojiTextView getPreviewContactName() {
         return binding.contactName;
@@ -91,4 +147,8 @@ public class OutcomingPreviewMessageViewHolder extends PreviewMessageViewHolder
 
     @Override
     public ReactionsInsideMessageBinding getReactionsBinding() { return binding.reactions; }
+
+    @NonNull
+    @Override
+    public EmojiTextView getMessageCaption() { return binding.messageCaption; }
 }

+ 17 - 1
app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt

@@ -51,11 +51,13 @@ import com.nextcloud.talk.databinding.ReactionsInsideMessageBinding
 import com.nextcloud.talk.extensions.loadChangelogBotAvatar
 import com.nextcloud.talk.models.json.chat.ChatMessage
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
+import com.nextcloud.talk.users.UserManager
 import com.nextcloud.talk.utils.DateUtils
 import com.nextcloud.talk.utils.DisplayUtils
 import com.nextcloud.talk.utils.DrawableUtils.getDrawableResourceIdForMimeType
 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 io.reactivex.Single
 import io.reactivex.SingleObserver
@@ -80,6 +82,12 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
     @Inject
     lateinit var dateUtils: DateUtils
 
+    @Inject
+    lateinit var messageUtils: MessageUtils
+
+    @Inject
+    lateinit var userManager: UserManager
+
     @JvmField
     @Inject
     var okHttpClient: OkHttpClient? = null
@@ -111,6 +119,7 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
         if (message.getCalculateMessageType() === ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE) {
             fileViewerUtils = FileViewerUtils(context!!, message.activeUser!!)
             val fileName = message.selectedIndividualHashMap!![KEY_NAME]
+
             messageText.text = fileName
 
             if (message.activeUser != null &&
@@ -123,7 +132,7 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
                         ProgressUi(progressBar, messageText, image)
                     )
                 }
-                clickView!!.setOnLongClickListener { l: View? ->
+                clickView!!.setOnLongClickListener {
                     previewMessageInterface!!.onPreviewMessageLongClick(message)
                     true
                 }
@@ -188,6 +197,12 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
                 }
             }
         }
+
+        messageCaption.setOnClickListener(null)
+        messageCaption.setOnLongClickListener {
+            previewMessageInterface!!.onPreviewMessageLongClick(message)
+            true
+        }
     }
 
     private fun longClickOnReaction(chatMessage: ChatMessage) {
@@ -312,6 +327,7 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
     }
 
     abstract val messageText: EmojiTextView
+    abstract val messageCaption: EmojiTextView
     abstract val previewContainer: View
     abstract val previewContactContainer: MaterialCardView
     abstract val previewContactPhoto: ImageView

+ 60 - 65
app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt

@@ -84,7 +84,6 @@ import android.widget.RelativeLayout.LayoutParams
 import android.widget.SeekBar
 import android.widget.TextView
 import androidx.activity.OnBackPressedCallback
-import androidx.appcompat.app.AlertDialog
 import androidx.appcompat.view.ContextThemeWrapper
 import androidx.core.content.ContextCompat
 import androidx.core.content.FileProvider
@@ -113,7 +112,6 @@ import coil.target.Target
 import coil.transform.CircleCropTransformation
 import com.google.android.flexbox.FlexboxLayout
 import com.google.android.material.button.MaterialButton
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.android.common.ui.theme.utils.ColorRole
 import com.nextcloud.talk.BuildConfig
@@ -188,6 +186,7 @@ import com.nextcloud.talk.ui.StatusDrawable
 import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet
 import com.nextcloud.talk.ui.dialog.AttachmentDialog
 import com.nextcloud.talk.ui.dialog.DateTimePickerFragment
+import com.nextcloud.talk.ui.dialog.FileAttachmentPreviewFragment
 import com.nextcloud.talk.ui.dialog.MessageActionsDialog
 import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment
 import com.nextcloud.talk.ui.dialog.ShowReactionsDialog
@@ -2412,40 +2411,12 @@ class ChatActivity :
                         filenamesWithLineBreaks.append(filename).append("\n")
                     }
 
-                    val confirmationQuestion = when (filesToUpload.size) {
-                        1 -> context.resources?.getString(R.string.nc_upload_confirm_send_single)?.let {
-                            String.format(it, title.trim())
-                        }
-
-                        else -> context.resources?.getString(R.string.nc_upload_confirm_send_multiple)?.let {
-                            String.format(it, title.trim())
-                        }
-                    }
-
-                    binding.messageInputView.context?.let {
-                        val materialAlertDialogBuilder = MaterialAlertDialogBuilder(it)
-                            .setTitle(confirmationQuestion)
-                            .setMessage(filenamesWithLineBreaks.toString())
-                            .setPositiveButton(R.string.nc_yes) { _, _ ->
-                                if (permissionUtil.isFilesPermissionGranted()) {
-                                    uploadFiles(filesToUpload)
-                                } else {
-                                    UploadAndShareFilesWorker.requestStoragePermission(this)
-                                }
-                            }
-                            .setNegativeButton(R.string.nc_no) { _, _ ->
-                                // unused atm
-                            }
-
-                        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(it, materialAlertDialogBuilder)
-
-                        val dialog = materialAlertDialogBuilder.show()
-
-                        viewThemeUtils.platform.colorTextButtons(
-                            dialog.getButton(AlertDialog.BUTTON_POSITIVE),
-                            dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
-                        )
-                    }
+                    val newFragment: DialogFragment = FileAttachmentPreviewFragment.newInstance(
+                        filenamesWithLineBreaks.toString(),
+                        filesToUpload,
+                        this::uploadFiles
+                    )
+                    newFragment.show(supportFragmentManager, FileAttachmentPreviewFragment.TAG)
                 } catch (e: IllegalStateException) {
                     context.resources?.getString(R.string.nc_upload_failed)?.let {
                         Snackbar.make(
@@ -2507,7 +2478,19 @@ class ChatActivity :
                         }
 
                         if (permissionUtil.isFilesPermissionGranted()) {
-                            uploadFiles(filesToUpload)
+                            val filenamesWithLineBreaks = StringBuilder("\n")
+
+                            for (file in filesToUpload) {
+                                val filename = FileUtils.getFileName(Uri.parse(file), context)
+                                filenamesWithLineBreaks.append(filename).append("\n")
+                            }
+
+                            val newFragment: DialogFragment = FileAttachmentPreviewFragment.newInstance(
+                                filenamesWithLineBreaks.toString(),
+                                filesToUpload,
+                                this::uploadFiles
+                            )
+                            newFragment.show(supportFragmentManager, FileAttachmentPreviewFragment.TAG)
                         } else {
                             UploadAndShareFilesWorker.requestStoragePermission(this)
                         }
@@ -2631,13 +2614,17 @@ class ChatActivity :
         }
     }
 
-    private fun uploadFiles(files: MutableList<String>) {
-        for (file in files) {
-            uploadFile(file, false)
+    private fun uploadFiles(files: MutableList<String>, caption: String = "") {
+        for (i in 0 until files.size) {
+            if (i == files.size - 1) {
+                uploadFile(files[i], false, caption)
+            } else {
+                uploadFile(files[i], false)
+            }
         }
     }
 
-    private fun uploadFile(fileUri: String, isVoiceMessage: Boolean) {
+    private fun uploadFile(fileUri: String, isVoiceMessage: Boolean, caption: String = "") {
         var metaData = ""
 
         if (!participantPermissions.hasChatPermission()) {
@@ -2649,6 +2636,10 @@ class ChatActivity :
             metaData = VOICE_MESSAGE_META_DATA
         }
 
+        if (caption != "") {
+            metaData = "{\"caption\":\"$caption\"}"
+        }
+
         try {
             require(fileUri.isNotEmpty())
             UploadAndShareFilesWorker.upload(
@@ -3211,30 +3202,7 @@ class ChatActivity :
                                 Integer.parseInt(it)
                             }
 
-                            try {
-                                val mostRecentCallSystemMessage = adapter?.items?.first {
-                                    it.item is ChatMessage &&
-                                        (it.item as ChatMessage).systemMessageType in
-                                        listOf(
-                                            ChatMessage.SystemMessageType.CALL_STARTED,
-                                            ChatMessage.SystemMessageType.CALL_JOINED,
-                                            ChatMessage.SystemMessageType.CALL_LEFT,
-                                            ChatMessage.SystemMessageType.CALL_ENDED,
-                                            ChatMessage.SystemMessageType.CALL_TRIED,
-                                            ChatMessage.SystemMessageType.CALL_ENDED_EVERYONE,
-                                            ChatMessage.SystemMessageType.CALL_MISSED
-                                        )
-                                }?.item
-
-                                if (mostRecentCallSystemMessage != null) {
-                                    processMostRecentMessage(
-                                        mostRecentCallSystemMessage as ChatMessage,
-                                        chatMessageList
-                                    )
-                                }
-                            } catch (e: java.util.NoSuchElementException) {
-                                Log.d(TAG, "No System messages found $e")
-                            }
+                            processCallStartedMessages(chatMessageList)
 
                             updateReadStatusOfAllMessages(newXChatLastCommonRead)
                             adapter?.notifyDataSetChanged()
@@ -3269,6 +3237,33 @@ class ChatActivity :
             })
     }
 
+    private fun processCallStartedMessages(chatMessageList: List<ChatMessage>) {
+        try {
+            val mostRecentCallSystemMessage = adapter?.items?.first {
+                it.item is ChatMessage &&
+                    (it.item as ChatMessage).systemMessageType in
+                    listOf(
+                        ChatMessage.SystemMessageType.CALL_STARTED,
+                        ChatMessage.SystemMessageType.CALL_JOINED,
+                        ChatMessage.SystemMessageType.CALL_LEFT,
+                        ChatMessage.SystemMessageType.CALL_ENDED,
+                        ChatMessage.SystemMessageType.CALL_TRIED,
+                        ChatMessage.SystemMessageType.CALL_ENDED_EVERYONE,
+                        ChatMessage.SystemMessageType.CALL_MISSED
+                    )
+            }?.item
+
+            if (mostRecentCallSystemMessage != null) {
+                processMostRecentMessage(
+                    mostRecentCallSystemMessage as ChatMessage,
+                    chatMessageList
+                )
+            }
+        } catch (e: NoSuchElementException) {
+            Log.d(TAG, "No System messages found $e")
+        }
+    }
+
     private fun setupFieldsForPullChatMessages(
         lookIntoFuture: Boolean,
         xChatLastCommonRead: Int?,

+ 1 - 0
app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt

@@ -285,6 +285,7 @@ data class ChatMessage(
             ""
         }
     }
+
     val lastMessageDisplayText: String
         get() {
             if (getCalculateMessageType() == MessageType.REGULAR_TEXT_MESSAGE ||

+ 2 - 0
app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt

@@ -138,6 +138,8 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe
                 is RemoteFileBrowserItemsViewModel.FinishState -> {
                     finishWithResult(state.selectedPaths)
                 }
+
+                else -> {}
             }
         }
 

+ 100 - 0
app/src/main/java/com/nextcloud/talk/ui/dialog/FileAttachmentPreviewFragment.kt

@@ -0,0 +1,100 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Julius Linus
+ * Copyright (C) 2023 Julius Linus <julius.linus@nextcloud.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.nextcloud.talk.ui.dialog
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.DialogFragment
+import autodagger.AutoInjector
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.nextcloud.talk.R
+import com.nextcloud.talk.application.NextcloudTalkApplication
+import com.nextcloud.talk.databinding.DialogFileAttachmentPreviewBinding
+import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
+import com.nextcloud.talk.ui.theme.ViewThemeUtils
+import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
+import javax.inject.Inject
+
+@AutoInjector(NextcloudTalkApplication::class)
+class FileAttachmentPreviewFragment(
+    filenames: String,
+    filesToUpload: MutableList<String>,
+    functionToCall: (files: MutableList<String>, caption: String) -> Unit
+) : DialogFragment() {
+    private val files = filenames
+    private val filesList = filesToUpload
+    private val uploadFiles = functionToCall
+    lateinit var binding: DialogFileAttachmentPreviewBinding
+
+    @Inject
+    lateinit var permissionUtil: PlatformPermissionUtil
+
+    @Inject
+    lateinit var viewThemeUtils: ViewThemeUtils
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        binding = DialogFileAttachmentPreviewBinding.inflate(LayoutInflater.from(context))
+        return MaterialAlertDialogBuilder(requireContext()).setView(binding.root).create()
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
+        setUpViews()
+        setUpListeners()
+        return inflater.inflate(R.layout.dialog_file_attachment_preview, container, false)
+    }
+
+    private fun setUpViews() {
+        binding.dialogFileAttachmentPreviewFilenames.text = files
+        viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(binding.buttonClose)
+        viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(binding.buttonSend)
+        viewThemeUtils.platform.colorViewBackground(binding.root)
+        viewThemeUtils.material.colorTextInputLayout(binding.dialogFileAttachmentPreviewLayout)
+    }
+
+    private fun setUpListeners() {
+        binding.buttonClose.setOnClickListener {
+            dismiss()
+        }
+
+        binding.buttonSend.setOnClickListener {
+            if (permissionUtil.isFilesPermissionGranted()) {
+                val caption: String = binding.dialogFileAttachmentPreviewCaption.text.toString()
+                uploadFiles(filesList, caption)
+            } else {
+                UploadAndShareFilesWorker.requestStoragePermission(requireActivity())
+            }
+            dismiss()
+        }
+    }
+
+    companion object {
+        @JvmStatic
+        fun newInstance(
+            filenames: String,
+            filesToUpload: MutableList<String>,
+            functionToCall: (files: MutableList<String>, caption: String) -> Unit
+        ) =
+            FileAttachmentPreviewFragment(filenames, filesToUpload, functionToCall)
+        val TAG: String = FilterConversationFragment::class.java.simpleName
+    }
+}

+ 102 - 0
app/src/main/res/layout/dialog_file_attachment_preview.xml

@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Nextcloud Talk application
+  ~
+  ~ @author Julius Linus
+  ~ Copyright (C) 2023 Julius Linus <julius.linus@nextcloud.com>
+  ~
+  ~ This program is free software: you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation, either version 3 of the License, or
+  ~ at your option) any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    tools:background="@color/white"
+    tools:visibility="visible">
+
+    <com.google.android.material.textview.MaterialTextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/standard_margin"
+        android:text="@string/nc_add_file"
+        android:textSize="@dimen/md_title_textsize" />
+
+    <com.google.android.material.divider.MaterialDivider
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="100dp">
+
+        <com.google.android.material.textview.MaterialTextView
+            android:id="@+id/dialog_file_attachment_preview_filenames"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/standard_margin"
+            android:textSize="@dimen/headline_text_size"
+            tools:text="a.png\nb.png\nc.png"/>
+
+    </ScrollView>
+
+    <com.google.android.material.textfield.TextInputLayout
+        android:id="@+id/dialog_file_attachment_preview_layout"
+        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/standard_margin">
+
+        <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/dialog_file_attachment_preview_caption"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="@string/nc_caption"
+            android:maxLines="3"
+            tools:text="a.png\nb.png\nc.png"/>
+
+    </com.google.android.material.textfield.TextInputLayout>
+
+    <com.google.android.material.divider.MaterialDivider
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/standard_half_margin"
+        android:gravity="end"
+        android:orientation="horizontal"
+        android:paddingStart="@dimen/dialog_padding"
+        android:paddingEnd="@dimen/dialog_padding"
+        android:paddingBottom="@dimen/dialog_padding_top_bottom">
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/button_close"
+            style="@style/Button.Borderless"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/min_size_clickable_area"
+            android:text="@string/nc_no" />
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/button_send"
+            style="@style/Button.Borderless"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/min_size_clickable_area"
+            android:text="@string/nc_yes" />
+
+    </LinearLayout>
+</LinearLayout>

+ 18 - 2
app/src/main/res/layout/item_custom_incoming_preview_message.xml

@@ -41,6 +41,7 @@
         android:contentDescription="@string/avatar" />
 
     <com.google.android.flexbox.FlexboxLayout
+        android:id="@+id/incoming_preview_message_bubble"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="8dp"
@@ -69,8 +70,6 @@
             android:id="@+id/preview_container"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginStart="2dp"
-            android:layout_marginEnd="2dp"
             android:adjustViewBounds="true"
             app:layout_alignSelf="flex_start"
             app:layout_flexGrow="1"
@@ -163,6 +162,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="2dp"
+            android:paddingHorizontal="@dimen/standard_half_padding"
             android:alpha="0.6"
             android:autoLink="none"
             android:textAlignment="viewStart"
@@ -180,6 +180,7 @@
             android:id="@id/messageTime"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:paddingHorizontal="@dimen/standard_half_padding"
             android:layout_alignParentEnd="true"
             android:layout_marginStart="8dp"
             android:layout_marginEnd="2dp"
@@ -190,6 +191,21 @@
             tools:ignore="TextContrastCheck"
             tools:text="12:38" />
 
+        <androidx.emoji2.widget.EmojiTextView
+            android:id="@+id/messageCaption"
+            android:padding="@dimen/standard_half_padding"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|center"
+            android:layout_marginBottom="4dp"
+            android:textColor="@color/high_emphasis_text"
+            android:textIsSelectable="false"
+            android:textSize="@dimen/chat_text_size"
+            android:visibility="gone"
+            tools:visibility="visible"
+            tools:ignore="TextContrastCheck"
+            tools:text="This is my caption" />
+
         <include
             android:id="@+id/reactions"
             layout="@layout/reactions_inside_message" />

+ 20 - 4
app/src/main/res/layout/item_custom_outcoming_preview_message.xml

@@ -33,6 +33,7 @@
     android:layout_marginBottom="2dp">
 
     <com.google.android.flexbox.FlexboxLayout
+        android:id="@+id/outgoing_preview_message_bubble"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_alignParentEnd="true"
@@ -48,8 +49,6 @@
             android:id="@+id/preview_container"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginStart="2dp"
-            android:layout_marginEnd="2dp"
             android:adjustViewBounds="true"
             app:layout_alignSelf="flex_start"
             app:layout_flexGrow="1"
@@ -81,7 +80,8 @@
             app:layout_alignSelf="flex_start"
             app:layout_flexGrow="1"
             app:layout_wrapBefore="true"
-            app:strokeWidth="0dp">
+            app:strokeWidth="0dp"
+            tools:visibility="gone">
 
             <LinearLayout
                 android:layout_width="wrap_content"
@@ -139,7 +139,7 @@
             android:id="@id/messageText"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginStart="2dp"
+            android:paddingHorizontal="@dimen/standard_half_padding"
             android:alpha="0.6"
             android:autoLink="none"
             android:textColor="@color/no_emphasis_text"
@@ -156,6 +156,7 @@
             android:id="@id/messageTime"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:paddingHorizontal="@dimen/standard_half_padding"
             android:layout_alignParentEnd="true"
             android:layout_marginStart="8dp"
             android:layout_marginEnd="2dp"
@@ -165,6 +166,21 @@
             app:layout_alignSelf="center"
             tools:text="12:34" />
 
+        <androidx.emoji2.widget.EmojiTextView
+            android:id="@+id/messageCaption"
+            android:padding="@dimen/standard_half_padding"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|center"
+            android:layout_marginBottom="4dp"
+            android:textColor="@color/high_emphasis_text"
+            android:textIsSelectable="false"
+            android:textSize="@dimen/chat_text_size"
+            android:visibility="gone"
+            tools:visibility="visible"
+            tools:ignore="TextContrastCheck"
+            tools:text="This is my caption" />
+
         <include
             android:id="@+id/reactions"
             layout="@layout/reactions_inside_message" />

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -721,4 +721,5 @@ How to translate with transifex:
     <string name="audio_call">Audio Call</string>
     <string name="started_a_call">started a call</string>
     <string name="nc_settings_phone_book_integration_phone_number_dialog_429">Error 429 Too Many Requests</string>
+    <string name="nc_caption">Caption</string>
 </resources>