From c1c8c045670b116b8e5e33d3238a6a09ba465376 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 25 Aug 2020 12:31:55 +0200 Subject: [PATCH 01/19] Room detail: Fix roomId not set up when activity is restored --- .../home/room/detail/RoomDetailActivity.kt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt index aa608ef4ef..de82689303 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt @@ -77,16 +77,15 @@ class RoomDetailActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) waitingView = waiting_view + val roomDetailArgs: RoomDetailArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) { + RoomDetailArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!) + } else { + intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS) + } + if (roomDetailArgs == null) return + currentRoomId = roomDetailArgs.roomId + if (isFirstCreation()) { - val roomDetailArgs: RoomDetailArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) { - RoomDetailArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!) - } else { - intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS) - } - - if (roomDetailArgs == null) return - - currentRoomId = roomDetailArgs.roomId replaceFragment(R.id.roomDetailContainer, RoomDetailFragment::class.java, roomDetailArgs) replaceFragment(R.id.roomDetailDrawerContainer, BreadcrumbsFragment::class.java) } From 8f1eff8782655c6009c99c9a539aed947e80bfde Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 25 Aug 2020 12:35:35 +0200 Subject: [PATCH 02/19] Start reusing RoomPreview for joining room not joined --- .../features/navigation/DefaultNavigator.kt | 18 ++++++++----- .../app/features/navigation/Navigator.kt | 3 ++- .../features/permalink/PermalinkHandler.kt | 25 ++++++++++++++----- .../roompreview/RoomPreviewActivity.kt | 22 ++++++++++------ .../RoomPreviewNoPreviewFragment.kt | 8 +++--- 5 files changed, 53 insertions(+), 23 deletions(-) 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 3da6a7257a..ea35bbc087 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 @@ -54,6 +54,7 @@ import im.vector.app.features.pin.PinMode import im.vector.app.features.roomdirectory.RoomDirectoryActivity import im.vector.app.features.roomdirectory.createroom.CreateRoomActivity import im.vector.app.features.roomdirectory.roompreview.RoomPreviewActivity +import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import im.vector.app.features.roommemberprofile.RoomMemberProfileActivity import im.vector.app.features.roommemberprofile.RoomMemberProfileArgs import im.vector.app.features.roomprofile.RoomProfileActivity @@ -64,6 +65,7 @@ import im.vector.app.features.terms.ReviewTermsActivity import im.vector.app.features.widgets.WidgetActivity import im.vector.app.features.widgets.WidgetArgsBuilder import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction +import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.session.terms.TermsService @@ -156,12 +158,16 @@ class DefaultNavigator @Inject constructor( } } - override fun openNotJoinedRoom(context: Context, roomIdOrAlias: String?, eventId: String?, buildTask: Boolean) { - if (context is VectorBaseActivity) { - context.notImplemented("Open not joined room") - } else { - context.toast(R.string.not_implemented) - } + override fun openNotJoinedRoom(context: Context, roomId: String, eventId: String?, roomSummary: RoomSummary?, buildTask: Boolean) { + val roomPreviewData = RoomPreviewData( + roomId = roomId, + eventId = eventId, + roomAlias = roomSummary?.canonicalAlias, + roomName = roomSummary?.displayName, + avatarUrl = roomSummary?.avatarUrl + ) + val intent = RoomPreviewActivity.newIntent(context, roomPreviewData) + context.startActivity(intent) } override fun openGroupDetail(groupId: String, context: Context, buildTask: Boolean) { 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 ee64c5fc75..b26c2711c7 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 @@ -28,6 +28,7 @@ import im.vector.app.features.pin.PinMode import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData import im.vector.app.features.terms.ReviewTermsActivity +import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.session.terms.TermsService @@ -50,7 +51,7 @@ interface Navigator { fun openRoomForSharingAndFinish(activity: Activity, roomId: String, sharedData: SharedData) - fun openNotJoinedRoom(context: Context, roomIdOrAlias: String?, eventId: String? = null, buildTask: Boolean = false) + fun openNotJoinedRoom(context: Context, roomId: String, eventId: String? = null, roomSummary: RoomSummary?, buildTask: Boolean = false) fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData) diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt index 87d114c01b..8ab716d01d 100644 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt @@ -19,12 +19,15 @@ package im.vector.app.features.permalink import android.content.Context import android.net.Uri import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.utils.toast import im.vector.app.features.navigation.Navigator import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.permalinks.PermalinkParser +import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.rx.rx import javax.inject.Inject @@ -82,8 +85,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti private fun PermalinkData.RoomLink.getRoomId(): Single> { val session = activeSessionHolder.getSafeActiveSession() return if (isRoomAlias && session != null) { - // At the moment we are not fetching on the server as we don't handle not join room - session.rx().getRoomIdByAlias(roomIdOrAlias, false).subscribeOn(Schedulers.io()) + session.rx().getRoomIdByAlias(roomIdOrAlias, true).subscribeOn(Schedulers.io()) } else { Single.just(Optional.from(roomIdOrAlias)) } @@ -94,10 +96,21 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti */ private fun openRoom(context: Context, roomId: String?, eventId: String?, buildTask: Boolean) { val session = activeSessionHolder.getSafeActiveSession() ?: return - return if (roomId != null && session.getRoom(roomId) != null) { - navigator.openRoom(context, roomId, eventId, buildTask) - } else { - navigator.openNotJoinedRoom(context, roomId, eventId, buildTask) + if (roomId == null) { + context.toast("Couldn't get roomId in permalink data.") + return + } + val roomSummary = session.getRoomSummary(roomId) + return when { + roomSummary?.membership?.isActive().orFalse() -> { + navigator.openRoom(context, roomId, eventId, buildTask) + } + roomSummary?.membership != Membership.BAN -> { + navigator.openNotJoinedRoom(context, roomId, eventId, roomSummary, buildTask) + } + else -> { + context.toast("Can't open a room where you are banned from.") + } } } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt index 0a25d93de2..9bf5646a99 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt @@ -24,20 +24,22 @@ import im.vector.app.R import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity +import kotlinx.android.parcel.Parcelize import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.util.MatrixItem -import kotlinx.android.parcel.Parcelize @Parcelize data class RoomPreviewData( val roomId: String, - val roomName: String?, - val roomAlias: String?, - val topic: String?, - val worldReadable: Boolean, - val avatarUrl: String?, - val homeServer: String? + val eventId: String? = null, + val roomName: String? = null, + val roomAlias: String? = null, + val topic: String? = null, + val worldReadable: Boolean = false, + val avatarUrl: String? = null, + val homeServer: String? = null, + val buildTask: Boolean = false ) : Parcelable { val matrixItem: MatrixItem get() = MatrixItem.RoomItem(roomId, roomName ?: roomAlias, avatarUrl) @@ -48,6 +50,12 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { private const val ARG = "ARG" + fun newIntent(context: Context, roomPreviewData: RoomPreviewData): Intent { + return Intent(context, RoomPreviewActivity::class.java).apply { + putExtra(ARG, roomPreviewData) + } + } + fun getIntent(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData): Intent { return Intent(context, RoomPreviewActivity::class.java).apply { putExtra(ARG, RoomPreviewData( diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt index 5d13d3d9fe..108c3bacf1 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt @@ -48,13 +48,15 @@ class RoomPreviewNoPreviewFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setupToolbar(roomPreviewNoPreviewToolbar) + val titleText = roomPreviewData.roomName ?: roomPreviewData.roomAlias ?: roomPreviewData.roomId + // Toolbar avatarRenderer.render(roomPreviewData.matrixItem, roomPreviewNoPreviewToolbarAvatar) - roomPreviewNoPreviewToolbarTitle.text = roomPreviewData.roomName ?: roomPreviewData.roomAlias + roomPreviewNoPreviewToolbarTitle.text = titleText // Screen avatarRenderer.render(roomPreviewData.matrixItem, roomPreviewNoPreviewAvatar) - roomPreviewNoPreviewName.text = roomPreviewData.roomName ?: roomPreviewData.roomAlias + roomPreviewNoPreviewName.text = titleText roomPreviewNoPreviewTopic.setTextOrHide(roomPreviewData.topic) if (roomPreviewData.worldReadable) { @@ -98,7 +100,7 @@ class RoomPreviewNoPreviewFragment @Inject constructor( // Quit this screen requireActivity().finish() // Open room - navigator.openRoom(requireActivity(), roomPreviewData.roomId) + navigator.openRoom(requireActivity(), roomPreviewData.roomId, roomPreviewData.eventId, roomPreviewData.buildTask) } } } From 9970398cf2ea7177186c4c5651a2ec0ab7f91656 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 25 Aug 2020 20:20:14 +0200 Subject: [PATCH 03/19] Join room: add "UnknownItem" to public room list --- .../features/navigation/DefaultNavigator.kt | 19 ++----- .../app/features/navigation/Navigator.kt | 6 +- .../features/permalink/PermalinkHandler.kt | 36 ++++++++++-- .../roomdirectory/PublicRoomsController.kt | 33 ++++++++++- .../roomdirectory/PublicRoomsFragment.kt | 27 ++++++++- .../features/roomdirectory/UnknownRoomItem.kt | 57 +++++++++++++++++++ .../roompreview/RoomPreviewActivity.kt | 23 ++++---- .../src/main/res/layout/item_unknown_room.xml | 52 +++++++++++++++++ vector/src/main/res/values/strings.xml | 2 + 9 files changed, 218 insertions(+), 37 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/roomdirectory/UnknownRoomItem.kt create mode 100644 vector/src/main/res/layout/item_unknown_room.xml 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 ea35bbc087..884433431b 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 @@ -158,18 +158,6 @@ class DefaultNavigator @Inject constructor( } } - override fun openNotJoinedRoom(context: Context, roomId: String, eventId: String?, roomSummary: RoomSummary?, buildTask: Boolean) { - val roomPreviewData = RoomPreviewData( - roomId = roomId, - eventId = eventId, - roomAlias = roomSummary?.canonicalAlias, - roomName = roomSummary?.displayName, - avatarUrl = roomSummary?.avatarUrl - ) - val intent = RoomPreviewActivity.newIntent(context, roomPreviewData) - context.startActivity(intent) - } - override fun openGroupDetail(groupId: String, context: Context, buildTask: Boolean) { if (context is VectorBaseActivity) { context.notImplemented("Open group detail") @@ -192,7 +180,12 @@ class DefaultNavigator @Inject constructor( } override fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData) { - val intent = RoomPreviewActivity.getIntent(context, publicRoom, roomDirectoryData) + val intent = RoomPreviewActivity.newIntent(context, publicRoom, roomDirectoryData) + context.startActivity(intent) + } + + override fun openRoomPreview(context: Context, roomPreviewData: RoomPreviewData) { + val intent = RoomPreviewActivity.newIntent(context, roomPreviewData) context.startActivity(intent) } 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 b26c2711c7..506da5bd3d 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 @@ -25,6 +25,7 @@ import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes import im.vector.app.features.media.AttachmentData import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinMode +import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData import im.vector.app.features.terms.ReviewTermsActivity @@ -51,10 +52,10 @@ interface Navigator { fun openRoomForSharingAndFinish(activity: Activity, roomId: String, sharedData: SharedData) - fun openNotJoinedRoom(context: Context, roomId: String, eventId: String? = null, roomSummary: RoomSummary?, buildTask: Boolean = false) - fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData) + fun openRoomPreview(context: Context, roomPreviewData: RoomPreviewData) + fun openCreateRoom(context: Context, initialName: String = "") fun openCreateDirectRoom(context: Context) @@ -106,4 +107,5 @@ interface Navigator { view: View, inMemory: List = emptyList(), options: ((MutableList>) -> Unit)?) + } diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt index 8ab716d01d..cd26027f94 100644 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt @@ -18,9 +18,11 @@ package im.vector.app.features.permalink import android.content.Context import android.net.Uri +import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.utils.toast import im.vector.app.features.navigation.Navigator +import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -61,7 +63,13 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti .map { val roomId = it.getOrNull() if (navigationInterceptor?.navToRoom(roomId, permalinkData.eventId) != true) { - openRoom(context, roomId, permalinkData.eventId, buildTask) + openRoom( + context = context, + roomId = roomId, + roomAlias = permalinkData.getRoomAliasOrNull(), + eventId = permalinkData.eventId, + buildTask = buildTask + ) } true } @@ -79,7 +87,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti is PermalinkData.FallbackLink -> { Single.just(false) } - } + }.onErrorReturnItem(false) } private fun PermalinkData.RoomLink.getRoomId(): Single> { @@ -91,13 +99,21 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti } } + private fun PermalinkData.RoomLink.getRoomAliasOrNull(): String? { + return if (isRoomAlias) { + roomIdOrAlias + } else { + null + } + } + /** * Open room either joined, or not */ - private fun openRoom(context: Context, roomId: String?, eventId: String?, buildTask: Boolean) { + private fun openRoom(context: Context, roomId: String?, roomAlias: String?, eventId: String?, buildTask: Boolean) { val session = activeSessionHolder.getSafeActiveSession() ?: return if (roomId == null) { - context.toast("Couldn't get roomId in permalink data.") + context.toast(R.string.room_error_not_found) return } val roomSummary = session.getRoomSummary(roomId) @@ -106,10 +122,18 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti navigator.openRoom(context, roomId, eventId, buildTask) } roomSummary?.membership != Membership.BAN -> { - navigator.openNotJoinedRoom(context, roomId, eventId, roomSummary, buildTask) + val roomPreviewData = RoomPreviewData( + roomId = roomId, + eventId = eventId, + roomAlias = roomAlias ?: roomSummary?.canonicalAlias, + roomName = roomSummary?.displayName, + avatarUrl = roomSummary?.avatarUrl, + buildTask = buildTask + ) + navigator.openRoomPreview(context, roomPreviewData) } else -> { - context.toast("Can't open a room where you are banned from.") + context.toast(R.string.error_opening_banned_room) } } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt index c60336c1af..18b92abdcf 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt @@ -28,8 +28,10 @@ import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom +import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -42,18 +44,23 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri override fun buildModels(viewState: PublicRoomsViewState) { val publicRooms = viewState.publicRooms - if (publicRooms.isEmpty() - && viewState.asyncPublicRoomsRequest is Success) { + val unknownRoomItem = viewState.buildUnknownRoomIfNeeded() + + val noResult = publicRooms.isEmpty() && viewState.asyncPublicRoomsRequest is Success && unknownRoomItem == null + if (noResult) { // No result noResultItem { id("noResult") text(stringProvider.getString(R.string.no_result_placeholder)) } } else { + publicRooms.forEach { buildPublicRoom(it, viewState) } + unknownRoomItem?.addTo(this) + if ((viewState.hasMore && viewState.asyncPublicRoomsRequest is Success) || viewState.asyncPublicRoomsRequest is Incomplete) { loadingItem { @@ -109,7 +116,29 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri } } + private fun PublicRoomsViewState.buildUnknownRoomIfNeeded(): UnknownRoomItem? { + val roomIdOrAlias = currentFilter.trim() + val isAlias = MatrixPatterns.isRoomAlias(roomIdOrAlias) && !publicRooms.any { it.canonicalAlias == roomIdOrAlias } + val isRoomId = !isAlias && MatrixPatterns.isRoomId(roomIdOrAlias) && !publicRooms.any { it.roomId == roomIdOrAlias } + val roomItem = when { + isAlias -> MatrixItem.RoomAliasItem(roomIdOrAlias, roomIdOrAlias) + isRoomId -> MatrixItem.RoomItem(roomIdOrAlias) + else -> null + } + return roomItem?.let { + UnknownRoomItem_().apply { + id(roomIdOrAlias) + matrixItem(roomItem) + avatarRenderer(this@PublicRoomsController.avatarRenderer) + globalListener { + callback?.onUnknownRoomClicked(roomIdOrAlias) + } + } + } + } + interface Callback { + fun onUnknownRoomClicked(roomIdOrAlias: String) fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) fun onPublicRoomJoin(publicRoom: PublicRoom) fun loadMore() diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt index 57006af6f1..b180018480 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt @@ -29,9 +29,13 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.trackItemsVisibilityChange import im.vector.app.core.platform.VectorBaseFragment -import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom +import im.vector.app.core.utils.toast +import im.vector.app.features.permalink.NavigationInterceptor +import im.vector.app.features.permalink.PermalinkHandler import io.reactivex.rxkotlin.subscribeBy import kotlinx.android.synthetic.main.fragment_public_rooms.* +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import timber.log.Timber import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -41,7 +45,9 @@ import javax.inject.Inject * - When filtering more (when entering new chars), we could filter on result we already have, during the new server request, to avoid empty screen effect */ class PublicRoomsFragment @Inject constructor( - private val publicRoomsController: PublicRoomsController + private val publicRoomsController: PublicRoomsController, + private val permalinkHandler: PermalinkHandler, + private val session: Session ) : VectorBaseFragment(), PublicRoomsController.Callback { private val viewModel: RoomDirectoryViewModel by activityViewModel() @@ -112,6 +118,23 @@ class PublicRoomsFragment @Inject constructor( publicRoomsController.callback = this } + override fun onUnknownRoomClicked(roomIdOrAlias: String) { + val permalink = session.permalinkService().createPermalink(roomIdOrAlias) + permalinkHandler + .launch(requireContext(), permalink, object : NavigationInterceptor { + override fun navToRoom(roomId: String?, eventId: String?): Boolean { + requireActivity().finish() + return false + } + }) + .subscribe { isSuccessful -> + if (!isSuccessful) { + requireContext().toast(R.string.room_error_not_found) + } + } + .disposeOnDestroyView() + } + override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) { Timber.v("PublicRoomClicked: $publicRoom") withState(viewModel) { state -> diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/UnknownRoomItem.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/UnknownRoomItem.kt new file mode 100644 index 0000000000..cf0ad811a2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/UnknownRoomItem.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2019 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 + +import android.view.ViewGroup +import android.widget.ImageView +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.features.home.AvatarRenderer +import org.matrix.android.sdk.api.util.MatrixItem + +@EpoxyModelClass(layout = R.layout.item_unknown_room) +abstract class UnknownRoomItem : VectorEpoxyModel() { + + @EpoxyAttribute + lateinit var avatarRenderer: AvatarRenderer + + @EpoxyAttribute + lateinit var matrixItem: MatrixItem + + @EpoxyAttribute + var identifier: String? = null + + @EpoxyAttribute + var globalListener: (() -> Unit)? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.rootView.setOnClickListener { globalListener?.invoke() } + avatarRenderer.render(matrixItem, holder.avatarView) + holder.nameView.text = matrixItem.displayName + } + + class Holder : VectorEpoxyHolder() { + val rootView by bind(R.id.itemUnknownRoomLayout) + val avatarView by bind(R.id.itemUnknownRoomAvatar) + val nameView by bind(R.id.itemUnknownRoomName) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt index 9bf5646a99..d304594474 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt @@ -56,18 +56,17 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { } } - fun getIntent(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData): Intent { - return Intent(context, RoomPreviewActivity::class.java).apply { - putExtra(ARG, RoomPreviewData( - roomId = publicRoom.roomId, - roomName = publicRoom.name, - roomAlias = publicRoom.getPrimaryAlias(), - topic = publicRoom.topic, - worldReadable = publicRoom.worldReadable, - avatarUrl = publicRoom.avatarUrl, - homeServer = roomDirectoryData.homeServer - )) - } + fun newIntent(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData): Intent { + val roomPreviewData = RoomPreviewData( + roomId = publicRoom.roomId, + roomName = publicRoom.name, + roomAlias = publicRoom.getPrimaryAlias(), + topic = publicRoom.topic, + worldReadable = publicRoom.worldReadable, + avatarUrl = publicRoom.avatarUrl, + homeServer = roomDirectoryData.homeServer + ) + return newIntent(context, roomPreviewData) } } diff --git a/vector/src/main/res/layout/item_unknown_room.xml b/vector/src/main/res/layout/item_unknown_room.xml new file mode 100644 index 0000000000..a097767e63 --- /dev/null +++ b/vector/src/main/res/layout/item_unknown_room.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 0b9cbab57a..7506a4d502 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2539,4 +2539,6 @@ Enable PIN If you want to reset your PIN, tap Forgot PIN to logout and reset. Confirm PIN to disable PIN + Can\'t open a room where you are banned from. + Can\'t find this room. Make sure it exists. From b986bfd5095559a53e4fa40b47ca01415e3ef19a Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 26 Aug 2020 16:37:48 +0200 Subject: [PATCH 04/19] Permalink: handle via parameters --- .../api/session/permalinks/PermalinkData.kt | 7 ++- .../api/session/permalinks/PermalinkParser.kt | 52 +++++++++++++++++-- .../features/permalink/PermalinkHandler.kt | 37 ++++++++++--- .../roompreview/RoomPreviewActivity.kt | 4 +- .../roompreview/RoomPreviewViewModel.kt | 5 +- .../roompreview/RoomPreviewViewState.kt | 7 ++- 6 files changed, 91 insertions(+), 21 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt index 85632d6e83..1da65b3002 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt @@ -25,7 +25,12 @@ import android.net.Uri */ sealed class PermalinkData { - data class RoomLink(val roomIdOrAlias: String, val isRoomAlias: Boolean, val eventId: String?) : PermalinkData() + data class RoomLink( + val roomIdOrAlias: String, + val isRoomAlias: Boolean, + val eventId: String?, + val viaParameters: List + ) : PermalinkData() data class UserLink(val userId: String) : PermalinkData() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt index dd6847f1e3..d741367376 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt @@ -19,6 +19,10 @@ package org.matrix.android.sdk.api.session.permalinks import android.net.Uri import org.matrix.android.sdk.api.MatrixPatterns +import java.io.UnsupportedEncodingException +import java.net.URLEncoder +import java.util.ArrayList +import java.util.Collections /** * This class turns an uri to a [PermalinkData] @@ -40,14 +44,13 @@ object PermalinkParser { if (!uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE)) { return PermalinkData.FallbackLink(uri) } - val fragment = uri.fragment if (fragment.isNullOrEmpty()) { return PermalinkData.FallbackLink(uri) } - val indexOfQuery = fragment.indexOf("?") val safeFragment = if (indexOfQuery != -1) fragment.substring(0, indexOfQuery) else fragment + val viaQueryParameters = fragment.getViaParameters(indexOfQuery) // we are limiting to 2 params val params = safeFragment @@ -65,17 +68,58 @@ object PermalinkParser { PermalinkData.RoomLink( roomIdOrAlias = identifier, isRoomAlias = false, - eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) } + eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }, + viaParameters = viaQueryParameters ) } MatrixPatterns.isRoomAlias(identifier) -> { PermalinkData.RoomLink( roomIdOrAlias = identifier, isRoomAlias = true, - eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) } + eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }, + viaParameters = viaQueryParameters ) } else -> PermalinkData.FallbackLink(uri) } } + + private fun String.getViaParameters(indexOfQuery: Int): List { + val query = try { + substring(indexOfQuery + 1) + } catch (e: IndexOutOfBoundsException) { + return emptyList() + } + val encodedKey = try { + URLEncoder.encode("via", "UTF-8") + } catch (e: UnsupportedEncodingException) { + return emptyList() + } + val values = ArrayList() + var start = 0 + do { + val nextAmpersand = query.indexOf('&', start) + val end = if (nextAmpersand != -1) nextAmpersand else query.length + var separator = query.indexOf('=', start) + if (separator > end || separator == -1) { + separator = end + } + if (separator - start == encodedKey.length + && query.regionMatches(start, encodedKey, 0, encodedKey.length)) { + if (separator == end) { + values.add("") + } else { + values.add(Uri.decode(query.substring(separator + 1, end))) + } + } + + // Move start to end of name. + start = if (nextAmpersand != -1) { + nextAmpersand + 1 + } else { + break + } + } while (true) + return Collections.unmodifiableList(values) + } } diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt index cd26027f94..4ca16888a5 100644 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt @@ -56,7 +56,25 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti if (deepLink == null) { return Single.just(false) } - return when (val permalinkData = PermalinkParser.parse(deepLink)) { + return Single + .fromCallable { + PermalinkParser.parse(deepLink) + } + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .flatMap { permalinkData -> + handlePermalink(permalinkData, context, navigationInterceptor, buildTask) + } + .onErrorReturnItem(false) + } + + private fun handlePermalink( + permalinkData: PermalinkData, + context: Context, + navigationInterceptor: NavigationInterceptor?, + buildTask: Boolean + ): Single { + return when (permalinkData) { is PermalinkData.RoomLink -> { permalinkData.getRoomId() .observeOn(AndroidSchedulers.mainThread()) @@ -66,8 +84,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti openRoom( context = context, roomId = roomId, - roomAlias = permalinkData.getRoomAliasOrNull(), - eventId = permalinkData.eventId, + permalinkData = permalinkData, buildTask = buildTask ) } @@ -87,7 +104,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti is PermalinkData.FallbackLink -> { Single.just(false) } - }.onErrorReturnItem(false) + } } private fun PermalinkData.RoomLink.getRoomId(): Single> { @@ -110,13 +127,20 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti /** * Open room either joined, or not */ - private fun openRoom(context: Context, roomId: String?, roomAlias: String?, eventId: String?, buildTask: Boolean) { + private fun openRoom( + context: Context, + roomId: String?, + permalinkData: PermalinkData.RoomLink, + buildTask: Boolean + ) { val session = activeSessionHolder.getSafeActiveSession() ?: return if (roomId == null) { context.toast(R.string.room_error_not_found) return } val roomSummary = session.getRoomSummary(roomId) + val eventId = permalinkData.eventId + val roomAlias = permalinkData.getRoomAliasOrNull() return when { roomSummary?.membership?.isActive().orFalse() -> { navigator.openRoom(context, roomId, eventId, buildTask) @@ -128,7 +152,8 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti roomAlias = roomAlias ?: roomSummary?.canonicalAlias, roomName = roomSummary?.displayName, avatarUrl = roomSummary?.avatarUrl, - buildTask = buildTask + buildTask = buildTask, + homeServers = permalinkData.viaParameters ) navigator.openRoomPreview(context, roomPreviewData) } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt index d304594474..8bbaa14ab0 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt @@ -38,7 +38,7 @@ data class RoomPreviewData( val topic: String? = null, val worldReadable: Boolean = false, val avatarUrl: String? = null, - val homeServer: String? = null, + val homeServers: List = emptyList(), val buildTask: Boolean = false ) : Parcelable { val matrixItem: MatrixItem @@ -64,7 +64,7 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { topic = publicRoom.topic, worldReadable = publicRoom.worldReadable, avatarUrl = publicRoom.avatarUrl, - homeServer = roomDirectoryData.homeServer + homeServers = listOfNotNull(roomDirectoryData.homeServer) ) return newIntent(context, roomPreviewData) } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index 04b7c33fb2..900ba537b5 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -106,10 +106,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini Timber.w("Try to join an already joining room. Should not happen") return@withState } - val viaServers = state.homeServer?.let { - listOf(it) - } ?: emptyList() - session.joinRoom(state.roomId, viaServers = viaServers, callback = object : MatrixCallback { + session.joinRoom(state.roomId, viaServers = state.homeServers, callback = object : MatrixCallback { override fun onSuccess(data: Unit) { // We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data. // Instead, we wait for the room to be joined diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt index f461225e2d..6816e54481 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt @@ -24,10 +24,9 @@ data class RoomPreviewViewState( val roomId: String = "", val roomAlias: String? = null, /** - * The server name (might be null) - * Set null when the server is the current user's home server. + * Can be empty when the server is the current user's home server. */ - val homeServer: String? = null, + val homeServers: List = emptyList(), // Current state of the room in preview val roomJoinState: JoinState = JoinState.NOT_JOINED, // Last error of join room request @@ -37,6 +36,6 @@ data class RoomPreviewViewState( constructor(args: RoomPreviewData) : this( roomId = args.roomId, roomAlias = args.roomAlias, - homeServer = args.homeServer + homeServers = args.homeServers ) } From 5d8d9cb19c56d6968ded704d816dafdf0b84c717 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 26 Aug 2020 17:02:24 +0200 Subject: [PATCH 05/19] Clean files --- .../java/im/vector/app/features/navigation/DefaultNavigator.kt | 1 - .../main/java/im/vector/app/features/navigation/Navigator.kt | 2 -- .../vector/app/features/roomdirectory/PublicRoomsController.kt | 1 - 3 files changed, 4 deletions(-) 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 884433431b..5de8796c06 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 @@ -65,7 +65,6 @@ import im.vector.app.features.terms.ReviewTermsActivity import im.vector.app.features.widgets.WidgetActivity import im.vector.app.features.widgets.WidgetArgsBuilder import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction -import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.session.terms.TermsService 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 506da5bd3d..ed710a124f 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 @@ -29,7 +29,6 @@ import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData import im.vector.app.features.terms.ReviewTermsActivity -import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.session.terms.TermsService @@ -107,5 +106,4 @@ interface Navigator { view: View, inMemory: List = emptyList(), options: ((MutableList>) -> Unit)?) - } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt index 18b92abdcf..a35725815b 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt @@ -54,7 +54,6 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri text(stringProvider.getString(R.string.no_result_placeholder)) } } else { - publicRooms.forEach { buildPublicRoom(it, viewState) } From 9c151417a823425313444212a591404c4fc333af Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 26 Aug 2020 17:03:51 +0200 Subject: [PATCH 06/19] Update CHANGES --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index d3a16c9b29..43a05e8a90 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,7 @@ Features ✨: - Improvements 🙌: - - + - You can now join room through permalink and within room directory search Bugfix 🐛: - Display name not shown under Settings/General (#1926) From cefdbe1d083bbb7bd612260efa44b46b8fdf7808 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 26 Aug 2020 19:21:14 +0200 Subject: [PATCH 07/19] Add CheckNumberType in json to fix sending in room v6 --- .../network/parsing/CheckNumberType.kt | 66 +++++++++++++++++++ .../internal/worker/WorkerParamsFactory.kt | 15 ++++- 2 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/CheckNumberType.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/CheckNumberType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/CheckNumberType.kt new file mode 100644 index 0000000000..d4ceca2006 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/CheckNumberType.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020 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 org.matrix.android.sdk.internal.network.parsing + +import androidx.annotation.Nullable +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonReader +import com.squareup.moshi.JsonWriter + +import com.squareup.moshi.Moshi +import java.io.IOException +import java.lang.reflect.Type +import java.math.BigDecimal + +/** + * This is used to check if NUMBER in json is integer or double, so we can preserve typing when serializing/deserializing in a row. + */ +interface CheckNumberType { + + companion object { + val JSON_ADAPTER_FACTORY = object : JsonAdapter.Factory { + @Nullable + override fun create(type: Type, annotations: Set?, moshi: Moshi): JsonAdapter<*>? { + if (type !== Any::class.java) { + return null + } + val delegate: JsonAdapter = moshi.nextAdapter(this, Any::class.java, emptySet()) + return object : JsonAdapter() { + @Nullable + @Throws(IOException::class) + override fun fromJson(reader: JsonReader): Any? { + return if (reader.peek() !== JsonReader.Token.NUMBER) { + delegate.fromJson(reader) + } else { + val numberAsString = reader.nextString() + val decimal = BigDecimal(numberAsString) + if (decimal.scale() <= 0) { + decimal.intValueExact() + } else { + decimal.toDouble() + } + } + } + + override fun toJson(writer: JsonWriter, value: Any?) { + delegate.toJson(writer, value) + } + } + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/WorkerParamsFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/WorkerParamsFactory.kt index 2b7cba0b0c..b162566403 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/WorkerParamsFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/WorkerParamsFactory.kt @@ -19,13 +19,23 @@ package org.matrix.android.sdk.internal.worker import androidx.work.Data import org.matrix.android.sdk.internal.di.MoshiProvider +import org.matrix.android.sdk.internal.network.parsing.CheckNumberType -object WorkerParamsFactory { +internal object WorkerParamsFactory { + + val moshi by lazy { + // We are adding the CheckNumberType as we are serializing/deserializing multiple time in a row + // and we lost typing information doing so. + // We don't want this check to be done on all adapters, so we just add it here. + MoshiProvider.providesMoshi() + .newBuilder() + .add(CheckNumberType.JSON_ADAPTER_FACTORY) + .build() + } const val KEY = "WORKER_PARAMS_JSON" inline fun toData(params: T): Data { - val moshi = MoshiProvider.providesMoshi() val adapter = moshi.adapter(T::class.java) val json = adapter.toJson(params) return Data.Builder().putString(KEY, json).build() @@ -36,7 +46,6 @@ object WorkerParamsFactory { return if (json == null) { null } else { - val moshi = MoshiProvider.providesMoshi() val adapter = moshi.adapter(T::class.java) adapter.fromJson(json) } From dc4135b506139042d4eed14bf6ddac89f55a0a20 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 26 Aug 2020 19:21:41 +0200 Subject: [PATCH 08/19] Remove unnecessary code now we have an other way to keep number types --- .../internal/session/room/send/SendEventWorker.kt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt index 2868ce29c1..d37b5e90c2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt @@ -21,16 +21,16 @@ import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass +import org.greenrobot.eventbus.EventBus import org.matrix.android.sdk.api.failure.shouldBeRetried +import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.send.SendState -import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.worker.SessionWorkerParams import org.matrix.android.sdk.internal.worker.WorkerParamsFactory import org.matrix.android.sdk.internal.worker.getSessionComponent -import org.greenrobot.eventbus.EventBus import timber.log.Timber import javax.inject.Inject @@ -47,12 +47,10 @@ internal class SendEventWorker(context: Context, @JsonClass(generateAdapter = true) internal data class Params( override val sessionId: String, - // TODO remove after some time, it's used for compat val event: Event? = null, val eventId: String? = null, val roomId: String? = null, val type: String? = null, - val contentStr: String? = null, override val lastFailureMessage: String? = null ) : SessionWorkerParams { @@ -61,7 +59,7 @@ internal class SendEventWorker(context: Context, eventId = event.eventId, roomId = event.roomId, type = event.type, - contentStr = ContentMapper.map(event.content), + event = event, lastFailureMessage = lastFailureMessage ) } @@ -91,7 +89,7 @@ internal class SendEventWorker(context: Context, .also { Timber.e("Work cancelled due to input error from parent") } } return try { - sendEvent(params.eventId, params.roomId, params.type, params.contentStr) + sendEvent(params.eventId, params.roomId, params.type, params.event?.content) Result.success() } catch (exception: Throwable) { // It does start from 0, we want it to stop if it fails the third time @@ -105,10 +103,10 @@ internal class SendEventWorker(context: Context, } } - private suspend fun sendEvent(eventId: String, roomId: String, type: String, contentStr: String?) { + private suspend fun sendEvent(eventId: String, roomId: String, type: String, content: Content?) { localEchoRepository.updateSendState(eventId, SendState.SENDING) executeRequest(eventBus) { - apiCall = roomAPI.send(eventId, roomId, type, contentStr) + apiCall = roomAPI.send(eventId, roomId, type, content) } localEchoRepository.updateSendState(eventId, SendState.SENT) } From 3c0177c2dd1a42f76380a533e5b4348d3e2f289b Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 26 Aug 2020 19:25:20 +0200 Subject: [PATCH 09/19] Update CHANGES --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index c923053b24..5d58668790 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,7 @@ Bugfix 🐛: - Fix FontSize issue (#1483, #1787) - Fix bad color for settings icon on Android < 24 (#1786) - Change user or room avatar: when selecting Gallery, I'm not proposed to crop the selected image (#1590) + - Fix uploads still don't work with room v6 (#1879) Translations 🗣: - From 9c1c9f96e192953e129198aecf6dde436164b216 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 27 Aug 2020 10:34:40 +0200 Subject: [PATCH 10/19] Room v6: finish cleaning up --- matrix-sdk-android/build.gradle | 2 - .../sdk/internal/network/RetrofitFactory.kt | 4 +- .../sdk/internal/session/room/RoomAPI.kt | 15 -------- .../room/relation/DefaultRelationService.kt | 2 +- .../session/room/send/EncryptEventWorker.kt | 9 +++-- .../MultipleEventSendingDispatcherWorker.kt | 2 +- .../session/room/send/RoomEventSender.kt | 2 +- .../session/room/send/SendEventWorker.kt | 37 ++++++++----------- 8 files changed, 25 insertions(+), 48 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index d70b0bbe84..90bdf02243 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -131,8 +131,6 @@ dependencies { // Network implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" - implementation "com.squareup.retrofit2:converter-scalars:$retrofit_version" - implementation(platform("com.squareup.okhttp3:okhttp-bom:4.8.1")) implementation 'com.squareup.okhttp3:okhttp' diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitFactory.kt index 368611dd7d..89a0ce597a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitFactory.kt @@ -19,13 +19,12 @@ package org.matrix.android.sdk.internal.network import com.squareup.moshi.Moshi import dagger.Lazy -import org.matrix.android.sdk.internal.util.ensureTrailingSlash import okhttp3.Call import okhttp3.OkHttpClient import okhttp3.Request +import org.matrix.android.sdk.internal.util.ensureTrailingSlash import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory -import retrofit2.converter.scalars.ScalarsConverterFactory import javax.inject.Inject internal class RetrofitFactory @Inject constructor(private val moshi: Moshi) { @@ -50,7 +49,6 @@ internal class RetrofitFactory @Inject constructor(private val moshi: Moshi) { return okHttpClient.get().newCall(request) } }) - .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(UnitConverterFactory) .addConverterFactory(MoshiConverterFactory.create(moshi)) .build() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index 25dcc69fa8..d8df86be8f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -130,21 +130,6 @@ internal interface RoomAPI { @Body content: Content? ): Call - /** - * Send an event to a room. - * - * @param txId the transaction Id - * @param roomId the room id - * @param eventType the event type - * @param content the event content as string - */ - @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/send/{eventType}/{txId}") - fun send(@Path("txId") txId: String, - @Path("roomId") roomId: String, - @Path("eventType") eventType: String, - @Body content: String? - ): Call - /** * Get the context surrounding an event. * diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt index 2199193de0..111551d66d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt @@ -208,7 +208,7 @@ internal class DefaultRelationService @AssistedInject constructor( } private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { - val sendContentWorkerParams = SendEventWorker.Params(sessionId, event) + val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) return timeLineSendEventWorkCommon.createWork(sendWorkData, startChain) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/EncryptEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/EncryptEventWorker.kt index d23835e838..f878df52b2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/EncryptEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/EncryptEventWorker.kt @@ -120,7 +120,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) localEchoRepository.updateEncryptedEcho(localEvent.eventId, safeResult.eventContent, decryptionLocalEcho) } - val nextWorkerParams = SendEventWorker.Params(params.sessionId, encryptedEvent) + val nextWorkerParams = SendEventWorker.Params(sessionId = params.sessionId, event = encryptedEvent) return Result.success(WorkerParamsFactory.toData(nextWorkerParams)) } else { val sendState = when (error) { @@ -129,8 +129,11 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) } localEchoRepository.updateSendState(localEvent.eventId, sendState) // always return success, or the chain will be stuck for ever! - val nextWorkerParams = SendEventWorker.Params(params.sessionId, localEvent, error?.localizedMessage - ?: "Error") + val nextWorkerParams = SendEventWorker.Params( + sessionId = params.sessionId, + event = localEvent, + lastFailureMessage = error?.localizedMessage ?: "Error" + ) return Result.success(WorkerParamsFactory.toData(nextWorkerParams)) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt index 73791e8412..ead2dc9377 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt @@ -105,7 +105,7 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo } private fun createSendEventWork(sessionId: String, event: Event, startChain: Boolean): OneTimeWorkRequest { - val sendContentWorkerParams = SendEventWorker.Params(sessionId, event) + val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) return timelineSendEventWorkCommon.createWork(sendWorkData, startChain) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RoomEventSender.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RoomEventSender.kt index 65c692f42e..e46adeb9c1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RoomEventSender.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RoomEventSender.kt @@ -65,7 +65,7 @@ internal class RoomEventSender @Inject constructor( } private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { - val sendContentWorkerParams = SendEventWorker.Params(sessionId, event) + val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) return timelineSendEventWorkCommon.createWork(sendWorkData, startChain) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt index d37b5e90c2..5da14f0a41 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt @@ -47,22 +47,11 @@ internal class SendEventWorker(context: Context, @JsonClass(generateAdapter = true) internal data class Params( override val sessionId: String, + override val lastFailureMessage: String? = null, val event: Event? = null, - val eventId: String? = null, - val roomId: String? = null, - val type: String? = null, - override val lastFailureMessage: String? = null - ) : SessionWorkerParams { - - constructor(sessionId: String, event: Event, lastFailureMessage: String? = null) : this( - sessionId = sessionId, - eventId = event.eventId, - roomId = event.roomId, - type = event.type, - event = event, - lastFailureMessage = lastFailureMessage - ) - } + // Keep for compat at the moment, will be removed later + val eventId: String? = null + ) : SessionWorkerParams @Inject lateinit var localEchoRepository: LocalEchoRepository @Inject lateinit var roomAPI: RoomAPI @@ -75,27 +64,31 @@ internal class SendEventWorker(context: Context, val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success() sessionComponent.inject(this) - if (params.eventId == null || params.roomId == null || params.type == null) { - // compat with old params, make it fail if any - if (params.event?.eventId != null) { - localEchoRepository.updateSendState(params.event.eventId, SendState.UNDELIVERED) + + val event = params.event + if (event?.eventId == null || event.roomId == null) { + // Old way of sending + if (params.eventId != null) { + localEchoRepository.updateSendState(params.eventId, SendState.UNDELIVERED) } return Result.success() + .also { Timber.e("Work cancelled due to bad input data") } } + if (params.lastFailureMessage != null) { - localEchoRepository.updateSendState(params.eventId, SendState.UNDELIVERED) + localEchoRepository.updateSendState(event.eventId, SendState.UNDELIVERED) // Transmit the error return Result.success(inputData) .also { Timber.e("Work cancelled due to input error from parent") } } return try { - sendEvent(params.eventId, params.roomId, params.type, params.event?.content) + sendEvent(event.eventId, event.roomId, event.type, event.content) Result.success() } catch (exception: Throwable) { // It does start from 0, we want it to stop if it fails the third time val currentAttemptCount = runAttemptCount + 1 if (currentAttemptCount >= MAX_NUMBER_OF_RETRY_BEFORE_FAILING || !exception.shouldBeRetried()) { - localEchoRepository.updateSendState(params.eventId, SendState.UNDELIVERED) + localEchoRepository.updateSendState(event.eventId, SendState.UNDELIVERED) return Result.success() } else { Result.retry() From 28a1cf6982ffc60a919d87c2bc63fb98a7653e7a Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 27 Aug 2020 11:44:21 +0200 Subject: [PATCH 11/19] Join room: clean up after benoit's remarks --- .../api/session/permalinks/PermalinkParser.kt | 50 ++++--------------- .../features/permalink/PermalinkHandler.kt | 9 ++-- .../roomdirectory/PublicRoomsController.kt | 2 +- .../features/roomdirectory/UnknownRoomItem.kt | 3 -- 4 files changed, 14 insertions(+), 50 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt index d741367376..59e289ffd7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt @@ -18,11 +18,8 @@ package org.matrix.android.sdk.api.session.permalinks import android.net.Uri +import android.net.UrlQuerySanitizer import org.matrix.android.sdk.api.MatrixPatterns -import java.io.UnsupportedEncodingException -import java.net.URLEncoder -import java.util.ArrayList -import java.util.Collections /** * This class turns an uri to a [PermalinkData] @@ -50,7 +47,7 @@ object PermalinkParser { } val indexOfQuery = fragment.indexOf("?") val safeFragment = if (indexOfQuery != -1) fragment.substring(0, indexOfQuery) else fragment - val viaQueryParameters = fragment.getViaParameters(indexOfQuery) + val viaQueryParameters = fragment.getViaParameters() // we are limiting to 2 params val params = safeFragment @@ -84,42 +81,13 @@ object PermalinkParser { } } - private fun String.getViaParameters(indexOfQuery: Int): List { - val query = try { - substring(indexOfQuery + 1) - } catch (e: IndexOutOfBoundsException) { - return emptyList() - } - val encodedKey = try { - URLEncoder.encode("via", "UTF-8") - } catch (e: UnsupportedEncodingException) { - return emptyList() - } - val values = ArrayList() - var start = 0 - do { - val nextAmpersand = query.indexOf('&', start) - val end = if (nextAmpersand != -1) nextAmpersand else query.length - var separator = query.indexOf('=', start) - if (separator > end || separator == -1) { - separator = end - } - if (separator - start == encodedKey.length - && query.regionMatches(start, encodedKey, 0, encodedKey.length)) { - if (separator == end) { - values.add("") - } else { - values.add(Uri.decode(query.substring(separator + 1, end))) + private fun String.getViaParameters(): List { + return UrlQuerySanitizer(this) + .parameterList + .filter { + it.mParameter == "via" + }.map { + it.mValue } - } - - // Move start to end of name. - start = if (nextAmpersand != -1) { - nextAmpersand + 1 - } else { - break - } - } while (true) - return Collections.unmodifiableList(values) } } diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt index 4ca16888a5..11c55f6a73 100644 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt @@ -139,13 +139,15 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti return } val roomSummary = session.getRoomSummary(roomId) + val membership = roomSummary?.membership val eventId = permalinkData.eventId val roomAlias = permalinkData.getRoomAliasOrNull() return when { - roomSummary?.membership?.isActive().orFalse() -> { + membership == Membership.BAN -> context.toast(R.string.error_opening_banned_room) + membership?.isActive().orFalse() -> { navigator.openRoom(context, roomId, eventId, buildTask) } - roomSummary?.membership != Membership.BAN -> { + else -> { val roomPreviewData = RoomPreviewData( roomId = roomId, eventId = eventId, @@ -157,9 +159,6 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti ) navigator.openRoomPreview(context, roomPreviewData) } - else -> { - context.toast(R.string.error_opening_banned_room) - } } } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt index a35725815b..b71b494948 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt @@ -127,7 +127,7 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri return roomItem?.let { UnknownRoomItem_().apply { id(roomIdOrAlias) - matrixItem(roomItem) + matrixItem(it) avatarRenderer(this@PublicRoomsController.avatarRenderer) globalListener { callback?.onUnknownRoomClicked(roomIdOrAlias) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/UnknownRoomItem.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/UnknownRoomItem.kt index cf0ad811a2..084f518405 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/UnknownRoomItem.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/UnknownRoomItem.kt @@ -36,9 +36,6 @@ abstract class UnknownRoomItem : VectorEpoxyModel() { @EpoxyAttribute lateinit var matrixItem: MatrixItem - @EpoxyAttribute - var identifier: String? = null - @EpoxyAttribute var globalListener: (() -> Unit)? = null From 59fa2e28c2943e7b321ec44344de87b79c52cc40 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2020 15:37:10 +0200 Subject: [PATCH 12/19] Fix sonar analysis version name issue --- vector/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vector/build.gradle b/vector/build.gradle index ef2d6c3d9e..e55ad31ef3 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -122,6 +122,9 @@ android { // Other branches (master, features, etc.) will have version code based on application version. versionCode project.getVersionCode() + // Required for sonar analysis + versionName "${versionMajor}.${versionMinor}.${versionPatch}-sonar" + buildConfigField "String", "GIT_REVISION", "\"${gitRevision()}\"" resValue "string", "git_revision", "\"${gitRevision()}\"" From b69616117f7578cc5053b625fd32808c1819287d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2020 15:37:39 +0200 Subject: [PATCH 13/19] Sonar: skip project `diff-match-patch` --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index fdaef6a2f7..93ea529163 100644 --- a/build.gradle +++ b/build.gradle @@ -105,6 +105,12 @@ project(":vector") { } } +project(":diff-match-patch") { + sonarqube { + skipProject = true + } +} + //project(":matrix-sdk-android") { // sonarqube { // properties { From cd28ad4c07aad9bf3c60d69d5ff12816c8bec254 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2020 15:51:28 +0200 Subject: [PATCH 14/19] Remove dead code (QrCode animation) --- .../app/features/debug/DebugMenuActivity.kt | 2 +- .../app/core/ui/views/QrCodeImageView.kt | 50 ++----------------- .../VerificationChooseMethodController.kt | 1 - .../BottomSheetVerificationQrCodeItem.kt | 5 +- 4 files changed, 5 insertions(+), 53 deletions(-) diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt index 855333d96b..5590e19c10 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt @@ -68,7 +68,7 @@ class DebugMenuActivity : VectorBaseActivity() { } private fun renderQrCode(text: String) { - debug_qr_code.setData(text, true) + debug_qr_code.setData(text) } @OnClick(R.id.debug_test_text_view_link) diff --git a/vector/src/main/java/im/vector/app/core/ui/views/QrCodeImageView.kt b/vector/src/main/java/im/vector/app/core/ui/views/QrCodeImageView.kt index 11e319ab78..f4b371bc52 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/QrCodeImageView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/QrCodeImageView.kt @@ -18,28 +18,23 @@ package im.vector.app.core.ui.views import android.content.Context import android.graphics.Color -import android.graphics.drawable.AnimationDrawable -import android.graphics.drawable.BitmapDrawable import android.util.AttributeSet import androidx.appcompat.widget.AppCompatImageView import im.vector.app.core.qrcode.toBitMatrix import im.vector.app.core.qrcode.toBitmap -import kotlin.random.Random class QrCodeImageView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : AppCompatImageView(context, attrs, defStyleAttr) { private var data: String? = null - private var animate = false init { setBackgroundColor(Color.WHITE) } - fun setData(data: String, animate: Boolean) { + fun setData(data: String) { this.data = data - this.animate = animate render() } @@ -53,47 +48,8 @@ class QrCodeImageView @JvmOverloads constructor( data ?.takeIf { height > 0 } ?.let { - if (animate) { - // NOT SUPPORTED YET val anim = createAnimation(it) - // NOT SUPPORTED YET setImageDrawable(anim) - // NOT SUPPORTED YET anim.start() - // NOT SUPPORTED YET setImageDrawable(BitmapDrawable(resources, it.toBitMatrix(height).toBitmap())) - val bitmap = it.toBitMatrix(height).toBitmap() - post { setImageBitmap(bitmap) } - } else { - val bitmap = it.toBitMatrix(height).toBitmap() - post { setImageBitmap(bitmap) } - } + val bitmap = it.toBitMatrix(height).toBitmap() + post { setImageBitmap(bitmap) } } } - - private fun createAnimation(data: String): AnimationDrawable { - val finalQr = data.toBitMatrix(height) - - val list = mutableListOf(finalQr) - - val random = Random(System.currentTimeMillis()) - val repeatTime = 8 - repeat(repeatTime) { index -> - val alteredQr = finalQr.clone() - for (x in 0 until alteredQr.width) { - for (y in 0 until alteredQr.height) { - if (random.nextInt(repeatTime - index) == 0) { - // Pb is that it does not toggle a whole black square, but only a pixel - alteredQr.unset(x, y) - } - } - } - list.add(alteredQr) - } - - val animDrawable = AnimationDrawable() - - list.asReversed() - .forEach { - animDrawable.addFrame(BitmapDrawable(resources, it.toBitmap()), 150) - } - - return animDrawable - } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodController.kt index 953b20cb0c..0406a63bc6 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodController.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodController.kt @@ -53,7 +53,6 @@ class VerificationChooseMethodController @Inject constructor( bottomSheetVerificationQrCodeItem { id("qr") data(state.qrCodeText) - animate(false) } dividerItem { diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationQrCodeItem.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationQrCodeItem.kt index 8d78c1c9d6..41c92fa76f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationQrCodeItem.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationQrCodeItem.kt @@ -32,12 +32,9 @@ abstract class BottomSheetVerificationQrCodeItem : VectorEpoxyModel Date: Thu, 27 Aug 2020 17:38:35 +0200 Subject: [PATCH 15/19] Remove commented out code and useless code --- .../settings/VectorSettingsLabsFragment.kt | 100 +----------------- 1 file changed, 1 insertion(+), 99 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt index c8cf44778c..6a4ea83d68 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt @@ -17,7 +17,6 @@ package im.vector.app.features.settings import im.vector.app.R -import im.vector.app.core.preference.VectorSwitchPreference import javax.inject.Inject class VectorSettingsLabsFragment @Inject constructor( @@ -28,103 +27,6 @@ class VectorSettingsLabsFragment @Inject constructor( override val preferenceXmlRes = R.xml.vector_settings_labs override fun bindPref() { - // Lab - - findPreference(VectorPreferences.SETTINGS_LABS_ALLOW_EXTENDED_LOGS)?.let { - it.isChecked = vectorPreferences.labAllowedExtendedLogging() - } - - findPreference(VectorPreferences.SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB)?.let { - it.isChecked = vectorPreferences.labAddNotificationTab() - } - -// val useCryptoPref = findPreference(VectorPreferences.SETTINGS_ROOM_SETTINGS_LABS_END_TO_END_PREFERENCE_KEY) as SwitchPreference -// val cryptoIsEnabledPref = findPreference(VectorPreferences.SETTINGS_ROOM_SETTINGS_LABS_END_TO_END_IS_ACTIVE_PREFERENCE_KEY) - - if (session.cryptoService().isCryptoEnabled()) { -// mLabsCategory.removePreference(useCryptoPref) -// -// cryptoIsEnabledPref.isEnabled = false - } else { -// mLabsCategory.removePreference(cryptoIsEnabledPref) -// -// useCryptoPref.isChecked = false -// -// useCryptoPref.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValueAsVoid -> -// if (TextUtils.isEmpty(mSession.sessionParams.deviceId)) { -// activity?.let { activity -> -// AlertDialog.Builder(activity) -// .setMessage(R.string.room_settings_labs_end_to_end_warnings) -// .setPositiveButton(R.string.logout) { _, _ -> -// notImplemented() -// // TODO CommonActivityUtils.logout(activity) -// } -// .setNegativeButton(R.string.cancel) { _, _ -> -// useCryptoPref.isChecked = false -// } -// .setOnCancelListener { -// useCryptoPref.isChecked = false -// } -// .show() -// } -// } else { -// val newValue = newValueAsVoid as Boolean -// -// if (mSession.isCryptoEnabled() != newValue) { -// notImplemented() -// /* TODO -// displayLoadingView() -// -// session.enableCrypto(newValue, object : MatrixCallback { -// private fun refresh() { -// activity?.runOnUiThread { -// hideLoadingView() -// useCryptoPref.isChecked = session.isCryptoEnabled -// -// if (session.isCryptoEnabled) { -// mLabsCategory.removePreference(useCryptoPref) -// mLabsCategory.addPreference(cryptoIsEnabledPref) -// } -// } -// } -// -// override fun onSuccess(info: Void?) { -// useCryptoPref.isEnabled = false -// refresh() -// } -// -// override fun onNetworkError(e: Exception) { -// useCryptoPref.isChecked = false -// } -// -// override fun onMatrixError(e: MatrixError) { -// useCryptoPref.isChecked = false -// } -// -// override fun onUnexpectedError(e: Exception) { -// useCryptoPref.isChecked = false -// } -// }) -// */ -// } -// } -// -// true -// } - } - - // SaveMode Management -// findPreference(VectorPreferences.SETTINGS_DATA_SAVE_MODE_PREFERENCE_KEY) -// .onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> -// notImplemented() -// /* TODO -// val sessions = Matrix.getMXSessions(activity) -// for (session in sessions) { -// session.setUseDataSaveMode(newValue as Boolean) -// } -// */ -// -// true -// } + // Nothing to do } } From a857d8e3063ab4e36d659869554c575d30c9dd29 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2020 17:39:09 +0200 Subject: [PATCH 16/19] Fix bugs detected by sonar --- build.gradle | 3 ++- .../roompreview/RoomPreviewActivity.kt | 2 ++ .../devices/VectorSettingsDevicesFragment.kt | 13 +++++++------ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 93ea529163..aa941e1445 100644 --- a/build.gradle +++ b/build.gradle @@ -100,7 +100,8 @@ project(":vector") { properties { property "sonar.sources", project(":vector").android.sourceSets.main.java.srcDirs // exclude source code from analyses separated by a colon (:) - // property "sonar.exclusions", "**/*.*" + // Exclude Java source + property "sonar.exclusions", "**/BugReporterMultipartBody.java" } } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt index 8bbaa14ab0..4876bd7f98 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt @@ -28,6 +28,7 @@ import kotlinx.android.parcel.Parcelize import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.util.MatrixItem +import timber.log.Timber @Parcelize data class RoomPreviewData( @@ -83,6 +84,7 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { if (args?.worldReadable == true) { // TODO Room preview: Note: M does not recommend to use /events anymore, so for now we just display the room preview // TODO the same way if it was not world readable + Timber.d("just display the room preview the same way if it was not world readable") addFragment(R.id.simpleFragmentContainer, RoomPreviewNoPreviewFragment::class.java, args) } else { addFragment(R.id.simpleFragmentContainer, RoomPreviewNoPreviewFragment::class.java, args) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt index b33a0cb5d8..bc72f812ff 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt @@ -48,7 +48,8 @@ class VectorSettingsDevicesFragment @Inject constructor( ) : VectorBaseFragment(), DevicesController.Callback { // used to avoid requesting to enter the password for each deletion - private var mAccountPassword: String = "" + // Note: Sonar does not like to use password for member name. + private var mAccountPass: String = "" override fun getLayoutResId() = R.layout.fragment_generic_recycler @@ -91,7 +92,7 @@ class VectorSettingsDevicesFragment @Inject constructor( super.showFailure(throwable) // Password is maybe not good, for safety measure, reset it here - mAccountPassword = "" + mAccountPass = "" } override fun onDestroyView() { @@ -153,12 +154,12 @@ class VectorSettingsDevicesFragment @Inject constructor( * Show a dialog to ask for user password, or use a previously entered password. */ private fun maybeShowDeleteDeviceWithPasswordDialog() { - if (mAccountPassword.isNotEmpty()) { - viewModel.handle(DevicesAction.Password(mAccountPassword)) + if (mAccountPass.isNotEmpty()) { + viewModel.handle(DevicesAction.Password(mAccountPass)) } else { PromptPasswordDialog().show(requireActivity()) { password -> - mAccountPassword = password - viewModel.handle(DevicesAction.Password(mAccountPassword)) + mAccountPass = password + viewModel.handle(DevicesAction.Password(mAccountPass)) } } } From 02145eaa06ac153ca5f9b2e44ac0ca54e6d88c5c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2020 17:43:22 +0200 Subject: [PATCH 17/19] Github repository has been renamed --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- AUTHORS.md | 2 +- README.md | 2 +- build.gradle | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 77dffb43d2..34d7b40a88 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ ### Pull Request Checklist - + - [ ] Changes has been tested on an Android device or Android emulator with API 21 - [ ] UI change has been tested on both light and dark themes diff --git a/AUTHORS.md b/AUTHORS.md index 3d9dffbef5..a85beb2d6f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,4 +1,4 @@ -A full developer contributors list can be found [here](https://github.com/vector-im/riotX-android/graphs/contributors). +A full developer contributors list can be found [here](https://github.com/vector-im/element-android/graphs/contributors). # Core team: diff --git a/README.md b/README.md index 1ec425793f..ab1a121792 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,6 @@ The team will work to add them on a regular basis. ## Contributing -Please refer to [CONTRIBUTING.md](https://github.com/vector-im/riotX-android/blob/develop/CONTRIBUTING.md) if you want to contribute on Matrix Android projects! +Please refer to [CONTRIBUTING.md](https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md) if you want to contribute on Matrix Android projects! Come chat with the community in the dedicated Matrix [room](https://matrix.to/#/#element-android:matrix.org). diff --git a/build.gradle b/build.gradle index aa941e1445..84fa1cd713 100644 --- a/build.gradle +++ b/build.gradle @@ -86,10 +86,10 @@ sonarqube { property "sonar.host.url", "https://sonarcloud.io" property "sonar.projectVersion", project(":vector").android.defaultConfig.versionName property "sonar.sourceEncoding", "UTF-8" - property "sonar.links.homepage", "https://github.com/vector-im/riotX-android/" + property "sonar.links.homepage", "https://github.com/vector-im/element-android/" property "sonar.links.ci", "https://buildkite.com/matrix-dot-org/riotx-android" - property "sonar.links.scm", "https://github.com/vector-im/riotX-android/" - property "sonar.links.issue", "https://github.com/vector-im/riotX-android/issues" + property "sonar.links.scm", "https://github.com/vector-im/element-android/" + property "sonar.links.issue", "https://github.com/vector-im/element-android/issues" property "sonar.organization", "new_vector_ltd_organization" property "sonar.login", project.hasProperty("SONAR_LOGIN") ? SONAR_LOGIN : "invalid" } From 8369003bdf517cad85bf819c992277b0d473ad9d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2020 17:48:19 +0200 Subject: [PATCH 18/19] Sonar: rename project name and project key --- README.md | 6 +++--- build.gradle | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ab1a121792..457b18d775 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ [![Buildkite](https://badge.buildkite.com/657d3db27364448d69d54f66c690f7788bc6aa80a7628e37f3.svg?branch=develop)](https://buildkite.com/matrix-dot-org/riotx-android/builds?branch=develop) [![Weblate](https://translate.riot.im/widgets/element-android/-/svg-badge.svg)](https://translate.riot.im/engage/element-android/?utm_source=widget) [![Element Android Matrix room #element-android:matrix.org](https://img.shields.io/matrix/element-android:matrix.org.svg?label=%23element-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-android:matrix.org) -[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=vector.android.riotx&metric=alert_status)](https://sonarcloud.io/dashboard?id=vector.android.riotx) -[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=vector.android.riotx&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=vector.android.riotx) -[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=vector.android.riotx&metric=bugs)](https://sonarcloud.io/dashboard?id=vector.android.riotx) +[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=im.vector.app.android&metric=alert_status)](https://sonarcloud.io/dashboard?id=im.vector.app.android) +[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=im.vector.app.android&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=im.vector.app.android) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=im.vector.app.android&metric=bugs)](https://sonarcloud.io/dashboard?id=im.vector.app.android) # Element Android diff --git a/build.gradle b/build.gradle index 84fa1cd713..061dd73a40 100644 --- a/build.gradle +++ b/build.gradle @@ -81,8 +81,8 @@ apply plugin: 'org.sonarqube' sonarqube { properties { - property "sonar.projectName", "RiotX-Android" - property "sonar.projectKey", "vector.android.riotx" + property "sonar.projectName", "Element-Android" + property "sonar.projectKey", "im.vector.app.android" property "sonar.host.url", "https://sonarcloud.io" property "sonar.projectVersion", project(":vector").android.defaultConfig.versionName property "sonar.sourceEncoding", "UTF-8" From ef4f930ba2b79186591b5b5c6fa4eedcf4eef9e1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Aug 2020 18:19:15 +0200 Subject: [PATCH 19/19] Don't pause the sync thread if there is an active or pending call. But pause the sync thread when there is no active call and the app is the background. Authors: Onuray, I just rebased and squashed all the commit --- CHANGES.md | 1 + .../api/session/call/CallSignalingService.kt | 2 + .../session/call/ActiveCallHandler.kt | 45 +++++++++++++++++++ .../call/DefaultCallSignalingService.kt | 25 ++++++----- .../internal/session/sync/job/SyncThread.kt | 34 ++++++++++++-- 5 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/ActiveCallHandler.kt diff --git a/CHANGES.md b/CHANGES.md index bc363683a2..1ffb6bcad0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ Bugfix 🐛: - Fix bad color for settings icon on Android < 24 (#1786) - Change user or room avatar: when selecting Gallery, I'm not proposed to crop the selected image (#1590) - Fix uploads still don't work with room v6 (#1879) + - Can't handle ongoing call events in background (#1992) Translations 🗣: - diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt index 2962f9fac3..382ab54248 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt @@ -34,4 +34,6 @@ interface CallSignalingService { fun removeCallListener(listener: CallsListener) fun getCallWithId(callId: String) : MxCall? + + fun isThereAnyActiveCall(): Boolean } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/ActiveCallHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/ActiveCallHandler.kt new file mode 100644 index 0000000000..40f5a56c33 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/ActiveCallHandler.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 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 org.matrix.android.sdk.internal.session.call + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import org.matrix.android.sdk.api.session.call.MxCall +import org.matrix.android.sdk.internal.session.SessionScope +import javax.inject.Inject + +@SessionScope +internal class ActiveCallHandler @Inject constructor() { + + private val activeCallListLiveData: MutableLiveData> by lazy { + MutableLiveData>(mutableListOf()) + } + + fun addCall(call: MxCall) { + activeCallListLiveData.postValue(activeCallListLiveData.value?.apply { add(call) }) + } + + fun removeCall(callId: String) { + activeCallListLiveData.postValue(activeCallListLiveData.value?.apply { removeAll { it.callId == callId } }) + } + + fun getCallWithId(callId: String): MxCall? { + return activeCallListLiveData.value?.find { it.callId == callId } + } + + fun getActiveCallsLiveData(): LiveData> = activeCallListLiveData +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt index 0b097cf64f..d9bc66eddf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt @@ -49,6 +49,7 @@ import javax.inject.Inject internal class DefaultCallSignalingService @Inject constructor( @UserId private val userId: String, + private val activeCallHandler: ActiveCallHandler, private val localEchoEventFactory: LocalEchoEventFactory, private val roomEventSender: RoomEventSender, private val taskExecutor: TaskExecutor, @@ -57,8 +58,6 @@ internal class DefaultCallSignalingService @Inject constructor( private val callListeners = mutableSetOf() - private val activeCalls = mutableListOf() - private val cachedTurnServerResponse = object { // Keep one minute safe to avoid considering the data is valid and then actually it is not when effectively using it. private val MIN_TTL = 60 @@ -97,7 +96,7 @@ internal class DefaultCallSignalingService @Inject constructor( } override fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall { - return MxCallImpl( + val call = MxCallImpl( callId = UUID.randomUUID().toString(), isOutgoing = true, roomId = roomId, @@ -106,8 +105,9 @@ internal class DefaultCallSignalingService @Inject constructor( isVideoCall = isVideoCall, localEchoEventFactory = localEchoEventFactory, roomEventSender = roomEventSender - ).also { - activeCalls.add(it) + ) + activeCallHandler.addCall(call).also { + return call } } @@ -120,8 +120,12 @@ internal class DefaultCallSignalingService @Inject constructor( } override fun getCallWithId(callId: String): MxCall? { - Timber.v("## VOIP getCallWithId $callId all calls ${activeCalls.map { it.callId }}") - return activeCalls.find { it.callId == callId } + Timber.v("## VOIP getCallWithId $callId all calls ${activeCallHandler.getActiveCallsLiveData().value?.map { it.callId }}") + return activeCallHandler.getCallWithId(callId) + } + + override fun isThereAnyActiveCall(): Boolean { + return activeCallHandler.getActiveCallsLiveData().value?.isNotEmpty() == true } internal fun onCallEvent(event: Event) { @@ -152,6 +156,7 @@ internal class DefaultCallSignalingService @Inject constructor( // Always ignore local echos of invite return } + event.getClearContent().toModel()?.let { content -> val incomingCall = MxCallImpl( callId = content.callId ?: return@let, @@ -163,7 +168,7 @@ internal class DefaultCallSignalingService @Inject constructor( localEchoEventFactory = localEchoEventFactory, roomEventSender = roomEventSender ) - activeCalls.add(incomingCall) + activeCallHandler.addCall(incomingCall) onCallInvite(incomingCall, content) } } @@ -185,8 +190,8 @@ internal class DefaultCallSignalingService @Inject constructor( return } + activeCallHandler.removeCall(content.callId) onCallHangup(content) - activeCalls.removeAll { it.callId == content.callId } } } EventType.CALL_CANDIDATES -> { @@ -195,7 +200,7 @@ internal class DefaultCallSignalingService @Inject constructor( return } event.getClearContent().toModel()?.let { content -> - activeCalls.firstOrNull { it.callId == content.callId }?.let { + activeCallHandler.getCallWithId(content.callId)?.let { onCallIceCandidate(it, content) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt index 1a2d6b1fd3..9fd9c313db 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.sync.job import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Observer import com.squareup.moshi.JsonEncodingException import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.isTokenError @@ -30,11 +31,14 @@ import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver import org.matrix.android.sdk.internal.util.Debouncer import org.matrix.android.sdk.internal.util.createUIHandler import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import org.matrix.android.sdk.api.session.call.MxCall +import org.matrix.android.sdk.internal.session.call.ActiveCallHandler import timber.log.Timber import java.net.SocketTimeoutException import java.util.Timer @@ -48,8 +52,9 @@ private const val DEFAULT_LONG_POOL_TIMEOUT = 30_000L internal class SyncThread @Inject constructor(private val syncTask: SyncTask, private val typingUsersTracker: DefaultTypingUsersTracker, private val networkConnectivityChecker: NetworkConnectivityChecker, - private val backgroundDetectionObserver: BackgroundDetectionObserver) - : Thread(), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener { + private val backgroundDetectionObserver: BackgroundDetectionObserver, + private val activeCallHandler: ActiveCallHandler +) : Thread(), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener { private var state: SyncState = SyncState.Idle private var liveState = MutableLiveData(state) @@ -62,6 +67,12 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, private var isTokenValid = true private var retryNoNetworkTask: TimerTask? = null + private val activeCallListObserver = Observer> { activeCalls -> + if (activeCalls.isEmpty() && backgroundDetectionObserver.isInBackground) { + pause() + } + } + init { updateStateTo(SyncState.Idle) } @@ -115,9 +126,11 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, override fun run() { Timber.v("Start syncing...") + isStarted = true networkConnectivityChecker.register(this) backgroundDetectionObserver.register(this) + registerActiveCallsObserver() while (state != SyncState.Killing) { Timber.v("Entering loop, state: $state") if (!isStarted) { @@ -163,6 +176,19 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, updateStateTo(SyncState.Killed) backgroundDetectionObserver.unregister(this) networkConnectivityChecker.unregister(this) + unregisterActiveCallsObserver() + } + + private fun registerActiveCallsObserver() { + syncScope.launch(Dispatchers.Main) { + activeCallHandler.getActiveCallsLiveData().observeForever(activeCallListObserver) + } + } + + private fun unregisterActiveCallsObserver() { + syncScope.launch(Dispatchers.Main) { + activeCallHandler.getActiveCallsLiveData().removeObserver(activeCallListObserver) + } } private suspend fun doSync(params: SyncTask.Params) { @@ -215,6 +241,8 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, } override fun onMoveToBackground() { - pause() + if (activeCallHandler.getActiveCallsLiveData().value.isNullOrEmpty()) { + pause() + } } }