From 2626a761ea2aff210c62ed97e1cf45068cabbb7e Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 10 Nov 2020 09:43:54 +0100 Subject: [PATCH 1/8] EmptyRoom tile with quick actions --- .../api/session/permalinks/MatrixLinkify.kt | 16 +- .../session/room/RoomAvatarResolver.kt | 12 +- .../room/alias/GetRoomIdByAliasTask.kt | 12 +- .../membership/RoomDisplayNameResolver.kt | 32 ++-- .../room/membership/RoomMemberHelper.kt | 4 + .../src/main/res/values/strings.xml | 6 + .../home/room/detail/RoomDetailAction.kt | 8 + .../home/room/detail/RoomDetailFragment.kt | 32 +++- .../home/room/detail/RoomDetailViewEvents.kt | 7 + .../home/room/detail/RoomDetailViewModel.kt | 30 ++++ .../factory/MergedHeaderItemFactory.kt | 17 +- .../timeline/item/MergedRoomCreationItem.kt | 162 ++++++++++++++---- .../features/navigation/DefaultNavigator.kt | 4 +- .../app/features/navigation/Navigator.kt | 2 +- .../roomprofile/RoomProfileActivity.kt | 16 +- .../src/main/res/drawable/ic_add_people.xml | 10 ++ ...meline_event_merged_room_creation_stub.xml | 159 +++++++++++++++-- vector/src/main/res/values/strings.xml | 10 ++ 18 files changed, 468 insertions(+), 71 deletions(-) create mode 100644 vector/src/main/res/drawable/ic_add_people.xml diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt index 7f264c6228..5e9f3e1eb9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.api.session.permalinks import android.text.Spannable +import org.matrix.android.sdk.api.MatrixPatterns /** * MatrixLinkify take a piece of text and turns all of the @@ -35,7 +36,7 @@ object MatrixLinkify { * I disable it because it mess up with pills, and even with pills, it does not work correctly: * The url is not correct. Ex: for @user:matrix.org, the url will be @user:matrix.org, instead of a matrix.to */ - /* + // sanity checks if (spannable.isEmpty()) { return false @@ -48,14 +49,21 @@ object MatrixLinkify { val startPos = match.range.first if (startPos == 0 || text[startPos - 1] != '/') { val endPos = match.range.last + 1 - val url = text.substring(match.range) + var url = text.substring(match.range) + if (MatrixPatterns.isUserId(url) + || MatrixPatterns.isRoomAlias(url) + || MatrixPatterns.isRoomId(url) + || MatrixPatterns.isGroupId(url) + || MatrixPatterns.isEventId(url)) { + url = PermalinkService.MATRIX_TO_URL_BASE + url + } val span = MatrixPermalinkSpan(url, callback) spannable.setSpan(span, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) } } } return hasMatch - */ - return false + +// return false } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt index 90ee99a919..58633c39ba 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt @@ -46,11 +46,13 @@ internal class RoomAvatarResolver @Inject constructor(@UserId private val userId val roomMembers = RoomMemberHelper(realm, roomId) val members = roomMembers.queryActiveRoomMembersEvent().findAll() // detect if it is a room with no more than 2 members (i.e. an alone or a 1:1 chat) - if (members.size == 1) { - res = members.firstOrNull()?.avatarUrl - } else if (members.size == 2) { - val firstOtherMember = members.where().notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId).findFirst() - res = firstOtherMember?.avatarUrl + if (roomMembers.isDirectRoom()) { + if (members.size == 1) { + res = members.firstOrNull()?.avatarUrl + } else if (members.size == 2) { + val firstOtherMember = members.where().notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId).findFirst() + res = firstOtherMember?.avatarUrl + } } return res } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt index 8b011980d0..ebbd3b041a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt @@ -26,6 +26,7 @@ import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.task.Task import io.realm.Realm import org.greenrobot.eventbus.EventBus +import timber.log.Timber import javax.inject.Inject internal interface GetRoomIdByAliasTask : Task> { @@ -50,9 +51,14 @@ internal class DefaultGetRoomIdByAliasTask @Inject constructor( } else if (!params.searchOnServer) { Optional.from(null) } else { - roomId = executeRequest(eventBus) { - apiCall = roomAPI.getRoomIdByAlias(params.roomAlias) - }.roomId + roomId = try { + executeRequest(eventBus) { + apiCall = roomAPI.getRoomIdByAlias(params.roomAlias) + }.roomId + } catch (throwable: Throwable) { + Timber.d(throwable, "## Failed to get roomId from alias") + null + } Optional.from(roomId) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt index 7f3796c1ce..73ae66a5b2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -93,6 +93,9 @@ internal class RoomDisplayNameResolver @Inject constructor( } } else if (roomEntity?.membership == Membership.JOIN) { val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst() + val invitedCount = roomSummary?.invitedMembersCount ?: 0 + val joinedCount = roomSummary?.joinedMembersCount ?: 0 + val othersTotalCount = invitedCount + joinedCount - 1 val otherMembersSubset: List = if (roomSummary?.heroes?.isNotEmpty() == true) { roomSummary.heroes.mapNotNull { userId -> roomMembers.getLastRoomMember(userId)?.takeIf { @@ -102,22 +105,29 @@ internal class RoomDisplayNameResolver @Inject constructor( } else { activeMembers.where() .notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId) - .limit(3) + .limit(5) .findAll() .createSnapshot() } val otherMembersCount = otherMembersSubset.count() name = when (otherMembersCount) { - 0 -> stringProvider.getString(R.string.room_displayname_empty_room) - 1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers) - 2 -> stringProvider.getString(R.string.room_displayname_two_members, - resolveRoomMemberName(otherMembersSubset[0], roomMembers), - resolveRoomMemberName(otherMembersSubset[1], roomMembers) - ) - else -> stringProvider.getQuantityString(R.plurals.room_displayname_three_and_more_members, - roomMembers.getNumberOfJoinedMembers() - 1, - resolveRoomMemberName(otherMembersSubset[0], roomMembers), - roomMembers.getNumberOfJoinedMembers() - 1) + 0 -> { + stringProvider.getString(R.string.room_displayname_empty_room) + // TODO (was xx and yyy) ... + } + 1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers) + else -> { + val names = otherMembersSubset.map { + resolveRoomMemberName(it, roomMembers) ?: "" + } + if (otherMembersCount <= othersTotalCount) { + val remainingCount = invitedCount + joinedCount - names.size + (names.joinToString("${stringProvider.getString(R.string.room_displayname_separator)} ") + + " " + stringProvider.getQuantityString(R.plurals.and_n_others, remainingCount, remainingCount)) + } else { + names.dropLast(1).joinToString(", ") + " & ${names.last()}" + } + } } } return name ?: roomId diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt index 7105a2cc22..c18eb0936b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt @@ -98,6 +98,10 @@ internal class RoomMemberHelper(private val realm: Realm, return getNumberOfJoinedMembers() + getNumberOfInvitedMembers() } + fun isDirectRoom() : Boolean { + return roomSummary?.isDirect ?: false + } + /** * Return all the roomMembers ids which are joined or invited to the room * diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml index de30a64c32..c391a4edc8 100644 --- a/matrix-sdk-android/src/main/res/values/strings.xml +++ b/matrix-sdk-android/src/main/res/values/strings.xml @@ -180,8 +180,14 @@ %1$s and 1 other %1$s and %2$d others + + & %d other + & %d others + + , Empty room + Empty room (was %s) Initial Sync:\nImporting account… Initial Sync:\nImporting crypto diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index 99adc0bf83..8891218a11 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -16,6 +16,8 @@ package im.vector.app.features.home.room.detail +import android.net.Uri +import android.view.View import im.vector.app.core.platform.VectorViewModelAction import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.events.model.Event @@ -24,6 +26,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachme import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.widgets.model.Widget +import org.matrix.android.sdk.api.util.MatrixItem sealed class RoomDetailAction : VectorViewModelAction { data class UserIsTyping(val isTyping: Boolean) : RoomDetailAction() @@ -90,4 +93,9 @@ sealed class RoomDetailAction : VectorViewModelAction { data class OpenOrCreateDm(val userId: String) : RoomDetailAction() data class JumpToReadReceipt(val userId: String) : RoomDetailAction() + object QuickActionInvitePeople : RoomDetailAction() + object QuickActionSetAvatar : RoomDetailAction() + data class SetAvatarAction(val newAvatarUri: Uri, val newAvatarFileName: String) : RoomDetailAction() + object QuickActionSetTopic : RoomDetailAction() + data class ShowRoomAvatarFullScreen(val matrixItem: MatrixItem?, val transitionView: View?) : RoomDetailAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 9c6c473a7f..bec84f6479 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -71,6 +71,7 @@ import com.google.android.material.textfield.TextInputEditText import com.jakewharton.rxbinding3.widget.textChanges import im.vector.app.R import im.vector.app.core.dialogs.ConfirmationDialogBuilder +import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper import im.vector.app.core.dialogs.withColoredButton import im.vector.app.core.epoxy.LayoutManagerStateRestorer import im.vector.app.core.extensions.cleanup @@ -82,6 +83,7 @@ import im.vector.app.core.extensions.showKeyboard import im.vector.app.core.extensions.trackItemsVisibilityChange import im.vector.app.core.glide.GlideApp import im.vector.app.core.glide.GlideRequests +import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider @@ -149,6 +151,7 @@ import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.permalink.NavigationInterceptor import im.vector.app.features.permalink.PermalinkHandler import im.vector.app.features.reactions.EmojiReactionPickerActivity +import im.vector.app.features.roomprofile.RoomProfileActivity import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData @@ -196,6 +199,7 @@ import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode import timber.log.Timber import java.io.File import java.net.URL +import java.util.UUID import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -229,7 +233,7 @@ class RoomDetailFragment @Inject constructor( JumpToReadMarkerView.Callback, AttachmentTypeSelectorView.Callback, AttachmentsHelper.Callback, -// RoomWidgetsBannerView.Callback, + GalleryOrCameraDialogHelper.Listener, ActiveCallView.Callback { companion object { @@ -250,6 +254,8 @@ class RoomDetailFragment @Inject constructor( private const val ircPattern = " (IRC)" } + private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) + private val roomDetailArgs: RoomDetailArgs by args() private val glideRequests by lazy { GlideApp.with(this) @@ -364,6 +370,12 @@ class RoomDetailFragment @Inject constructor( RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView() is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it) is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(it) + RoomDetailViewEvents.OpenInvitePeople -> navigator.openInviteUsersToRoom(requireContext(), roomDetailArgs.roomId) + RoomDetailViewEvents.OpenSetRoomAvatarDialog -> galleryOrCameraDialogHelper.show() + RoomDetailViewEvents.OpenRoomSettings -> handleOpenRoomSettings() + is RoomDetailViewEvents.ShowRoomAvatarFullScreen -> it.matrixItem?.let { item -> + navigator.openBigImageViewer(requireActivity(), it.view, item) + } }.exhaustive } @@ -372,6 +384,24 @@ class RoomDetailFragment @Inject constructor( } } + override fun onImageReady(uri: Uri?) { + uri ?: return + roomDetailViewModel.handle( + RoomDetailAction.SetAvatarAction( + newAvatarUri = uri, + newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString() + ) + ) + } + + private fun handleOpenRoomSettings() { + navigator.openRoomProfile( + requireContext(), + roomDetailArgs.roomId, + RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_SETTINGS + ) + } + private fun handleOpenRoom(openRoom: RoomDetailViewEvents.OpenRoom) { navigator.openRoom(requireContext(), openRoom.roomId, null) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index b9e3e6b31d..d5d94a0ca5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -17,10 +17,12 @@ package im.vector.app.features.home.room.detail import android.net.Uri +import android.view.View import androidx.annotation.StringRes import im.vector.app.core.platform.VectorViewEvents import im.vector.app.features.command.Command import org.matrix.android.sdk.api.session.widgets.model.Widget +import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode import java.io.File @@ -43,6 +45,11 @@ sealed class RoomDetailViewEvents : VectorViewEvents { data class NavigateToEvent(val eventId: String) : RoomDetailViewEvents() data class JoinJitsiConference(val widget: Widget, val withVideo: Boolean) : RoomDetailViewEvents() + object OpenInvitePeople : RoomDetailViewEvents() + object OpenSetRoomAvatarDialog : RoomDetailViewEvents() + object OpenRoomSettings : RoomDetailViewEvents() + data class ShowRoomAvatarFullScreen(val matrixItem: MatrixItem?, val view: View?) : RoomDetailViewEvents() + object ShowWaitingView : RoomDetailViewEvents() object HideWaitingView : RoomDetailViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 98bcdbe60e..beaecbb898 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -277,9 +277,39 @@ class RoomDetailViewModel @AssistedInject constructor( is RoomDetailAction.CancelSend -> handleCancel(action) is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action) is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action) + RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople() + RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar() + is RoomDetailAction.SetAvatarAction -> handleSetNewAvatar(action) + RoomDetailAction.QuickActionSetTopic -> _viewEvents.post(RoomDetailViewEvents.OpenRoomSettings) + is RoomDetailAction.ShowRoomAvatarFullScreen -> { + _viewEvents.post( + RoomDetailViewEvents.ShowRoomAvatarFullScreen(action.matrixItem, action.transitionView) + ) + } }.exhaustive } + private fun handleSetNewAvatar(action: RoomDetailAction.SetAvatarAction) { + viewModelScope.launch(Dispatchers.IO) { + try { + awaitCallback { + room.updateAvatar(action.newAvatarUri, action.newAvatarFileName, it) + } + _viewEvents.post(RoomDetailViewEvents.ActionSuccess(action)) + } catch (failure: Throwable) { + _viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure)) + } + } + } + + private fun handleInvitePeople() { + _viewEvents.post(RoomDetailViewEvents.OpenInvitePeople) + } + + private fun handleQuickSetAvatar() { + _viewEvents.post(RoomDetailViewEvents.OpenSetRoomAvatarDialog) + } + private fun handleOpenOrCreateDm(action: RoomDetailAction.OpenOrCreateDm) { val existingDmRoomId = session.getExistingDirectRoomWithUser(action.userId) if (existingDmRoomId == null) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt index e7a911ceb1..23bd041e95 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt @@ -31,10 +31,14 @@ import im.vector.app.features.home.room.detail.timeline.item.MergedMembershipEve import im.vector.app.features.home.room.detail.timeline.item.MergedMembershipEventsItem_ import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem_ +import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovementMethod import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent +import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent @@ -187,6 +191,11 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde collapsedEventIds.removeAll(mergedEventIds) } val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() } + val powerLevelsHelper = roomSummaryHolder.roomSummary?.roomId + ?.let { activeSessionHolder.getSafeActiveSession()?.getRoom(it) } + ?.let { it.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition)?.content?.toModel() } + ?.let { PowerLevelsHelper(it) } + val currentUserId = activeSessionHolder.getSafeActiveSession()?.myUserId ?: "" val attributes = MergedRoomCreationItem.Attributes( isCollapsed = isCollapsed, mergeData = mergedData, @@ -198,13 +207,19 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde hasEncryptionEvent = hasEncryption, isEncryptionAlgorithmSecure = encryptionAlgorithm == MXCRYPTO_ALGORITHM_MEGOLM, readReceiptsCallback = callback, - currentUserId = activeSessionHolder.getSafeActiveSession()?.myUserId ?: "" + callback = callback, + currentUserId = currentUserId, + roomSummary = roomSummaryHolder.roomSummary, + canChangeAvatar = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_AVATAR) ?: false, + canChangeTopic = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_TOPIC) ?: false, + canChangeName = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_NAME) ?: false ) MergedRoomCreationItem_() .id(mergeId) .leftGuideline(avatarSizeProvider.leftGuideline) .highlighted(isCollapsed && highlighted) .attributes(attributes) + .movementMethod(createLinkMovementMethod(callback)) .also { it.setOnVisibilityStateChanged(MergedTimelineEventVisibilityStateChangedListener(callback, mergedEvents)) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt index 1896a812fc..a1c0b869e2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt @@ -16,11 +16,14 @@ package im.vector.app.features.home.room.detail.timeline.item +import android.text.SpannableString +import android.text.method.MovementMethod +import android.text.style.ClickableSpan import android.view.View import android.view.ViewGroup import android.widget.ImageView -import android.widget.RelativeLayout import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.core.view.isGone import androidx.core.view.isVisible @@ -28,8 +31,16 @@ import androidx.core.view.updateLayoutParams import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R +import im.vector.app.core.extensions.setTextOrHide +import im.vector.app.core.utils.DebouncedClickListener +import im.vector.app.core.utils.tappableMatchingText import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.timeline.TimelineEventController +import im.vector.app.features.home.room.detail.timeline.tools.linkify +import me.gujun.android.span.span +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.util.toMatrixItem @EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo) abstract class MergedRoomCreationItem : BasedMergedItem() { @@ -37,11 +48,16 @@ abstract class MergedRoomCreationItem : BasedMergedItem { - this.marginEnd = leftGuideline - } - if (attributes.isEncryptionAlgorithmSecure) { - holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_enabled) - holder.e2eTitleDescriptionView.text = if (data?.isDirectRoom == true) { - holder.expandView.resources.getString(R.string.direct_room_encryption_enabled_tile_description) - } else { - holder.expandView.resources.getString(R.string.encryption_enabled_tile_description) - } - holder.e2eTitleDescriptionView.textAlignment = View.TEXT_ALIGNMENT_CENTER - holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds( - ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_black), - null, null, null - ) - } else { - holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_not_enabled) - holder.e2eTitleDescriptionView.text = holder.expandView.resources.getString(R.string.encryption_unknown_algorithm_tile_description) - holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds( - ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_warning), - null, null, null - ) - } - } else { - holder.encryptionTile.isVisible = false - } + bindEncryptionTile(holder, data) } else { holder.avatarView.visibility = View.INVISIBLE holder.summaryView.visibility = View.GONE @@ -107,6 +96,107 @@ abstract class MergedRoomCreationItem : BasedMergedItem { + this.marginEnd = leftGuideline + } + if (attributes.isEncryptionAlgorithmSecure) { + holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_enabled) + holder.e2eTitleDescriptionView.text = if (data?.isDirectRoom == true) { + holder.expandView.resources.getString(R.string.direct_room_encryption_enabled_tile_description) + } else { + holder.expandView.resources.getString(R.string.encryption_enabled_tile_description) + } + holder.e2eTitleDescriptionView.textAlignment = View.TEXT_ALIGNMENT_CENTER + holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds( + ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_black), + null, null, null + ) + } else { + holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_not_enabled) + holder.e2eTitleDescriptionView.text = holder.expandView.resources.getString(R.string.encryption_unknown_algorithm_tile_description) + holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds( + ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_warning), + null, null, null + ) + } + } else { + holder.encryptionTile.isVisible = false + } + } + + private fun bindCreationSummaryTile(holder: Holder) { + val roomSummary = attributes.roomSummary + val roomDisplayName = roomSummary?.displayName + holder.roomNameText.setTextOrHide(roomDisplayName) + val isDirect = roomSummary?.isDirect == true + val membersCount = roomSummary?.otherMemberIds?.size ?: 0 + + if (isDirect) { + holder.roomDescriptionText.text = holder.view.resources.getString(R.string.this_is_the_beginning_of_dm, roomSummary?.displayName ?: "") + } else if (roomDisplayName.isNullOrBlank() || roomSummary.name.isBlank()) { + holder.roomDescriptionText.text = holder.view.resources.getString(R.string.this_is_the_beginning_of_room_no_name) + } else { + holder.roomDescriptionText.text = holder.view.resources.getString(R.string.this_is_the_beginning_of_room, roomDisplayName) + } + + val topic = roomSummary?.topic + if (topic.isNullOrBlank()) { + // do not show hint for DMs or group DMs + if (!isDirect) { + val addTopicLink = holder.view.resources.getString(R.string.add_a_topic_link_text) + val styledText = SpannableString(holder.view.resources.getString(R.string.room_created_summary_no_topic_creation_text, addTopicLink)) + holder.roomTopicText.setTextOrHide(styledText.tappableMatchingText(addTopicLink, object : ClickableSpan() { + override fun onClick(widget: View) { + attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionSetTopic) + } + })) + } + } else { + holder.roomTopicText.setTextOrHide(span { + span(holder.view.resources.getString(R.string.topic_prefix)) { + textStyle = "bold" + } + +topic.linkify(attributes.callback) + } + ) + } + holder.roomTopicText.movementMethod = movementMethod + val roomItem = roomSummary?.toMatrixItem() + if (roomItem != null) { + holder.roomAvatarImageView.isVisible = true + attributes.avatarRenderer.render(roomItem, holder.roomAvatarImageView) + holder.roomAvatarImageView.setOnClickListener(DebouncedClickListener({ view -> + attributes.callback?.onTimelineItemAction(RoomDetailAction.ShowRoomAvatarFullScreen(roomItem, view)) + })) + } else { + holder.roomAvatarImageView.isVisible = false + } + + if (isDirect) { + holder.addPeopleButton.isVisible = false + } else { + holder.addPeopleButton.isVisible = true + holder.addPeopleButton.setOnClickListener(DebouncedClickListener({ _ -> + attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionInvitePeople) + })) + } + + val shouldShowSetAvatar = attributes.canChangeAvatar + && (roomSummary?.isDirect == false || (isDirect && membersCount >= 2)) + + if (shouldShowSetAvatar && roomItem?.avatarUrl.isNullOrBlank()) { + holder.setAvatarButton.isVisible = true + holder.setAvatarButton.setOnClickListener(DebouncedClickListener({ _ -> + attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionSetAvatar) + })) + } else { + holder.setAvatarButton.isVisible = false + } + } + class Holder : BasedMergedItem.Holder(STUB_ID) { val summaryView by bind(R.id.itemNoticeTextView) val avatarView by bind(R.id.itemNoticeAvatarView) @@ -114,6 +204,13 @@ abstract class MergedRoomCreationItem : BasedMergedItem(R.id.itemVerificationDoneTitleTextView) val e2eTitleDescriptionView by bind(R.id.itemVerificationDoneDetailTextView) + + val roomNameText by bind(R.id.roomNameTileText) + val roomDescriptionText by bind(R.id.roomNameDescriptionText) + val roomTopicText by bind(R.id.roomNameTopicText) + val roomAvatarImageView by bind(R.id.roomAvatarImageView) + val addPeopleButton by bind(R.id.creationTileAddPeopleButton) + val setAvatarButton by bind(R.id.creationTileSetAvatarButton) } companion object { @@ -126,8 +223,13 @@ abstract class MergedRoomCreationItem : BasedMergedItem Unit, + val callback: TimelineEventController.Callback? = null, val currentUserId: String, val hasEncryptionEvent: Boolean, - val isEncryptionAlgorithmSecure: Boolean + val isEncryptionAlgorithmSecure: Boolean, + val roomSummary: RoomSummary?, + val canChangeAvatar: Boolean = false, + val canChangeName: Boolean = false, + val canChangeTopic: Boolean = false ) : BasedMergedItem.Attributes } diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 106d804cd3..2d0ca86d52 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -248,8 +248,8 @@ class DefaultNavigator @Inject constructor( context.startActivity(KeysBackupManageActivity.intent(context)) } - override fun openRoomProfile(context: Context, roomId: String) { - context.startActivity(RoomProfileActivity.newIntent(context, roomId)) + override fun openRoomProfile(context: Context, roomId: String, directAccess: Int?) { + context.startActivity(RoomProfileActivity.newIntent(context, roomId, directAccess)) } override fun openBigImageViewer(activity: Activity, sharedElement: View?, matrixItem: MatrixItem) { diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index 1d01a5e4f0..504fccb63a 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -78,7 +78,7 @@ interface Navigator { fun openRoomMemberProfile(userId: String, roomId: String?, context: Context, buildTask: Boolean = false) - fun openRoomProfile(context: Context, roomId: String) + fun openRoomProfile(context: Context, roomId: String, directAccess: Int? = null) fun openBigImageViewer(activity: Activity, sharedElement: View?, matrixItem: MatrixItem) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt index 734620e378..609042ffa4 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt @@ -46,10 +46,16 @@ class RoomProfileActivity : companion object { - fun newIntent(context: Context, roomId: String): Intent { + private const val EXTRA_DIRECT_ACCESS = "EXTRA_DIRECT_ACCESS" + + const val EXTRA_DIRECT_ACCESS_ROOM_ROOT = 0 + const val EXTRA_DIRECT_ACCESS_ROOM_SETTINGS = 1 + + fun newIntent(context: Context, roomId: String, directAccess: Int?): Intent { val roomProfileArgs = RoomProfileArgs(roomId) return Intent(context, RoomProfileActivity::class.java).apply { putExtra(MvRx.KEY_ARG, roomProfileArgs) + putExtra(EXTRA_DIRECT_ACCESS, directAccess) } } } @@ -80,7 +86,13 @@ class RoomProfileActivity : sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java) roomProfileArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return if (isFirstCreation()) { - addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs) + when (intent?.extras?.getInt(EXTRA_DIRECT_ACCESS, EXTRA_DIRECT_ACCESS_ROOM_ROOT)) { + EXTRA_DIRECT_ACCESS_ROOM_SETTINGS -> { + addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs) + addFragmentToBackstack(R.id.simpleFragmentContainer, RoomSettingsFragment::class.java, roomProfileArgs) + } + else -> addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs) + } } sharedActionViewModel .observe() diff --git a/vector/src/main/res/drawable/ic_add_people.xml b/vector/src/main/res/drawable/ic_add_people.xml new file mode 100644 index 0000000000..3ec60095ff --- /dev/null +++ b/vector/src/main/res/drawable/ic_add_people.xml @@ -0,0 +1,10 @@ + + + diff --git a/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml b/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml index eefa387254..6fdd30b9e2 100644 --- a/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml @@ -1,31 +1,168 @@ - + android:layout_marginTop="0dp"> - + - + + + + + + + + + + + + + + + + + + + + + + + + + + android:layout_below="@+id/creationTile" + android:layout_marginTop="8dp"> You created and configured the room. %s joined. You joined. + This is the beginning of %s. + This is the beginning of this conversation. + This is the beginning of your direct message history with %s. + + %s to let people know what this room is about. + Add a topic + "Topic: " Almost there! Is the other device showing the same shield? Almost there! Waiting for confirmation… @@ -2511,6 +2518,7 @@ "We couldn't create your DM. Please check the users you want to invite and try again." Add members + Add people INVITE Inviting users… Invite Users @@ -2575,6 +2583,8 @@ Room Name Topic You changed room settings successfully + Set avatar + You cannot access this message Waiting for this message, this may take a while From 264bc52bcc2d8844765201892c9869e7ed9d5cbd Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 13 Nov 2020 09:27:34 +0100 Subject: [PATCH 2/8] WIP review --- .../session/room/alias/GetRoomIdByAliasTask.kt | 15 ++++++--------- .../timeline/item/MergedRoomCreationItem.kt | 13 +++++++------ .../RoomMemberProfileController.kt | 2 +- ...m_timeline_event_merged_room_creation_stub.xml | 2 +- vector/src/main/res/values/strings.xml | 2 +- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt index ebbd3b041a..58a119cc77 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt @@ -17,6 +17,9 @@ package org.matrix.android.sdk.internal.session.room.alias import com.zhuinden.monarchy.Monarchy +import io.realm.Realm +import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.findByAlias @@ -24,9 +27,6 @@ import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.task.Task -import io.realm.Realm -import org.greenrobot.eventbus.EventBus -import timber.log.Timber import javax.inject.Inject internal interface GetRoomIdByAliasTask : Task> { @@ -51,14 +51,11 @@ internal class DefaultGetRoomIdByAliasTask @Inject constructor( } else if (!params.searchOnServer) { Optional.from(null) } else { - roomId = try { + roomId = tryOrNull("## Failed to get roomId from alias") { executeRequest(eventBus) { apiCall = roomAPI.getRoomIdByAlias(params.roomAlias) - }.roomId - } catch (throwable: Throwable) { - Timber.d(throwable, "## Failed to get roomId from alias") - null - } + } + }?.roomId Optional.from(roomId) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt index a1c0b869e2..dcdb2bab29 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt @@ -155,12 +155,13 @@ abstract class MergedRoomCreationItem : BasedMergedItem + tools:text="@string/room_created_summary_no_topic_creation_text" /> This is the beginning of this conversation. This is the beginning of your direct message history with %s. - %s to let people know what this room is about. + %s to let people know what this room is about. Add a topic "Topic: " From 1de5cd2e61b53d00c0289bff5f7333962c97c239 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 16 Nov 2020 09:56:26 +0100 Subject: [PATCH 3/8] Code review --- .../session/room/RoomAvatarResolver.kt | 5 ++- .../membership/RoomDisplayNameResolver.kt | 38 ++++++++++++++----- .../room/membership/RoomMemberHelper.kt | 8 +--- .../src/main/res/values/strings.xml | 8 ++-- .../RoomMemberProfileController.kt | 2 +- 5 files changed, 38 insertions(+), 23 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt index 58633c39ba..99f9d3644d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt @@ -26,6 +26,8 @@ import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import io.realm.Realm +import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity +import org.matrix.android.sdk.internal.database.query.where import javax.inject.Inject internal class RoomAvatarResolver @Inject constructor(@UserId private val userId: String) { @@ -46,7 +48,8 @@ internal class RoomAvatarResolver @Inject constructor(@UserId private val userId val roomMembers = RoomMemberHelper(realm, roomId) val members = roomMembers.queryActiveRoomMembersEvent().findAll() // detect if it is a room with no more than 2 members (i.e. an alone or a 1:1 chat) - if (roomMembers.isDirectRoom()) { + val isDirectRoom = RoomSummaryEntity.where(realm, roomId).findFirst()?.isDirect ?: false + if (isDirectRoom) { if (members.size == 1) { res = members.firstOrNull()?.avatarUrl } else if (members.size == 2) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt index 73ae66a5b2..8d0789d675 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -116,17 +116,35 @@ internal class RoomDisplayNameResolver @Inject constructor( // TODO (was xx and yyy) ... } 1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers) + 2 -> { + stringProvider.getString(R.string.room_displayname_two_members, + resolveRoomMemberName(otherMembersSubset[0], roomMembers), + resolveRoomMemberName(otherMembersSubset[1], roomMembers) + ) + } + 3 -> { + stringProvider.getString(R.string.room_displayname_3_members, + resolveRoomMemberName(otherMembersSubset[0], roomMembers), + resolveRoomMemberName(otherMembersSubset[1], roomMembers), + resolveRoomMemberName(otherMembersSubset[2], roomMembers) + ) + } + 4 -> { + stringProvider.getString(R.string.room_displayname_4_members, + resolveRoomMemberName(otherMembersSubset[0], roomMembers), + resolveRoomMemberName(otherMembersSubset[1], roomMembers), + resolveRoomMemberName(otherMembersSubset[2], roomMembers), + resolveRoomMemberName(otherMembersSubset[3], roomMembers) + ) + } else -> { - val names = otherMembersSubset.map { - resolveRoomMemberName(it, roomMembers) ?: "" - } - if (otherMembersCount <= othersTotalCount) { - val remainingCount = invitedCount + joinedCount - names.size - (names.joinToString("${stringProvider.getString(R.string.room_displayname_separator)} ") - + " " + stringProvider.getQuantityString(R.plurals.and_n_others, remainingCount, remainingCount)) - } else { - names.dropLast(1).joinToString(", ") + " & ${names.last()}" - } + val remainingCount = invitedCount + joinedCount - otherMembersCount + 1 + stringProvider.getString(R.string.room_displayname_four_and_more_members, + resolveRoomMemberName(otherMembersSubset[0], roomMembers), + resolveRoomMemberName(otherMembersSubset[1], roomMembers), + resolveRoomMemberName(otherMembersSubset[2], roomMembers), + remainingCount + ) } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt index c18eb0936b..2a7c46bd42 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomMemberHelper.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.internal.session.room.membership +import io.realm.Realm +import io.realm.RealmQuery import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity @@ -25,8 +27,6 @@ import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFie import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.database.query.where -import io.realm.Realm -import io.realm.RealmQuery /** * This class is an helper around STATE_ROOM_MEMBER events. @@ -98,10 +98,6 @@ internal class RoomMemberHelper(private val realm: Realm, return getNumberOfJoinedMembers() + getNumberOfInvitedMembers() } - fun isDirectRoom() : Boolean { - return roomSummary?.isDirect ?: false - } - /** * Return all the roomMembers ids which are joined or invited to the room * diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml index c391a4edc8..130ad5570c 100644 --- a/matrix-sdk-android/src/main/res/values/strings.xml +++ b/matrix-sdk-android/src/main/res/values/strings.xml @@ -175,16 +175,14 @@ %1$s and %2$s + %1$s, %2$s and %3$s + %1$s, %2$s, %3$s and %4$s + %1$s, %2$s, %3$s and %4$d others %1$s and 1 other %1$s and %2$d others - - & %d other - & %d others - - , Empty room Empty room (was %s) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index 44e726735f..2e91091443 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -161,7 +161,7 @@ class RoomMemberProfileController @Inject constructor( } else { genericFooterItem { id("verify_footer_not_encrypted") - text(RRstringProvider.getString(R.string.room_profile_not_encrypted_subtitle)) + text(stringProvider.getString(R.string.room_profile_not_encrypted_subtitle)) centered(false) } } From 206e68b1d202a30523a59eaddd2081b8a24cf3d0 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 18 Nov 2020 10:17:50 +0100 Subject: [PATCH 4/8] Unused val --- .../internal/session/room/membership/RoomDisplayNameResolver.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt index 8d0789d675..c69773fbeb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -95,7 +95,6 @@ internal class RoomDisplayNameResolver @Inject constructor( val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst() val invitedCount = roomSummary?.invitedMembersCount ?: 0 val joinedCount = roomSummary?.joinedMembersCount ?: 0 - val othersTotalCount = invitedCount + joinedCount - 1 val otherMembersSubset: List = if (roomSummary?.heroes?.isNotEmpty() == true) { roomSummary.heroes.mapNotNull { userId -> roomMembers.getLastRoomMember(userId)?.takeIf { From 1eac90e5b153879d9a2a16dba35ba98a87933699 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Nov 2020 16:07:32 +0100 Subject: [PATCH 5/8] Use plurals for proper i18n --- .../room/membership/RoomDisplayNameResolver.kt | 14 ++++++++------ matrix-sdk-android/src/main/res/values/strings.xml | 9 +++++++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt index c69773fbeb..a7dfcfc96f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -110,25 +110,25 @@ internal class RoomDisplayNameResolver @Inject constructor( } val otherMembersCount = otherMembersSubset.count() name = when (otherMembersCount) { - 0 -> { + 0 -> { stringProvider.getString(R.string.room_displayname_empty_room) // TODO (was xx and yyy) ... } - 1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers) - 2 -> { + 1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers) + 2 -> { stringProvider.getString(R.string.room_displayname_two_members, resolveRoomMemberName(otherMembersSubset[0], roomMembers), resolveRoomMemberName(otherMembersSubset[1], roomMembers) ) } - 3 -> { + 3 -> { stringProvider.getString(R.string.room_displayname_3_members, resolveRoomMemberName(otherMembersSubset[0], roomMembers), resolveRoomMemberName(otherMembersSubset[1], roomMembers), resolveRoomMemberName(otherMembersSubset[2], roomMembers) ) } - 4 -> { + 4 -> { stringProvider.getString(R.string.room_displayname_4_members, resolveRoomMemberName(otherMembersSubset[0], roomMembers), resolveRoomMemberName(otherMembersSubset[1], roomMembers), @@ -138,7 +138,9 @@ internal class RoomDisplayNameResolver @Inject constructor( } else -> { val remainingCount = invitedCount + joinedCount - otherMembersCount + 1 - stringProvider.getString(R.string.room_displayname_four_and_more_members, + stringProvider.getQuantityString( + R.plurals.room_displayname_four_and_more_members, + remainingCount, resolveRoomMemberName(otherMembersSubset[0], roomMembers), resolveRoomMemberName(otherMembersSubset[1], roomMembers), resolveRoomMemberName(otherMembersSubset[2], roomMembers), diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml index 130ad5570c..f77cd3203d 100644 --- a/matrix-sdk-android/src/main/res/values/strings.xml +++ b/matrix-sdk-android/src/main/res/values/strings.xml @@ -175,10 +175,15 @@ %1$s and %2$s + %1$s, %2$s and %3$s + %1$s, %2$s, %3$s and %4$s - %1$s, %2$s, %3$s and %4$d others - + + + %1$s, %2$s, %3$s and %4$d other + %1$s, %2$s, %3$s and %4$d others + %1$s and 1 other %1$s and %2$d others From b82b378cfe386e394874a2a4ea46f4cf069ba1b3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Nov 2020 17:00:44 +0100 Subject: [PATCH 6/8] Cleanup and changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 936e6b0ffe..e98329d91f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ Features ✨: - Improvements 🙌: + - New room creation tile with quick action (#2346) - Open an existing DM instead of creating a new one (#2319) - Ask for explicit user consent to send their contact details to the identity server (#2375) - Handle events of type "m.room.server_acl" (#890) From 9ed8f26d7c78301ae1b619ab6c25e51a80e77d0c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Nov 2020 17:15:36 +0100 Subject: [PATCH 7/8] Make the room default avatar clickable to set it (as per the small picto) And do some cleanup --- .../timeline/item/MergedRoomCreationItem.kt | 43 ++++++++++--------- ...meline_event_merged_room_creation_stub.xml | 28 +++++------- 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt index dcdb2bab29..34b9ae1b9d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt @@ -165,36 +165,37 @@ abstract class MergedRoomCreationItem : BasedMergedItem= 2)) + && roomItem?.avatarUrl.isNullOrBlank() + + holder.roomAvatarImageView.isVisible = roomItem != null if (roomItem != null) { - holder.roomAvatarImageView.isVisible = true attributes.avatarRenderer.render(roomItem, holder.roomAvatarImageView) holder.roomAvatarImageView.setOnClickListener(DebouncedClickListener({ view -> - attributes.callback?.onTimelineItemAction(RoomDetailAction.ShowRoomAvatarFullScreen(roomItem, view)) - })) - } else { - holder.roomAvatarImageView.isVisible = false - } - - if (isDirect) { - holder.addPeopleButton.isVisible = false - } else { - holder.addPeopleButton.isVisible = true - holder.addPeopleButton.setOnClickListener(DebouncedClickListener({ _ -> - attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionInvitePeople) + if (shouldSetAvatar) { + attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionSetAvatar) + } else { + // Note: this is no op if there is no avatar on the room + attributes.callback?.onTimelineItemAction(RoomDetailAction.ShowRoomAvatarFullScreen(roomItem, view)) + } })) } - val shouldShowSetAvatar = attributes.canChangeAvatar - && (roomSummary?.isDirect == false || (isDirect && membersCount >= 2)) - - if (shouldShowSetAvatar && roomItem?.avatarUrl.isNullOrBlank()) { - holder.setAvatarButton.isVisible = true + holder.setAvatarButton.isVisible = shouldSetAvatar + if (shouldSetAvatar) { holder.setAvatarButton.setOnClickListener(DebouncedClickListener({ _ -> attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionSetAvatar) })) - } else { - holder.setAvatarButton.isVisible = false + } + + holder.addPeopleButton.isVisible = !isDirect + if (!isDirect) { + holder.addPeopleButton.setOnClickListener(DebouncedClickListener({ _ -> + attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionInvitePeople) + })) } } @@ -209,7 +210,7 @@ abstract class MergedRoomCreationItem : BasedMergedItem(R.id.roomNameTileText) val roomDescriptionText by bind(R.id.roomNameDescriptionText) val roomTopicText by bind(R.id.roomNameTopicText) - val roomAvatarImageView by bind(R.id.roomAvatarImageView) + val roomAvatarImageView by bind(R.id.creationTileRoomAvatarImageView) val addPeopleButton by bind(R.id.creationTileAddPeopleButton) val setAvatarButton by bind(R.id.creationTileSetAvatarButton) } diff --git a/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml b/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml index 8dba361f5e..ccb9bacd30 100644 --- a/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml @@ -3,16 +3,12 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - + android:layout_height="wrap_content"> + android:layout_height="wrap_content"> + @@ -74,8 +71,8 @@ android:textStyle="bold" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/roomAvatarImageView" - tools:text="Room Name" /> + app:layout_constraintTop_toBottomOf="@id/creationTileRoomAvatarImageView" + tools:text="@sample/matrix.json/data/roomName" /> - + tools:text="@string/this_is_the_beginning_of_room_no_name" /> - + app:layout_constraintTop_toTopOf="@id/addPeopleButtonBg" /> + From c29e4648ea6a8a1a8d2bb3553f43b74103c7bd2d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Nov 2020 17:34:51 +0100 Subject: [PATCH 8/8] Small cleanup --- ...meline_event_merged_room_creation_stub.xml | 33 ++++++------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml b/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml index ccb9bacd30..728b90b696 100644 --- a/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_merged_room_creation_stub.xml @@ -102,7 +102,7 @@ app:layout_constraintTop_toBottomOf="@id/roomNameDescriptionText" tools:text="@string/room_created_summary_no_topic_creation_text" /> - - - - + android:importantForAccessibility="no" + android:scaleType="center" + android:src="@drawable/ic_add_people" /> + android:textColor="@color/riotx_accent" /> - +