diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryData.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryData.kt index a2e1bc8bee..49bb769460 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryData.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryData.kt @@ -17,10 +17,9 @@ package im.vector.app.features.roomdirectory /** - * This class describes a rooms directory server. + * This class describes a rooms directory server protocol. */ data class RoomDirectoryData( - /** * The server name (might be null) * Set null when the server is the current user's home server. @@ -30,7 +29,12 @@ data class RoomDirectoryData( /** * The display name (the server description) */ - val displayName: String = DEFAULT_HOME_SERVER_NAME, + val displayName: String = MATRIX_PROTOCOL_NAME, + + /** + * the avatar url + */ + val avatarUrl: String? = null, /** * The third party server identifier @@ -40,15 +44,10 @@ data class RoomDirectoryData( /** * Tell if all the federated servers must be included */ - val includeAllNetworks: Boolean = false, - - /** - * the avatar url - */ - val avatarUrl: String? = null + val includeAllNetworks: Boolean = false ) { companion object { - const val DEFAULT_HOME_SERVER_NAME = "Matrix" + const val MATRIX_PROTOCOL_NAME = "Matrix" } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryServer.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryServer.kt new file mode 100644 index 0000000000..63ca4998f4 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryServer.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.roomdirectory + +data class RoomDirectoryServer( + val serverName: String, + + /** + * True if this is the current user server + */ + val isUserServer: Boolean, + + /** + * Supported protocols + * TODO Rename RoomDirectoryData to RoomDirectoryProtocols + */ + val protocols: List +) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt index 7b717df6f2..dc1cbfc58d 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt @@ -229,9 +229,7 @@ class RoomDirectoryViewModel @AssistedInject constructor( Timber.w("Try to join an already joining room. Should not happen") return@withState } - val viaServers = state.roomDirectoryData.homeServer - ?.let { listOf(it) } - .orEmpty() + val viaServers = listOfNotNull(state.roomDirectoryData.homeServer) viewModelScope.launch { try { session.joinRoom(action.roomId, viaServers = viaServers) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt index a55d028e10..e3cd9278a3 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt @@ -19,54 +19,88 @@ package im.vector.app.features.roomdirectory.picker import im.vector.app.R import im.vector.app.core.resources.StringArrayProvider import im.vector.app.features.roomdirectory.RoomDirectoryData +import im.vector.app.features.roomdirectory.RoomDirectoryServer import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol import javax.inject.Inject -class RoomDirectoryListCreator @Inject constructor(private val stringArrayProvider: StringArrayProvider, - private val session: Session) { +class RoomDirectoryListCreator @Inject constructor( + private val stringArrayProvider: StringArrayProvider, + private val session: Session +) { - fun computeDirectories(thirdPartyProtocolData: Map): List { - val result = ArrayList() + fun computeDirectories(thirdPartyProtocolData: Map): List { + val result = ArrayList() + + val protocols = ArrayList() // Add user homeserver name val userHsName = session.myUserId.substringAfter(":") - result.add(RoomDirectoryData( - displayName = userHsName, - includeAllNetworks = true - )) - - // Add user's HS but for Matrix public rooms only - result.add(RoomDirectoryData()) - - // Add custom directory servers - val hsNamesList = stringArrayProvider.getStringArray(R.array.room_directory_servers) - hsNamesList.forEach { - if (it != userHsName) { - // Use the server name as a default display name - result.add(RoomDirectoryData( - homeServer = it, - displayName = it, - includeAllNetworks = true - )) - } - } + // Add default protocol + protocols.add( + RoomDirectoryData( + homeServer = null, + displayName = RoomDirectoryData.MATRIX_PROTOCOL_NAME, + includeAllNetworks = false + ) + ) // Add result of the request thirdPartyProtocolData.forEach { it.value.instances?.forEach { thirdPartyProtocolInstance -> - result.add(RoomDirectoryData( - homeServer = null, - displayName = thirdPartyProtocolInstance.desc ?: "", - thirdPartyInstanceId = thirdPartyProtocolInstance.instanceId, - includeAllNetworks = false, - // Default to protocol icon - avatarUrl = thirdPartyProtocolInstance.icon ?: it.value.icon - )) + protocols.add( + RoomDirectoryData( + homeServer = null, + displayName = thirdPartyProtocolInstance.desc ?: "", + thirdPartyInstanceId = thirdPartyProtocolInstance.instanceId, + includeAllNetworks = false, + // Default to protocol icon + avatarUrl = thirdPartyProtocolInstance.icon ?: it.value.icon + ) + ) } } + // Add all rooms + protocols.add( + RoomDirectoryData( + homeServer = null, + displayName = RoomDirectoryData.MATRIX_PROTOCOL_NAME, + includeAllNetworks = true + ) + ) + + result.add( + RoomDirectoryServer( + serverName = userHsName, + isUserServer = true, + protocols = protocols + ) + ) + + // Add custom directory servers, form the config file, excluding the current user homeserver + stringArrayProvider.getStringArray(R.array.room_directory_servers) + .filter { it != userHsName } + .forEach { + // Use the server name as a default display name + result.add( + RoomDirectoryServer( + serverName = it, + isUserServer = false, + protocols = listOf( + RoomDirectoryData( + homeServer = it, + displayName = RoomDirectoryData.MATRIX_PROTOCOL_NAME, + includeAllNetworks = false + ) + ) + ) + ) + } + + // TODO Add manually added server by the user + return result } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt index 9e2006b145..4f9d1d0394 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt @@ -21,33 +21,38 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Success import im.vector.app.R +import im.vector.app.core.epoxy.dividerItem import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.extensions.join +import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.features.roomdirectory.RoomDirectoryData +import im.vector.app.features.roomdirectory.RoomDirectoryServer import javax.inject.Inject -class RoomDirectoryPickerController @Inject constructor(private val stringProvider: StringProvider, - private val errorFormatter: ErrorFormatter, - private val roomDirectoryListCreator: RoomDirectoryListCreator +class RoomDirectoryPickerController @Inject constructor( + private val stringProvider: StringProvider, + colorProvider: ColorProvider, + private val errorFormatter: ErrorFormatter, + private val roomDirectoryListCreator: RoomDirectoryListCreator ) : TypedEpoxyController() { var callback: Callback? = null - var index = 0 + private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) override fun buildModels(viewState: RoomDirectoryPickerViewState) { val host = this - val asyncThirdPartyProtocol = viewState.asyncThirdPartyRequest - when (asyncThirdPartyProtocol) { + when (val asyncThirdPartyProtocol = viewState.asyncThirdPartyRequest) { is Success -> { val directories = roomDirectoryListCreator.computeDirectories(asyncThirdPartyProtocol()) - - directories.forEach { - buildDirectory(it) - } + directories.join( + each = { _, roomDirectoryServer -> buildDirectory(roomDirectoryServer) }, + between = { idx, _ -> buildDivider(idx) } + ) } is Incomplete -> { loadingItem { @@ -64,28 +69,46 @@ class RoomDirectoryPickerController @Inject constructor(private val stringProvid } } - private fun buildDirectory(roomDirectoryData: RoomDirectoryData) { + private fun buildDivider(idx: Int) { val host = this - roomDirectoryItem { - id(host.index++) + dividerItem { + id("divider_${idx}") + color(host.dividerColor) + } + } - directoryName(roomDirectoryData.displayName) + private fun buildDirectory(roomDirectoryServer: RoomDirectoryServer) { + val host = this + roomDirectoryServerItem { + id("server_" + roomDirectoryServer.serverName) + serverName(roomDirectoryServer.serverName) - val description = when { - roomDirectoryData.includeAllNetworks -> - host.stringProvider.getString(R.string.directory_server_all_rooms_on_server, roomDirectoryData.displayName) - "Matrix" == roomDirectoryData.displayName -> - host.stringProvider.getString(R.string.directory_server_native_rooms, roomDirectoryData.displayName) - else -> - null + if (roomDirectoryServer.isUserServer) { + serverDescription(host.stringProvider.getString(R.string.directory_your_server)) } + } - directoryDescription(description) - directoryAvatarUrl(roomDirectoryData.avatarUrl) - includeAllNetworks(roomDirectoryData.includeAllNetworks) + roomDirectoryServer.protocols.forEach { roomDirectoryData -> + roomDirectoryItem { + id("server_" + roomDirectoryServer.serverName + "_proto_" + roomDirectoryData.displayName) + directoryName( + if (roomDirectoryData.includeAllNetworks) { + host.stringProvider.getString(R.string.directory_server_all_rooms_on_server, roomDirectoryServer.serverName) + } else { + roomDirectoryData.displayName + } + ) + if (roomDirectoryData.displayName == RoomDirectoryData.MATRIX_PROTOCOL_NAME && !roomDirectoryData.includeAllNetworks) { + directoryDescription( + host.stringProvider.getString(R.string.directory_server_native_rooms, roomDirectoryServer.serverName) + ) + } + directoryAvatarUrl(roomDirectoryData.avatarUrl) + includeAllNetworks(roomDirectoryData.includeAllNetworks) - globalListener { - host.callback?.onRoomDirectoryClicked(roomDirectoryData) + globalListener { + host.callback?.onRoomDirectoryClicked(roomDirectoryData) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryServerItem.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryServerItem.kt new file mode 100644 index 0000000000..83acfa581c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryServerItem.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.roomdirectory.picker + +import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.extensions.setTextOrHide + +@EpoxyModelClass(layout = R.layout.item_room_directory_server) +abstract class RoomDirectoryServerItem : VectorEpoxyModel() { + + @EpoxyAttribute + var serverName: String? = null + + @EpoxyAttribute + var serverDescription: String? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.nameView.text = serverName + holder.descriptionView.setTextOrHide(serverDescription) + } + + class Holder : VectorEpoxyHolder() { + val nameView by bind(R.id.itemRoomDirectoryServerName) + val descriptionView by bind(R.id.itemRoomDirectoryServerDescription) + } +} diff --git a/vector/src/main/res/layout/item_room_directory.xml b/vector/src/main/res/layout/item_room_directory.xml index 391f52ad92..f7bd3344e4 100644 --- a/vector/src/main/res/layout/item_room_directory.xml +++ b/vector/src/main/res/layout/item_room_directory.xml @@ -1,5 +1,4 @@ - + tools:src="@drawable/network_matrix" /> - - + tools:text="@string/directory_server_native_rooms" + tools:visibility="visible" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_room_directory_server.xml b/vector/src/main/res/layout/item_room_directory_server.xml new file mode 100644 index 0000000000..5459652e2c --- /dev/null +++ b/vector/src/main/res/layout/item_room_directory_server.xml @@ -0,0 +1,48 @@ + + + + + + + + \ No newline at end of file