diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java index 507944ce693..d9a83cb0c8b 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java +++ b/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.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,48 @@ public IncomingPreviewMessageViewHolder(View itemView, Object payload) { @Override public void onBind(@NonNull ChatMessage message) { super.onBind(message); - + if(!message.isVoiceMessage() + && !Objects.equals(message.getMessage(), "{file}") + ) { + Spanned processedMessageText = null; + if (viewThemeUtils != null ) { + processedMessageText = messageUtils.enrichChatMessageText( + binding.messageCaption.getContext(), + message, + true, + viewThemeUtils); + } + + 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> 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); + binding.incomingPreviewMessageBubble.setBackgroundResource(R.drawable.shape_grouped_incoming_message); + viewThemeUtils.talk.themeIncomingMessageBubble(binding.incomingPreviewMessageBubble, true, false); + } 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 +110,12 @@ public EmojiTextView getMessageText() { return binding.messageText; } + @NonNull + @Override + public EmojiTextView getMessageCaption() { + return binding.messageCaption; + } + @Override public ProgressBar getProgressBar() { return binding.progressBar; @@ -99,5 +152,4 @@ public ProgressBar getPreviewContactProgressBar() { @Override public ReactionsInsideMessageBinding getReactionsBinding(){ return binding.reactions; } - } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java index c6f74642711..690104db226 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java @@ -22,6 +22,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; @@ -31,7 +33,12 @@ 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 +52,50 @@ public OutcomingPreviewMessageViewHolder(View itemView) { } @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; + if (viewThemeUtils != null) { + processedMessageText = messageUtils.enrichChatMessageText( + binding.messageCaption.getContext(), + message, + false, + viewThemeUtils); + } + + 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> 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); + binding.outgoingPreviewMessageBubble.setBackgroundResource(R.drawable.shape_grouped_outcoming_message); + viewThemeUtils.talk.themeOutgoingMessageBubble(binding.outgoingPreviewMessageBubble, true, false); + } 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 +103,7 @@ public void onBind(ChatMessage message) { R.color.no_emphasis_text)); } + @NonNull @Override public EmojiTextView getMessageText() { return binding.messageText; @@ -64,21 +114,25 @@ public ProgressBar getProgressBar() { 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 +145,8 @@ public ProgressBar getPreviewContactProgressBar() { @Override public ReactionsInsideMessageBinding getReactionsBinding() { return binding.reactions; } + + @NonNull + @Override + public EmojiTextView getMessageCaption() { return binding.messageCaption; } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt index edc73786664..9f72102af3a 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt +++ b/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 && @@ -312,6 +321,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 diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index a6af87bdd4e..32223d561c6 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/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) { - for (file in files) { - uploadFile(file, false) + private fun uploadFiles(files: MutableList, 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) { + 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?, diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt index e05fea28b01..bb68e697d57 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt +++ b/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 || diff --git a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt index ec0ee64fd6b..dde05d64824 100644 --- a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt +++ b/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 -> {} } } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/FileAttachmentPreviewFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/FileAttachmentPreviewFragment.kt new file mode 100644 index 00000000000..e4f004b95b6 --- /dev/null +++ b/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 + * + * 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 . + */ +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, + functionToCall: (files: MutableList, 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, + functionToCall: (files: MutableList, caption: String) -> Unit + ) = + FileAttachmentPreviewFragment(filenames, filesToUpload, functionToCall) + val TAG: String = FilterConversationFragment::class.java.simpleName + } +} diff --git a/app/src/main/res/layout/dialog_file_attachment_preview.xml b/app/src/main/res/layout/dialog_file_attachment_preview.xml new file mode 100644 index 00000000000..91bc4148cec --- /dev/null +++ b/app/src/main/res/layout/dialog_file_attachment_preview.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_custom_incoming_preview_message.xml b/app/src/main/res/layout/item_custom_incoming_preview_message.xml index da6061cd0cb..76f5af9c98d 100644 --- a/app/src/main/res/layout/item_custom_incoming_preview_message.xml +++ b/app/src/main/res/layout/item_custom_incoming_preview_message.xml @@ -41,6 +41,7 @@ android:contentDescription="@string/avatar" /> + + diff --git a/app/src/main/res/layout/item_custom_outcoming_preview_message.xml b/app/src/main/res/layout/item_custom_outcoming_preview_message.xml index 6d2dd6b7cc6..1380a2dfbb7 100644 --- a/app/src/main/res/layout/item_custom_outcoming_preview_message.xml +++ b/app/src/main/res/layout/item_custom_outcoming_preview_message.xml @@ -33,6 +33,7 @@ android:layout_marginBottom="2dp"> + app:strokeWidth="0dp" + tools:visibility="gone"> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dd345ce04a1..f80f15f292b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -721,4 +721,5 @@ How to translate with transifex: Audio Call started a call Error 429 Too Many Requests + Caption