From 45ea5c356e193a55969b50e1cde901298d16c43b Mon Sep 17 00:00:00 2001 From: Valere <valeref@matrix.org> Date: Thu, 23 May 2019 16:44:51 +0200 Subject: [PATCH] WIP / edit message --- .../room/model/annotation/ReactionService.kt | 10 + .../api/session/room/send/SendService.kt | 1 + .../room/annotation/DefaultReactionService.kt | 20 ++ .../session/room/send/DefaultSendService.kt | 11 + .../room/send/LocalEchoEventFactory.kt | 25 ++ .../riotredesign/core/utils/AnimationUtils.kt | 39 +++ .../riotredesign/features/DebugActivity.kt | 12 + .../home/room/detail/RoomDetailActions.kt | 7 +- .../home/room/detail/RoomDetailFragment.kt | 96 +++++++- .../home/room/detail/RoomDetailViewModel.kt | 231 +++++++++++++----- .../home/room/detail/RoomDetailViewState.kt | 19 +- .../timeline/action/MessageMenuViewModel.kt | 22 +- .../drawable-hdpi/ic_attach_file_white.png | Bin 394 -> 0 bytes .../main/res/drawable-hdpi/ic_send_white.png | Bin 335 -> 0 bytes .../drawable-mdpi/ic_attach_file_white.png | Bin 285 -> 0 bytes .../main/res/drawable-mdpi/ic_send_white.png | Bin 257 -> 0 bytes .../drawable-xhdpi/ic_attach_file_white.png | Bin 507 -> 0 bytes .../main/res/drawable-xhdpi/ic_send_white.png | Bin 423 -> 0 bytes .../drawable-xxhdpi/ic_attach_file_white.png | Bin 809 -> 0 bytes .../res/drawable-xxhdpi/ic_send_white.png | Bin 550 -> 0 bytes .../drawable-xxxhdpi/ic_attach_file_white.png | Bin 1059 -> 0 bytes .../res/drawable-xxxhdpi/ic_send_white.png | Bin 728 -> 0 bytes .../src/main/res/drawable/ic_add_reaction.xml | 54 ++++ .../src/main/res/drawable/ic_attachment.xml | 14 ++ .../src/main/res/drawable/ic_close_round.xml | 20 ++ .../drawable/{ic_smile.xml => ic_delete.xml} | 20 +- vector/src/main/res/drawable/ic_edit.xml | 8 +- vector/src/main/res/drawable/ic_send.xml | 14 ++ .../main/res/layout/adapter_item_action.xml | 8 +- ...constraint_set_composer_layout_compact.xml | 153 ++++++++++++ ...onstraint_set_composer_layout_expanded.xml | 156 ++++++++++++ .../main/res/layout/fragment_room_detail.xml | 55 +---- .../res/layout/include_composer_layout.xml | 123 ++++++++++ vector/src/main/res/values/attrs.xml | 1 + vector/src/main/res/values/colors_riot.xml | 1 + vector/src/main/res/values/theme_black.xml | 1 + vector/src/main/res/values/theme_dark.xml | 1 + vector/src/main/res/values/theme_light.xml | 1 + vector/src/main/res/values/theme_status.xml | 1 + 39 files changed, 978 insertions(+), 146 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotredesign/core/utils/AnimationUtils.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/DebugActivity.kt delete mode 100644 vector/src/main/res/drawable-hdpi/ic_attach_file_white.png delete mode 100644 vector/src/main/res/drawable-hdpi/ic_send_white.png delete mode 100644 vector/src/main/res/drawable-mdpi/ic_attach_file_white.png delete mode 100644 vector/src/main/res/drawable-mdpi/ic_send_white.png delete mode 100644 vector/src/main/res/drawable-xhdpi/ic_attach_file_white.png delete mode 100644 vector/src/main/res/drawable-xhdpi/ic_send_white.png delete mode 100644 vector/src/main/res/drawable-xxhdpi/ic_attach_file_white.png delete mode 100644 vector/src/main/res/drawable-xxhdpi/ic_send_white.png delete mode 100644 vector/src/main/res/drawable-xxxhdpi/ic_attach_file_white.png delete mode 100644 vector/src/main/res/drawable-xxxhdpi/ic_send_white.png create mode 100644 vector/src/main/res/drawable/ic_add_reaction.xml create mode 100644 vector/src/main/res/drawable/ic_attachment.xml create mode 100644 vector/src/main/res/drawable/ic_close_round.xml rename vector/src/main/res/drawable/{ic_smile.xml => ic_delete.xml} (51%) create mode 100644 vector/src/main/res/drawable/ic_send.xml create mode 100644 vector/src/main/res/layout/constraint_set_composer_layout_compact.xml create mode 100644 vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml create mode 100644 vector/src/main/res/layout/include_composer_layout.xml diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/annotation/ReactionService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/annotation/ReactionService.kt index ace61159..3f8fec00 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/annotation/ReactionService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/annotation/ReactionService.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.api.session.room.model.annotation import im.vector.matrix.android.api.util.Cancelable +//TODO rename in relationService? interface ReactionService { @@ -49,4 +50,13 @@ interface ReactionService { */ fun updateQuickReaction(reaction: String, oppositeReaction: String, targetEventId: String, myUserId: String) + + /** + * Edit a text message body. Limited to "m.text" contentType + * @param targetEventId The event to edit + * @param newBodyText The edited body + * @param compatibilityBodyText The text that will appear on clients that don't support yet edition + */ + fun editTextMessage(targetEventId: String, newBodyText: String, compatibilityBodyText: String = "* $newBodyText"): Cancelable + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt index 6852931c..551ab545 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt @@ -34,6 +34,7 @@ interface SendService { * @return a [Cancelable] */ fun sendTextMessage(text: String, msgType: String = MessageType.MSGTYPE_TEXT): Cancelable + fun sendFormattedTextMessage(text: String,formattedText: String): Cancelable /** * Method to send a media asynchronously. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/annotation/DefaultReactionService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/annotation/DefaultReactionService.kt index dbef5461..fdb75a8f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/annotation/DefaultReactionService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/annotation/DefaultReactionService.kt @@ -19,6 +19,7 @@ import androidx.work.* import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.model.annotation.ReactionService +import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.session.room.send.RedactEventWorker @@ -150,4 +151,23 @@ internal class DefaultReactionService(private val roomId: String, .setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS) .build() } + + override fun editTextMessage(targetEventId: String, newBodyText: String, compatibilityBodyText: String): Cancelable { + val event = eventFactory.createReplaceTextEvent(roomId, targetEventId, newBodyText, MessageType.MSGTYPE_TEXT, compatibilityBodyText) + val sendContentWorkerParams = SendEventWorker.Params(roomId, event) + val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) + + //TODO use relation API? + val workRequest = OneTimeWorkRequestBuilder<SendEventWorker>() + .setConstraints(WORK_CONSTRAINTS) + .setInputData(sendWorkData) + .setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS) + .build() + + WorkManager.getInstance() + .beginUniqueWork(buildWorkIdentifier(REACTION_WORK), ExistingWorkPolicy.APPEND, workRequest) + .enqueue() + return CancelableWork(workRequest.id) + + } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index 28ae63b8..1d6197a3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -60,6 +60,17 @@ internal class DefaultSendService(private val roomId: String, return CancelableWork(sendWork.id) } + override fun sendFormattedTextMessage(text: String, formattedText: String): Cancelable { + val event = eventFactory.createFormattedTextEvent(roomId, text, formattedText).also { + saveLocalEcho(it) + } + val sendWork = createSendEventWork(event) + WorkManager.getInstance() + .beginUniqueWork(buildWorkIdentifier(SEND_WORK), ExistingWorkPolicy.APPEND, sendWork) + .enqueue() + return CancelableWork(sendWork.id) + } + override fun sendMedias(attachments: List<ContentAttachmentData>): Cancelable { val cancelableBag = CancelableBag() attachments.forEach { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index a2076d3b..e1cc9c74 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -25,6 +25,7 @@ import im.vector.matrix.android.api.session.events.model.RelationType import im.vector.matrix.android.api.session.events.model.toContent import im.vector.matrix.android.api.session.room.model.annotation.ReactionContent import im.vector.matrix.android.api.session.room.model.annotation.ReactionInfo +import im.vector.matrix.android.api.session.room.model.annotation.RelationDefaultContent import im.vector.matrix.android.api.session.room.model.message.* import im.vector.matrix.android.internal.session.content.ThumbnailExtractor @@ -35,6 +36,30 @@ internal class LocalEchoEventFactory(private val credentials: Credentials) { return createEvent(roomId, content) } + fun createFormattedTextEvent(roomId: String, text: String, formattedText: String): Event { + val content = MessageTextContent( + type = MessageType.MSGTYPE_TEXT, + format = MessageType.FORMAT_MATRIX_HTML, + body = text, + formattedBody = formattedText + ) + return createEvent(roomId, content) + } + + + fun createReplaceTextEvent(roomId: String, targetEventId: String, newBodyText: String, msgType: String, compatibilityText: String): Event { + val content = MessageTextContent( + type = msgType, + body = compatibilityText, + relatesTo = RelationDefaultContent(RelationType.REPLACE, targetEventId), + newContent = MessageTextContent( + type = MessageType.MSGTYPE_TEXT, + body = newBodyText + ).toContent() + ) + return createEvent(roomId, content) + } + fun createMediaEvent(roomId: String, attachment: ContentAttachmentData): Event { return when (attachment.type) { ContentAttachmentData.Type.IMAGE -> createImageEvent(roomId, attachment) diff --git a/vector/src/main/java/im/vector/riotredesign/core/utils/AnimationUtils.kt b/vector/src/main/java/im/vector/riotredesign/core/utils/AnimationUtils.kt new file mode 100644 index 00000000..a773991d --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/core/utils/AnimationUtils.kt @@ -0,0 +1,39 @@ +package im.vector.riotredesign.core.utils + +import android.view.animation.OvershootInterpolator +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.transition.ChangeBounds +import androidx.transition.Transition +import androidx.transition.TransitionManager + + +inline fun ConstraintLayout.updateConstraintSet(@LayoutRes layoutId: Int, rootLayoutForAnimation: ConstraintLayout? = null, noinline onAnimationEnd: (() -> Unit)? = null) { + if (rootLayoutForAnimation != null) { + val transition = ChangeBounds() + transition.interpolator = OvershootInterpolator() + transition.addListener(object : Transition.TransitionListener { + override fun onTransitionResume(transition: Transition) { + } + + override fun onTransitionPause(transition: Transition) { + } + + override fun onTransitionCancel(transition: Transition) { + } + + override fun onTransitionStart(transition: Transition) { + } + + override fun onTransitionEnd(transition: Transition) { + onAnimationEnd?.invoke() + } + }) + TransitionManager.beginDelayedTransition(rootLayoutForAnimation, transition) + } + ConstraintSet().also { + it.clone(this@updateConstraintSet.context, layoutId) + it.applyTo(this@updateConstraintSet) + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/DebugActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/DebugActivity.kt new file mode 100644 index 00000000..00708d03 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/DebugActivity.kt @@ -0,0 +1,12 @@ +package im.vector.riotredesign.features + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle + +class DebugActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_debug) + } +} diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActions.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActions.kt index 7d6ffc04..190c37de 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActions.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActions.kt @@ -31,10 +31,13 @@ sealed class RoomDetailActions { data class SendReaction(val reaction: String, val targetEventId: String) : RoomDetailActions() data class RedactAction(val targetEventId: String, val reason: String? = "") : RoomDetailActions() data class UndoReaction(val targetEventId: String, val key: String, val reason: String? = "") : RoomDetailActions() - data class UpdateQuickReactAction(val targetEventId: String,val selectedReaction: String,val opposite: String) : RoomDetailActions() - data class ShowEditHistoryAction(val event: String,val editAggregatedSummary: EditAggregatedSummary) : RoomDetailActions() + data class UpdateQuickReactAction(val targetEventId: String, val selectedReaction: String, val opposite: String) : RoomDetailActions() + data class ShowEditHistoryAction(val event: String, val editAggregatedSummary: EditAggregatedSummary) : RoomDetailActions() object AcceptInvite : RoomDetailActions() object RejectInvite : RoomDetailActions() + data class EnterEditMode(val eventId: String) : RoomDetailActions() + data class EnterQuoteMode(val eventId: String) : RoomDetailActions() + } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt index 0278607a..0ae0dda5 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt @@ -32,14 +32,17 @@ import android.view.HapticFeedbackConstants import android.view.LayoutInflater import android.view.View import android.view.inputmethod.InputMethodManager +import android.widget.ImageButton import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AlertDialog +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import butterknife.BindView import com.airbnb.epoxy.EpoxyVisibilityTracker import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.fragmentViewModel @@ -53,6 +56,7 @@ import com.otaliastudios.autocomplete.Autocomplete import com.otaliastudios.autocomplete.AutocompleteCallback import com.otaliastudios.autocomplete.CharPolicy import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.message.* @@ -92,6 +96,7 @@ import im.vector.riotredesign.features.media.VideoMediaViewerActivity import im.vector.riotredesign.features.reactions.EmojiReactionPickerActivity import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_detail.* +import kotlinx.android.synthetic.main.include_composer_layout.* import org.koin.android.ext.android.inject import org.koin.android.scope.ext.android.bindScope import org.koin.android.scope.ext.android.getOrCreateScope @@ -165,6 +170,15 @@ class RoomDetailFragment : private lateinit var actionViewModel: ActionsHandler + @BindView(R.id.composer_related_message_sender) + lateinit var composerRelatedMessageTitle: TextView + @BindView(R.id.composer_related_message_preview) + lateinit var composerRelatedMessageContent: TextView + @BindView(R.id.composerLayout) + lateinit var composerLayout: ConstraintLayout + @BindView(R.id.rootConstraintLayout) + lateinit var rootConstraintLayout: ConstraintLayout + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) actionViewModel = ViewModelProviders.of(requireActivity()).get(ActionsHandler::class.java) @@ -187,6 +201,65 @@ class RoomDetailFragment : actionViewModel.actionCommandEvent.observe(this, Observer { handleActions(it) }) + + roomDetailViewModel.selectSubscribe(RoomDetailViewState::sendMode, RoomDetailViewState::selectedEvent, RoomDetailViewState::roomId) { mode, event, roomId -> + when (mode) { + SendMode.REGULAR -> { + val uid = session.sessionParams.credentials.userId + val meMember = session.getRoom(roomId)?.getRoomMember(uid) + AvatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composer_avatar_view) + composerLayout.updateConstraintSet(R.layout.constraint_set_composer_layout_compact, rootConstraintLayout) { + focusComposerAndShowKeyboard() + } + } + SendMode.EDIT, + SendMode.QUOTE -> { + if (event == null) { + //we should ignore? can this happen? + Timber.e("Enter edit mode with no event selected") + return@selectSubscribe + } + //switch to expanded bar + composerRelatedMessageTitle.text = event.senderName + composerRelatedMessageTitle.setTextColor( + ContextCompat.getColor(requireContext(), AvatarRenderer.getColorFromUserId(event.root.sender + ?: "")) + ) + + val messageContent: MessageContent? = + event.annotations?.editSummary?.aggregatedContent?.toModel() + ?: event.root.content.toModel() + val eventTextBody = messageContent?.body + composerRelatedMessageContent.text = eventTextBody + + + if (mode == SendMode.EDIT) { + composerEditText.setText(eventTextBody) + composer_related_message_action_image.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_edit)) + } else { + composerEditText.setText("") + composer_related_message_action_image.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_quote)) + } + + AvatarRenderer.render(event.senderAvatar, event.root.sender + ?: "", event.senderName, composer_avatar_view) + + composerEditText.setSelection(composerEditText.text.length) + composerLayout.updateConstraintSet(R.layout.constraint_set_composer_layout_expanded, rootConstraintLayout) { + focusComposerAndShowKeyboard() + } + + view?.findViewById<ImageButton>(R.id.composer_related_message_close)?.setOnClickListener { + + composerRelatedMessageTitle.text = "" + composerRelatedMessageContent.text = "" + composerEditText.setText("") + roomDetailViewModel.resetSendMode() + } + + } + } + } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -398,6 +471,11 @@ class RoomDetailFragment : if (summary?.membership == Membership.JOIN) { timelineEventController.setTimeline(state.timeline) inviteView.visibility = View.GONE + + val uid = session.sessionParams.credentials.userId + val meMember = session.getRoom(state.roomId)?.getRoomMember(uid) + AvatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composer_avatar_view) + } else if (summary?.membership == Membership.INVITE && inviter != null) { inviteView.visibility = View.VISIBLE inviteView.render(inviter, VectorInviteView.Mode.LARGE) @@ -601,6 +679,14 @@ class RoomDetailFragment : roomDetailViewModel.process(RoomDetailActions.UpdateQuickReactAction(eventId, clickedOn, opposite)) } } + MessageMenuViewModel.ACTION_EDIT -> { + val eventId = actionData.data.toString() ?: return@let + roomDetailViewModel.process(RoomDetailActions.EnterEditMode(eventId)) + } + MessageMenuViewModel.ACTION_QUOTE -> { + val eventId = actionData.data.toString() ?: return@let + roomDetailViewModel.process(RoomDetailActions.EnterQuoteMode(eventId)) + } else -> { Toast.makeText(context, "Action ${actionData.actionId} not implemented", Toast.LENGTH_LONG).show() } @@ -648,12 +734,16 @@ class RoomDetailFragment : // v.vibrate(100) // } // } - composerEditText.requestFocus() - val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager - imm?.showSoftInput(composerEditText, InputMethodManager.SHOW_FORCED) + focusComposerAndShowKeyboard() } } + private fun focusComposerAndShowKeyboard() { + composerEditText.requestFocus() + val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager + imm?.showSoftInput(composerEditText, InputMethodManager.SHOW_IMPLICIT) + } + fun showSnackWithMessage(message: String, duration: Int = Snackbar.LENGTH_SHORT) { val snack = Snackbar.make(view!!, message, Snackbar.LENGTH_SHORT) snack.view.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.notification_accent_color)) diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt index 44c2aa8f..f5664efc 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt @@ -16,6 +16,7 @@ package im.vector.riotredesign.features.home.room.detail +import android.text.TextUtils import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.airbnb.mvrx.MvRxViewModelFactory @@ -25,8 +26,11 @@ import com.jakewharton.rxrelay2.BehaviorRelay import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.content.ContentAttachmentData +import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.Membership +import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageType +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.rx.rx import im.vector.riotredesign.R import im.vector.riotredesign.core.platform.VectorViewModel @@ -36,11 +40,14 @@ import im.vector.riotredesign.features.command.ParsedCommand import im.vector.riotredesign.features.home.room.VisibleRoomStore import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDisplayableEvents import io.reactivex.rxkotlin.subscribeBy +import org.commonmark.parser.Parser +import org.commonmark.renderer.html.HtmlRenderer import org.koin.android.ext.android.get import java.text.SimpleDateFormat import java.util.* import java.util.concurrent.TimeUnit + class RoomDetailViewModel(initialState: RoomDetailViewState, private val session: Session, private val visibleRoomHolder: VisibleRoomStore @@ -87,9 +94,28 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, is RoomDetailActions.UndoReaction -> handleUndoReact(action) is RoomDetailActions.UpdateQuickReactAction -> handleUpdateQuickReaction(action) is RoomDetailActions.ShowEditHistoryAction -> handleShowEditHistoryReaction(action) + is RoomDetailActions.EnterEditMode -> handleEditAction(action) + is RoomDetailActions.EnterQuoteMode -> handleQuoteAction(action) } } + fun enterEditMode(event: TimelineEvent) { + setState { + copy( + sendMode = SendMode.EDIT, + selectedEvent = event + ) + } + } + + fun resetSendMode() { + setState { + copy( + sendMode = SendMode.REGULAR, + selectedEvent = null + ) + } + } private val _nonBlockingPopAlert = MutableLiveData<LiveEvent<Pair<Int, List<Any>>>>() val nonBlockingPopAlert: LiveData<LiveEvent<Pair<Int, List<Any>>>> @@ -103,71 +129,135 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, // PRIVATE METHODS ***************************************************************************** private fun handleSendMessage(action: RoomDetailActions.SendMessage) { - // Handle slash command - val slashCommandResult = CommandParser.parseSplashCommand(action.text) + withState { state -> + when (state.sendMode) { + SendMode.REGULAR -> { + val slashCommandResult = CommandParser.parseSplashCommand(action.text) - when (slashCommandResult) { - is ParsedCommand.ErrorNotACommand -> { - // Send the text message to the room - room.sendTextMessage(action.text) - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) - } - is ParsedCommand.ErrorSyntax -> { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandError(slashCommandResult.command))) - } - is ParsedCommand.ErrorEmptySlashCommand -> { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandUnknown("/"))) - } - is ParsedCommand.ErrorUnknownSlashCommand -> { - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandUnknown(slashCommandResult.slashCommand))) - } - is ParsedCommand.Invite -> { - handleInviteSlashCommand(slashCommandResult) - } - is ParsedCommand.SetUserPowerLevel -> { - // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) - } - is ParsedCommand.ClearScalarToken -> { - // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) - } - is ParsedCommand.SetMarkdown -> { - // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) - } - is ParsedCommand.UnbanUser -> { - // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) - } - is ParsedCommand.BanUser -> { - // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) - } - is ParsedCommand.KickUser -> { - // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) - } - is ParsedCommand.JoinRoom -> { - // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) - } - is ParsedCommand.PartRoom -> { - // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) - } - is ParsedCommand.SendEmote -> { - room.sendTextMessage(slashCommandResult.message, msgType = MessageType.MSGTYPE_EMOTE) - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandHandled)) - } - is ParsedCommand.ChangeTopic -> { - handleChangeTopicSlashCommand(slashCommandResult) - } - is ParsedCommand.ChangeDisplayName -> { - // TODO - _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + when (slashCommandResult) { + is ParsedCommand.ErrorNotACommand -> { + // Send the text message to the room + room.sendTextMessage(action.text) + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) + } + is ParsedCommand.ErrorSyntax -> { + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandError(slashCommandResult.command))) + } + is ParsedCommand.ErrorEmptySlashCommand -> { + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandUnknown("/"))) + } + is ParsedCommand.ErrorUnknownSlashCommand -> { + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandUnknown(slashCommandResult.slashCommand))) + } + is ParsedCommand.Invite -> { + handleInviteSlashCommand(slashCommandResult) + } + is ParsedCommand.SetUserPowerLevel -> { + // TODO + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + } + is ParsedCommand.ClearScalarToken -> { + // TODO + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + } + is ParsedCommand.SetMarkdown -> { + // TODO + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + } + is ParsedCommand.UnbanUser -> { + // TODO + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + } + is ParsedCommand.BanUser -> { + // TODO + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + } + is ParsedCommand.KickUser -> { + // TODO + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + } + is ParsedCommand.JoinRoom -> { + // TODO + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + } + is ParsedCommand.PartRoom -> { + // TODO + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + } + is ParsedCommand.SendEmote -> { + room.sendTextMessage(slashCommandResult.message, msgType = MessageType.MSGTYPE_EMOTE) + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandHandled)) + } + is ParsedCommand.ChangeTopic -> { + handleChangeTopicSlashCommand(slashCommandResult) + } + is ParsedCommand.ChangeDisplayName -> { + // TODO + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandNotImplemented)) + } + } + } + SendMode.EDIT -> { + room.editTextMessage(state?.selectedEvent?.root?.eventId ?: "", action.text) + setState { + copy( + sendMode = SendMode.REGULAR, + selectedEvent = null + ) + } + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) + } + SendMode.QUOTE -> { + withState { state -> + val messageContent: MessageContent? = + state.selectedEvent?.annotations?.editSummary?.aggregatedContent?.toModel() + ?: state.selectedEvent?.root?.content.toModel() + val textMsg = messageContent?.body + + val finalText = legacyRiotQuoteText(textMsg, action.text) + + //TODO Refactor this, just temporary for quotes + val parser = Parser.builder().build() + val document = parser.parse(finalText) + val renderer = HtmlRenderer.builder().build() + val htmlText = renderer.render(document) + if (TextUtils.equals(finalText, htmlText)) { + room.sendTextMessage(finalText) + } else { + room.sendFormattedTextMessage(finalText, htmlText) + } + setState { + copy( + sendMode = SendMode.REGULAR, + selectedEvent = null + ) + } + _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) + } + + } } } + // Handle slash command + + } + + private fun legacyRiotQuoteText(quotedText: String?, myText: String): String { + val messageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray() + var quotedTextMsg = StringBuilder() + if (messageParagraphs != null) { + for (i in messageParagraphs.indices) { + if (messageParagraphs[i].trim({ it <= ' ' }) != "") { + quotedTextMsg.append("> ").append(messageParagraphs[i]) + } + + if (i + 1 != messageParagraphs.size) { + quotedTextMsg.append("\n\n") + } + } + } + val finalText = "$quotedTextMsg\n\n$myText" + return finalText } private fun handleShowEditHistoryReaction(action: RoomDetailActions.ShowEditHistoryAction) { @@ -271,6 +361,23 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, room.join(object : MatrixCallback<Unit> {}) } + private fun handleEditAction(action: RoomDetailActions.EnterEditMode) { + room.getTimeLineEvent(action.eventId)?.let { + enterEditMode(it) + } + } + + private fun handleQuoteAction(action: RoomDetailActions.EnterQuoteMode) { + room.getTimeLineEvent(action.eventId)?.let { + setState { + copy( + sendMode = SendMode.QUOTE, + selectedEvent = it + ) + } + } + } + private fun observeEventDisplayedActions() { // We are buffering scroll events for one second diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewState.kt index 43fbe9bd..00ce0b47 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewState.kt @@ -22,15 +22,32 @@ import com.airbnb.mvrx.Uninitialized import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.TimelineData +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.user.model.User +/** + * Describes the current send mode: + * REGULAR: sends the text as a regular message + * QUOTE: User is currently quoting a message + * EDIT: User is currently editing an existing message + * + * Depending on the state the bottom toolbar will change (icons/preview/actions...) + */ +enum class SendMode { + REGULAR, + QUOTE, + EDIT +} + data class RoomDetailViewState( val roomId: String, val eventId: String?, val timeline: Timeline? = null, val inviter: Async<User> = Uninitialized, val asyncRoomSummary: Async<RoomSummary> = Uninitialized, - val asyncTimelineData: Async<TimelineData> = Uninitialized + val asyncTimelineData: Async<TimelineData> = Uninitialized, + val sendMode: SendMode = SendMode.REGULAR, + val selectedEvent: TimelineEvent? = null ) : MvRxState { constructor(args: RoomDetailArgs) : this(roomId = args.roomId, eventId = args.eventId) diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt index c39da484..4fee2f41 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt @@ -59,7 +59,7 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes listOf( SimpleAction(ACTION_RESEND, R.string.resend, R.drawable.ic_corner_down_right, event.root.eventId), //TODO delete icon - SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_material_delete, event.root.eventId) + SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, event.root.eventId) ) ) } @@ -67,14 +67,18 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes //TODO determine if can copy, forward, reply, quote, report? val actions = ArrayList<SimpleAction>().apply { - this.add(SimpleAction(ACTION_ADD_REACTION, R.string.message_add_reaction, R.drawable.ic_smile, event.root.eventId)) + this.add(SimpleAction(ACTION_ADD_REACTION, R.string.message_add_reaction, R.drawable.ic_add_reaction, event.root.eventId)) if (canCopy(type)) { //TODO copy images? html? see ClipBoard this.add(SimpleAction(ACTION_COPY, R.string.copy, R.drawable.ic_copy, messageContent.body)) } + if (canEdit(event, currentSession.sessionParams.credentials.userId)) { + this.add(SimpleAction(ACTION_EDIT, R.string.edit, R.drawable.ic_edit, event.root.eventId)) + } + if (canRedact(event, currentSession.sessionParams.credentials.userId)) { - this.add(SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_material_delete, event.root.eventId)) + this.add(SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, event.root.eventId)) } if (canQuote(event, messageContent)) { @@ -159,6 +163,17 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes return event.root.sender == myUserId } + private fun canEdit(event: TimelineEvent, myUserId: String): Boolean { + //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment + if (event.root.type != EventType.MESSAGE) return false + //TODO if user is admin or moderator + val messageContent = event.root.content.toModel<MessageContent>() + return event.root.sender == myUserId && ( + messageContent?.type == MessageType.MSGTYPE_TEXT + || messageContent?.type == MessageType.MSGTYPE_EMOTE + ) + } + private fun canCopy(type: String): Boolean { return when (type) { @@ -187,6 +202,7 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes const val ACTION_ADD_REACTION = "add_reaction" const val ACTION_COPY = "copy" + const val ACTION_EDIT = "edit" const val ACTION_QUOTE = "quote" const val ACTION_REPLY = "reply" const val ACTION_SHARE = "share" diff --git a/vector/src/main/res/drawable-hdpi/ic_attach_file_white.png b/vector/src/main/res/drawable-hdpi/ic_attach_file_white.png deleted file mode 100644 index 06f642bea5a174105f33b363b67a2028994dd67b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 394 zcmV;50d@X~P)<h;3K|Lk000e1NJLTq001Na001Ni1^@s6;Q*MJ0003|Nkl<Zc-rll zL23dq6oy@nVg)y?E0^LziqLwFGDorA!y>5IQ?#}RC|!%)lu~tD_qrI-aTEW6e;_S7 zn$cviB=CU~-+TGUL@+NUgh(mkfI1ie5BT5%G(b8=y<ey%6v3z1J-!%YM97h3#=r$R zkOg;~3#A+rLUl<dzvcV9Bz12>Xu!qXh!~eKLlZ(C7t2M&Trg&7LMR-kiu?aUyB0n) z&I(x}D`bVNkQK5*R_NS?7JR;x<_etik`Q{~b01t$=9P0#NeGp}e9L8V21-c?g;LLa zMILvibj*Kj6`?f?tK3R6%<**yO<4R!A*aTiZyiEC7H=x#w3yS@Ayj4Y6{s8#f6Z4V z_*qLk=8+5h?2!rtri|_A><<MEsds6o9>6URZ-!^4CGE_cYHKFWbvG^TRMhCi=@T8( oehbc27mT<gK7UPZl{52~4Io?>E>CY>q5uE@07*qoM6N<$f}e__;Q#;t diff --git a/vector/src/main/res/drawable-hdpi/ic_send_white.png b/vector/src/main/res/drawable-hdpi/ic_send_white.png deleted file mode 100644 index f133cdbeb2757f5d4f51321c302c7fcd81be52fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 335 zcmV-V0kHmwP)<h;3K|Lk000e1NJLTq001Na001Ni1^@s6;Q*MJ0003NNkl<Zc-rll zv2DUY6hMtcOTh|3VF5A%kzjyi1gK0)MhI=FU<Bw%0VBWw6fgpj(ngATE-VEUf$!h{ z=8$~SYg|41`ObHak|c>jA}WRMU8oC{kc85)g#pw;5z59ljG-BdP&R(yv#6YTg#Hfm zoUTIVIi*5oIemrHb7~5y<g^g-%4sPi%6WdF4lQ_rAxvNiJNPLgr%T@|JU|<I@D4Lr z!$B*j<{Bb>x+gicg`VIQ-e3wV*yj+6B|>Sk20TL_MlgpBd<l{*pmQym^e$aq>JNAf zVcN%%X|KI7{+s0%pN@-Jiu}0d%3{6!aL!`2K@(@u-okok(b(jxX7M=&nPib16iu_- h^-;R~`%wzHas(7`hA~Sk`s4rr002ovPDHLkV1mtQjA#G= diff --git a/vector/src/main/res/drawable-mdpi/ic_attach_file_white.png b/vector/src/main/res/drawable-mdpi/ic_attach_file_white.png deleted file mode 100644 index a819f4034d49512d474d5dd27781f40d6837bd0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 285 zcmV+&0pk9NP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv0002wNkl<Zc-rli zJqp4=5JqziF@>5&yolhb5=1KtwTyP^0hF9Tv=VP27K)%~8d2A8;S&~#`Pr;p0*~y@ z4)caBb}S);^-t0yH9!DDa03faHzkn8FW><BU;vKboK}*Q5U_YziP@CQO$b7E{v98J zs|kVp+=#f#Gou1^tqNjgg`f}=f^32(=y2WT-l$;BN3I7>U}-||0v^{6Sb;l>R~h%1 zrzO~fNkYyH>~sZVKFg&a=QgLDuPbmk6E+bNS$xX|DtTsdi(Se>Q_$wMd}7VKLtjz@ jBi{9ce?PACtO9M`=wB&0PW1Bf00000NkvXXu0mjfmu++^ diff --git a/vector/src/main/res/drawable-mdpi/ic_send_white.png b/vector/src/main/res/drawable-mdpi/ic_send_white.png deleted file mode 100644 index 34e49af7ab119a8581624cba15d1874ddb3ee8d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmV+c0sj7pP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv0002UNkl<Zc-rOF zF%AJi6o>IG(5t990^tH2frgqR&{1*(Y8sBf1tc7SR!1$0@ii-(STgUK*D%SiQ2mo{ z?QG<ni!4PyHLyb$Ije#AoH0hxY9Ky0EK#=_NFH@Q-vM<#>3}$&cECGd@<$#Ov@pO7 z8ys=h=JS168I;gK4->4g$K_K#$v_!R^fAR62V7GI7O15qs-le{=GfvSNHjvh;vsjC z4gEKLW(fVFy<`Y=RlLpc&1L76L33S;GDto+sto`0O*ZiWxujSd^@pKl00000NkvXX Hu0mjfBSB_$ diff --git a/vector/src/main/res/drawable-xhdpi/ic_attach_file_white.png b/vector/src/main/res/drawable-xhdpi/ic_attach_file_white.png deleted file mode 100644 index c572c35997127449a4b660c4c9f1cd5c70ff7a1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 507 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezr3(Fdp)BaSW-r^>)^Iufq-k$1ZmV z=4-tQtP-@HdT;e3W|5DYQCpWcl?yt4(0hB(UEsfnyhCl%BKD2j7uh-L=GsW4Cw3hc znv`qi>o%wH{iJi}>h^EAIc2WJzW3Jkk$nt*R&iuCSt;;;<;rT{n!}jpAYNBdqkPB2 zoFVn!Mf3jOOB8dsUzGn73UQk3CA~mfclklq1uG<1w?C1~I=%7$<ARlUf3El=J*oa2 z;{s=%sXU*|J^yjDGxSRsRT`vw{+qze(CgVJ*id-3WoD1D8v~02I{L?Zpdlt+<@)rK zU+vNvs=}&xqnti(za^;|-4NUCe9tJIVd-Xp&&xgKw%=f=ndrHksqQ|b?SrcKk2h(u zKiRpYZ@St6>6sN*7oD?OdD&O^L7z{|))m3ZrBRto7dme*i0P5aIwQPtGf)-Ni^)E5 zoq>-VdfqoayOd`*SAh8y>o(J4H*#&#<c|i1`R+^ST_m(|PEp14_FF<Fo3l1QbCkao zw~ps+^R){zx_A5fMt|12=cpZNzUwpNF`q@b$#yUO_GTH&K0YiVY8&(Un(brHE&rbv uTIW3M&ir!IQT+df(8q7gK5b+V|I4r>ZAo!J6cZ?V89ZJ6T-G@yGywo`ncEKl diff --git a/vector/src/main/res/drawable-xhdpi/ic_send_white.png b/vector/src/main/res/drawable-xhdpi/ic_send_white.png deleted file mode 100644 index e5f9ba41acdb7d7f9353981e1aa0bcff06353b14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 423 zcmV;Y0a*TtP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0004QNkl<Zc-rlm zu`<L^6hKp;SE1z-7)#G5&@3&VKxe7eC+yZ%!6#5N44*(tMUB~gjxjrv$;9j=x%a;J z%zHDZNq3Ty<mD!F&Sf!^C4nT61pc4EE$qTftO7&#yI$c07Gf0`uTMCK^;iY+>prx+ z$_|d)<n@!+aR_<U6)5Jl7OQ~wyd(vRdCmJ0kXj~rX$qL+WhJ1Nm$g7XuYp8fz6A2? z4tDD16@oycyvh#F!ZK{Z0rcPsp5V(cua?I5z!WUP2JAr>F5m&)eB>qlT-LgJFVHE= z%W~f$arHimd07iA!6xj(DO|!MyjKu7gH_FhR$v<rp%2&aERfI<%t_qDP0wQ0o45_r zRUe}oXVKNoB$gN1pHG??wi4~wwi4q^S}wB9T&+w(5NNdH^!Z6SkjTXPqh34{D*?4k zv<F-2nP`m`JE4viCk=PaGm#jti%bxF{NM;;0$o+$CbUe_U&<tq1d>3wd;=dyV1##h Rc_07)002ovPDHLkV1lk5y~_Xq diff --git a/vector/src/main/res/drawable-xxhdpi/ic_attach_file_white.png b/vector/src/main/res/drawable-xxhdpi/ic_attach_file_white.png deleted file mode 100644 index a5dc29f0d272d854bf2ad39ead95d01b1d791d47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 809 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@Zgyv2VEW+c;uunK>+PJo-lC2Y?X8RF zHvMAOIw7+9wbJ5xC9N0B_$R*5TQplwh4YfqZY>Qa)jj9!Jhb}6*hJ&R1H;2qYBj=2 zij6#0Zr;oM{$9`fH|H#?)9%jwyZ1vupVjAcXNuF(%Tm8@VR5{`bjE>+<*9#w`bB>q z!OusGjF0cP_Ri5=w1DrC-beio{?KP;7vn@6ZI8TP@GnB8@0Z>ReGj?x8qq7lhA-Y9 zkuW^6;=8@lDfy4S0qqYrL@88thAs4yE}Zj2?aFW0oR6LtWD_Ukcl3&tnhWN){@Pw6 zct!I~827XyC$rCqE8ge5wc~gtZuj>d+ow4KpT7K5$nc7o5G{S;vYO5v{~7_4^}JbY zI<s$z%d_ZaH%oa<c)e*__DOcfDjt;+rV3A*6*!s}L^y5}%4M;f%;6}&LNuC^>}b07 z%mT&htZQS|YBbelrOHVZ*$U-7*KS#G{LmST1ye0m&TQ#i#9^g2CD=$#XnMu7D=kLt z-AW#Jzy7jXp&uu-bP-2T4u9A^ojk6u6;9!|$`|ewO4;qKxlM4rPMTl1OP^!+T)p5X zv8{aZoto?Uq`$`8P&vP1{~xP^*?yYK{)MjHn{|z~_KH=_`9N#M$5GYtoAfe*UVlhG z-t=tCH?2tVlDG)>GOMiY?H8`?T*G}!yQJz!VDw_A=(~~PODfiv{_;QLcZI{cY3ljb zGbWpPr#Ja=&wo?gEqhXIYI;=0<CHj-wU3WR_GkN^s&_tN>pkVOl1Jf%f=A`H2Ys|8 zpRBlX{f*(0j}9k%4=2QZh%DKgeQt~5{SDp!mMePr{!DLk)ZKU>RxJAGiVX_wAE!i2 z4tMP06`XYFhu59wr`x*!Sn17S@hZBu`&IR<^9!ac^KExazH#+TB(J05?@oWtqi1$S zOxUdCv2mJ(>z>uYXL#m6pK|$Cw%EQS8T|3qp+B_tocjN6#i#S4|Ej-dec_Kk%<3rc bWXgYOqoS1K?z8p+vjBsqtDnm{r-UW|NG4=C diff --git a/vector/src/main/res/drawable-xxhdpi/ic_send_white.png b/vector/src/main/res/drawable-xxhdpi/ic_send_white.png deleted file mode 100644 index 0ba718b6e25df33c10d4a2b91af75d08ccf25eff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 550 zcmV+>0@?kEP)<h;3K|Lk000e1NJLTq002k;002k`1^@s6RqeA!0005)Nkl<Zc-rlo zy)r{l6h>2^SE1z*7@_A8XwdQqbbcxxfm#$i0yQJUBhX4kjY*Dg#$+aQ-5}@Qea_zJ ze0ye9>DKIZ|I={JHOO!%fr2QAf+&cBD2Re6=qGnz3rtW@IbHWX9dHC@D5zXee?Gtk zSf-$IL4(uIB|l$5ZXmzIl=G`9sF>eUPJ(*QZ<5MSLQqoqNeW6LzYu~<=NDR#x%__n zF~q3PFSa1{`DqeV%x{{4{?4xzJ3q~W2In4Z#>mf#plJD76BIl@tAc{%mx3VC{8AEB z%&#deXcEkWHLwd#!8LdSpSk4cYjzCGf)%h0j=?2(0Iw<KCw27Kx_u+(7vt(w%l7>% zKaF?Lp||e^91P3P+7mv>?RziF&!V6?unKm-3Ah4};H?Hh=U~x7Vhdm$?13|I1D*vF z+Xqvc4$@LvFzP{i1B}Ny&4=8AN*PIAC~DY(TFz8eZZ$M+flo#vw;EPw<cx9=<egCt z56miMl%k-1Moq^@uJReB^4do%BddafWn@iI@QkboiuU%@DgXSX6+0teLA?MOnf`#P zmrF*m1*y*{mY|N6j6w@)Tg%8-P&b#1Bn2grk%XY6GOA0EijOKa3QGPzVZMTHbIB;i odoc>4AVNVDL_ripK@?=1FD5h?{9Ic8+W-In07*qoM6N<$f?2ikg8%>k diff --git a/vector/src/main/res/drawable-xxxhdpi/ic_attach_file_white.png b/vector/src/main/res/drawable-xxxhdpi/ic_attach_file_white.png deleted file mode 100644 index 60147bc7a378eb0cb32cfe4cc9ae61b4bcd046de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1059 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>V1DK4;uunK>+RgT-Xeh#?Yae~ zckT3?4kc+`+x7aj(g*ny9~P}S>$7sV6YF~JUB<r_9O_=waX?^6#%Yc&hmL;@3%XBq zIBimTwXA1?QFiZ)dx!UVbHA%jOZ$FiPT{xm2aJ)=YxX>!X<2+ua>mL+ndNLZnCCJu z3NR=vP&wTxmo%fot48(N%*=njf7j3MZ)I@KQF80BJYN0FGe$j1VL{C_LFXg#%l|A{ z*T?N*%km>x`Qs(S>1rJ&EEUPtACDT&w-b(5Jn;7n|Dw;`s!J3XnDENA&Aop_W1?b^ zi|*t{_wzm&<{b{SWzk7FvGnmKm(A@(*MySwQs=fev<CZTZu>m_h5q6*jOVn^_FWSA zFkkTO&0Fz~JDFz{wJR!|=Q^vToxPwY(_)tDB!LO{<0j<oILme_-u+37)h-1Edyy$# zi`ic9(7aN}%k*h>#?qi;39@sYWc?Ks_OFiATFP^6W87wg)m4n2ot|HjTC>=1g)%#1 zQPi5R4h$?H<WL|Roaf#a$`9s2#Hd9!><^bV$@-+yYGcXqLH-5%!yk8N*;KdMyjM6- zzu<ez<9K0hZ<+mFGekc1I5zxu;LrY~!6|F=MCIq1`B!A_Gc*2U4qZR*=aZ0mR|2Lr zG<;%uYowqc?67;)t5n%=trvg18Mm#@V$snJKID3in^Eucua@I~*gN{=j@PW$XWH_u z%UGIeALqe!-vZkvm3Hj#-t)Zg)JL28o^)$Y`KP9wxwHBfGi1)%ao8!EYfIL$og0Ne z1aN#?;mTO+H2dB0zyp?wH`cf^Ui!W4{E<M1-2!jQ)&B>!Z@9hK>}y)9+18_h4O~B_ z3w#jde*04NmXY`|PQk6-|9X?Z@$QOC3(n8w>)gh&CHvX?<4!rpIeo2fJ<>_w{ODBt zGM{fD?`@f%%ns)dz2cs&uX}gB<?m$`g(4w$vnwvDE$zz^SWvO%Wy^C$&w#o13s;ug zoUz-WyP$RX{9W3K-47Gp%b%SWkY454u;_Mbu&-}qf4GIU*9YF~Zx`p!O*}5o%Gh;T zuFQOS#qLEHpU+wxZhTe8Pd)dDoG4@0YPs*<>jlay{x6&QUbxgp`2V%z*}akdsd3vx z%crnt_*p(&?l{}f-M8V<g44Y>LuPF>e5TLF$o2K^ad*bDe-$@2&poQP;DB8m{|v9J z*4^q}hHkMAjj=~^&+4txTe-G3I`Qq>BcTqBZ0}BNKEhm=ViEhrC*tqluK6h|+cm9q oZIe6JHr#0RYhd7D5c|pAuy^9+A9F0;05d;>r>mdKI;Vst01_g}=l}o! diff --git a/vector/src/main/res/drawable-xxxhdpi/ic_send_white.png b/vector/src/main/res/drawable-xxxhdpi/ic_send_white.png deleted file mode 100644 index f02b6453d5c3a05f683d2c49aaa8495033a14b8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 728 zcmV;}0w?{6P)<h;3K|Lk000e1NJLTq003YB003YJ1^@s6;+S_h0007?Nkl<Zc-rlq zyHXTE7)A*|V<Rr1K7y7gu{;7KqGtLCnt@0Fj{q?&6h4B68hCpIO}zvfvw-clwY60X zUYK=e_V54i(^V&%BAC;CcIlbwok%H7fF0Zf1RwwqfB--M0ssLB00bZaG86DxJP?<; zVKxGeQh$6C8)A+dXCq)Ry5c}Aa06`w43GEZseJ+6bn*Bqo{=Xz0puwY0mI{+xKEz! z1dykO1RSSkMo;8Ed9o8go-z_Zo<<OGlG-1GJl#fsjetMp=@J5L1PsO(u`Xs2U^_zR zsmevCDWCCH+>4YaZvw*Q$*X|jJas(hsmet^laJvtPmu(;%u_@GuJROFfQvj;xhT@; z<>_foc`8l7`8?f1K;d~p!0&mQK|n!yLV$6es$6tMTo+5?vDg+b#K)GOmKBkwx_@9& zTorf3L$N6iQ~!;*??Lml*JPgBZd~Nm^G2-H&6DMK;3%u7R-SU+sPeXY26>u~BEa+N zshp>%54&?&J)gzezw%V#%TN}p=jd;FDpkNWu_zviE%97@5I<TJuq$qsl%N}8Sv(Ot z;-&axkf2pD9q~hW+_QMp58=CF%KO*Q9A<It%;fx!SkAM!v2QxoTM?dS@&9DTbSomt zEFLv8^J&c6(NSmdSTe%{8RH#K1dL%*4FMTv1_8#IL4a{)5ODVCRWCEHUQLYha;{ls zN)^!f8w?1jo|&xPfG$Nqwwb9b;FQcDz;$LK32>R2x&m@Is-WXJGhPLR$&5Dvz2j;^ zod@ATK<LcW70~vPW#P{R%27a3naM?f`;$$q1a!%a-=mAkjKSgE`((!E_<Ay9@B<DU zL2UFBCS=Ay0LRc933x?jYy|)U5C8~303ZMXfB*yl0uTTQh`}%A!-1tO8hI)J0000< KMNUMnLSTYl^*@yW diff --git a/vector/src/main/res/drawable/ic_add_reaction.xml b/vector/src/main/res/drawable/ic_add_reaction.xml new file mode 100644 index 00000000..ba33bc3f --- /dev/null +++ b/vector/src/main/res/drawable/ic_add_reaction.xml @@ -0,0 +1,54 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="22dp" + android:viewportWidth="24" + android:viewportHeight="22"> + <path + android:pathData="m13.5909,20.8117c-0.8681,0.2472 -1.7837,0.3795 -2.7298,0.3795 -5.5574,0 -10.0625,-4.5627 -10.0625,-10.1912 0,-5.6284 4.5051,-10.1912 10.0625,-10.1912 5.4556,0 9.8971,4.3972 10.058,9.8831h-0.9588c-0.1605,-4.9498 -4.1729,-8.9125 -9.0992,-8.9125 -5.0281,0 -9.1042,4.1282 -9.1042,9.2206 0,5.0924 4.0761,9.2206 9.1042,9.2206 0.951,0 1.868,-0.1477 2.7298,-0.4217z" + android:strokeLineJoin="round" + android:strokeWidth="1.047619" + android:fillColor="#00000000" + android:fillType="evenOdd" + android:strokeColor="#9e9e9e" + android:strokeLineCap="round"/> + <path + android:pathData="m14.6944,16.8235h7.6667" + android:strokeLineJoin="round" + android:strokeWidth="2.095238" + android:fillColor="#00000000" + android:fillType="evenOdd" + android:strokeColor="#9e9e9e" + android:strokeLineCap="round"/> + <path + android:pathData="m18.5278,12.9412l-0,7.7647" + android:strokeLineJoin="round" + android:strokeWidth="2.095238" + android:fillColor="#00000000" + android:fillType="evenOdd" + android:strokeColor="#9e9e9e" + android:strokeLineCap="round"/> + <path + android:pathData="m7.0278,12.9412s1.4375,1.9412 3.8333,1.9412c2.3958,0 3.8333,-1.9412 3.8333,-1.9412" + android:strokeLineJoin="round" + android:strokeWidth="2.095238" + android:fillColor="#00000000" + android:fillType="evenOdd" + android:strokeColor="#9e9e9e" + android:strokeLineCap="round"/> + <path + android:pathData="m7.9861,8.0882h0.0096" + android:strokeLineJoin="round" + android:strokeWidth="2.095238" + android:fillColor="#00000000" + android:fillType="evenOdd" + android:strokeColor="#9e9e9e" + android:strokeLineCap="round"/> + <path + android:pathData="m13.7361,8.0882h0.0096" + android:strokeLineJoin="round" + android:strokeWidth="2.095238" + android:fillColor="#00000000" + android:fillType="evenOdd" + android:strokeColor="#9e9e9e" + android:strokeLineCap="round"/> +</vector> diff --git a/vector/src/main/res/drawable/ic_attachment.xml b/vector/src/main/res/drawable/ic_attachment.xml new file mode 100644 index 00000000..e54b9302 --- /dev/null +++ b/vector/src/main/res/drawable/ic_attachment.xml @@ -0,0 +1,14 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="21dp" + android:height="22dp" + android:viewportWidth="21" + android:viewportHeight="22"> + <path + android:pathData="M19.468,10.571l-8.73,8.753a5.693,5.693 0,0 1,-8.066 0,5.728 5.728,0 0,1 0,-8.086l8.73,-8.752a3.795,3.795 0,0 1,5.378 0,3.818 3.818,0 0,1 0,5.39L8.04,16.63a1.898,1.898 0,0 1,-2.689 0,1.91 1.91,0 0,1 0,-2.696l8.065,-8.076" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:fillType="evenOdd" + android:strokeColor="#9E9E9E" + android:strokeLineCap="round"/> +</vector> diff --git a/vector/src/main/res/drawable/ic_close_round.xml b/vector/src/main/res/drawable/ic_close_round.xml new file mode 100644 index 00000000..413a233b --- /dev/null +++ b/vector/src/main/res/drawable/ic_close_round.xml @@ -0,0 +1,20 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="22dp" + android:height="22dp" + android:viewportWidth="22" + android:viewportHeight="22"> + <path + android:pathData="M11,11m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#979797" + android:fillType="evenOdd"/> + <path + android:pathData="M7.667,7.667L14.333,14.333M14.333,7.667L7.667,14.333" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#9E9E9E" + android:fillType="evenOdd" + android:strokeLineCap="round"/> +</vector> diff --git a/vector/src/main/res/drawable/ic_smile.xml b/vector/src/main/res/drawable/ic_delete.xml similarity index 51% rename from vector/src/main/res/drawable/ic_smile.xml rename to vector/src/main/res/drawable/ic_delete.xml index e2f3402e..b740db3c 100644 --- a/vector/src/main/res/drawable/ic_smile.xml +++ b/vector/src/main/res/drawable/ic_delete.xml @@ -4,31 +4,19 @@ android:viewportWidth="22" android:viewportHeight="22"> <path - android:pathData="M11,11m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0" + android:pathData="M3.222,1L18.778,1A2.222,2.222 0,0 1,21 3.222L21,18.778A2.222,2.222 0,0 1,18.778 21L3.222,21A2.222,2.222 0,0 1,1 18.778L1,3.222A2.222,2.222 0,0 1,3.222 1z" android:strokeLineJoin="round" android:strokeWidth="2" android:fillColor="#00000000" - android:strokeColor="#9E9E9E" android:fillType="evenOdd" + android:strokeColor="#9E9E9E" android:strokeLineCap="round"/> <path - android:pathData="M7,13C7,13 8.5,15 11,15C13.5,15 15,13 15,13" + android:pathData="M7.667,7.667l6.666,6.666M14.333,7.667l-6.666,6.666" android:strokeLineJoin="round" android:strokeWidth="2" android:fillColor="#00000000" + android:fillType="evenOdd" android:strokeColor="#9E9E9E" - android:fillType="evenOdd" android:strokeLineCap="round"/> - <path - android:pathData="M7.5,7.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0" - android:strokeWidth="1" - android:fillColor="#9E9E9E" - android:fillType="evenOdd" - android:strokeColor="#00000000"/> - <path - android:pathData="M14.5,7.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0" - android:strokeWidth="1" - android:fillColor="#9E9E9E" - android:fillType="evenOdd" - android:strokeColor="#00000000"/> </vector> diff --git a/vector/src/main/res/drawable/ic_edit.xml b/vector/src/main/res/drawable/ic_edit.xml index ec5cf418..1ad914fc 100644 --- a/vector/src/main/res/drawable/ic_edit.xml +++ b/vector/src/main/res/drawable/ic_edit.xml @@ -4,19 +4,19 @@ android:viewportWidth="21" android:viewportHeight="22"> <path - android:pathData="M9.497,3.06H2.888C1.845,3.06 1,3.93 1,5v13.576c0,1.07 0.845,1.94 1.888,1.94h13.218c1.042,0 1.888,-0.87 1.888,-1.94v-6.788" + android:pathData="M9.4969,3.0606L2.8882,3.0606C1.8454,3.0606 1,3.9289 1,5L1,18.5758C1,19.6469 1.8454,20.5152 2.8882,20.5152L16.1056,20.5152C17.1484,20.5152 17.9938,19.6469 17.9938,18.5758L17.9938,11.7879" android:strokeLineJoin="round" android:strokeWidth="2" android:fillColor="#00000000" - android:fillType="evenOdd" android:strokeColor="#9E9E9E" + android:fillType="evenOdd" android:strokeLineCap="round"/> <path - android:pathData="M16.578,1.606a1.966,1.966 0,0 1,2.832 0,2.097 2.097,0 0,1 0,2.91l-8.969,9.211 -3.776,0.97 0.944,-3.879 8.969,-9.212z" + android:pathData="M16.5776,1.6061C17.3598,0.8027 18.6278,0.8027 19.4099,1.6061C20.1921,2.4094 20.1921,3.7118 19.4099,4.5152L10.441,13.7273L6.6646,14.697L7.6087,10.8182L16.5776,1.6061Z" android:strokeLineJoin="round" android:strokeWidth="2" android:fillColor="#00000000" - android:fillType="evenOdd" android:strokeColor="#9E9E9E" + android:fillType="evenOdd" android:strokeLineCap="round"/> </vector> diff --git a/vector/src/main/res/drawable/ic_send.xml b/vector/src/main/res/drawable/ic_send.xml new file mode 100644 index 00000000..d79ba7c1 --- /dev/null +++ b/vector/src/main/res/drawable/ic_send.xml @@ -0,0 +1,14 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="22dp" + android:height="22dp" + android:viewportWidth="22" + android:viewportHeight="22"> + <path + android:pathData="M20.142,11H4.586M20.142,11L1.05,20.192 4.586,11 1.05,1.808z" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:fillType="evenOdd" + android:strokeColor="#03B381" + android:strokeLineCap="round"/> +</vector> diff --git a/vector/src/main/res/layout/adapter_item_action.xml b/vector/src/main/res/layout/adapter_item_action.xml index 5ee60d32..ce518071 100644 --- a/vector/src/main/res/layout/adapter_item_action.xml +++ b/vector/src/main/res/layout/adapter_item_action.xml @@ -3,7 +3,6 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:layout_height="50dp" android:clickable="true" android:focusable="true" android:foreground="?attr/selectableItemBackground" @@ -12,7 +11,8 @@ android:paddingLeft="@dimen/layout_horizontal_margin" android:paddingTop="8dp" android:paddingRight="@dimen/layout_horizontal_margin" - android:paddingBottom="8dp"> + android:paddingBottom="8dp" + tools:layout_height="50dp"> <ImageView android:id="@+id/action_icon" @@ -21,8 +21,8 @@ android:layout_gravity="center_vertical" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" - android:src="@drawable/ic_material_delete" - android:tint="?android:attr/textColorSecondary" /> + tools:src="@drawable/ic_delete" + android:tint="?android:attr/textColorTertiary" /> <TextView android:id="@+id/action_title" diff --git a/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml b/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml new file mode 100644 index 00000000..8e7c3d85 --- /dev/null +++ b/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml @@ -0,0 +1,153 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/composerLayout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent"> + + <View + android:id="@+id/related_message_backround" + android:layout_width="0dp" + android:layout_height="0dp" + android:background="?vctr_bottom_nav_background_color" + app:layout_constraintBottom_toTopOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + tools:layout_height="40dp" /> + + <View + android:id="@+id/related_message_background_top_separator" + android:layout_width="0dp" + android:layout_height="0dp" + android:background="?vctr_bottom_nav_background_border_color" + app:layout_constraintBottom_toTopOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + <View + android:id="@+id/related_message_background_bottom_separator" + android:layout_width="0dp" + android:layout_height="0dp" + android:background="?vctr_bottom_nav_background_border_color" + app:layout_constraintBottom_toTopOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + <TextView + android:id="@+id/composer_related_message_sender" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:textStyle="bold" + android:visibility="invisible" + app:layout_constraintBottom_toTopOf="@id/composer_related_message_preview" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + tools:text="@tools:sample/first_names" /> + + <TextView + android:id="@+id/composer_related_message_preview" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:visibility="invisible" + app:layout_constraintBottom_toTopOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + tools:text="@tools:sample/lorem/random" /> + + <ImageView + android:id="@+id/composer_related_message_action_image" + android:layout_width="20dp" + android:layout_height="20dp" + android:layout_marginTop="8dp" + android:layout_marginBottom="38dp" + android:alpha="0" + android:tint="?android:attr/textColorTertiary" + app:layout_constraintEnd_toStartOf="parent" + app:layout_constraintTop_toBottomOf="parent" + tools:ignore="MissingConstraints" + tools:src="@drawable/ic_edit" /> + + + <ImageButton + android:id="@+id/composer_related_message_close" + android:layout_width="22dp" + android:layout_height="22dp" + android:background="?android:attr/selectableItemBackground" + android:src="@drawable/ic_close_round" + android:tint="@color/rosy_pink" + app:layout_constraintBottom_toTopOf="parent" + app:layout_constraintStart_toEndOf="parent" /> + + <ImageView + android:id="@+id/composer_avatar_view" + android:layout_width="32dp" + android:layout_height="32dp" + android:layout_marginStart="8dp" + android:layout_marginLeft="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" + android:layout_marginBottom="8dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/composerEditText" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="1" + tools:src="@tools:sample/avatars" /> + + <ImageButton + android:id="@+id/attachmentButton" + android:layout_width="48dp" + android:layout_height="48dp" + android:background="?android:attr/selectableItemBackground" + android:src="@drawable/ic_attachment" + android:tint="?attr/colorAccent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/sendButton" + app:layout_constraintStart_toEndOf="@id/composerEditText" /> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/composer_preview_barrier" + android:layout_width="0dp" + android:layout_height="0dp" + app:barrierDirection="bottom" + app:barrierMargin="8dp" + app:constraint_referenced_ids="composer_related_message_preview,composer_related_message_action_image" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + <ImageButton + android:id="@+id/sendButton" + android:layout_width="48dp" + android:layout_height="48dp" + android:background="?android:attr/selectableItemBackground" + android:src="@drawable/ic_send" + android:tint="?attr/colorAccent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/attachmentButton" /> + + <EditText + android:id="@+id/composerEditText" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:background="@android:color/transparent" + android:hint="@string/room_message_placeholder_not_encrypted" + android:maxHeight="200dp" + android:minHeight="48dp" + android:nextFocusLeft="@id/composerEditText" + android:nextFocusUp="@id/composerEditText" + android:padding="16dp" + android:textSize="14sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/attachmentButton" + app:layout_constraintStart_toEndOf="@+id/composer_avatar_view" + app:layout_constraintTop_toTopOf="parent" + tools:text="@tools:sample/lorem/random" /> + <!--tools:text="@tools:sample/lorem/random"--> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml b/vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml new file mode 100644 index 00000000..9a354fc2 --- /dev/null +++ b/vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/composerLayout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent"> + + <View + android:id="@+id/related_message_backround" + android:layout_width="match_parent" + android:layout_height="0dp" + android:background="?vctr_bottom_nav_background_color" + app:layout_constraintBottom_toBottomOf="@id/composer_preview_barrier" + app:layout_constraintTop_toTopOf="parent" /> + + <View + android:id="@+id/related_message_background_top_separator" + android:layout_width="0dp" + android:layout_height="1dp" + android:background="?vctr_bottom_nav_background_border_color" + app:layout_constraintTop_toTopOf="@id/related_message_backround" + app:layout_constraintEnd_toEndOf="@id/related_message_backround" + app:layout_constraintStart_toStartOf="@+id/related_message_backround" /> + + <View + android:id="@+id/related_message_background_bottom_separator" + android:layout_width="0dp" + android:layout_height="1dp" + android:background="?vctr_bottom_nav_background_border_color" + app:layout_constraintBottom_toBottomOf="@id/related_message_backround" + app:layout_constraintEnd_toEndOf="@id/related_message_backround" + app:layout_constraintStart_toStartOf="@+id/related_message_backround" /> + + <TextView + android:id="@+id/composer_related_message_sender" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_margin="8dp" + android:textStyle="bold" + app:layout_constraintEnd_toStartOf="@id/composer_related_message_close" + app:layout_constraintStart_toEndOf="@id/composer_avatar_view" + app:layout_constraintTop_toTopOf="parent" + tools:text="@tools:sample/first_names" /> + + <TextView + android:id="@+id/composer_related_message_preview" + android:layout_width="0dp" + android:layout_height="match_parent" + android:ellipsize="end" + android:maxLines="2" + android:textColor="?vctr_message_text_color" + app:layout_constrainedHeight="true" + app:layout_constraintEnd_toEndOf="@id/composer_related_message_sender" + app:layout_constraintStart_toStartOf="@id/composer_related_message_sender" + app:layout_constraintTop_toBottomOf="@id/composer_related_message_sender" + tools:text="@tools:sample/lorem/random" /> + + <ImageView + android:id="@+id/composer_related_message_action_image" + android:layout_width="10dp" + android:layout_height="10dp" + android:layout_marginTop="6dp" + android:layout_marginBottom="38dp" + android:alpha="1" + android:tint="?android:attr/textColorTertiary" + android:visibility="visible" + app:layout_constraintEnd_toEndOf="@id/composer_avatar_view" + app:layout_constraintStart_toStartOf="@id/composer_avatar_view" + app:layout_constraintTop_toBottomOf="@id/composer_avatar_view" + tools:src="@drawable/ic_edit" /> + + + <ImageButton + android:id="@+id/composer_related_message_close" + android:layout_width="22dp" + android:layout_height="22dp" + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" + android:background="?android:attr/selectableItemBackground" + android:src="@drawable/ic_close_round" + android:tint="@color/rosy_pink" + app:layout_constraintBottom_toBottomOf="@id/composer_related_message_preview" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="@id/composer_related_message_preview" /> + + + <ImageView + android:id="@+id/composer_avatar_view" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_marginStart="8dp" + android:layout_marginLeft="8dp" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp" + app:layout_constraintBottom_toTopOf="@id/composer_related_message_action_image" + app:layout_constraintEnd_toStartOf="@+id/composer_related_message_sender" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@id/composer_related_message_sender" + tools:src="@tools:sample/avatars" /> + + + <ImageButton + android:id="@+id/attachmentButton" + android:layout_width="48dp" + android:layout_height="48dp" + android:background="?android:attr/selectableItemBackground" + android:src="@drawable/ic_attachment" + android:tint="?attr/colorAccent" + app:layout_constraintEnd_toStartOf="@+id/sendButton" + app:layout_constraintTop_toBottomOf="parent" /> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/composer_preview_barrier" + android:layout_width="0dp" + android:layout_height="0dp" + app:barrierDirection="bottom" + app:barrierMargin="8dp" + app:constraint_referenced_ids="composer_related_message_preview,composer_related_message_action_image" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + <ImageButton + android:id="@+id/sendButton" + android:layout_width="48dp" + android:layout_height="48dp" + android:background="?android:attr/selectableItemBackground" + android:src="@drawable/ic_send" + android:tint="?attr/colorAccent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/composerEditText" + app:layout_constraintTop_toBottomOf="@id/composer_preview_barrier" + app:layout_constraintVertical_bias="1" /> + + <EditText + android:id="@+id/composerEditText" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:background="@android:color/transparent" + android:hint="@string/room_message_placeholder_not_encrypted" + android:minHeight="48dp" + android:nextFocusLeft="@id/composerEditText" + android:nextFocusUp="@id/composerEditText" + android:padding="16dp" + android:textSize="14sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/sendButton" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/composer_preview_barrier" + tools:text="@tools:sample/lorem" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_room_detail.xml b/vector/src/main/res/layout/fragment_room_detail.xml index a5711bce..0f631722 100644 --- a/vector/src/main/res/layout/fragment_room_detail.xml +++ b/vector/src/main/res/layout/fragment_room_detail.xml @@ -2,6 +2,7 @@ <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/rootConstraintLayout" android:layout_width="match_parent" android:layout_height="match_parent"> @@ -89,66 +90,18 @@ android:background="?vctr_list_divider_color" app:layout_constraintBottom_toTopOf="@+id/composerLayout" /> - <RelativeLayout - android:id="@+id/composerLayout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent"> - - <ImageButton - android:id="@+id/attachmentButton" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_centerVertical="true" - android:layout_toStartOf="@id/sendButton" - android:layout_toLeftOf="@id/sendButton" - android:background="?android:attr/selectableItemBackground" - android:src="@drawable/ic_attach_file_white" - android:tint="?attr/colorAccent" /> - - <ImageButton - android:id="@+id/sendButton" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_alignParentEnd="true" - android:layout_alignParentRight="true" - android:layout_centerVertical="true" - android:background="?android:attr/selectableItemBackground" - android:src="@drawable/ic_send_white" - android:tint="?attr/colorAccent" /> - - <EditText - android:id="@+id/composerEditText" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentStart="true" - android:layout_alignParentLeft="true" - android:layout_centerVertical="true" - android:layout_toStartOf="@id/attachmentButton" - android:layout_toLeftOf="@id/attachmentButton" - android:background="@android:color/transparent" - android:gravity="center_vertical" - android:hint="@string/room_message_placeholder_not_encrypted" - android:minHeight="48dp" - android:nextFocusLeft="@id/composerEditText" - android:nextFocusUp="@id/composerEditText" - android:padding="16dp" - android:textSize="14sp" /> - - </RelativeLayout> + <include layout="@layout/include_composer_layout" /> <im.vector.riotredesign.features.invite.VectorInviteView android:id="@+id/inviteView" android:layout_width="0dp" android:layout_height="wrap_content" android:visibility="gone" - tools:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/toolbar" - app:layout_constraintVertical_bias="1.0" /> + app:layout_constraintVertical_bias="1.0" + tools:visibility="gone" /> </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/vector/src/main/res/layout/include_composer_layout.xml b/vector/src/main/res/layout/include_composer_layout.xml new file mode 100644 index 00000000..655691e9 --- /dev/null +++ b/vector/src/main/res/layout/include_composer_layout.xml @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/composerLayout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:constraintSet="@layout/constraint_set_composer_layout_compact" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent"> + + <!-- ======================== + /!\ Constraints for this layout are defined in external layout files that are used as constraint set for animation. + /!\ These 3 files must be modified to stay coherent! + ======================== --> + <View + android:id="@+id/related_message_backround" + android:layout_width="0dp" + android:layout_height="0dp" + android:background="?vctr_bottom_nav_background_color" + tools:ignore="MissingConstraints" /> + + <View + android:id="@+id/related_message_background_top_separator" + android:layout_width="0dp" + android:layout_height="0dp" + android:background="?vctr_bottom_nav_background_border_color" + tools:ignore="MissingConstraints" /> + + <View + android:id="@+id/related_message_background_bottom_separator" + android:layout_width="0dp" + android:layout_height="0dp" + android:background="?vctr_bottom_nav_background_border_color" + tools:ignore="MissingConstraints" /> + + <TextView + android:id="@+id/composer_related_message_sender" + android:layout_width="0dp" + android:layout_height="0dp" + android:textStyle="bold" + tools:ignore="MissingConstraints" + tools:text="@tools:sample/first_names" + tools:visibility="gone" /> + + <TextView + android:id="@+id/composer_related_message_preview" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="3" + android:textColor="?vctr_message_text_color" + tools:ignore="MissingConstraints" + tools:text="@tools:sample/lorem" + tools:visibility="gone" /> + + <ImageView + android:id="@+id/composer_related_message_action_image" + android:layout_width="0dp" + android:layout_height="0dp" + android:tint="?android:attr/textColorTertiary" + tools:ignore="MissingConstraints" /> + + <ImageButton + android:id="@+id/composer_related_message_close" + android:layout_width="22dp" + android:layout_height="22dp" + android:background="?android:attr/selectableItemBackground" + android:src="@drawable/ic_close_round" + android:tint="@color/rosy_pink" + tools:ignore="MissingConstraints" /> + + + <ImageView + android:id="@+id/composer_avatar_view" + android:layout_width="0dp" + android:layout_height="0dp" + tools:ignore="MissingConstraints" + tools:src="@tools:sample/avatars" /> + + <ImageButton + android:id="@+id/attachmentButton" + android:layout_width="0dp" + android:layout_height="0dp" + android:background="?android:attr/selectableItemBackground" + android:src="@drawable/ic_attachment" + android:tint="?attr/colorAccent" + tools:ignore="MissingConstraints" /> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/composer_preview_barrier" + android:layout_width="0dp" + android:layout_height="0dp" + app:barrierDirection="bottom" + app:barrierMargin="8dp" + app:constraint_referenced_ids="composer_related_message_preview,composer_related_message_action_image" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + <ImageButton + android:id="@+id/sendButton" + android:layout_width="0dp" + android:layout_height="0dp" + android:background="?android:attr/selectableItemBackground" + android:src="@drawable/ic_send" + android:tint="?attr/colorAccent" + tools:ignore="MissingConstraints" /> + + <EditText + android:id="@+id/composerEditText" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:background="@android:color/transparent" + android:hint="@string/room_message_placeholder_not_encrypted" + android:nextFocusLeft="@id/composerEditText" + android:nextFocusUp="@id/composerEditText" + android:padding="8dp" + android:textColor="?vctr_message_text_color" + android:textSize="14sp" + tools:ignore="MissingConstraints" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/vector/src/main/res/values/attrs.xml b/vector/src/main/res/values/attrs.xml index 17cb3d75..04c959ee 100644 --- a/vector/src/main/res/values/attrs.xml +++ b/vector/src/main/res/values/attrs.xml @@ -4,6 +4,7 @@ <declare-styleable name="VectorStyles"> <attr name="vctr_bottom_nav_background_color" format="color" /> + <attr name="vctr_bottom_nav_background_border_color" format="color" /> <!-- waiting view background --> <attr name="vctr_waiting_background_color" format="color" /> diff --git a/vector/src/main/res/values/colors_riot.xml b/vector/src/main/res/values/colors_riot.xml index f60d4991..395ebbce 100644 --- a/vector/src/main/res/values/colors_riot.xml +++ b/vector/src/main/res/values/colors_riot.xml @@ -19,6 +19,7 @@ <color name="tab_rooms">@color/accent_color_light</color> <color name="tab_rooms_secondary">#5EA584</color> <color name="tab_groups">#a6d0e5</color> + <color name="tab_groups_secondary">#81bddb</color> <!-- color of the direct chat avatar ring (it's 50% of color accent) --> diff --git a/vector/src/main/res/values/theme_black.xml b/vector/src/main/res/values/theme_black.xml index f816b9be..dabd32ae 100644 --- a/vector/src/main/res/values/theme_black.xml +++ b/vector/src/main/res/values/theme_black.xml @@ -27,6 +27,7 @@ <!-- activities background --> <item name="android:windowBackground">@color/riot_primary_background_color_black</item> <item name="vctr_bottom_nav_background_color">@color/primary_color_black</item> + <item name="vctr_bottom_nav_background_border_color">#FFE9EDF1</item> <item name="vctr_direct_chat_circle">@drawable/direct_chat_circle_black</item> </style> diff --git a/vector/src/main/res/values/theme_dark.xml b/vector/src/main/res/values/theme_dark.xml index 5c1a506e..ba78e510 100644 --- a/vector/src/main/res/values/theme_dark.xml +++ b/vector/src/main/res/values/theme_dark.xml @@ -21,6 +21,7 @@ <!-- default background color --> <item name="android:colorBackground">@color/riot_primary_background_color_dark</item> <item name="vctr_bottom_nav_background_color">@color/primary_color_dark</item> + <item name="vctr_bottom_nav_background_border_color">#FFE9EDF1</item> <!-- waiting view background --> <item name="vctr_waiting_background_color">#55555555</item> diff --git a/vector/src/main/res/values/theme_light.xml b/vector/src/main/res/values/theme_light.xml index 68076a4b..e0b58bf0 100644 --- a/vector/src/main/res/values/theme_light.xml +++ b/vector/src/main/res/values/theme_light.xml @@ -23,6 +23,7 @@ <!-- default background color --> <item name="android:colorBackground">@color/riot_primary_background_color_light</item> <item name="vctr_bottom_nav_background_color">#FFF3F8FD</item> + <item name="vctr_bottom_nav_background_border_color">#FFE9EDF1</item> <!-- default button --> <item name="android:buttonStyle">@style/Widget.Vector.Button</item> diff --git a/vector/src/main/res/values/theme_status.xml b/vector/src/main/res/values/theme_status.xml index d2874e69..9a50b88d 100644 --- a/vector/src/main/res/values/theme_status.xml +++ b/vector/src/main/res/values/theme_status.xml @@ -23,6 +23,7 @@ <item name="android:colorBackground">@color/riot_primary_background_color_status</item> <item name="vctr_bottom_nav_background_color">@color/riot_primary_background_color_status </item> + <item name="vctr_bottom_nav_background_border_color">#FFE9EDF1</item> <item name="buttonStyle">@style/Widget.Vector.Button</item>