From a26e9594302c1503a2797af7ae5074a6904f048d Mon Sep 17 00:00:00 2001 From: Ganard Date: Tue, 4 Feb 2020 16:35:04 +0100 Subject: [PATCH 001/187] Attachements: introduce structure for preview, cropping and compressing --- build.gradle | 2 + vector/build.gradle | 1 + vector/src/main/AndroidManifest.xml | 5 ++ .../im/vector/riotx/core/di/FragmentModule.kt | 7 ++ .../preview/AttachmentPreviewController.kt | 30 +++++++ .../preview/AttachmentPreviewItem.kt | 58 +++++++++++++ .../preview/AttachmentsPreviewAction.kt | 22 +++++ .../preview/AttachmentsPreviewActivity.kt | 54 ++++++++++++ .../preview/AttachmentsPreviewFragment.kt | 67 +++++++++++++++ .../preview/AttachmentsPreviewViewEvents.kt | 22 +++++ .../preview/AttachmentsPreviewViewModel.kt | 47 ++++++++++ .../preview/AttachmentsPreviewViewState.kt | 28 ++++++ .../layout/fragment_attachments_preview.xml | 86 +++++++++++++++++++ .../res/layout/item_attachment_preview.xml | 19 ++++ 14 files changed, 448 insertions(+) create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewController.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItem.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewAction.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewEvents.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt create mode 100644 vector/src/main/res/layout/fragment_attachments_preview.xml create mode 100644 vector/src/main/res/layout/item_attachment_preview.xml diff --git a/build.gradle b/build.gradle index a2fac55175..7f80eaf5fe 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,8 @@ allprojects { includeGroupByRegex "com\\.github\\.jaiselrahman" // And monarchy includeGroupByRegex "com\\.github\\.Zhuinden" + // And ucrop + includeGroupByRegex "com\\.github\\.yalantis" } } maven { diff --git a/vector/build.gradle b/vector/build.gradle index 0517482904..7352adc712 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -341,6 +341,7 @@ dependencies { implementation "com.github.bumptech.glide:glide:$glide_version" kapt "com.github.bumptech.glide:compiler:$glide_version" implementation 'com.danikula:videocache:2.7.1' + implementation 'com.github.yalantis:ucrop:2.2.4' // Badge for compatibility implementation 'me.leolin:ShortcutBadger:1.1.22@aar' diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 3207ab257a..f908880e6f 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -134,6 +134,11 @@ + + + () { + + override fun buildModels(data: AttachmentsPreviewViewState) { + data.attachments.forEach { + + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItem.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItem.kt new file mode 100644 index 0000000000..03ac160d62 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItem.kt @@ -0,0 +1,58 @@ +/* + * Copyright 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 im.vector.riotx.features.attachments.preview + +import android.view.View +import android.widget.ImageView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import im.vector.matrix.android.api.session.content.ContentAttachmentData +import im.vector.riotx.R +import im.vector.riotx.core.epoxy.VectorEpoxyHolder +import im.vector.riotx.core.epoxy.VectorEpoxyModel +import kotlinx.android.synthetic.main.item_attachment_preview.view.* + +@EpoxyModelClass(layout = R.layout.item_attachment_preview) +abstract class AttachmentPreviewItem : VectorEpoxyModel() { + + @EpoxyAttribute lateinit var attachment: ContentAttachmentData + @EpoxyAttribute var clickListener: View.OnClickListener? = null + + override fun bind(holder: Holder) { + holder.view.setOnClickListener(clickListener) + // If name is empty, use userId as name and force it being centered + val mimeType = attachment.mimeType + val path = attachment.path + if (mimeType != null && (mimeType.startsWith("image") || mimeType.startsWith("video"))) { + Glide.with(holder.view.context) + .asBitmap() + .load(path) + .apply(RequestOptions().frame(0)) + .into(holder.imageView) + } else { + holder.imageView.attachmentPreviewImageView.setImageResource(R.drawable.filetype_attachment) + holder.imageView.attachmentPreviewImageView.scaleType = ImageView.ScaleType.FIT_CENTER + } + } + + class Holder : VectorEpoxyHolder() { + val imageView by bind(R.id.attachmentPreviewImageView) + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewAction.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewAction.kt new file mode 100644 index 0000000000..61fb41a774 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewAction.kt @@ -0,0 +1,22 @@ +/* + * Copyright 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 im.vector.riotx.features.attachments.preview + +import im.vector.riotx.core.platform.VectorViewModelAction + +sealed class AttachmentsPreviewAction : VectorViewModelAction diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt new file mode 100644 index 0000000000..509be251c3 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt @@ -0,0 +1,54 @@ +/* + * Copyright 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 im.vector.riotx.features.attachments.preview + +import android.content.Context +import android.content.Intent +import androidx.appcompat.widget.Toolbar +import im.vector.riotx.R +import im.vector.riotx.core.extensions.addFragment +import im.vector.riotx.core.platform.ToolbarConfigurable +import im.vector.riotx.core.platform.VectorBaseActivity + +class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { + + companion object { + + private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS" + + fun newIntent(context: Context, args: AttachmentsPreviewArgs): Intent { + return Intent(context, AttachmentsPreviewArgs::class.java).apply { + putExtra(EXTRA_FRAGMENT_ARGS, args) + } + } + } + + override fun getLayoutRes() = R.layout.activity_simple + + override fun initUiAndData() { + if (isFirstCreation()) { + val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS) + ?: return + addFragment(R.id.simpleFragmentContainer, AttachmentsPreviewFragment::class.java, fragmentArgs) + } + } + + override fun configure(toolbar: Toolbar) { + configureToolbar(toolbar) + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt new file mode 100644 index 0000000000..6e99758c6d --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -0,0 +1,67 @@ +/* + * Copyright 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 im.vector.riotx.features.attachments.preview + +import android.os.Bundle +import android.os.Parcelable +import android.view.View +import com.airbnb.mvrx.args +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.matrix.android.api.session.content.ContentAttachmentData +import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith +import im.vector.riotx.core.platform.VectorBaseFragment +import kotlinx.android.parcel.Parcelize +import kotlinx.android.synthetic.main.fragment_attachments_preview.* +import javax.inject.Inject + +@Parcelize +data class AttachmentsPreviewArgs( + val attachments: List +) : Parcelable + +class AttachmentsPreviewFragment @Inject constructor( + val viewModelFactory: AttachmentsPreviewViewModel.Factory, + private val attachmentPreviewController: AttachmentPreviewController +) : VectorBaseFragment() { + + private val fragmentArgs: AttachmentsPreviewArgs by args() + private val viewModel: AttachmentsPreviewViewModel by fragmentViewModel() + + override fun getLayoutResId() = R.layout.fragment_attachments_preview + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupRecyclerView() + } + + override fun onDestroyView() { + super.onDestroyView() + attachmentPreviewerList.cleanup() + } + + private fun setupRecyclerView() { + attachmentPreviewerList.configureWith(attachmentPreviewController, hasFixedSize = true) + } + + override fun invalidate() = withState(viewModel) { state -> + + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewEvents.kt new file mode 100644 index 0000000000..12a9d9aa24 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewEvents.kt @@ -0,0 +1,22 @@ +/* + * Copyright 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 im.vector.riotx.features.attachments.preview + +import im.vector.riotx.core.platform.VectorViewEvents + +sealed class AttachmentsPreviewViewEvents : VectorViewEvents diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt new file mode 100644 index 0000000000..28a0aaebd6 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt @@ -0,0 +1,47 @@ +/* + * Copyright 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 im.vector.riotx.features.attachments.preview + +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.riotx.core.platform.VectorViewModel + +class AttachmentsPreviewViewModel @AssistedInject constructor(@Assisted initialState: AttachmentsPreviewViewState) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: AttachmentsPreviewViewState): AttachmentsPreviewViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: AttachmentsPreviewViewState): AttachmentsPreviewViewModel? { + val fragment: AttachmentsPreviewFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.viewModelFactory.create(state) + } + } + + override fun handle(action: AttachmentsPreviewAction) { + //TODO + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt new file mode 100644 index 0000000000..f92abb97ea --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt @@ -0,0 +1,28 @@ +/* + * Copyright 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 im.vector.riotx.features.attachments.preview + +import com.airbnb.mvrx.MvRxState +import im.vector.matrix.android.api.session.content.ContentAttachmentData + +data class AttachmentsPreviewViewState( + val attachments: List +) : MvRxState { + + constructor(args: AttachmentsPreviewArgs) : this(attachments = args.attachments) +} diff --git a/vector/src/main/res/layout/fragment_attachments_preview.xml b/vector/src/main/res/layout/fragment_attachments_preview.xml new file mode 100644 index 0000000000..60d311b96d --- /dev/null +++ b/vector/src/main/res/layout/fragment_attachments_preview.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vector/src/main/res/layout/item_attachment_preview.xml b/vector/src/main/res/layout/item_attachment_preview.xml new file mode 100644 index 0000000000..3c769d17d9 --- /dev/null +++ b/vector/src/main/res/layout/item_attachment_preview.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file From 41f1ec5d880995afbd0eeb145f3ae1f283f4de96 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 5 Feb 2020 11:24:18 +0100 Subject: [PATCH 002/187] Attachments: preview with pager mode --- .../preview/AttachmentPreviewController.kt | 30 ------- .../preview/AttachmentPreviewControllers.kt | 54 ++++++++++++ .../preview/AttachmentPreviewItem.kt | 58 ------------- .../preview/AttachmentPreviewItems.kt | 82 ++++++++++++++++++ .../preview/AttachmentsPreviewActivity.kt | 10 ++- .../preview/AttachmentsPreviewFragment.kt | 33 +++++-- .../home/room/detail/RoomDetailFragment.kt | 5 +- .../layout/fragment_attachments_preview.xml | 85 ++++--------------- .../layout/item_attachment_big_preview.xml | 12 +++ ... => item_attachment_miniature_preview.xml} | 9 +- 10 files changed, 209 insertions(+), 169 deletions(-) delete mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewController.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewControllers.kt delete mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItem.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItems.kt create mode 100644 vector/src/main/res/layout/item_attachment_big_preview.xml rename vector/src/main/res/layout/{item_attachment_preview.xml => item_attachment_miniature_preview.xml} (67%) diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewController.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewController.kt deleted file mode 100644 index 273fd4272c..0000000000 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewController.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 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 im.vector.riotx.features.attachments.preview - -import com.airbnb.epoxy.TypedEpoxyController -import javax.inject.Inject - -class AttachmentPreviewController @Inject constructor() : TypedEpoxyController() { - - override fun buildModels(data: AttachmentsPreviewViewState) { - data.attachments.forEach { - - } - } -} diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewControllers.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewControllers.kt new file mode 100644 index 0000000000..9cef33d402 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewControllers.kt @@ -0,0 +1,54 @@ +/* + * Copyright 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 im.vector.riotx.features.attachments.preview + +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.matrix.android.api.session.content.ContentAttachmentData +import javax.inject.Inject + +class AttachmentBigPreviewController @Inject constructor() : TypedEpoxyController() { + + override fun buildModels(data: AttachmentsPreviewViewState) { + data.attachments.forEach { + attachmentBigPreviewItem { + id(it.path) + attachment(it) + } + } + } +} + +class AttachmentMiniaturePreviewController @Inject constructor() : TypedEpoxyController() { + + interface Callback { + fun onAttachmentClicked(contentAttachmentData: ContentAttachmentData) + } + + var callback: Callback? = null + + override fun buildModels(data: AttachmentsPreviewViewState) { + data.attachments.forEach { + attachmentMiniaturePreviewItem { + id(it.path) + attachment(it) + clickListener { _ -> + callback?.onAttachmentClicked(it) + } + } + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItem.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItem.kt deleted file mode 100644 index 03ac160d62..0000000000 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItem.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 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 im.vector.riotx.features.attachments.preview - -import android.view.View -import android.widget.ImageView -import com.airbnb.epoxy.EpoxyAttribute -import com.airbnb.epoxy.EpoxyModelClass -import com.bumptech.glide.Glide -import com.bumptech.glide.request.RequestOptions -import im.vector.matrix.android.api.session.content.ContentAttachmentData -import im.vector.riotx.R -import im.vector.riotx.core.epoxy.VectorEpoxyHolder -import im.vector.riotx.core.epoxy.VectorEpoxyModel -import kotlinx.android.synthetic.main.item_attachment_preview.view.* - -@EpoxyModelClass(layout = R.layout.item_attachment_preview) -abstract class AttachmentPreviewItem : VectorEpoxyModel() { - - @EpoxyAttribute lateinit var attachment: ContentAttachmentData - @EpoxyAttribute var clickListener: View.OnClickListener? = null - - override fun bind(holder: Holder) { - holder.view.setOnClickListener(clickListener) - // If name is empty, use userId as name and force it being centered - val mimeType = attachment.mimeType - val path = attachment.path - if (mimeType != null && (mimeType.startsWith("image") || mimeType.startsWith("video"))) { - Glide.with(holder.view.context) - .asBitmap() - .load(path) - .apply(RequestOptions().frame(0)) - .into(holder.imageView) - } else { - holder.imageView.attachmentPreviewImageView.setImageResource(R.drawable.filetype_attachment) - holder.imageView.attachmentPreviewImageView.scaleType = ImageView.ScaleType.FIT_CENTER - } - } - - class Holder : VectorEpoxyHolder() { - val imageView by bind(R.id.attachmentPreviewImageView) - } -} diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItems.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItems.kt new file mode 100644 index 0000000000..24d7b312ec --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewItems.kt @@ -0,0 +1,82 @@ +/* + * Copyright 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 im.vector.riotx.features.attachments.preview + +import android.view.View +import android.widget.ImageView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import im.vector.matrix.android.api.session.content.ContentAttachmentData +import im.vector.riotx.R +import im.vector.riotx.core.epoxy.VectorEpoxyHolder +import im.vector.riotx.core.epoxy.VectorEpoxyModel + +abstract class AttachmentPreviewItem : VectorEpoxyModel() { + + abstract val attachment: ContentAttachmentData + + override fun bind(holder: H) { + val path = attachment.path + if (attachment.type == ContentAttachmentData.Type.VIDEO || attachment.type == ContentAttachmentData.Type.IMAGE) { + Glide.with(holder.view.context) + .asBitmap() + .load(path) + .apply(RequestOptions().frame(0)) + .into(holder.imageView) + } else { + holder.imageView.setImageResource(R.drawable.filetype_attachment) + holder.imageView.scaleType = ImageView.ScaleType.FIT_CENTER + } + } + + abstract class Holder : VectorEpoxyHolder() { + abstract val imageView: ImageView + } +} + +@EpoxyModelClass(layout = R.layout.item_attachment_miniature_preview) +abstract class AttachmentMiniaturePreviewItem : AttachmentPreviewItem() { + + @EpoxyAttribute override lateinit var attachment: ContentAttachmentData + @EpoxyAttribute + var clickListener: View.OnClickListener? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.view.setOnClickListener(clickListener) + } + + class Holder : AttachmentPreviewItem.Holder() { + override val imageView: ImageView + get() = miniatureImageView + private val miniatureImageView by bind(R.id.attachmentMiniatureImageView) + } +} + +@EpoxyModelClass(layout = R.layout.item_attachment_big_preview) +abstract class AttachmentBigPreviewItem : AttachmentPreviewItem() { + + @EpoxyAttribute override lateinit var attachment: ContentAttachmentData + + class Holder : AttachmentPreviewItem.Holder() { + override val imageView: ImageView + get() = bigImageView + private val bigImageView by bind(R.id.attachmentBigImageView) + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt index 509be251c3..d8d19cd550 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt @@ -19,6 +19,8 @@ package im.vector.riotx.features.attachments.preview import android.content.Context import android.content.Intent +import android.os.Bundle +import android.view.WindowManager import androidx.appcompat.widget.Toolbar import im.vector.riotx.R import im.vector.riotx.core.extensions.addFragment @@ -30,9 +32,10 @@ class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS" + const val REQUEST_CODE = 55 fun newIntent(context: Context, args: AttachmentsPreviewArgs): Intent { - return Intent(context, AttachmentsPreviewArgs::class.java).apply { + return Intent(context, AttachmentsPreviewActivity::class.java).apply { putExtra(EXTRA_FRAGMENT_ARGS, args) } } @@ -40,6 +43,11 @@ class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { override fun getLayoutRes() = R.layout.activity_simple + override fun onCreate(savedInstanceState: Bundle?) { + window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) + super.onCreate(savedInstanceState) + } + override fun initUiAndData() { if (isFirstCreation()) { val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS) diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt index 6e99758c6d..99280c3830 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -20,13 +20,14 @@ package im.vector.riotx.features.attachments.preview import android.os.Bundle import android.os.Parcelable import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.PagerSnapHelper import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup -import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseFragment import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_attachments_preview.* @@ -39,8 +40,9 @@ data class AttachmentsPreviewArgs( class AttachmentsPreviewFragment @Inject constructor( val viewModelFactory: AttachmentsPreviewViewModel.Factory, - private val attachmentPreviewController: AttachmentPreviewController -) : VectorBaseFragment() { + private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController, + private val attachmentBigPreviewController: AttachmentBigPreviewController +) : VectorBaseFragment(), AttachmentMiniaturePreviewController.Callback { private val fragmentArgs: AttachmentsPreviewArgs by args() private val viewModel: AttachmentsPreviewViewModel by fragmentViewModel() @@ -49,19 +51,36 @@ class AttachmentsPreviewFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setupRecyclerView() + setupRecyclerViews() + setupToolbar(attachmentPreviewerToolbar) } override fun onDestroyView() { super.onDestroyView() - attachmentPreviewerList.cleanup() + attachmentPreviewerMiniatureList.cleanup() } - private fun setupRecyclerView() { - attachmentPreviewerList.configureWith(attachmentPreviewController, hasFixedSize = true) + private fun setupRecyclerViews() { + attachmentPreviewerMiniatureList.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + attachmentPreviewerMiniatureList.setHasFixedSize(true) + attachmentPreviewerMiniatureList.adapter = attachmentMiniaturePreviewController.adapter + attachmentMiniaturePreviewController.callback = this + + attachmentPreviewerBigList.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + val snapHelper = PagerSnapHelper() + snapHelper.attachToRecyclerView(attachmentPreviewerBigList) + attachmentPreviewerBigList.setHasFixedSize(true) + attachmentPreviewerBigList.adapter = attachmentBigPreviewController.adapter + } override fun invalidate() = withState(viewModel) { state -> + attachmentMiniaturePreviewController.setData(state) + attachmentBigPreviewController.setData(state) + } + + override fun onAttachmentClicked(contentAttachmentData: ContentAttachmentData) { } + } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 5cb3024712..71ab8f2852 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -116,6 +116,8 @@ import im.vector.riotx.core.utils.toast import im.vector.riotx.features.attachments.AttachmentTypeSelectorView import im.vector.riotx.features.attachments.AttachmentsHelper import im.vector.riotx.features.attachments.ContactAttachment +import im.vector.riotx.features.attachments.preview.AttachmentsPreviewActivity +import im.vector.riotx.features.attachments.preview.AttachmentsPreviewArgs import im.vector.riotx.features.command.Command import im.vector.riotx.features.crypto.util.toImageRes import im.vector.riotx.features.crypto.verification.VerificationBottomSheet @@ -1337,7 +1339,8 @@ class RoomDetailFragment @Inject constructor( // AttachmentsHelper.Callback override fun onContentAttachmentsReady(attachments: List) { - roomDetailViewModel.handle(RoomDetailAction.SendMedia(attachments)) + val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(attachments)) + startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE) } override fun onAttachmentsProcessFailed() { diff --git a/vector/src/main/res/layout/fragment_attachments_preview.xml b/vector/src/main/res/layout/fragment_attachments_preview.xml index 60d311b96d..5acc8835b0 100644 --- a/vector/src/main/res/layout/fragment_attachments_preview.xml +++ b/vector/src/main/res/layout/fragment_attachments_preview.xml @@ -1,57 +1,32 @@ - + android:layout_height="match_parent" + xmlns:tools="http://schemas.android.com/tools"> - + + - - - - - - - - - - - - - - - - + diff --git a/vector/src/main/res/layout/item_attachment_big_preview.xml b/vector/src/main/res/layout/item_attachment_big_preview.xml new file mode 100644 index 0000000000..3705965520 --- /dev/null +++ b/vector/src/main/res/layout/item_attachment_big_preview.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_attachment_preview.xml b/vector/src/main/res/layout/item_attachment_miniature_preview.xml similarity index 67% rename from vector/src/main/res/layout/item_attachment_preview.xml rename to vector/src/main/res/layout/item_attachment_miniature_preview.xml index 3c769d17d9..23dfcf381b 100644 --- a/vector/src/main/res/layout/item_attachment_preview.xml +++ b/vector/src/main/res/layout/item_attachment_miniature_preview.xml @@ -1,19 +1,18 @@ - - \ No newline at end of file + \ No newline at end of file From 64717872327339e9d44752dad3319dd645e587d5 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 5 Feb 2020 17:54:40 +0100 Subject: [PATCH 003/187] Share: start managing multi selection and warning --- vector/src/main/AndroidManifest.xml | 8 +++- .../home/room/detail/RoomDetailFragment.kt | 5 ++- .../features/home/room/list/RoomListAction.kt | 5 ++- .../home/room/list/RoomListFragment.kt | 38 +++++++++++++------ .../home/room/list/RoomListViewEvents.kt | 3 +- .../home/room/list/RoomListViewModel.kt | 31 ++++++++++++++- .../home/room/list/RoomListViewState.kt | 4 +- .../home/room/list/RoomSummaryController.kt | 15 +++++--- .../home/room/list/RoomSummaryItem.kt | 23 ++++++++++- .../home/room/list/RoomSummaryItemFactory.kt | 9 +++-- .../features/share/IncomingShareActivity.kt | 1 + .../res/layout/activity_incoming_share.xml | 1 - .../main/res/layout/fragment_room_list.xml | 13 +++++++ vector/src/main/res/layout/item_room.xml | 34 ++++++++++++----- vector/src/main/res/values/strings_riotX.xml | 1 + 15 files changed, 151 insertions(+), 40 deletions(-) diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index f908880e6f..d1873b650e 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -88,7 +88,13 @@ - + + + diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 71ab8f2852..1caada8ce6 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -154,6 +154,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_detail.* +import kotlinx.android.synthetic.main.merge_composer_layout.* import kotlinx.android.synthetic.main.merge_composer_layout.view.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* import org.commonmark.parser.Parser @@ -300,7 +301,9 @@ class RoomDetailFragment @Inject constructor( super.onActivityCreated(savedInstanceState) if (savedInstanceState == null) { when (val sharedData = roomDetailArgs.sharedData) { - is SharedData.Text -> roomDetailViewModel.handle(RoomDetailAction.SendMessage(sharedData.text, false)) + is SharedData.Text -> { + roomDetailViewModel.handle(RoomDetailAction.ExitSpecialMode(composerLayout.text.toString())) + } is SharedData.Attachments -> roomDetailViewModel.handle(RoomDetailAction.SendMedia(sharedData.attachmentData)) null -> Timber.v("No share data to process") } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListAction.kt index 9db7374169..801bb48aed 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListAction.kt @@ -16,17 +16,20 @@ package im.vector.riotx.features.home.room.list +import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.riotx.core.platform.VectorViewModelAction +import im.vector.riotx.features.share.SharedData sealed class RoomListAction : VectorViewModelAction { - data class SelectRoom(val roomSummary: RoomSummary) : RoomListAction() + data class SelectRoom(val roomSummary: RoomSummary, val enableMultiSelect: Boolean) : RoomListAction() data class ToggleCategory(val category: RoomCategory) : RoomListAction() data class AcceptInvitation(val roomSummary: RoomSummary) : RoomListAction() data class RejectInvitation(val roomSummary: RoomSummary) : RoomListAction() data class FilterWith(val filter: String) : RoomListAction() data class ChangeRoomNotificationState(val roomId: String, val notificationState: RoomNotificationState) : RoomListAction() data class LeaveRoom(val roomId: String) : RoomListAction() + data class ShareToSelectedRooms(val sharedData: SharedData, val optionalMessage: String? = null): RoomListAction() object MarkAllRoomsRead : RoomListAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index 5a32f4b8b3..8bb8eeb75c 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -106,10 +106,15 @@ class RoomListFragment @Inject constructor( when (it) { is RoomListViewEvents.Loading -> showLoading(it.message) is RoomListViewEvents.Failure -> showFailure(it.throwable) - is RoomListViewEvents.SelectRoom -> openSelectedRoom(it) + is RoomListViewEvents.SelectRoom -> handleSelectRoom(it) }.exhaustive } + sendShareButton.setOnClickListener { _ -> + roomListViewModel.handle(RoomListAction.ShareToSelectedRooms(roomListParams.sharedData!!)) + requireActivity().finish() + } + createChatFabMenu.listener = this sharedActionViewModel @@ -131,12 +136,19 @@ class RoomListFragment @Inject constructor( super.onDestroyView() } - private fun openSelectedRoom(event: RoomListViewEvents.SelectRoom) { + private fun handleSelectRoom(event: RoomListViewEvents.SelectRoom) { if (roomListParams.displayMode == RoomListDisplayMode.SHARE) { val sharedData = roomListParams.sharedData ?: return - navigator.openRoomForSharing(requireActivity(), event.roomId, sharedData) + AlertDialog.Builder(requireActivity()) + .setTitle(R.string.send_attachment) + .setMessage(getString(R.string.share_confirm_room, event.roomSummary.displayName)) + .setPositiveButton(R.string.send) { _, _ -> + navigator.openRoomForSharing(requireActivity(), event.roomSummary.roomId, sharedData) + } + .setNegativeButton(R.string.cancel, null) + .show() } else { - navigator.openRoom(requireActivity(), event.roomId) + navigator.openRoom(requireActivity(), event.roomSummary.roomId) } } @@ -256,7 +268,7 @@ class RoomListFragment @Inject constructor( is Fail -> renderFailure(state.asyncFilteredRooms.error) } roomController.update(state) - + sendShareButton.isVisible = state.multiSelectionEnabled // Mark all as read menu when (roomListParams.displayMode) { RoomListDisplayMode.HOME, @@ -338,22 +350,24 @@ class RoomListFragment @Inject constructor( if (createChatFabMenu.onBackPressed()) { return true } - return false } // RoomSummaryController.Callback ************************************************************** override fun onRoomClicked(room: RoomSummary) { - roomListViewModel.handle(RoomListAction.SelectRoom(room)) + roomListViewModel.handle(RoomListAction.SelectRoom(room, enableMultiSelect = false)) } override fun onRoomLongClicked(room: RoomSummary): Boolean { - roomController.onRoomLongClicked() - - RoomListQuickActionsBottomSheet - .newInstance(room.roomId, RoomListActionsArgs.Mode.FULL) - .show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") + if (roomListParams.displayMode == RoomListDisplayMode.SHARE) { + roomListViewModel.handle(RoomListAction.SelectRoom(room, enableMultiSelect = true)) + } else { + roomController.onRoomLongClicked() + RoomListQuickActionsBottomSheet + .newInstance(room.roomId, RoomListActionsArgs.Mode.FULL) + .show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") + } return true } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt index 2e147293ec..1798174ef0 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt @@ -17,6 +17,7 @@ package im.vector.riotx.features.home.room.list +import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.riotx.core.platform.VectorViewEvents /** @@ -26,5 +27,5 @@ sealed class RoomListViewEvents : VectorViewEvents { data class Loading(val message: CharSequence? = null) : RoomListViewEvents() data class Failure(val throwable: Throwable) : RoomListViewEvents() - data class SelectRoom(val roomId: String) : RoomListViewEvents() + data class SelectRoom(val roomSummary: RoomSummary) : RoomListViewEvents() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt index 22c18e9134..7ee641236c 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt @@ -25,9 +25,11 @@ import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.tag.RoomTag +import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.utils.DataSource import im.vector.riotx.features.home.RoomListDisplayMode +import im.vector.riotx.features.share.SharedData import io.reactivex.schedulers.Schedulers import timber.log.Timber import javax.inject.Inject @@ -67,13 +69,38 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, is RoomListAction.MarkAllRoomsRead -> handleMarkAllRoomsRead() is RoomListAction.LeaveRoom -> handleLeaveRoom(action) is RoomListAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action) + is RoomListAction.ShareToSelectedRooms -> handleShareToSelectedRooms(action) + }.exhaustive + } + + private fun handleShareToSelectedRooms(action: RoomListAction.ShareToSelectedRooms) = withState { + val sharedData = action.sharedData + it.selectedRoomIds.forEach { roomId -> + val room = session.getRoom(roomId) + if (sharedData is SharedData.Text) { + room?.sendTextMessage(sharedData.text) + } else if (sharedData is SharedData.Attachments) { + room?.sendMedias(sharedData.attachmentData) + } } } // PRIVATE METHODS ***************************************************************************** - private fun handleSelectRoom(action: RoomListAction.SelectRoom) { - _viewEvents.post(RoomListViewEvents.SelectRoom(action.roomSummary.roomId)) + private fun handleSelectRoom(action: RoomListAction.SelectRoom) = withState { + if (it.multiSelectionEnabled) { + val selectedRooms = it.selectedRoomIds + val newSelectedRooms = if (selectedRooms.contains(action.roomSummary.roomId)) { + selectedRooms.minus(action.roomSummary.roomId) + } else { + selectedRooms.plus(action.roomSummary.roomId) + } + setState { copy(multiSelectionEnabled = newSelectedRooms.isNotEmpty(), selectedRoomIds = newSelectedRooms) } + } else if (action.enableMultiSelect) { + setState { copy(multiSelectionEnabled = true, selectedRoomIds = setOf(action.roomSummary.roomId)) } + } else { + _viewEvents.post(RoomListViewEvents.SelectRoom(action.roomSummary)) + } } private fun handleToggleCategory(action: RoomListAction.ToggleCategory) = setState { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt index c127fa10e2..e660cc47c0 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewState.kt @@ -46,7 +46,9 @@ data class RoomListViewState( val isServerNoticeRoomsExpanded: Boolean = true, // For sharing val isRecentExpanded: Boolean = true, - val isOtherExpanded: Boolean = true + val isOtherExpanded: Boolean = true, + val selectedRoomIds: Set = emptySet(), + val multiSelectionEnabled: Boolean = false ) : MvRxState { constructor(args: RoomListParams) : this(displayMode = args.displayMode) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt index c4afd442ab..eb05802d6e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryController.kt @@ -77,7 +77,8 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri viewState.joiningRoomsIds, viewState.joiningErrorRoomsIds, viewState.rejectingRoomsIds, - viewState.rejectingErrorRoomsIds) + viewState.rejectingErrorRoomsIds, + viewState.selectedRoomIds) addFilterFooter(viewState) } @@ -85,7 +86,6 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri private fun buildShareRooms(viewState: RoomListViewState) { var hasResult = false val roomSummaries = viewState.asyncFilteredRooms() - roomListNameFilter.filter = viewState.roomFilter roomSummaries?.forEach { (category, summaries) -> @@ -105,7 +105,8 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri emptySet(), emptySet(), emptySet(), - emptySet() + emptySet(), + viewState.selectedRoomIds ) } } @@ -131,7 +132,8 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri viewState.joiningRoomsIds, viewState.joiningErrorRoomsIds, viewState.rejectingRoomsIds, - viewState.rejectingErrorRoomsIds) + viewState.rejectingErrorRoomsIds, + viewState.selectedRoomIds) // Never set showHelp to true for invitation if (category != RoomCategory.INVITE) { showHelp = userPreferencesProvider.shouldShowLongClickOnRoomHelp() @@ -196,10 +198,11 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri joiningRoomsIds: Set, joiningErrorRoomsIds: Set, rejectingRoomsIds: Set, - rejectingErrorRoomsIds: Set) { + rejectingErrorRoomsIds: Set, + selectedRoomIds: Set) { summaries.forEach { roomSummary -> roomSummaryItemFactory - .create(roomSummary, joiningRoomsIds, joiningErrorRoomsIds, rejectingRoomsIds, rejectingErrorRoomsIds, listener) + .create(roomSummary, joiningRoomsIds, joiningErrorRoomsIds, rejectingRoomsIds, rejectingErrorRoomsIds,selectedRoomIds, listener) .addTo(this) } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItem.kt index 652740c0b7..3d4ce6b7ff 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItem.kt @@ -16,14 +16,17 @@ package im.vector.riotx.features.home.room.list +import android.view.HapticFeedbackConstants import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.core.view.isInvisible import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass +import com.amulyakhare.textdrawable.TextDrawable import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel import im.vector.matrix.android.api.util.MatrixItem import im.vector.riotx.R @@ -48,11 +51,15 @@ abstract class RoomSummaryItem : VectorEpoxyModel() { @EpoxyAttribute var showHighlighted: Boolean = false @EpoxyAttribute var itemLongClickListener: View.OnLongClickListener? = null @EpoxyAttribute var itemClickListener: View.OnClickListener? = null + @EpoxyAttribute var showSelected: Boolean = false override fun bind(holder: Holder) { super.bind(holder) holder.rootView.setOnClickListener(itemClickListener) - holder.rootView.setOnLongClickListener(itemLongClickListener) + holder.rootView.setOnLongClickListener { + it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + itemLongClickListener?.onLongClick(it) ?: false + } holder.titleView.text = matrixItem.getBestName() holder.lastEventTimeView.text = lastEventTime holder.lastEventView.text = lastFormattedEvent @@ -64,6 +71,19 @@ abstract class RoomSummaryItem : VectorEpoxyModel() { avatarRenderer.render(matrixItem, holder.avatarImageView) holder.roomAvatarDecorationImageView.isVisible = encryptionTrustLevel != null holder.roomAvatarDecorationImageView.setImageResource(encryptionTrustLevel.toImageRes()) + renderSelection(holder, showSelected) + } + + private fun renderSelection(holder: Holder, isSelected: Boolean) { + if (isSelected) { + holder.avatarCheckedImageView.visibility = View.VISIBLE + val backgroundColor = ContextCompat.getColor(holder.view.context, R.color.riotx_accent) + val backgroundDrawable = TextDrawable.builder().buildRound("", backgroundColor) + holder.avatarImageView.setImageDrawable(backgroundDrawable) + } else { + holder.avatarCheckedImageView.visibility = View.GONE + avatarRenderer.render(matrixItem, holder.avatarImageView) + } } class Holder : VectorEpoxyHolder() { @@ -74,6 +94,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel() { val typingView by bind(R.id.roomTypingView) val draftView by bind(R.id.roomDraftBadge) val lastEventTimeView by bind(R.id.roomLastEventTimeView) + val avatarCheckedImageView by bind(R.id.roomAvatarCheckedImageView) val avatarImageView by bind(R.id.roomAvatarImageView) val roomAvatarDecorationImageView by bind(R.id.roomAvatarDecorationImageView) val rootView by bind(R.id.itemRoomLayout) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt index d224ccec47..b61f8fcbe5 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt @@ -36,7 +36,6 @@ import javax.inject.Inject class RoomSummaryItemFactory @Inject constructor(private val displayableEventFormatter: DisplayableEventFormatter, private val dateFormatter: VectorDateFormatter, - private val colorProvider: ColorProvider, private val stringProvider: StringProvider, private val typingHelper: TypingHelper, private val session: Session, @@ -47,10 +46,11 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor joiningErrorRoomsIds: Set, rejectingRoomsIds: Set, rejectingErrorRoomsIds: Set, + selectedRoomIds: Set, listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> { return when (roomSummary.membership) { Membership.INVITE -> createInvitationItem(roomSummary, joiningRoomsIds, joiningErrorRoomsIds, rejectingRoomsIds, rejectingErrorRoomsIds, listener) - else -> createRoomItem(roomSummary, listener) + else -> createRoomItem(roomSummary, selectedRoomIds, listener) } } @@ -82,10 +82,10 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor .listener { listener?.onRoomClicked(roomSummary) } } - private fun createRoomItem(roomSummary: RoomSummary, listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> { + private fun createRoomItem(roomSummary: RoomSummary, selectedRoomIds: Set, listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> { val unreadCount = roomSummary.notificationCount val showHighlighted = roomSummary.highlightCount > 0 - + val showSelected = selectedRoomIds.contains(roomSummary.roomId) var latestFormattedEvent: CharSequence = "" var latestEventTime: CharSequence = "" val latestEvent = roomSummary.latestPreviewableEvent @@ -119,6 +119,7 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor .typingString(typingString) .lastFormattedEvent(latestFormattedEvent) .showHighlighted(showHighlighted) + .showSelected(showSelected) .unreadNotificationCount(unreadCount) .hasUnreadMessage(roomSummary.hasUnreadMessages) .hasDraft(roomSummary.userDrafts.isNotEmpty()) diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt index 3669a51937..b9a9bdc103 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt @@ -19,6 +19,7 @@ package im.vector.riotx.features.share import android.content.ClipDescription import android.content.Intent import android.os.Bundle +import android.view.Window import android.widget.Toast import androidx.appcompat.widget.SearchView import com.airbnb.mvrx.viewModel diff --git a/vector/src/main/res/layout/activity_incoming_share.xml b/vector/src/main/res/layout/activity_incoming_share.xml index 986a852b5a..031e1b958e 100644 --- a/vector/src/main/res/layout/activity_incoming_share.xml +++ b/vector/src/main/res/layout/activity_incoming_share.xml @@ -13,7 +13,6 @@ style="@style/VectorToolbarStyle" android:layout_width="0dp" android:layout_height="?attr/actionBarSize" - android:elevation="4dp" app:contentInsetStart="0dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/vector/src/main/res/layout/fragment_room_list.xml b/vector/src/main/res/layout/fragment_room_list.xml index 2c828c8397..54e6db72e7 100644 --- a/vector/src/main/res/layout/fragment_room_list.xml +++ b/vector/src/main/res/layout/fragment_room_list.xml @@ -55,4 +55,17 @@ tools:layout_marginEnd="144dp" tools:visibility="visible" /> + + diff --git a/vector/src/main/res/layout/item_room.xml b/vector/src/main/res/layout/item_room.xml index 3ae16ddca9..05a2ff4ca9 100644 --- a/vector/src/main/res/layout/item_room.xml +++ b/vector/src/main/res/layout/item_room.xml @@ -21,21 +21,37 @@ app:layout_constraintTop_toTopOf="parent" tools:visibility="visible" /> - + app:layout_constraintTop_toTopOf="parent"> + + + + + + diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 75790348de..8799d56ce6 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -21,6 +21,7 @@ Remove… + Do you want to send this attachment to %1$s? From b7a7aa2f153ed9d48844d560562cd7b76ff9ec9a Mon Sep 17 00:00:00 2001 From: Ganard Date: Thu, 6 Feb 2020 18:42:18 +0100 Subject: [PATCH 004/187] Attachment: continue working on preview screen --- .../session/content/ContentAttachmentData.kt | 1 + vector/src/main/AndroidManifest.xml | 5 +- .../riotx/core/utils/SnapHelperUtils.kt | 76 +++++++++++ .../features/attachments/AttachmentsMapper.kt | 12 +- .../attachments/ContentAttachmentData.kt | 32 +++++ .../preview/AttachmentPreviewControllers.kt | 10 +- .../preview/AttachmentsPreviewAction.kt | 8 +- .../preview/AttachmentsPreviewActivity.kt | 16 ++- .../preview/AttachmentsPreviewFragment.kt | 129 ++++++++++++++++-- .../preview/AttachmentsPreviewViewModel.kt | 35 ++++- .../preview/AttachmentsPreviewViewState.kt | 3 +- .../home/room/detail/RoomDetailFragment.kt | 19 ++- .../features/themes/ActivityOtherThemes.kt | 6 + .../layout/fragment_attachments_preview.xml | 42 ++++-- .../res/menu/vector_attachments_preview.xml | 19 +++ .../src/main/res/values-v21/theme_common.xml | 16 +++ vector/src/main/res/values/theme_common.xml | 2 + 17 files changed, 385 insertions(+), 46 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/utils/SnapHelperUtils.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/ContentAttachmentData.kt create mode 100755 vector/src/main/res/menu/vector_attachments_preview.xml create mode 100644 vector/src/main/res/values-v21/theme_common.xml diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt index 0d8ef2c52b..48dff4e56d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt @@ -29,6 +29,7 @@ data class ContentAttachmentData( val width: Long? = 0, val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED, val name: String? = null, + val queryUri: String, val path: String, val mimeType: String?, val type: Type diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index d1873b650e..9d7495ef23 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -143,7 +143,10 @@ - + + diff --git a/vector/src/main/java/im/vector/riotx/core/utils/SnapHelperUtils.kt b/vector/src/main/java/im/vector/riotx/core/utils/SnapHelperUtils.kt new file mode 100644 index 0000000000..70831730b4 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/utils/SnapHelperUtils.kt @@ -0,0 +1,76 @@ +/* + * 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 im.vector.riotx.core.utils + +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.SnapHelper + +interface OnSnapPositionChangeListener { + + fun onSnapPositionChange(position: Int) +} + +fun RecyclerView.attachSnapHelperWithListener( + snapHelper: SnapHelper, + behavior: SnapOnScrollListener.Behavior = SnapOnScrollListener.Behavior.NOTIFY_ON_SCROLL_STATE_IDLE, + onSnapPositionChangeListener: OnSnapPositionChangeListener) { + snapHelper.attachToRecyclerView(this) + val snapOnScrollListener = SnapOnScrollListener(snapHelper, behavior, onSnapPositionChangeListener) + addOnScrollListener(snapOnScrollListener) +} + +fun SnapHelper.getSnapPosition(recyclerView: RecyclerView): Int { + val layoutManager = recyclerView.layoutManager ?: return RecyclerView.NO_POSITION + val snapView = findSnapView(layoutManager) ?: return RecyclerView.NO_POSITION + return layoutManager.getPosition(snapView) +} + +class SnapOnScrollListener( + private val snapHelper: SnapHelper, + var behavior: Behavior = Behavior.NOTIFY_ON_SCROLL, + var onSnapPositionChangeListener: OnSnapPositionChangeListener? = null +) : RecyclerView.OnScrollListener() { + + enum class Behavior { + NOTIFY_ON_SCROLL, + NOTIFY_ON_SCROLL_STATE_IDLE + } + + private var snapPosition = RecyclerView.NO_POSITION + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + if (behavior == Behavior.NOTIFY_ON_SCROLL) { + maybeNotifySnapPositionChange(recyclerView) + } + } + + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + if (behavior == Behavior.NOTIFY_ON_SCROLL_STATE_IDLE + && newState == RecyclerView.SCROLL_STATE_IDLE) { + maybeNotifySnapPositionChange(recyclerView) + } + } + + private fun maybeNotifySnapPositionChange(recyclerView: RecyclerView) { + val snapPosition = snapHelper.getSnapPosition(recyclerView) + val snapPositionChanged = this.snapPosition != snapPosition + if (snapPositionChanged) { + onSnapPositionChangeListener?.onSnapPositionChange(snapPosition) + this.snapPosition = snapPosition + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt index 4b51c548a7..c0009a7e49 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt @@ -37,7 +37,8 @@ fun ChosenFile.toContentAttachmentData(): ContentAttachmentData { type = mapType(), size = size, date = createdAt?.time ?: System.currentTimeMillis(), - name = displayName + name = displayName, + queryUri = queryUri ) } @@ -50,7 +51,8 @@ fun ChosenAudio.toContentAttachmentData(): ContentAttachmentData { size = size, date = createdAt?.time ?: System.currentTimeMillis(), name = displayName, - duration = duration + duration = duration, + queryUri = queryUri ) } @@ -74,7 +76,8 @@ fun ChosenImage.toContentAttachmentData(): ContentAttachmentData { height = height.toLong(), width = width.toLong(), exifOrientation = orientation, - date = createdAt?.time ?: System.currentTimeMillis() + date = createdAt?.time ?: System.currentTimeMillis(), + queryUri = queryUri ) } @@ -89,6 +92,7 @@ fun ChosenVideo.toContentAttachmentData(): ContentAttachmentData { height = height.toLong(), width = width.toLong(), duration = duration, - name = displayName + name = displayName, + queryUri = queryUri ) } diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/ContentAttachmentData.kt b/vector/src/main/java/im/vector/riotx/features/attachments/ContentAttachmentData.kt new file mode 100644 index 0000000000..ecd979f900 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/attachments/ContentAttachmentData.kt @@ -0,0 +1,32 @@ +/* + * 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 im.vector.riotx.features.attachments + +import im.vector.matrix.android.api.session.content.ContentAttachmentData + +fun ContentAttachmentData.isPreviewable(): Boolean { + return type == ContentAttachmentData.Type.IMAGE || type == ContentAttachmentData.Type.VIDEO +} + +fun List.filterPreviewables(): List { + return filter { it.isPreviewable() } +} + +fun List.filterNonPreviewables(): List { + return filter { it.isPreviewable().not() } +} + diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewControllers.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewControllers.kt index 9cef33d402..966bfe26cd 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewControllers.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentPreviewControllers.kt @@ -35,18 +35,18 @@ class AttachmentBigPreviewController @Inject constructor() : TypedEpoxyControlle class AttachmentMiniaturePreviewController @Inject constructor() : TypedEpoxyController() { interface Callback { - fun onAttachmentClicked(contentAttachmentData: ContentAttachmentData) + fun onAttachmentClicked(position: Int, contentAttachmentData: ContentAttachmentData) } var callback: Callback? = null override fun buildModels(data: AttachmentsPreviewViewState) { - data.attachments.forEach { + data.attachments.forEachIndexed { index, contentAttachmentData -> attachmentMiniaturePreviewItem { - id(it.path) - attachment(it) + id(contentAttachmentData.path) + attachment(contentAttachmentData) clickListener { _ -> - callback?.onAttachmentClicked(it) + callback?.onAttachmentClicked(index, contentAttachmentData) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewAction.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewAction.kt index 61fb41a774..a016b71fab 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewAction.kt @@ -19,4 +19,10 @@ package im.vector.riotx.features.attachments.preview import im.vector.riotx.core.platform.VectorViewModelAction -sealed class AttachmentsPreviewAction : VectorViewModelAction +sealed class AttachmentsPreviewAction : VectorViewModelAction { + object RemoveCurrentAttachment : AttachmentsPreviewAction() + data class SetCurrentAttachment(val index: Int): AttachmentsPreviewAction() + data class UpdatePathOfCurrentAttachment(val newPath: String): AttachmentsPreviewAction() + + +} diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt index d8d19cd550..70dc0734ed 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt @@ -19,19 +19,20 @@ package im.vector.riotx.features.attachments.preview import android.content.Context import android.content.Intent -import android.os.Bundle -import android.view.WindowManager import androidx.appcompat.widget.Toolbar +import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.riotx.R import im.vector.riotx.core.extensions.addFragment import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.VectorBaseActivity +import im.vector.riotx.features.themes.ActivityOtherThemes class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS" + const val RESULT_NAME = "ATTACHMENTS_PREVIEW_RESULT" const val REQUEST_CODE = 55 fun newIntent(context: Context, args: AttachmentsPreviewArgs): Intent { @@ -39,15 +40,16 @@ class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { putExtra(EXTRA_FRAGMENT_ARGS, args) } } + + fun getOutput(intent: Intent): List { + return intent.getParcelableArrayListExtra(RESULT_NAME) + } } + override fun getOtherThemes() = ActivityOtherThemes.AttachmentsPreview + override fun getLayoutRes() = R.layout.activity_simple - override fun onCreate(savedInstanceState: Bundle?) { - window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) - super.onCreate(savedInstanceState) - } - override fun initUiAndData() { if (isFirstCreation()) { val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS) diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt index 99280c3830..e13e7c6b60 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -17,24 +17,41 @@ package im.vector.riotx.features.attachments.preview +import android.app.Activity.RESULT_CANCELED +import android.app.Activity.RESULT_OK +import android.content.Intent import android.os.Bundle import android.os.Parcelable +import android.view.MenuItem import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.net.toUri +import androidx.core.view.ViewCompat +import androidx.core.view.updateLayoutParams +import androidx.core.view.updatePadding import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.PagerSnapHelper import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import com.yalantis.ucrop.UCrop import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseFragment +import im.vector.riotx.core.utils.OnSnapPositionChangeListener +import im.vector.riotx.core.utils.SnapOnScrollListener +import im.vector.riotx.core.utils.attachSnapHelperWithListener import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_attachments_preview.* +import timber.log.Timber +import java.io.File import javax.inject.Inject @Parcelize data class AttachmentsPreviewArgs( + val roomId: String, val attachments: List ) : Parcelable @@ -51,13 +68,107 @@ class AttachmentsPreviewFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + applyInsets() setupRecyclerViews() setupToolbar(attachmentPreviewerToolbar) + attachmentPreviewerSendButton.setOnClickListener { + setResultAndFinish() + } } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (resultCode == RESULT_OK) { + if (requestCode == UCrop.REQUEST_CROP && data != null) { + Timber.v("Crop success") + handleCropResult(data) + } + } + if (resultCode == UCrop.RESULT_ERROR) { + Timber.v("Crop error") + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.attachmentsPreviewRemoveAction -> { + handleRemoveAction() + true + } + R.id.attachmentsPreviewEditAction -> { + handleEditAction() + true + } + else -> { + super.onOptionsItemSelected(item) + } + } + } + + override fun getMenuRes() = R.menu.vector_attachments_preview + override fun onDestroyView() { super.onDestroyView() attachmentPreviewerMiniatureList.cleanup() + attachmentPreviewerBigList.cleanup() + } + + override fun invalidate() = withState(viewModel) { state -> + if (state.attachments.isEmpty()) { + requireActivity().setResult(RESULT_CANCELED) + requireActivity().finish() + } else { + attachmentMiniaturePreviewController.setData(state) + attachmentBigPreviewController.setData(state) + attachmentPreviewerBigList.scrollToPosition(state.currentAttachmentIndex) + attachmentPreviewerMiniatureList.scrollToPosition(state.currentAttachmentIndex) + } + } + + override fun onAttachmentClicked(position: Int, contentAttachmentData: ContentAttachmentData) { + viewModel.handle(AttachmentsPreviewAction.SetCurrentAttachment(position)) + } + + private fun setResultAndFinish() = withState(viewModel) { + val resultIntent = Intent().apply { + putParcelableArrayListExtra(AttachmentsPreviewActivity.RESULT_NAME, ArrayList(it.attachments)) + } + requireActivity().setResult(RESULT_OK, resultIntent) + requireActivity().finish() + } + + private fun applyInsets() { + view?.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + ViewCompat.setOnApplyWindowInsetsListener(attachmentPreviewerBottomContainer) { v, insets -> + v.updatePadding(bottom = insets.systemWindowInsetBottom) + insets + } + ViewCompat.setOnApplyWindowInsetsListener(attachmentPreviewerToolbar) { v, insets -> + v.updateLayoutParams { + topMargin = insets.systemWindowInsetTop + } + insets + } + } + + private fun handleCropResult(result: Intent) { + val resultPath = UCrop.getOutput(result)?.path + if (resultPath != null) { + viewModel.handle(AttachmentsPreviewAction.UpdatePathOfCurrentAttachment(resultPath)) + } else { + Toast.makeText(requireContext(), "Cannot retrieve cropped value", Toast.LENGTH_SHORT).show() + } + } + + private fun handleRemoveAction() { + viewModel.handle(AttachmentsPreviewAction.RemoveCurrentAttachment) + } + + private fun handleEditAction() = withState(viewModel) { + val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState + val destinationFile = File(requireContext().cacheDir, "${currentAttachment.name}_edited_image_${System.currentTimeMillis()}") + UCrop.of(currentAttachment.queryUri.toUri(), destinationFile.toUri()) + .withOptions(UCrop.Options()) + .start(requireContext(), this) } private fun setupRecyclerViews() { @@ -67,20 +178,12 @@ class AttachmentsPreviewFragment @Inject constructor( attachmentMiniaturePreviewController.callback = this attachmentPreviewerBigList.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - val snapHelper = PagerSnapHelper() - snapHelper.attachToRecyclerView(attachmentPreviewerBigList) + attachmentPreviewerBigList.attachSnapHelperWithListener(PagerSnapHelper(), SnapOnScrollListener.Behavior.NOTIFY_ON_SCROLL_STATE_IDLE, object : OnSnapPositionChangeListener { + override fun onSnapPositionChange(position: Int) { + viewModel.handle(AttachmentsPreviewAction.SetCurrentAttachment(position)) + } + }) attachmentPreviewerBigList.setHasFixedSize(true) attachmentPreviewerBigList.adapter = attachmentBigPreviewController.adapter - } - - override fun invalidate() = withState(viewModel) { state -> - attachmentMiniaturePreviewController.setData(state) - attachmentBigPreviewController.setData(state) - } - - override fun onAttachmentClicked(contentAttachmentData: ContentAttachmentData) { - - } - } diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt index 28a0aaebd6..b3be853eea 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt @@ -17,12 +17,15 @@ package im.vector.riotx.features.attachments.preview +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.platform.VectorViewModel +import kotlinx.coroutines.launch class AttachmentsPreviewViewModel @AssistedInject constructor(@Assisted initialState: AttachmentsPreviewViewState) : VectorViewModel(initialState) { @@ -42,6 +45,36 @@ class AttachmentsPreviewViewModel @AssistedInject constructor(@Assisted initialS } override fun handle(action: AttachmentsPreviewAction) { - //TODO + when (action) { + is AttachmentsPreviewAction.SetCurrentAttachment -> handleSetCurrentAttachment(action) + is AttachmentsPreviewAction.UpdatePathOfCurrentAttachment -> handleUpdatePathOfCurrentAttachment(action) + AttachmentsPreviewAction.RemoveCurrentAttachment -> handleRemoveCurrentAttachment() + }.exhaustive + } + + private fun handleRemoveCurrentAttachment() = withState { + val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState + val attachments = it.attachments.minusElement(currentAttachment) + val newAttachmentIndex = it.currentAttachmentIndex.coerceAtMost(attachments.size - 1) + setState { + copy(attachments = attachments, currentAttachmentIndex = newAttachmentIndex) + } + } + + private fun handleUpdatePathOfCurrentAttachment(action: AttachmentsPreviewAction.UpdatePathOfCurrentAttachment) = withState { + val attachments = it.attachments.mapIndexed { index, contentAttachmentData -> + if (index == it.currentAttachmentIndex) { + contentAttachmentData.copy(path = action.newPath) + } else { + contentAttachmentData + } + } + setState { + copy(attachments = attachments) + } + } + + private fun handleSetCurrentAttachment(action: AttachmentsPreviewAction.SetCurrentAttachment) = setState { + copy(currentAttachmentIndex = action.index) } } diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt index f92abb97ea..feb1a32210 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt @@ -21,7 +21,8 @@ import com.airbnb.mvrx.MvRxState import im.vector.matrix.android.api.session.content.ContentAttachmentData data class AttachmentsPreviewViewState( - val attachments: List + val attachments: List, + val currentAttachmentIndex: Int = 0 ) : MvRxState { constructor(args: AttachmentsPreviewArgs) : this(attachments = args.attachments) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 1caada8ce6..f63f82ca26 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -116,6 +116,8 @@ import im.vector.riotx.core.utils.toast import im.vector.riotx.features.attachments.AttachmentTypeSelectorView import im.vector.riotx.features.attachments.AttachmentsHelper import im.vector.riotx.features.attachments.ContactAttachment +import im.vector.riotx.features.attachments.filterNonPreviewables +import im.vector.riotx.features.attachments.filterPreviewables import im.vector.riotx.features.attachments.preview.AttachmentsPreviewActivity import im.vector.riotx.features.attachments.preview.AttachmentsPreviewArgs import im.vector.riotx.features.command.Command @@ -502,7 +504,11 @@ class RoomDetailFragment @Inject constructor( val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data) if (!hasBeenHandled && resultCode == RESULT_OK && data != null) { when (requestCode) { - REACTION_SELECT_REQUEST_CODE -> { + AttachmentsPreviewActivity.REQUEST_CODE -> { + val sendData = AttachmentsPreviewActivity.getOutput(data) + roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData)) + } + REACTION_SELECT_REQUEST_CODE -> { val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction)) } @@ -1342,8 +1348,15 @@ class RoomDetailFragment @Inject constructor( // AttachmentsHelper.Callback override fun onContentAttachmentsReady(attachments: List) { - val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(attachments)) - startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE) + val previewable = attachments.filterPreviewables() + val nonPreviewable = attachments.filterNonPreviewables() + if (nonPreviewable.isNotEmpty()) { + roomDetailViewModel.handle(RoomDetailAction.SendMedia(nonPreviewable)) + } + if (previewable.isNotEmpty()) { + val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(roomDetailArgs.roomId, previewable)) + startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE) + } } override fun onAttachmentsProcessFailed() { diff --git a/vector/src/main/java/im/vector/riotx/features/themes/ActivityOtherThemes.kt b/vector/src/main/java/im/vector/riotx/features/themes/ActivityOtherThemes.kt index 411069e9ca..b37c1a4818 100644 --- a/vector/src/main/java/im/vector/riotx/features/themes/ActivityOtherThemes.kt +++ b/vector/src/main/java/im/vector/riotx/features/themes/ActivityOtherThemes.kt @@ -32,4 +32,10 @@ sealed class ActivityOtherThemes(@StyleRes val dark: Int, R.style.AppTheme_Black, R.style.AppTheme_Status ) + + object AttachmentsPreview : ActivityOtherThemes( + R.style.AppTheme_AttachmentsPreview, + R.style.AppTheme_AttachmentsPreview, + R.style.AppTheme_AttachmentsPreview + ) } diff --git a/vector/src/main/res/layout/fragment_attachments_preview.xml b/vector/src/main/res/layout/fragment_attachments_preview.xml index 5acc8835b0..bcb5d682c4 100644 --- a/vector/src/main/res/layout/fragment_attachments_preview.xml +++ b/vector/src/main/res/layout/fragment_attachments_preview.xml @@ -1,37 +1,59 @@ + android:layout_height="match_parent"> + app:layout_constraintTop_toTopOf="parent" + tools:listitem="@layout/item_attachment_big_preview" /> - + app:layout_constraintStart_toStartOf="parent"> + + + + + + diff --git a/vector/src/main/res/menu/vector_attachments_preview.xml b/vector/src/main/res/menu/vector_attachments_preview.xml new file mode 100755 index 0000000000..4adf9dcff9 --- /dev/null +++ b/vector/src/main/res/menu/vector_attachments_preview.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/vector/src/main/res/values-v21/theme_common.xml b/vector/src/main/res/values-v21/theme_common.xml new file mode 100644 index 0000000000..155c73acdc --- /dev/null +++ b/vector/src/main/res/values-v21/theme_common.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/theme_common.xml b/vector/src/main/res/values/theme_common.xml index 159b2ecb55..151d97c097 100644 --- a/vector/src/main/res/values/theme_common.xml +++ b/vector/src/main/res/values/theme_common.xml @@ -8,4 +8,6 @@ @color/primary_color_dark + - @@ -375,9 +376,7 @@ SOFTWARE. Copyright (c) 2014 Dushyanth Maguluru
  • - JsonViewer -
    - Copyright 2017 smuyyh, All right reserved. + BillCarsonFr/JsonViewer
  • diff --git a/vector/src/main/java/im/vector/riotx/core/utils/UserColor.kt b/vector/src/main/java/im/vector/riotx/core/utils/UserColor.kt
    index 1f8308cd5c..15c4ce8a15 100644
    --- a/vector/src/main/java/im/vector/riotx/core/utils/UserColor.kt
    +++ b/vector/src/main/java/im/vector/riotx/core/utils/UserColor.kt
    @@ -18,6 +18,8 @@ package im.vector.riotx.core.utils
     
     import androidx.annotation.ColorRes
     import im.vector.riotx.R
    +import im.vector.riotx.core.resources.ColorProvider
    +import org.billcarsonfr.jsonviewer.JSonViewerStyleProvider
     import kotlin.math.abs
     
     @ColorRes
    @@ -37,3 +39,14 @@ fun getColorFromUserId(userId: String?): Int {
             else -> R.color.riotx_username_1
         }
     }
    +
    +fun jsonViewerStyler(colorProvider: ColorProvider): JSonViewerStyleProvider {
    +    return JSonViewerStyleProvider(
    +            keyColor = colorProvider.getColor(R.color.riotx_accent),
    +            secondaryColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary),
    +            stringColor = colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color),
    +            baseColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_primary),
    +            booleanColor = colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color),
    +            numberColor = colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color)
    +    )
    +}
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    index 5cb3024712..79eb63bfb7 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    @@ -27,12 +27,10 @@ import android.os.Bundle
     import android.os.Parcelable
     import android.text.Spannable
     import android.view.HapticFeedbackConstants
    -import android.view.LayoutInflater
     import android.view.Menu
     import android.view.MenuItem
     import android.view.View
     import android.view.Window
    -import android.widget.TextView
     import android.widget.Toast
     import androidx.annotation.DrawableRes
     import androidx.annotation.StringRes
    @@ -96,6 +94,7 @@ import im.vector.riotx.core.extensions.showKeyboard
     import im.vector.riotx.core.files.addEntryToDownloadManager
     import im.vector.riotx.core.glide.GlideApp
     import im.vector.riotx.core.platform.VectorBaseFragment
    +import im.vector.riotx.core.resources.ColorProvider
     import im.vector.riotx.core.ui.views.JumpToReadMarkerView
     import im.vector.riotx.core.ui.views.NotificationAreaView
     import im.vector.riotx.core.utils.Debouncer
    @@ -110,6 +109,7 @@ import im.vector.riotx.core.utils.checkPermissions
     import im.vector.riotx.core.utils.copyToClipboard
     import im.vector.riotx.core.utils.createUIHandler
     import im.vector.riotx.core.utils.getColorFromUserId
    +import im.vector.riotx.core.utils.jsonViewerStyler
     import im.vector.riotx.core.utils.openUrlInExternalBrowser
     import im.vector.riotx.core.utils.shareMedia
     import im.vector.riotx.core.utils.toast
    @@ -154,6 +154,7 @@ import kotlinx.android.parcel.Parcelize
     import kotlinx.android.synthetic.main.fragment_room_detail.*
     import kotlinx.android.synthetic.main.merge_composer_layout.view.*
     import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
    +import org.billcarsonfr.jsonviewer.JSonViewerDialog
     import org.commonmark.parser.Parser
     import timber.log.Timber
     import java.io.File
    @@ -178,8 +179,8 @@ class RoomDetailFragment @Inject constructor(
             private val notificationDrawerManager: NotificationDrawerManager,
             val roomDetailViewModelFactory: RoomDetailViewModel.Factory,
             private val eventHtmlRenderer: EventHtmlRenderer,
    -        private val vectorPreferences: VectorPreferences
    -) :
    +        private val vectorPreferences: VectorPreferences,
    +        private val colorProvider: ColorProvider) :
             VectorBaseFragment(),
             TimelineEventController.Callback,
             VectorInviteView.Callback,
    @@ -1155,26 +1156,18 @@ class RoomDetailFragment @Inject constructor(
                     onEditedDecorationClicked(action.messageInformationData)
                 }
                 is EventSharedAction.ViewSource                 -> {
    -                val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null)
    -                view.findViewById(R.id.event_content_text_view)?.let {
    -                    it.text = action.content
    -                }
    -
    -                AlertDialog.Builder(requireActivity())
    -                        .setView(view)
    -                        .setPositiveButton(R.string.ok, null)
    -                        .show()
    +                JSonViewerDialog.newInstance(
    +                        action.content,
    +                        -1,
    +                        jsonViewerStyler(colorProvider)
    +                ).show(childFragmentManager, "JSON_VIEWER")
                 }
                 is EventSharedAction.ViewDecryptedSource        -> {
    -                val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null)
    -                view.findViewById(R.id.event_content_text_view)?.let {
    -                    it.text = action.content
    -                }
    -
    -                AlertDialog.Builder(requireActivity())
    -                        .setView(view)
    -                        .setPositiveButton(R.string.ok, null)
    -                        .show()
    +                JSonViewerDialog.newInstance(
    +                        action.content,
    +                        -1,
    +                        jsonViewerStyler(colorProvider)
    +                ).show(childFragmentManager, "JSON_VIEWER")
                 }
                 is EventSharedAction.QuickReact                 -> {
                     // eventId,ClickedOn,Add
    diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt
    index a7d5d82fb1..0799ae270b 100644
    --- a/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt
    @@ -27,12 +27,16 @@ import im.vector.riotx.R
     import im.vector.riotx.core.extensions.configureWith
     import im.vector.riotx.core.platform.VectorBaseActivity
     import im.vector.riotx.core.platform.VectorBaseFragment
    +import im.vector.riotx.core.resources.ColorProvider
    +import im.vector.riotx.core.utils.jsonViewerStyler
     import kotlinx.android.synthetic.main.fragment_generic_recycler.*
    +import org.billcarsonfr.jsonviewer.JSonViewerDialog
     import javax.inject.Inject
     
     class AccountDataFragment @Inject constructor(
             val viewModelFactory: AccountDataViewModel.Factory,
    -        private val epoxyController: AccountDataEpoxyController
    +        private val epoxyController: AccountDataEpoxyController,
    +        private val colorProvider: ColorProvider
     ) : VectorBaseFragment(), AccountDataEpoxyController.InteractionListener {
     
         override fun getLayoutResId() = R.layout.fragment_generic_recycler
    @@ -59,7 +63,10 @@ class AccountDataFragment @Inject constructor(
             val jsonString = MoshiProvider.providesMoshi()
                     .adapter(UserAccountDataEvent::class.java)
                     .toJson(fb)
    -        JsonViewerBottomSheetDialog.newInstance(jsonString)
    -                .show(childFragmentManager, "JSON_VIEWER")
    +        JSonViewerDialog.newInstance(
    +                jsonString,
    +                -1, // open All
    +                jsonViewerStyler(colorProvider)
    +        ).show(childFragmentManager, "JSON_VIEWER")
         }
     }
    diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/JsonViewerBottomSheetDialog.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/JsonViewerBottomSheetDialog.kt
    deleted file mode 100644
    index c638846f51..0000000000
    --- a/vector/src/main/java/im/vector/riotx/features/settings/devtools/JsonViewerBottomSheetDialog.kt
    +++ /dev/null
    @@ -1,57 +0,0 @@
    -/*
    - * 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 im.vector.riotx.features.settings.devtools
    -
    -import android.os.Bundle
    -import android.view.View
    -import androidx.core.content.ContextCompat
    -import butterknife.BindView
    -import com.airbnb.mvrx.MvRx
    -import com.yuyh.jsonviewer.library.JsonRecyclerView
    -import im.vector.riotx.R
    -import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
    -import im.vector.riotx.features.themes.ThemeUtils
    -
    -class JsonViewerBottomSheetDialog : VectorBaseBottomSheetDialogFragment() {
    -
    -    override fun getLayoutResId() = R.layout.fragment_jsonviewer
    -
    -    @BindView(R.id.rv_json)
    -    lateinit var jsonRecyclerView: JsonRecyclerView
    -
    -    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    -        super.onViewCreated(view, savedInstanceState)
    -
    -        jsonRecyclerView.setKeyColor(ThemeUtils.getColor(requireContext(), R.attr.colorAccent))
    -        jsonRecyclerView.setValueTextColor(ContextCompat.getColor(requireContext(), R.color.riotx_notice_secondary))
    -        jsonRecyclerView.setValueNumberColor(ContextCompat.getColor(requireContext(), R.color.riotx_notice_secondary))
    -        jsonRecyclerView.setValueUrlColor(ThemeUtils.getColor(requireContext(), android.R.attr.textColorLink))
    -        jsonRecyclerView.setValueNullColor(ContextCompat.getColor(requireContext(), R.color.riotx_notice_secondary))
    -        jsonRecyclerView.setBracesColor(ThemeUtils.getColor(requireContext(), R.attr.riotx_text_primary))
    -
    -        val jsonString = arguments?.getString(MvRx.KEY_ARG)
    -        jsonRecyclerView.bindJson(jsonString)
    -    }
    -
    -    companion object {
    -        fun newInstance(jsonString: String): JsonViewerBottomSheetDialog {
    -            return JsonViewerBottomSheetDialog().apply {
    -                setArguments(Bundle().apply { putString(MvRx.KEY_ARG, jsonString) })
    -            }
    -        }
    -    }
    -}
    diff --git a/vector/src/main/res/layout/fragment_jsonviewer.xml b/vector/src/main/res/layout/fragment_jsonviewer.xml
    deleted file mode 100644
    index 5a4aecc56c..0000000000
    --- a/vector/src/main/res/layout/fragment_jsonviewer.xml
    +++ /dev/null
    @@ -1,16 +0,0 @@
    -
    -
    -
    -    
    -
    \ No newline at end of file
    
    From def01cca8fcedd4a480a55c0495ebea0b1db1a0d Mon Sep 17 00:00:00 2001
    From: Valere 
    Date: Wed, 12 Feb 2020 17:20:01 +0100
    Subject: [PATCH 024/187] Fix test + changes
    
    ---
     CHANGES.md                                                       | 1 +
     .../im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt  | 1 -
     2 files changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/CHANGES.md b/CHANGES.md
    index e09aa3fed3..69efff64b9 100644
    --- a/CHANGES.md
    +++ b/CHANGES.md
    @@ -2,6 +2,7 @@ Changes in RiotX 0.16.0 (2020-XX-XX)
     ===================================================
     
     Features ✨:
    + - Secured Shared Storage Support (#984, #936)
      - Polls and Bot Buttons (MSC 2192 matrix-org/matrix-doc#2192)
     
     Improvements 🙌:
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    index 4ea611b875..c84716c4d9 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    @@ -185,7 +185,6 @@ class QuadSTests : InstrumentedTest {
             aliceSession.sharedSecretStorageService.getSecret("secret.of.life" ,
                      null, //default key
                     keySpec!!,
    -                null,
                     object : MatrixCallback {
                         override fun onFailure(failure: Throwable) {
                             fail("Fail to decrypt -> " +failure.localizedMessage)
    
    From 64647cb465c83d78f42ec73841a2541c9fd3b463 Mon Sep 17 00:00:00 2001
    From: Valere 
    Date: Wed, 12 Feb 2020 18:59:00 +0100
    Subject: [PATCH 025/187] Fix / Save account data after update (local echo)
    
    ---
     .../internal/crypto/ssss/QuadSTests.kt        | 186 +++++++++++++++++-
     .../session/accountdata/AccountDataService.kt |   3 +-
     .../secrets/DefaultSharedSecretStorage.kt     |   7 +-
     .../sync/UserAccountDataSyncHandler.kt        |   2 +-
     .../accountdata/DefaultAccountDataService.kt  |  20 +-
     .../accountdata/UpdateUserAccountDataTask.kt  |   1 +
     6 files changed, 206 insertions(+), 13 deletions(-)
    
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    index c84716c4d9..af15738cc8 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    @@ -33,6 +33,7 @@ import im.vector.matrix.android.common.CryptoTestHelper
     import im.vector.matrix.android.common.SessionTestParams
     import im.vector.matrix.android.common.TestConstants
     import im.vector.matrix.android.common.TestMatrixCallback
    +import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
     import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecureStorage
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
     import kotlinx.coroutines.Dispatchers
    @@ -164,9 +165,9 @@ class QuadSTests : InstrumentedTest {
                     TestMatrixCallback(storeCountDownLatch)
             )
     
    -        val secretAccountData = assertAccountData(aliceSession,"secret.of.life" )
    +        val secretAccountData = assertAccountData(aliceSession, "secret.of.life")
     
    -        val encryptedContent = secretAccountData.content.get("encrypted") as? Map<*,*>
    +        val encryptedContent = secretAccountData.content.get("encrypted") as? Map<*, *>
             Assert.assertNotNull("Element should be encrypted", encryptedContent)
             Assert.assertNotNull("Secret should be encrypted with default key", encryptedContent?.get(keyId))
     
    @@ -182,12 +183,12 @@ class QuadSTests : InstrumentedTest {
             var decryptedSecret: String? = null
     
             val decryptCountDownLatch = CountDownLatch(1)
    -        aliceSession.sharedSecretStorageService.getSecret("secret.of.life" ,
    -                 null, //default key
    +        aliceSession.sharedSecretStorageService.getSecret("secret.of.life",
    +                null, //default key
                     keySpec!!,
                     object : MatrixCallback {
                         override fun onFailure(failure: Throwable) {
    -                        fail("Fail to decrypt -> " +failure.localizedMessage)
    +                        fail("Fail to decrypt -> " + failure.localizedMessage)
                             decryptCountDownLatch.countDown()
                         }
     
    @@ -202,7 +203,136 @@ class QuadSTests : InstrumentedTest {
     
             Assert.assertEquals("Secret mismatch", clearSecret, decryptedSecret)
             mTestHelper.signout(aliceSession)
    +    }
     
    +    @Test
    +    fun test_SetDefaultLocalEcho() {
    +        val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
    +
    +        val quadS = aliceSession.sharedSecretStorageService
    +
    +        val emptyKeySigner = object : KeySigner {
    +            override fun sign(canonicalJson: String): Map>? {
    +                return null
    +            }
    +        }
    +
    +        val TEST_KEY_ID = "my.test.Key"
    +
    +        val countDownLatch = CountDownLatch(1)
    +        quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner,
    +                TestMatrixCallback(countDownLatch))
    +
    +        mTestHelper.await(countDownLatch)
    +
    +        //Test that we don't need to wait for an account data sync to access directly the keyid from DB
    +        val defaultLatch = CountDownLatch(1)
    +        quadS.setDefaultKey(TEST_KEY_ID, TestMatrixCallback(defaultLatch))
    +        mTestHelper.await(defaultLatch)
    +
    +
    +        mTestHelper.signout(aliceSession)
    +    }
    +
    +    @Test
    +    fun test_StoreSecretWithMultipleKey() {
    +        val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
    +        val keyId1 = "Key.1"
    +        val key1Info = generatedSecret(aliceSession, keyId1, true)
    +        val keyId2 = "Key2"
    +        val key2Info = generatedSecret(aliceSession, keyId2, true)
    +
    +        val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
    +
    +        val storeLatch = CountDownLatch(1)
    +        aliceSession.sharedSecretStorageService.storeSecret(
    +                "my.secret",
    +                mySecretText.toByteArray().toBase64NoPadding(),
    +                listOf(keyId1, keyId2),
    +                TestMatrixCallback(storeLatch)
    +        )
    +        mTestHelper.await(storeLatch)
    +
    +        val accountDataEvent = aliceSession.getAccountData("my.secret")
    +        val encryptedContent = accountDataEvent?.content?.get("encrypted") as? Map<*, *>
    +
    +        Assert.assertEquals("Content should contains two encryptions", 2, encryptedContent?.keys?.size ?: 0)
    +
    +        Assert.assertNotNull(encryptedContent?.get(keyId1))
    +        Assert.assertNotNull(encryptedContent?.get(keyId2))
    +
    +        // Assert that can decrypt with both keys
    +        val decryptCountDownLatch = CountDownLatch(2)
    +        aliceSession.sharedSecretStorageService.getSecret("my.secret",
    +                keyId1,
    +                Curve25519AesSha2KeySpec.fromRecoveryKey(key1Info.recoveryKey)!!,
    +                TestMatrixCallback(decryptCountDownLatch)
    +        )
    +
    +        aliceSession.sharedSecretStorageService.getSecret("my.secret",
    +                keyId2,
    +                Curve25519AesSha2KeySpec.fromRecoveryKey(key2Info.recoveryKey)!!,
    +                TestMatrixCallback(decryptCountDownLatch)
    +        )
    +
    +        mTestHelper.await(decryptCountDownLatch)
    +
    +        mTestHelper.signout(aliceSession)
    +    }
    +
    +    @Test
    +    fun test_GetSecretWithBadPassphrase() {
    +        val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
    +        val keyId1 = "Key.1"
    +        val passphrase = "The good pass phrase"
    +        val key1Info = generatedSecretFromPassphrase(aliceSession, passphrase, keyId1, true)
    +
    +        val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
    +
    +        val storeLatch = CountDownLatch(1)
    +        aliceSession.sharedSecretStorageService.storeSecret(
    +                "my.secret",
    +                mySecretText.toByteArray().toBase64NoPadding(),
    +                listOf(keyId1),
    +                TestMatrixCallback(storeLatch)
    +        )
    +        mTestHelper.await(storeLatch)
    +
    +        val decryptCountDownLatch = CountDownLatch(2)
    +        aliceSession.sharedSecretStorageService.getSecret("my.secret",
    +                keyId1,
    +                Curve25519AesSha2KeySpec.fromPassphrase(
    +                        "A bad passphrase",
    +                        key1Info.content?.passphrase?.salt ?: "",
    +                        key1Info.content?.passphrase?.iterations ?: 0,
    +                        null),
    +                object : MatrixCallback {
    +                    override fun onSuccess(data: String) {
    +                        decryptCountDownLatch.countDown()
    +                        fail("Should not be able to decrypt")
    +                    }
    +
    +                    override fun onFailure(failure: Throwable) {
    +                        Assert.assertTrue(true)
    +                        decryptCountDownLatch.countDown()
    +                    }
    +                }
    +        )
    +
    +        // Now try with correct key
    +        aliceSession.sharedSecretStorageService.getSecret("my.secret",
    +                keyId1,
    +                Curve25519AesSha2KeySpec.fromPassphrase(
    +                        passphrase,
    +                        key1Info.content?.passphrase?.salt ?: "",
    +                        key1Info.content?.passphrase?.iterations ?: 0,
    +                        null),
    +                TestMatrixCallback(decryptCountDownLatch)
    +        )
    +
    +        mTestHelper.await(decryptCountDownLatch)
    +
    +        mTestHelper.signout(aliceSession)
         }
     
         private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
    @@ -268,4 +398,50 @@ class QuadSTests : InstrumentedTest {
     
             return creationInfo!!
         }
    +
    +    private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SSSSKeyCreationInfo {
    +
    +        val quadS = session.sharedSecretStorageService
    +
    +        val emptyKeySigner = object : KeySigner {
    +            override fun sign(canonicalJson: String): Map>? {
    +                return null
    +            }
    +        }
    +
    +        var creationInfo: SSSSKeyCreationInfo? = null
    +
    +        val generateLatch = CountDownLatch(1)
    +
    +        quadS.generateKeyWithPassphrase(keyId, keyId,
    +                passphrase,
    +                emptyKeySigner,
    +                null,
    +                object : MatrixCallback {
    +
    +                    override fun onSuccess(data: SSSSKeyCreationInfo) {
    +                        creationInfo = data
    +                        generateLatch.countDown()
    +                    }
    +
    +                    override fun onFailure(failure: Throwable) {
    +                        Assert.fail("onFailure " + failure.localizedMessage)
    +                        generateLatch.countDown()
    +                    }
    +                })
    +
    +        mTestHelper.await(generateLatch)
    +
    +        Assert.assertNotNull(creationInfo)
    +
    +        assertAccountData(session, "m.secret_storage.key.$keyId")
    +        if (asDefault) {
    +            val setDefaultLatch = CountDownLatch(1)
    +            quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
    +            mTestHelper.await(setDefaultLatch)
    +            assertAccountData(session, DefaultSharedSecureStorage.DEFAULT_KEY_ID)
    +        }
    +
    +        return creationInfo!!
    +    }
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
    index a832921dc7..f4ffdce71c 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
    @@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session.accountdata
     
     import androidx.lifecycle.LiveData
     import im.vector.matrix.android.api.MatrixCallback
    +import im.vector.matrix.android.api.session.events.model.Content
     import im.vector.matrix.android.api.util.Optional
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
     
    @@ -31,5 +32,5 @@ interface AccountDataService {
     
         fun getLiveAccountData(filterType: List): LiveData>
     
    -    fun updateAccountData(type: String, data: Any, callback: MatrixCallback? = null)
    +    fun updateAccountData(type: String, content: Content, callback: MatrixCallback? = null)
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt
    index a4cf02aeae..2c6d6702eb 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt
    @@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.crypto.secrets
     import im.vector.matrix.android.api.MatrixCallback
     import im.vector.matrix.android.api.listeners.ProgressListener
     import im.vector.matrix.android.api.session.accountdata.AccountDataService
    +import im.vector.matrix.android.api.session.events.model.toContent
     import im.vector.matrix.android.api.session.securestorage.Curve25519AesSha2KeySpec
     import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent
     import im.vector.matrix.android.api.session.securestorage.KeyInfo
    @@ -82,7 +83,7 @@ internal class DefaultSharedSecureStorage @Inject constructor(
     
                 accountDataService.updateAccountData(
                         "$KEY_ID_BASE.$keyId",
    -                    signedContent,
    +                    signedContent.toContent(),
                         object : MatrixCallback {
                             override fun onFailure(failure: Throwable) {
                                 callback.onFailure(failure)
    @@ -136,7 +137,7 @@ internal class DefaultSharedSecureStorage @Inject constructor(
     
                 accountDataService.updateAccountData(
                         "$KEY_ID_BASE.$keyId",
    -                    signedContent,
    +                    signedContent.toContent(),
                         object : MatrixCallback {
                             override fun onFailure(failure: Throwable) {
                                 callback.onFailure(failure)
    @@ -254,7 +255,7 @@ internal class DefaultSharedSecureStorage @Inject constructor(
     
                     accountDataService.updateAccountData(
                             type = name,
    -                        data = mapOf(
    +                        content = mapOf(
                                     "encrypted" to encryptedContents
                             ),
                             callback = callback
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
    index fc4f73630b..541d11cfc9 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
    @@ -210,7 +210,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(
             }
         }
     
    -    private fun handleGenericAccountData(realm: Realm, type: String, content: Content?) {
    +    fun handleGenericAccountData(realm: Realm, type: String, content: Content?) {
             val existing = realm.where().equalTo(UserAccountDataEntityFields.TYPE, type)
                     .findFirst()
             if (existing != null) {
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
    index 0bb57f0dae..ee9e1d2550 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
    @@ -21,6 +21,7 @@ import androidx.lifecycle.Transformations
     import com.zhuinden.monarchy.Monarchy
     import im.vector.matrix.android.api.MatrixCallback
     import im.vector.matrix.android.api.session.accountdata.AccountDataService
    +import im.vector.matrix.android.api.session.events.model.Content
     import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE
     import im.vector.matrix.android.api.util.Optional
     import im.vector.matrix.android.api.util.toOptional
    @@ -28,6 +29,7 @@ import im.vector.matrix.android.internal.database.model.UserAccountDataEntity
     import im.vector.matrix.android.internal.database.model.UserAccountDataEntityFields
     import im.vector.matrix.android.internal.di.MoshiProvider
     import im.vector.matrix.android.internal.di.SessionId
    +import im.vector.matrix.android.internal.session.sync.UserAccountDataSyncHandler
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
     import im.vector.matrix.android.internal.task.TaskExecutor
     import im.vector.matrix.android.internal.task.configureWith
    @@ -37,6 +39,7 @@ internal class DefaultAccountDataService @Inject constructor(
             private val monarchy: Monarchy,
             @SessionId private val sessionId: String,
             private val updateUserAccountDataTask: UpdateUserAccountDataTask,
    +        private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
             private val taskExecutor: TaskExecutor
     ) : AccountDataService {
     
    @@ -87,13 +90,24 @@ internal class DefaultAccountDataService @Inject constructor(
             })
         }
     
    -    override fun updateAccountData(type: String, data: Any, callback: MatrixCallback?) {
    +    override fun updateAccountData(type: String, content: Content, callback: MatrixCallback?) {
             updateUserAccountDataTask.configureWith(UpdateUserAccountDataTask.AnyParams(
                     type = type,
    -                any = data
    +                any = content
             )) {
                 this.retryCount = 5
    -            callback?.let { this.callback = it }
    +            this.callback = object : MatrixCallback {
    +                override fun onSuccess(data: Unit) {
    +                    monarchy.runTransactionSync { realm ->
    +                        userAccountDataSyncHandler.handleGenericAccountData(realm, type, content)
    +                    }
    +                    callback?.onSuccess(data)
    +                }
    +
    +                override fun onFailure(failure: Throwable) {
    +                    callback?.onFailure(failure)
    +                }
    +            }
             }
                     .executeBy(taskExecutor)
         }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
    index beb3a0fcc0..9f8a851ee2 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
    @@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.user.accountdata
     
     import im.vector.matrix.android.internal.di.UserId
     import im.vector.matrix.android.internal.network.executeRequest
    +import im.vector.matrix.android.internal.session.sync.UserAccountDataSyncHandler
     import im.vector.matrix.android.internal.session.sync.model.accountdata.BreadcrumbsContent
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
     import im.vector.matrix.android.internal.task.Task
    
    From e0eede1150f3f1c1cad5757d3931bd207e71c01e Mon Sep 17 00:00:00 2001
    From: Valere 
    Date: Thu, 13 Feb 2020 14:06:04 +0100
    Subject: [PATCH 026/187] cleaning
    
    ---
     .../internal/crypto/ssss/QuadSTests.kt        | 28 ++++++-------------
     .../securestorage/SSSSKeyCreationInfo.kt      |  2 +-
     .../api/session/securestorage/SSSSKeySpec.kt  |  1 -
     .../securestorage/SecretStorageKeyContent.kt  |  1 -
     .../securestorage/SharedSecretStorageError.kt |  4 ++-
     .../secrets/DefaultSharedSecretStorage.kt     | 12 ++------
     .../parsing/AccountDataJsonAdapterFactory.kt  |  3 +-
     .../android/internal/session/SessionModule.kt |  4 +--
     .../sync/UserAccountDataSyncHandler.kt        |  3 +-
     .../accountdata/UpdateUserAccountDataTask.kt  |  1 -
     .../settings/devtools/AccountDataViewModel.kt |  1 -
     11 files changed, 20 insertions(+), 40 deletions(-)
    
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    index af15738cc8..badfd09d3f 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    @@ -34,7 +34,7 @@ import im.vector.matrix.android.common.SessionTestParams
     import im.vector.matrix.android.common.TestConstants
     import im.vector.matrix.android.common.TestMatrixCallback
     import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
    -import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecureStorage
    +import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorage
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
     import kotlinx.coroutines.Dispatchers
     import kotlinx.coroutines.GlobalScope
    @@ -57,7 +57,6 @@ class QuadSTests : InstrumentedTest {
     
         @Test
         fun test_Generate4SKey() {
    -
             val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
     
             val aliceLatch = CountDownLatch(1)
    @@ -76,7 +75,6 @@ class QuadSTests : InstrumentedTest {
     
             quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner,
                     object : MatrixCallback {
    -
                         override fun onSuccess(data: SSSSKeyCreationInfo) {
                             recoveryKey = data.recoveryKey
                             aliceLatch.countDown()
    @@ -110,13 +108,13 @@ class QuadSTests : InstrumentedTest {
             Assert.assertNotNull("Key should be stored in account data", accountData)
             val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
             Assert.assertNotNull("Key Content cannot be parsed", parsed)
    -        Assert.assertEquals("Unexpected Algorithm", DefaultSharedSecureStorage.ALGORITHM_CURVE25519_AES_SHA2, parsed!!.algorithm)
    +        Assert.assertEquals("Unexpected Algorithm", DefaultSharedSecretStorage.ALGORITHM_CURVE25519_AES_SHA2, parsed!!.algorithm)
             Assert.assertEquals("Unexpected key name", "Test Key", parsed.name)
             Assert.assertNull("Key was not generated from passphrase", parsed.passphrase)
             Assert.assertNotNull("Pubkey should be defined", parsed.publicKey)
     
             val privateKeySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(recoveryKey!!)
    -        DefaultSharedSecureStorage.withOlmDecryption { olmPkDecryption ->
    +        DefaultSharedSecretStorage.withOlmDecryption { olmPkDecryption ->
                 val pubKey = olmPkDecryption.setPrivateKey(privateKeySpec!!.privateKey)
                 Assert.assertEquals("Unexpected Public Key", pubKey, parsed.publicKey)
             }
    @@ -128,10 +126,10 @@ class QuadSTests : InstrumentedTest {
             val defaultDataLock = CountDownLatch(1)
     
             val liveDefAccountData = runBlocking(Dispatchers.Main) {
    -            aliceSession.getLiveAccountData(DefaultSharedSecureStorage.DEFAULT_KEY_ID)
    +            aliceSession.getLiveAccountData(DefaultSharedSecretStorage.DEFAULT_KEY_ID)
             }
             val accountDefDataObserver = Observer?> { t ->
    -            if (t?.getOrNull()?.type == DefaultSharedSecureStorage.DEFAULT_KEY_ID) {
    +            if (t?.getOrNull()?.type == DefaultSharedSecretStorage.DEFAULT_KEY_ID) {
                     defaultKeyAccountData = t.getOrNull()!!
                     defaultDataLock.countDown()
                 }
    @@ -140,11 +138,9 @@ class QuadSTests : InstrumentedTest {
     
             mTestHelper.await(defaultDataLock)
     
    -
             Assert.assertNotNull(defaultKeyAccountData?.content)
             Assert.assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key"))
     
    -
             mTestHelper.signout(aliceSession)
         }
     
    @@ -184,7 +180,7 @@ class QuadSTests : InstrumentedTest {
     
             val decryptCountDownLatch = CountDownLatch(1)
             aliceSession.sharedSecretStorageService.getSecret("secret.of.life",
    -                null, //default key
    +                null, // default key
                     keySpec!!,
                     object : MatrixCallback {
                         override fun onFailure(failure: Throwable) {
    @@ -200,7 +196,6 @@ class QuadSTests : InstrumentedTest {
             )
             mTestHelper.await(decryptCountDownLatch)
     
    -
             Assert.assertEquals("Secret mismatch", clearSecret, decryptedSecret)
             mTestHelper.signout(aliceSession)
         }
    @@ -225,12 +220,11 @@ class QuadSTests : InstrumentedTest {
     
             mTestHelper.await(countDownLatch)
     
    -        //Test that we don't need to wait for an account data sync to access directly the keyid from DB
    +        // Test that we don't need to wait for an account data sync to access directly the keyid from DB
             val defaultLatch = CountDownLatch(1)
             quadS.setDefaultKey(TEST_KEY_ID, TestMatrixCallback(defaultLatch))
             mTestHelper.await(defaultLatch)
     
    -
             mTestHelper.signout(aliceSession)
         }
     
    @@ -357,7 +351,6 @@ class QuadSTests : InstrumentedTest {
         }
     
         private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SSSSKeyCreationInfo {
    -
             val quadS = session.sharedSecretStorageService
     
             val emptyKeySigner = object : KeySigner {
    @@ -372,7 +365,6 @@ class QuadSTests : InstrumentedTest {
     
             quadS.generateKey(keyId, keyId, emptyKeySigner,
                     object : MatrixCallback {
    -
                         override fun onSuccess(data: SSSSKeyCreationInfo) {
                             creationInfo = data
                             generateLatch.countDown()
    @@ -393,14 +385,13 @@ class QuadSTests : InstrumentedTest {
                 val setDefaultLatch = CountDownLatch(1)
                 quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
                 mTestHelper.await(setDefaultLatch)
    -            assertAccountData(session, DefaultSharedSecureStorage.DEFAULT_KEY_ID)
    +            assertAccountData(session, DefaultSharedSecretStorage.DEFAULT_KEY_ID)
             }
     
             return creationInfo!!
         }
     
         private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SSSSKeyCreationInfo {
    -
             val quadS = session.sharedSecretStorageService
     
             val emptyKeySigner = object : KeySigner {
    @@ -418,7 +409,6 @@ class QuadSTests : InstrumentedTest {
                     emptyKeySigner,
                     null,
                     object : MatrixCallback {
    -
                         override fun onSuccess(data: SSSSKeyCreationInfo) {
                             creationInfo = data
                             generateLatch.countDown()
    @@ -439,7 +429,7 @@ class QuadSTests : InstrumentedTest {
                 val setDefaultLatch = CountDownLatch(1)
                 quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
                 mTestHelper.await(setDefaultLatch)
    -            assertAccountData(session, DefaultSharedSecureStorage.DEFAULT_KEY_ID)
    +            assertAccountData(session, DefaultSharedSecretStorage.DEFAULT_KEY_ID)
             }
     
             return creationInfo!!
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeyCreationInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeyCreationInfo.kt
    index 3c629290fc..5b608dd658 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeyCreationInfo.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeyCreationInfo.kt
    @@ -16,7 +16,7 @@
     
     package im.vector.matrix.android.api.session.securestorage
     
    -data class SSSSKeyCreationInfo (
    +data class SSSSKeyCreationInfo(
             val keyId: String = "",
             var content: SecretStorageKeyContent?,
             val recoveryKey: String = ""
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeySpec.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeySpec.kt
    index dcdba38d8e..9e61f7f8ff 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeySpec.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeySpec.kt
    @@ -64,4 +64,3 @@ data class Curve25519AesSha2KeySpec(
             return privateKey.contentHashCode()
         }
     }
    -
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt
    index 0aba3d700d..02c3e96658 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt
    @@ -98,4 +98,3 @@ data class SSSSPassphrase(
             @Json(name = "iterations") val iterations: Int,
             @Json(name = "salt") val salt: String?
     )
    -
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt
    index 1ff5cf12f3..f882375e5c 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt
    @@ -23,7 +23,9 @@ sealed class SharedSecretStorageError(message: String?) : Throwable(message) {
         data class UnknownAlgorithm(val keyId: String) : SharedSecretStorageError("Unknown algorithm $keyId")
         data class UnsupportedAlgorithm(val algorithm: String) : SharedSecretStorageError("Unknown algorithm $algorithm")
         data class SecretNotEncrypted(val secretName: String) : SharedSecretStorageError("Missing content for secret $secretName")
    -    data class SecretNotEncryptedWithKey(val secretName: String, val keyId: String) : SharedSecretStorageError("Missing content for secret $secretName with key $keyId")
    +    data class SecretNotEncryptedWithKey(val secretName: String, val keyId: String)
    +        : SharedSecretStorageError("Missing content for secret $secretName with key $keyId")
    +
         object BadKeyFormat : SharedSecretStorageError("Bad Key Format")
         object ParsingError : SharedSecretStorageError("parsing Error")
         data class OtherError(val reason: Throwable) : SharedSecretStorageError(reason.localizedMessage)
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt
    index 2c6d6702eb..fe6936f7fd 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt
    @@ -42,7 +42,7 @@ import org.matrix.olm.OlmPkEncryption
     import org.matrix.olm.OlmPkMessage
     import javax.inject.Inject
     
    -internal class DefaultSharedSecureStorage @Inject constructor(
    +internal class DefaultSharedSecretStorage @Inject constructor(
             private val accountDataService: AccountDataService,
             private val coroutineDispatchers: MatrixCoroutineDispatchers,
             private val cryptoCoroutineScope: CoroutineScope
    @@ -52,7 +52,6 @@ internal class DefaultSharedSecureStorage @Inject constructor(
                                  keyName: String,
                                  keySigner: KeySigner,
                                  callback: MatrixCallback) {
    -
             cryptoCoroutineScope.launch(coroutineDispatchers.main) {
                 val pkDecryption = OlmPkDecryption()
                 val pubKey: String
    @@ -108,7 +107,6 @@ internal class DefaultSharedSecureStorage @Inject constructor(
                                                progressListener: ProgressListener?,
                                                callback: MatrixCallback) {
             cryptoCoroutineScope.launch(coroutineDispatchers.main) {
    -
                 val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener)
     
                 val pkDecryption = OlmPkDecryption()
    @@ -152,7 +150,6 @@ internal class DefaultSharedSecureStorage @Inject constructor(
                             }
                         }
                 )
    -
             }
         }
     
    @@ -191,13 +188,11 @@ internal class DefaultSharedSecureStorage @Inject constructor(
         }
     
         override fun storeSecret(name: String, secretBase64: String, keys: List?, callback: MatrixCallback) {
    -
             cryptoCoroutineScope.launch(coroutineDispatchers.main) {
                 val encryptedContents = HashMap()
                 try {
    -
                     if (keys == null || keys.isEmpty()) {
    -                    //use default key
    +                    // use default key
                         val key = getDefaultKey()
                         when (key) {
                             is KeyInfoResult.Success -> {
    @@ -263,7 +258,6 @@ internal class DefaultSharedSecureStorage @Inject constructor(
                 } catch (failure: Throwable) {
                     callback.onFailure(failure)
                 }
    -
             }
     
             // Add default key
    @@ -363,5 +357,3 @@ internal class DefaultSharedSecureStorage @Inject constructor(
             }
         }
     }
    -
    -
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/AccountDataJsonAdapterFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/AccountDataJsonAdapterFactory.kt
    index e7290077dd..bf8ae84478 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/AccountDataJsonAdapterFactory.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/AccountDataJsonAdapterFactory.kt
    @@ -22,8 +22,7 @@ import java.lang.reflect.Type
     
     class AccountDataJsonAdapterFactory : JsonAdapter.Factory {
     
    -
         override fun create(type: Type, annotations: MutableSet, moshi: Moshi): JsonAdapter<*>? {
    -        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    +        TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
         }
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
    index 02d8a35009..b06ddbe123 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
    @@ -36,7 +36,7 @@ import im.vector.matrix.android.api.session.accountdata.AccountDataService
     import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
     import im.vector.matrix.android.api.session.securestorage.SecureStorageService
     import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
    -import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecureStorage
    +import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorage
     import im.vector.matrix.android.internal.crypto.verification.VerificationMessageLiveObserver
     import im.vector.matrix.android.internal.database.LiveEntityObserver
     import im.vector.matrix.android.internal.database.SessionRealmConfigurationFactory
    @@ -272,5 +272,5 @@ internal abstract class SessionModule {
         abstract fun bindAccountDataServiceService(accountDataService: DefaultAccountDataService): AccountDataService
     
         @Binds
    -    abstract fun bindSharedSecuredSecretStorageService(service: DefaultSharedSecureStorage): SharedSecretStorageService
    +    abstract fun bindSharedSecuredSecretStorageService(service: DefaultSharedSecretStorage): SharedSecretStorageService
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
    index 541d11cfc9..c530578538 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
    @@ -211,7 +211,8 @@ internal class UserAccountDataSyncHandler @Inject constructor(
         }
     
         fun handleGenericAccountData(realm: Realm, type: String, content: Content?) {
    -        val existing = realm.where().equalTo(UserAccountDataEntityFields.TYPE, type)
    +        val existing = realm.where()
    +                .equalTo(UserAccountDataEntityFields.TYPE, type)
                     .findFirst()
             if (existing != null) {
                 // Update current value
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
    index 9f8a851ee2..beb3a0fcc0 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
    @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.session.user.accountdata
     
     import im.vector.matrix.android.internal.di.UserId
     import im.vector.matrix.android.internal.network.executeRequest
    -import im.vector.matrix.android.internal.session.sync.UserAccountDataSyncHandler
     import im.vector.matrix.android.internal.session.sync.model.accountdata.BreadcrumbsContent
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
     import im.vector.matrix.android.internal.task.Task
    diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataViewModel.kt
    index 4a6b0f896a..b0b23a62d1 100644
    --- a/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataViewModel.kt
    @@ -39,7 +39,6 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A
                                                            private val session: Session)
         : VectorViewModel(initialState) {
     
    -
         init {
             session.rx().liveAccountData(emptyList())
                     .execute {
    
    From f99eca8014654985de4245b7418a85d7872e0903 Mon Sep 17 00:00:00 2001
    From: Valere 
    Date: Fri, 14 Feb 2020 10:15:32 +0100
    Subject: [PATCH 027/187] Code review
    
    ---
     .../java/im/vector/matrix/rx/RxSession.kt     |  4 +-
     .../internal/crypto/ssss/QuadSTests.kt        | 43 ++++++++++---------
     .../session/accountdata/AccountDataService.kt |  8 ++--
     .../SharedSecretStorageService.kt             | 32 +++++++++-----
     ...CreationInfo.kt => SsssKeyCreationInfo.kt} |  2 +-
     .../{SSSSKeySpec.kt => SsssKeySpec.kt}        |  0
     .../internal/crypto/CryptoConstants.kt        |  5 +++
     ...t => DefaultSharedSecretStorageService.kt} | 35 ++++++++-------
     .../database/model/UserAccountDataEntity.kt   |  5 ++-
     .../parsing/AccountDataJsonAdapterFactory.kt  | 28 ------------
     .../android/internal/session/SessionModule.kt |  6 +--
     .../accountdata/DefaultAccountDataService.kt  | 12 +++---
     .../settings/devtools/AccountDataFragment.kt  |  7 +++
     13 files changed, 91 insertions(+), 96 deletions(-)
     rename matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/{SSSSKeyCreationInfo.kt => SsssKeyCreationInfo.kt} (96%)
     rename matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/{SSSSKeySpec.kt => SsssKeySpec.kt} (100%)
     rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/{DefaultSharedSecretStorage.kt => DefaultSharedSecretStorageService.kt} (93%)
     delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/AccountDataJsonAdapterFactory.kt
    
    diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
    index 50f55577d0..0417504cb7 100644
    --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
    +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
    @@ -124,9 +124,9 @@ class RxSession(private val session: Session) {
         }
     
         fun liveAccountData(filter: List): Observable> {
    -        return session.getLiveAccountData(filter).asObservable()
    +        return session.getLiveAccountDataEvents(filter).asObservable()
                     .startWithCallable {
    -                    session.getAccountData(filter)
    +                    session.getAccountDataEvents(filter)
                     }
         }
     }
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    index badfd09d3f..f0e2161d4c 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    @@ -25,7 +25,7 @@ import im.vector.matrix.android.api.session.Session
     import im.vector.matrix.android.api.session.securestorage.Curve25519AesSha2KeySpec
     import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent
     import im.vector.matrix.android.api.session.securestorage.KeySigner
    -import im.vector.matrix.android.api.session.securestorage.SSSSKeyCreationInfo
    +import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
     import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent
     import im.vector.matrix.android.api.util.Optional
     import im.vector.matrix.android.common.CommonTestHelper
    @@ -33,8 +33,9 @@ import im.vector.matrix.android.common.CryptoTestHelper
     import im.vector.matrix.android.common.SessionTestParams
     import im.vector.matrix.android.common.TestConstants
     import im.vector.matrix.android.common.TestMatrixCallback
    +import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_CURVE25519_AES_SHA2
     import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
    -import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorage
    +import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorageService
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
     import kotlinx.coroutines.Dispatchers
     import kotlinx.coroutines.GlobalScope
    @@ -74,8 +75,8 @@ class QuadSTests : InstrumentedTest {
             val TEST_KEY_ID = "my.test.Key"
     
             quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner,
    -                object : MatrixCallback {
    -                    override fun onSuccess(data: SSSSKeyCreationInfo) {
    +                object : MatrixCallback {
    +                    override fun onSuccess(data: SsssKeyCreationInfo) {
                             recoveryKey = data.recoveryKey
                             aliceLatch.countDown()
                         }
    @@ -93,7 +94,7 @@ class QuadSTests : InstrumentedTest {
             var accountData: UserAccountDataEvent? = null
     
             val liveAccountData = runBlocking(Dispatchers.Main) {
    -            aliceSession.getLiveAccountData("m.secret_storage.key.$TEST_KEY_ID")
    +            aliceSession.getLiveAccountDataEvent("m.secret_storage.key.$TEST_KEY_ID")
             }
             val accountDataObserver = Observer?> { t ->
                 if (t?.getOrNull()?.type == "m.secret_storage.key.$TEST_KEY_ID") {
    @@ -108,13 +109,13 @@ class QuadSTests : InstrumentedTest {
             Assert.assertNotNull("Key should be stored in account data", accountData)
             val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
             Assert.assertNotNull("Key Content cannot be parsed", parsed)
    -        Assert.assertEquals("Unexpected Algorithm", DefaultSharedSecretStorage.ALGORITHM_CURVE25519_AES_SHA2, parsed!!.algorithm)
    +        Assert.assertEquals("Unexpected Algorithm", SSSS_ALGORITHM_CURVE25519_AES_SHA2, parsed!!.algorithm)
             Assert.assertEquals("Unexpected key name", "Test Key", parsed.name)
             Assert.assertNull("Key was not generated from passphrase", parsed.passphrase)
             Assert.assertNotNull("Pubkey should be defined", parsed.publicKey)
     
             val privateKeySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(recoveryKey!!)
    -        DefaultSharedSecretStorage.withOlmDecryption { olmPkDecryption ->
    +        DefaultSharedSecretStorageService.withOlmDecryption { olmPkDecryption ->
                 val pubKey = olmPkDecryption.setPrivateKey(privateKeySpec!!.privateKey)
                 Assert.assertEquals("Unexpected Public Key", pubKey, parsed.publicKey)
             }
    @@ -126,10 +127,10 @@ class QuadSTests : InstrumentedTest {
             val defaultDataLock = CountDownLatch(1)
     
             val liveDefAccountData = runBlocking(Dispatchers.Main) {
    -            aliceSession.getLiveAccountData(DefaultSharedSecretStorage.DEFAULT_KEY_ID)
    +            aliceSession.getLiveAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
             }
             val accountDefDataObserver = Observer?> { t ->
    -            if (t?.getOrNull()?.type == DefaultSharedSecretStorage.DEFAULT_KEY_ID) {
    +            if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) {
                     defaultKeyAccountData = t.getOrNull()!!
                     defaultDataLock.countDown()
                 }
    @@ -247,7 +248,7 @@ class QuadSTests : InstrumentedTest {
             )
             mTestHelper.await(storeLatch)
     
    -        val accountDataEvent = aliceSession.getAccountData("my.secret")
    +        val accountDataEvent = aliceSession.getAccountDataEvent("my.secret")
             val encryptedContent = accountDataEvent?.content?.get("encrypted") as? Map<*, *>
     
             Assert.assertEquals("Content should contains two encryptions", 2, encryptedContent?.keys?.size ?: 0)
    @@ -334,7 +335,7 @@ class QuadSTests : InstrumentedTest {
             var accountData: UserAccountDataEvent? = null
     
             val liveAccountData = runBlocking(Dispatchers.Main) {
    -            session.getLiveAccountData(type)
    +            session.getLiveAccountDataEvent(type)
             }
             val accountDataObserver = Observer?> { t ->
                 if (t?.getOrNull()?.type == type) {
    @@ -350,7 +351,7 @@ class QuadSTests : InstrumentedTest {
             return accountData!!
         }
     
    -    private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SSSSKeyCreationInfo {
    +    private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
             val quadS = session.sharedSecretStorageService
     
             val emptyKeySigner = object : KeySigner {
    @@ -359,13 +360,13 @@ class QuadSTests : InstrumentedTest {
                 }
             }
     
    -        var creationInfo: SSSSKeyCreationInfo? = null
    +        var creationInfo: SsssKeyCreationInfo? = null
     
             val generateLatch = CountDownLatch(1)
     
             quadS.generateKey(keyId, keyId, emptyKeySigner,
    -                object : MatrixCallback {
    -                    override fun onSuccess(data: SSSSKeyCreationInfo) {
    +                object : MatrixCallback {
    +                    override fun onSuccess(data: SsssKeyCreationInfo) {
                             creationInfo = data
                             generateLatch.countDown()
                         }
    @@ -385,13 +386,13 @@ class QuadSTests : InstrumentedTest {
                 val setDefaultLatch = CountDownLatch(1)
                 quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
                 mTestHelper.await(setDefaultLatch)
    -            assertAccountData(session, DefaultSharedSecretStorage.DEFAULT_KEY_ID)
    +            assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
             }
     
             return creationInfo!!
         }
     
    -    private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SSSSKeyCreationInfo {
    +    private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
             val quadS = session.sharedSecretStorageService
     
             val emptyKeySigner = object : KeySigner {
    @@ -400,7 +401,7 @@ class QuadSTests : InstrumentedTest {
                 }
             }
     
    -        var creationInfo: SSSSKeyCreationInfo? = null
    +        var creationInfo: SsssKeyCreationInfo? = null
     
             val generateLatch = CountDownLatch(1)
     
    @@ -408,8 +409,8 @@ class QuadSTests : InstrumentedTest {
                     passphrase,
                     emptyKeySigner,
                     null,
    -                object : MatrixCallback {
    -                    override fun onSuccess(data: SSSSKeyCreationInfo) {
    +                object : MatrixCallback {
    +                    override fun onSuccess(data: SsssKeyCreationInfo) {
                             creationInfo = data
                             generateLatch.countDown()
                         }
    @@ -429,7 +430,7 @@ class QuadSTests : InstrumentedTest {
                 val setDefaultLatch = CountDownLatch(1)
                 quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
                 mTestHelper.await(setDefaultLatch)
    -            assertAccountData(session, DefaultSharedSecretStorage.DEFAULT_KEY_ID)
    +            assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
             }
     
             return creationInfo!!
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
    index f4ffdce71c..7af7fea214 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
    @@ -24,13 +24,13 @@ import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAcco
     
     interface AccountDataService {
     
    -    fun getAccountData(type: String): UserAccountDataEvent?
    +    fun getAccountDataEvent(type: String): UserAccountDataEvent?
     
    -    fun getLiveAccountData(type: String): LiveData>
    +    fun getLiveAccountDataEvent(type: String): LiveData>
     
    -    fun getAccountData(filterType: List): List
    +    fun getAccountDataEvents(filterType: List): List
     
    -    fun getLiveAccountData(filterType: List): LiveData>
    +    fun getLiveAccountDataEvents(filterType: List): LiveData>
     
         fun updateAccountData(type: String, content: Content, callback: MatrixCallback? = null)
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
    index 9923aab606..02ccc11026 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2019 New Vector Ltd
    + * Copyright 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.
    @@ -31,26 +31,38 @@ import im.vector.matrix.android.api.listeners.ProgressListener
     interface SharedSecretStorageService {
     
         /**
    -     * Add a key for encrypting secrets.
    +     * Generates a SSSS key for encrypting secrets.
    +     * Use the SsssKeyCreationInfo object returned by the callback to get more information about the created key (recovery key ...)
          *
    -     * @param algorithm the algorithm used by the key.
    -     * @param opts the options for the algorithm.  The properties used
    -     *     depend on the algorithm given.
          * @param keyId the ID of the key
    +     * @param keyName a human readable name
    +     * @param keySigner Used to add a signature to the key (client should check key signature before storing secret)
          *
    -     * @return {string} the ID of the key
    +     * @param callback Get key creation info
          */
         fun generateKey(keyId: String,
                         keyName: String,
                         keySigner: KeySigner,
    -                    callback: MatrixCallback)
    +                    callback: MatrixCallback)
     
    +    /**
    +     * Generates a SSSS key using the given passphrase.
    +     * Use the SsssKeyCreationInfo object returned by the callback to get more information about the created key (recovery key, salt, iteration ...)
    +     *
    +     * @param keyId the ID of the key
    +     * @param keyName human readable key name
    +     * @param passphrase The passphrase used to generate the key
    +     * @param keySigner Used to add a signature to the key (client should check key signature before retrieving secret)
    +     * @param progressListener The derivation of the passphrase may take long depending on the device, use this to report progress
    +     *
    +     * @param callback Get key creation info
    +     */
         fun generateKeyWithPassphrase(keyId: String,
                                       keyName: String,
                                       passphrase: String,
                                       keySigner: KeySigner,
                                       progressListener: ProgressListener?,
    -                                  callback: MatrixCallback)
    +                                  callback: MatrixCallback)
     
         fun getKey(keyId: String): KeyInfoResult
     
    @@ -92,11 +104,9 @@ interface SharedSecretStorageService {
          *
          * @param name The name of the secret
          * @param keyId The id of the key that should be used to decrypt (null for default key)
    -     * @param privateKey the passphrase/secret
    +     * @param secretKey the secret key to use (@see #Curve25519AesSha2KeySpec)
          *
    -     * @return The decrypted value
          */
         @Throws
    -
         fun getSecret(name: String, keyId: String?, secretKey: SSSSKeySpec, callback: MatrixCallback)
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeyCreationInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeyCreationInfo.kt
    similarity index 96%
    rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeyCreationInfo.kt
    rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeyCreationInfo.kt
    index 5b608dd658..1d5522b8bf 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeyCreationInfo.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeyCreationInfo.kt
    @@ -16,7 +16,7 @@
     
     package im.vector.matrix.android.api.session.securestorage
     
    -data class SSSSKeyCreationInfo(
    +data class SsssKeyCreationInfo(
             val keyId: String = "",
             var content: SecretStorageKeyContent?,
             val recoveryKey: String = ""
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeySpec.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeySpec.kt
    similarity index 100%
    rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SSSSKeySpec.kt
    rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeySpec.kt
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoConstants.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoConstants.kt
    index a3b0a567fe..fee81a853d 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoConstants.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoConstants.kt
    @@ -31,6 +31,11 @@ const val MXCRYPTO_ALGORITHM_MEGOLM = "m.megolm.v1.aes-sha2"
      */
     const val MXCRYPTO_ALGORITHM_MEGOLM_BACKUP = "m.megolm_backup.v1.curve25519-aes-sha2"
     
    +/**
    + * Secured Shared Storage algorithm constant
    + */
    +const val SSSS_ALGORITHM_CURVE25519_AES_SHA2 = "m.secret_storage.v1.curve25519-aes-sha2"
    +
     // TODO Refacto: use this constants everywhere
     const val ed25519 = "ed25519"
     const val curve25519 = "curve25519"
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
    similarity index 93%
    rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt
    rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
    index fe6936f7fd..f741021e6c 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorage.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
    @@ -25,12 +25,13 @@ import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent
     import im.vector.matrix.android.api.session.securestorage.KeyInfo
     import im.vector.matrix.android.api.session.securestorage.KeyInfoResult
     import im.vector.matrix.android.api.session.securestorage.KeySigner
    -import im.vector.matrix.android.api.session.securestorage.SSSSKeyCreationInfo
    +import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
     import im.vector.matrix.android.api.session.securestorage.SSSSKeySpec
     import im.vector.matrix.android.api.session.securestorage.SSSSPassphrase
     import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent
     import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageError
     import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
    +import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_CURVE25519_AES_SHA2
     import im.vector.matrix.android.internal.crypto.keysbackup.generatePrivateKeyWithPassword
     import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey
     import im.vector.matrix.android.internal.extensions.foldToCallback
    @@ -42,7 +43,7 @@ import org.matrix.olm.OlmPkEncryption
     import org.matrix.olm.OlmPkMessage
     import javax.inject.Inject
     
    -internal class DefaultSharedSecretStorage @Inject constructor(
    +internal class DefaultSharedSecretStorageService @Inject constructor(
             private val accountDataService: AccountDataService,
             private val coroutineDispatchers: MatrixCoroutineDispatchers,
             private val cryptoCoroutineScope: CoroutineScope
    @@ -51,7 +52,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
         override fun generateKey(keyId: String,
                                  keyName: String,
                                  keySigner: KeySigner,
    -                             callback: MatrixCallback) {
    +                             callback: MatrixCallback) {
             cryptoCoroutineScope.launch(coroutineDispatchers.main) {
                 val pkDecryption = OlmPkDecryption()
                 val pubKey: String
    @@ -69,7 +70,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
     
                 val storageKeyContent = SecretStorageKeyContent(
                         name = keyName,
    -                    algorithm = ALGORITHM_CURVE25519_AES_SHA2,
    +                    algorithm = SSSS_ALGORITHM_CURVE25519_AES_SHA2,
                         passphrase = null,
                         publicKey = pubKey
                 )
    @@ -89,7 +90,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
                             }
     
                             override fun onSuccess(data: Unit) {
    -                            callback.onSuccess(SSSSKeyCreationInfo(
    +                            callback.onSuccess(SsssKeyCreationInfo(
                                         keyId = keyId,
                                         content = storageKeyContent,
                                         recoveryKey = computeRecoveryKey(privateKey)
    @@ -105,7 +106,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
                                                passphrase: String,
                                                keySigner: KeySigner,
                                                progressListener: ProgressListener?,
    -                                           callback: MatrixCallback) {
    +                                           callback: MatrixCallback) {
             cryptoCoroutineScope.launch(coroutineDispatchers.main) {
                 val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener)
     
    @@ -122,7 +123,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
                 }
     
                 val storageKeyContent = SecretStorageKeyContent(
    -                    algorithm = ALGORITHM_CURVE25519_AES_SHA2,
    +                    algorithm = SSSS_ALGORITHM_CURVE25519_AES_SHA2,
                         passphrase = SSSSPassphrase(algorithm = "m.pbkdf2", iterations = privatePart.iterations, salt = privatePart.salt),
                         publicKey = pubKey
                 )
    @@ -142,7 +143,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
                             }
     
                             override fun onSuccess(data: Unit) {
    -                            callback.onSuccess(SSSSKeyCreationInfo(
    +                            callback.onSuccess(SsssKeyCreationInfo(
                                         keyId = keyId,
                                         content = storageKeyContent,
                                         recoveryKey = computeRecoveryKey(privatePart.privateKey)
    @@ -154,11 +155,11 @@ internal class DefaultSharedSecretStorage @Inject constructor(
         }
     
         override fun hasKey(keyId: String): Boolean {
    -        return accountDataService.getAccountData("$KEY_ID_BASE.$keyId") != null
    +        return accountDataService.getAccountDataEvent("$KEY_ID_BASE.$keyId") != null
         }
     
         override fun getKey(keyId: String): KeyInfoResult {
    -        val accountData = accountDataService.getAccountData("$KEY_ID_BASE.$keyId")
    +        val accountData = accountDataService.getAccountDataEvent("$KEY_ID_BASE.$keyId")
                     ?: return KeyInfoResult.Error(SharedSecretStorageError.UnknownKey(keyId))
             return SecretStorageKeyContent.fromJson(accountData.content)?.let {
                 KeyInfoResult.Success(
    @@ -180,7 +181,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
         }
     
         override fun getDefaultKey(): KeyInfoResult {
    -        val accountData = accountDataService.getAccountData(DEFAULT_KEY_ID)
    +        val accountData = accountDataService.getAccountDataEvent(DEFAULT_KEY_ID)
                     ?: return KeyInfoResult.Error(SharedSecretStorageError.UnknownKey(DEFAULT_KEY_ID))
             val keyId = accountData.content["key"] as? String
                     ?: return KeyInfoResult.Error(SharedSecretStorageError.UnknownKey(DEFAULT_KEY_ID))
    @@ -196,7 +197,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
                         val key = getDefaultKey()
                         when (key) {
                             is KeyInfoResult.Success -> {
    -                            if (key.keyInfo.content.algorithm == ALGORITHM_CURVE25519_AES_SHA2) {
    +                            if (key.keyInfo.content.algorithm == SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
                                     withOlmEncryption { olmEncrypt ->
                                         olmEncrypt.setRecipientKey(key.keyInfo.content.publicKey)
                                         val encryptedResult = olmEncrypt.encrypt(secretBase64)
    @@ -224,7 +225,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
                             val key = getKey(keyId)
                             when (key) {
                                 is KeyInfoResult.Success -> {
    -                                if (key.keyInfo.content.algorithm == ALGORITHM_CURVE25519_AES_SHA2) {
    +                                if (key.keyInfo.content.algorithm == SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
                                         withOlmEncryption { olmEncrypt ->
                                             olmEncrypt.setRecipientKey(key.keyInfo.content.publicKey)
                                             val encryptedResult = olmEncrypt.encrypt(secretBase64)
    @@ -264,7 +265,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
         }
     
         override fun getAlgorithmsForSecret(name: String): List {
    -        val accountData = accountDataService.getAccountData(name)
    +        val accountData = accountDataService.getAccountDataEvent(name)
                     ?: return listOf(KeyInfoResult.Error(SharedSecretStorageError.UnknownSecret(name)))
             val encryptedContent = accountData.content[ENCRYPTED] as? Map<*, *>
                     ?: return listOf(KeyInfoResult.Error(SharedSecretStorageError.SecretNotEncrypted(name)))
    @@ -279,7 +280,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
         }
     
         override fun getSecret(name: String, keyId: String?, secretKey: SSSSKeySpec, callback: MatrixCallback) {
    -        val accountData = accountDataService.getAccountData(name) ?: return Unit.also {
    +        val accountData = accountDataService.getAccountDataEvent(name) ?: return Unit.also {
                 callback.onFailure(SharedSecretStorageError.UnknownSecret(name))
             }
             val encryptedContent = accountData.content[ENCRYPTED] as? Map<*, *> ?: return Unit.also {
    @@ -299,7 +300,7 @@ internal class DefaultSharedSecretStorage @Inject constructor(
                     }
     
             val algorithm = key.keyInfo.content
    -        if (ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) {
    +        if (SSSS_ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) {
                 val keySpec = secretKey as? Curve25519AesSha2KeySpec ?: return Unit.also {
                     callback.onFailure(SharedSecretStorageError.BadKeyFormat)
                 }
    @@ -332,8 +333,6 @@ internal class DefaultSharedSecretStorage @Inject constructor(
             const val ENCRYPTED = "encrypted"
             const val DEFAULT_KEY_ID = "m.secret_storage.default_key"
     
    -        const val ALGORITHM_CURVE25519_AES_SHA2 = "m.secret_storage.v1.curve25519-aes-sha2"
    -
             fun withOlmEncryption(block: (OlmPkEncryption) -> Unit) {
                 val olmPkEncryption = OlmPkEncryption()
                 try {
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/UserAccountDataEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/UserAccountDataEntity.kt
    index 9ffbcca527..90f73381dc 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/UserAccountDataEntity.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/UserAccountDataEntity.kt
    @@ -20,9 +20,10 @@ import io.realm.RealmObject
     import io.realm.annotations.Index
     
     /**
    - * Clients can store custom config data for their account on their homeserver.
    + * Clients can store custom config data for their account on their HomeServer.
      * This account data will be synced between different devices and can persist across installations on a particular device.
    - * Users may only view the account data for their own accountThe account_data may be either global or scoped to a particular rooms.
    + * Users may only view the account data for their own account.
    + * The account_data may be either global or scoped to a particular rooms.
      */
     internal open class UserAccountDataEntity(
             @Index var type: String? = null,
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/AccountDataJsonAdapterFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/AccountDataJsonAdapterFactory.kt
    deleted file mode 100644
    index bf8ae84478..0000000000
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/AccountDataJsonAdapterFactory.kt
    +++ /dev/null
    @@ -1,28 +0,0 @@
    -/*
    - * 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 im.vector.matrix.android.internal.network.parsing
    -
    -import com.squareup.moshi.JsonAdapter
    -import com.squareup.moshi.Moshi
    -import java.lang.reflect.Type
    -
    -class AccountDataJsonAdapterFactory : JsonAdapter.Factory {
    -
    -    override fun create(type: Type, annotations: MutableSet, moshi: Moshi): JsonAdapter<*>? {
    -        TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
    -    }
    -}
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
    index b06ddbe123..7352b79073 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
    @@ -36,7 +36,7 @@ import im.vector.matrix.android.api.session.accountdata.AccountDataService
     import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
     import im.vector.matrix.android.api.session.securestorage.SecureStorageService
     import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
    -import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorage
    +import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorageService
     import im.vector.matrix.android.internal.crypto.verification.VerificationMessageLiveObserver
     import im.vector.matrix.android.internal.database.LiveEntityObserver
     import im.vector.matrix.android.internal.database.SessionRealmConfigurationFactory
    @@ -269,8 +269,8 @@ internal abstract class SessionModule {
         abstract fun bindHomeServerCapabilitiesService(homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService): HomeServerCapabilitiesService
     
         @Binds
    -    abstract fun bindAccountDataServiceService(accountDataService: DefaultAccountDataService): AccountDataService
    +    abstract fun bindAccountDataService(accountDataService: DefaultAccountDataService): AccountDataService
     
         @Binds
    -    abstract fun bindSharedSecuredSecretStorageService(service: DefaultSharedSecretStorage): SharedSecretStorageService
    +    abstract fun bindSharedSecretStorageService(service: DefaultSharedSecretStorageService): SharedSecretStorageService
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
    index ee9e1d2550..b40c75992a 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
    @@ -46,17 +46,17 @@ internal class DefaultAccountDataService @Inject constructor(
         private val moshi = MoshiProvider.providesMoshi()
         private val adapter = moshi.adapter>(JSON_DICT_PARAMETERIZED_TYPE)
     
    -    override fun getAccountData(type: String): UserAccountDataEvent? {
    -        return getAccountData(listOf(type)).firstOrNull()
    +    override fun getAccountDataEvent(type: String): UserAccountDataEvent? {
    +        return getAccountDataEvents(listOf(type)).firstOrNull()
         }
     
    -    override fun getLiveAccountData(type: String): LiveData> {
    -        return Transformations.map(getLiveAccountData(listOf(type))) {
    +    override fun getLiveAccountDataEvent(type: String): LiveData> {
    +        return Transformations.map(getLiveAccountDataEvents(listOf(type))) {
                 it.firstOrNull()?.toOptional()
             }
         }
     
    -    override fun getAccountData(filterType: List): List {
    +    override fun getAccountDataEvents(filterType: List): List {
             return monarchy.fetchAllCopiedSync { realm ->
                 realm.where(UserAccountDataEntity::class.java)
                         .apply {
    @@ -74,7 +74,7 @@ internal class DefaultAccountDataService @Inject constructor(
             } ?: emptyList()
         }
     
    -    override fun getLiveAccountData(filterType: List): LiveData> {
    +    override fun getLiveAccountDataEvents(filterType: List): LiveData> {
             return monarchy.findAllMappedWithChanges({ realm ->
                 realm.where(UserAccountDataEntity::class.java)
                         .apply {
    diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt
    index 0799ae270b..7a57a03deb 100644
    --- a/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataFragment.kt
    @@ -24,6 +24,7 @@ import im.vector.matrix.android.internal.di.MoshiProvider
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
     import im.vector.riotx.R
    +import im.vector.riotx.core.extensions.cleanup
     import im.vector.riotx.core.extensions.configureWith
     import im.vector.riotx.core.platform.VectorBaseActivity
     import im.vector.riotx.core.platform.VectorBaseFragment
    @@ -58,6 +59,12 @@ class AccountDataFragment @Inject constructor(
             epoxyController.interactionListener = this
         }
     
    +    override fun onDestroyView() {
    +        super.onDestroyView()
    +        recyclerView.cleanup()
    +        epoxyController.interactionListener = null
    +    }
    +
         override fun didTap(data: UserAccountData) {
             val fb = data as? UserAccountDataEvent ?: return
             val jsonString = MoshiProvider.providesMoshi()
    
    From 35835be03e04afba53ec13085941956274c3b3a7 Mon Sep 17 00:00:00 2001
    From: Valere 
    Date: Fri, 14 Feb 2020 12:06:07 +0100
    Subject: [PATCH 028/187] klint
    
    ---
     .../android/internal/session/room/send/EncryptEventWorker.kt     | 1 -
     1 file changed, 1 deletion(-)
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt
    index 21080d9037..6f1593bc08 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt
    @@ -20,7 +20,6 @@ import android.content.Context
     import androidx.work.CoroutineWorker
     import androidx.work.WorkerParameters
     import com.squareup.moshi.JsonClass
    -import im.vector.matrix.android.api.MatrixCallback
     import im.vector.matrix.android.api.failure.Failure
     import im.vector.matrix.android.api.session.crypto.CryptoService
     import im.vector.matrix.android.api.session.events.model.Event
    
    From 1660a0f846bda64aecb45161d604f8ed12106208 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Fri, 14 Feb 2020 15:16:00 +0100
    Subject: [PATCH 029/187] Version++
    
    ---
     CHANGES.md          | 24 ++++++++++++++++++++++++
     vector/build.gradle |  2 +-
     2 files changed, 25 insertions(+), 1 deletion(-)
    
    diff --git a/CHANGES.md b/CHANGES.md
    index 5b7c04d3ac..bebb419564 100644
    --- a/CHANGES.md
    +++ b/CHANGES.md
    @@ -1,3 +1,27 @@
    +Changes in RiotX 0.17.0 (2020-XX-XX)
    +===================================================
    +
    +Features ✨:
    + -
    +
    +Improvements 🙌:
    + -
    +
    +Bugfix 🐛:
    + -
    +
    +Translations 🗣:
    + -
    +
    +SDK API changes ⚠️:
    + -
    +
    +Build 🧱:
    + -
    +
    +Other changes:
    + -
    +
     Changes in RiotX 0.16.0 (2020-02-14)
     ===================================================
     
    diff --git a/vector/build.gradle b/vector/build.gradle
    index 0517482904..a091f7aaa2 100644
    --- a/vector/build.gradle
    +++ b/vector/build.gradle
    @@ -15,7 +15,7 @@ androidExtensions {
     }
     
     ext.versionMajor = 0
    -ext.versionMinor = 16
    +ext.versionMinor = 17
     ext.versionPatch = 0
     
     static def getGitTimestamp() {
    
    From cad818c34160180536758007d04a40d65bf89a67 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Fri, 14 Feb 2020 17:05:14 +0100
    Subject: [PATCH 030/187] ktlint
    
    ---
     .../android/internal/session/room/send/EncryptEventWorker.kt     | 1 -
     1 file changed, 1 deletion(-)
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt
    index 21080d9037..6f1593bc08 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt
    @@ -20,7 +20,6 @@ import android.content.Context
     import androidx.work.CoroutineWorker
     import androidx.work.WorkerParameters
     import com.squareup.moshi.JsonClass
    -import im.vector.matrix.android.api.MatrixCallback
     import im.vector.matrix.android.api.failure.Failure
     import im.vector.matrix.android.api.session.crypto.CryptoService
     import im.vector.matrix.android.api.session.events.model.Event
    
    From 01597a89dc828033836f218828ff02d7b61fdae2 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Fri, 14 Feb 2020 17:53:27 +0100
    Subject: [PATCH 031/187] Avoid code duplication
    
    ---
     .../internal/crypto/ssss/QuadSTests.kt        | 36 +++++--------------
     1 file changed, 8 insertions(+), 28 deletions(-)
    
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    index f0e2161d4c..57c065aaf8 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    @@ -25,11 +25,10 @@ import im.vector.matrix.android.api.session.Session
     import im.vector.matrix.android.api.session.securestorage.Curve25519AesSha2KeySpec
     import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent
     import im.vector.matrix.android.api.session.securestorage.KeySigner
    -import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
     import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent
    +import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
     import im.vector.matrix.android.api.util.Optional
     import im.vector.matrix.android.common.CommonTestHelper
    -import im.vector.matrix.android.common.CryptoTestHelper
     import im.vector.matrix.android.common.SessionTestParams
     import im.vector.matrix.android.common.TestConstants
     import im.vector.matrix.android.common.TestMatrixCallback
    @@ -50,11 +49,16 @@ import org.junit.runners.MethodSorters
     import java.util.concurrent.CountDownLatch
     
     @RunWith(AndroidJUnit4::class)
    -@FixMethodOrder(MethodSorters.NAME_ASCENDING)
    +@FixMethodOrder(MethodSorters.JVM)
     class QuadSTests : InstrumentedTest {
     
         private val mTestHelper = CommonTestHelper(context())
    -    private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
    +
    +    private val emptyKeySigner = object : KeySigner {
    +        override fun sign(canonicalJson: String): Map>? {
    +            return null
    +        }
    +    }
     
         @Test
         fun test_Generate4SKey() {
    @@ -64,12 +68,6 @@ class QuadSTests : InstrumentedTest {
     
             val quadS = aliceSession.sharedSecretStorageService
     
    -        val emptyKeySigner = object : KeySigner {
    -            override fun sign(canonicalJson: String): Map>? {
    -                return null
    -            }
    -        }
    -
             var recoveryKey: String? = null
     
             val TEST_KEY_ID = "my.test.Key"
    @@ -207,12 +205,6 @@ class QuadSTests : InstrumentedTest {
     
             val quadS = aliceSession.sharedSecretStorageService
     
    -        val emptyKeySigner = object : KeySigner {
    -            override fun sign(canonicalJson: String): Map>? {
    -                return null
    -            }
    -        }
    -
             val TEST_KEY_ID = "my.test.Key"
     
             val countDownLatch = CountDownLatch(1)
    @@ -354,12 +346,6 @@ class QuadSTests : InstrumentedTest {
         private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
             val quadS = session.sharedSecretStorageService
     
    -        val emptyKeySigner = object : KeySigner {
    -            override fun sign(canonicalJson: String): Map>? {
    -                return null
    -            }
    -        }
    -
             var creationInfo: SsssKeyCreationInfo? = null
     
             val generateLatch = CountDownLatch(1)
    @@ -395,12 +381,6 @@ class QuadSTests : InstrumentedTest {
         private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
             val quadS = session.sharedSecretStorageService
     
    -        val emptyKeySigner = object : KeySigner {
    -            override fun sign(canonicalJson: String): Map>? {
    -                return null
    -            }
    -        }
    -
             var creationInfo: SsssKeyCreationInfo? = null
     
             val generateLatch = CountDownLatch(1)
    
    From 7878da25b88a3757db52c6fa28b0cbb9bbeb26ad Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Fri, 14 Feb 2020 18:18:10 +0100
    Subject: [PATCH 032/187] Use doSync<>() to reduce boilerplate and add test
     checks
    
    and more cleanup
    ---
     .../internal/crypto/ssss/QuadSTests.kt        | 280 +++++++-----------
     1 file changed, 114 insertions(+), 166 deletions(-)
    
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    index 57c065aaf8..82594f2645 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    @@ -40,8 +40,10 @@ import kotlinx.coroutines.Dispatchers
     import kotlinx.coroutines.GlobalScope
     import kotlinx.coroutines.launch
     import kotlinx.coroutines.runBlocking
    -import org.junit.Assert
    -import org.junit.Assert.fail
    +import org.amshove.kluent.shouldBe
    +import org.junit.Assert.assertEquals
    +import org.junit.Assert.assertNotNull
    +import org.junit.Assert.assertNull
     import org.junit.FixMethodOrder
     import org.junit.Test
     import org.junit.runner.RunWith
    @@ -64,28 +66,13 @@ class QuadSTests : InstrumentedTest {
         fun test_Generate4SKey() {
             val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
     
    -        val aliceLatch = CountDownLatch(1)
    -
             val quadS = aliceSession.sharedSecretStorageService
     
    -        var recoveryKey: String? = null
    -
             val TEST_KEY_ID = "my.test.Key"
     
    -        quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner,
    -                object : MatrixCallback {
    -                    override fun onSuccess(data: SsssKeyCreationInfo) {
    -                        recoveryKey = data.recoveryKey
    -                        aliceLatch.countDown()
    -                    }
    -
    -                    override fun onFailure(failure: Throwable) {
    -                        Assert.fail("onFailure " + failure.localizedMessage)
    -                        aliceLatch.countDown()
    -                    }
    -                })
    -
    -        mTestHelper.await(aliceLatch)
    +        val ssssKeyCreationInfo = mTestHelper.doSync {
    +            quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner, it)
    +        }
     
             // Assert Account data is updated
             val accountDataLock = CountDownLatch(1)
    @@ -104,18 +91,18 @@ class QuadSTests : InstrumentedTest {
     
             mTestHelper.await(accountDataLock)
     
    -        Assert.assertNotNull("Key should be stored in account data", accountData)
    +        assertNotNull("Key should be stored in account data", accountData)
             val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
    -        Assert.assertNotNull("Key Content cannot be parsed", parsed)
    -        Assert.assertEquals("Unexpected Algorithm", SSSS_ALGORITHM_CURVE25519_AES_SHA2, parsed!!.algorithm)
    -        Assert.assertEquals("Unexpected key name", "Test Key", parsed.name)
    -        Assert.assertNull("Key was not generated from passphrase", parsed.passphrase)
    -        Assert.assertNotNull("Pubkey should be defined", parsed.publicKey)
    +        assertNotNull("Key Content cannot be parsed", parsed)
    +        assertEquals("Unexpected Algorithm", SSSS_ALGORITHM_CURVE25519_AES_SHA2, parsed!!.algorithm)
    +        assertEquals("Unexpected key name", "Test Key", parsed.name)
    +        assertNull("Key was not generated from passphrase", parsed.passphrase)
    +        assertNotNull("Pubkey should be defined", parsed.publicKey)
     
    -        val privateKeySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(recoveryKey!!)
    +        val privateKeySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(ssssKeyCreationInfo.recoveryKey)
             DefaultSharedSecretStorageService.withOlmDecryption { olmPkDecryption ->
                 val pubKey = olmPkDecryption.setPrivateKey(privateKeySpec!!.privateKey)
    -            Assert.assertEquals("Unexpected Public Key", pubKey, parsed.publicKey)
    +            assertEquals("Unexpected Public Key", pubKey, parsed.publicKey)
             }
     
             // Set as default key
    @@ -137,8 +124,8 @@ class QuadSTests : InstrumentedTest {
     
             mTestHelper.await(defaultDataLock)
     
    -        Assert.assertNotNull(defaultKeyAccountData?.content)
    -        Assert.assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key"))
    +        assertNotNull(defaultKeyAccountData?.content)
    +        assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key"))
     
             mTestHelper.signout(aliceSession)
         }
    @@ -150,52 +137,40 @@ class QuadSTests : InstrumentedTest {
             val info = generatedSecret(aliceSession, keyId, true)
     
             // Store a secret
    -
    -        val storeCountDownLatch = CountDownLatch(1)
             val clearSecret = Base64.encodeToString("42".toByteArray(), Base64.NO_PADDING or Base64.NO_WRAP)
    -        aliceSession.sharedSecretStorageService.storeSecret(
    -                "secret.of.life",
    -                clearSecret,
    -                null, // default key
    -                TestMatrixCallback(storeCountDownLatch)
    -        )
    +        mTestHelper.doSync {
    +            aliceSession.sharedSecretStorageService.storeSecret(
    +                    "secret.of.life",
    +                    clearSecret,
    +                    null, // default key
    +                    it
    +            )
    +        }
     
             val secretAccountData = assertAccountData(aliceSession, "secret.of.life")
     
             val encryptedContent = secretAccountData.content.get("encrypted") as? Map<*, *>
    -        Assert.assertNotNull("Element should be encrypted", encryptedContent)
    -        Assert.assertNotNull("Secret should be encrypted with default key", encryptedContent?.get(keyId))
    +        assertNotNull("Element should be encrypted", encryptedContent)
    +        assertNotNull("Secret should be encrypted with default key", encryptedContent?.get(keyId))
     
             val secret = EncryptedSecretContent.fromJson(encryptedContent?.get(keyId))
    -        Assert.assertNotNull(secret?.ciphertext)
    -        Assert.assertNotNull(secret?.mac)
    -        Assert.assertNotNull(secret?.ephemeral)
    +        assertNotNull(secret?.ciphertext)
    +        assertNotNull(secret?.mac)
    +        assertNotNull(secret?.ephemeral)
     
             // Try to decrypt??
     
             val keySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(info.recoveryKey)
     
    -        var decryptedSecret: String? = null
    +        val decryptedSecret = mTestHelper.doSync {
    +            aliceSession.sharedSecretStorageService.getSecret("secret.of.life",
    +                    null, // default key
    +                    keySpec!!,
    +                    it
    +            )
    +        }
     
    -        val decryptCountDownLatch = CountDownLatch(1)
    -        aliceSession.sharedSecretStorageService.getSecret("secret.of.life",
    -                null, // default key
    -                keySpec!!,
    -                object : MatrixCallback {
    -                    override fun onFailure(failure: Throwable) {
    -                        fail("Fail to decrypt -> " + failure.localizedMessage)
    -                        decryptCountDownLatch.countDown()
    -                    }
    -
    -                    override fun onSuccess(data: String) {
    -                        decryptedSecret = data
    -                        decryptCountDownLatch.countDown()
    -                    }
    -                }
    -        )
    -        mTestHelper.await(decryptCountDownLatch)
    -
    -        Assert.assertEquals("Secret mismatch", clearSecret, decryptedSecret)
    +        assertEquals("Secret mismatch", clearSecret, decryptedSecret)
             mTestHelper.signout(aliceSession)
         }
     
    @@ -207,16 +182,14 @@ class QuadSTests : InstrumentedTest {
     
             val TEST_KEY_ID = "my.test.Key"
     
    -        val countDownLatch = CountDownLatch(1)
    -        quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner,
    -                TestMatrixCallback(countDownLatch))
    -
    -        mTestHelper.await(countDownLatch)
    +        mTestHelper.doSync {
    +            quadS.generateKey(TEST_KEY_ID, "Test Key", emptyKeySigner, it)
    +        }
     
             // Test that we don't need to wait for an account data sync to access directly the keyid from DB
    -        val defaultLatch = CountDownLatch(1)
    -        quadS.setDefaultKey(TEST_KEY_ID, TestMatrixCallback(defaultLatch))
    -        mTestHelper.await(defaultLatch)
    +        mTestHelper.doSync {
    +            quadS.setDefaultKey(TEST_KEY_ID, it)
    +        }
     
             mTestHelper.signout(aliceSession)
         }
    @@ -231,38 +204,39 @@ class QuadSTests : InstrumentedTest {
     
             val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
     
    -        val storeLatch = CountDownLatch(1)
    -        aliceSession.sharedSecretStorageService.storeSecret(
    -                "my.secret",
    -                mySecretText.toByteArray().toBase64NoPadding(),
    -                listOf(keyId1, keyId2),
    -                TestMatrixCallback(storeLatch)
    -        )
    -        mTestHelper.await(storeLatch)
    +        mTestHelper.doSync {
    +            aliceSession.sharedSecretStorageService.storeSecret(
    +                    "my.secret",
    +                    mySecretText.toByteArray().toBase64NoPadding(),
    +                    listOf(keyId1, keyId2),
    +                    it
    +            )
    +        }
     
             val accountDataEvent = aliceSession.getAccountDataEvent("my.secret")
             val encryptedContent = accountDataEvent?.content?.get("encrypted") as? Map<*, *>
     
    -        Assert.assertEquals("Content should contains two encryptions", 2, encryptedContent?.keys?.size ?: 0)
    +        assertEquals("Content should contains two encryptions", 2, encryptedContent?.keys?.size ?: 0)
     
    -        Assert.assertNotNull(encryptedContent?.get(keyId1))
    -        Assert.assertNotNull(encryptedContent?.get(keyId2))
    +        assertNotNull(encryptedContent?.get(keyId1))
    +        assertNotNull(encryptedContent?.get(keyId2))
     
             // Assert that can decrypt with both keys
    -        val decryptCountDownLatch = CountDownLatch(2)
    -        aliceSession.sharedSecretStorageService.getSecret("my.secret",
    -                keyId1,
    -                Curve25519AesSha2KeySpec.fromRecoveryKey(key1Info.recoveryKey)!!,
    -                TestMatrixCallback(decryptCountDownLatch)
    -        )
    +        mTestHelper.doSync {
    +            aliceSession.sharedSecretStorageService.getSecret("my.secret",
    +                    keyId1,
    +                    Curve25519AesSha2KeySpec.fromRecoveryKey(key1Info.recoveryKey)!!,
    +                    it
    +            )
    +        }
     
    -        aliceSession.sharedSecretStorageService.getSecret("my.secret",
    -                keyId2,
    -                Curve25519AesSha2KeySpec.fromRecoveryKey(key2Info.recoveryKey)!!,
    -                TestMatrixCallback(decryptCountDownLatch)
    -        )
    -
    -        mTestHelper.await(decryptCountDownLatch)
    +        mTestHelper.doSync {
    +            aliceSession.sharedSecretStorageService.getSecret("my.secret",
    +                    keyId2,
    +                    Curve25519AesSha2KeySpec.fromRecoveryKey(key2Info.recoveryKey)!!,
    +                    it
    +            )
    +        }
     
             mTestHelper.signout(aliceSession)
         }
    @@ -276,16 +250,17 @@ class QuadSTests : InstrumentedTest {
     
             val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
     
    -        val storeLatch = CountDownLatch(1)
    -        aliceSession.sharedSecretStorageService.storeSecret(
    -                "my.secret",
    -                mySecretText.toByteArray().toBase64NoPadding(),
    -                listOf(keyId1),
    -                TestMatrixCallback(storeLatch)
    -        )
    -        mTestHelper.await(storeLatch)
    +        mTestHelper.doSync {
    +            aliceSession.sharedSecretStorageService.storeSecret(
    +                    "my.secret",
    +                    mySecretText.toByteArray().toBase64NoPadding(),
    +                    listOf(keyId1),
    +                    it
    +            )
    +        }
     
    -        val decryptCountDownLatch = CountDownLatch(2)
    +        val decryptCountDownLatch = CountDownLatch(1)
    +        var error = false
             aliceSession.sharedSecretStorageService.getSecret("my.secret",
                     keyId1,
                     Curve25519AesSha2KeySpec.fromPassphrase(
    @@ -296,29 +271,32 @@ class QuadSTests : InstrumentedTest {
                     object : MatrixCallback {
                         override fun onSuccess(data: String) {
                             decryptCountDownLatch.countDown()
    -                        fail("Should not be able to decrypt")
                         }
     
                         override fun onFailure(failure: Throwable) {
    -                        Assert.assertTrue(true)
    +                        error = true
                             decryptCountDownLatch.countDown()
                         }
                     }
             )
     
    -        // Now try with correct key
    -        aliceSession.sharedSecretStorageService.getSecret("my.secret",
    -                keyId1,
    -                Curve25519AesSha2KeySpec.fromPassphrase(
    -                        passphrase,
    -                        key1Info.content?.passphrase?.salt ?: "",
    -                        key1Info.content?.passphrase?.iterations ?: 0,
    -                        null),
    -                TestMatrixCallback(decryptCountDownLatch)
    -        )
    -
             mTestHelper.await(decryptCountDownLatch)
     
    +        error shouldBe true
    +
    +        // Now try with correct key
    +        mTestHelper.doSync {
    +            aliceSession.sharedSecretStorageService.getSecret("my.secret",
    +                    keyId1,
    +                    Curve25519AesSha2KeySpec.fromPassphrase(
    +                            passphrase,
    +                            key1Info.content?.passphrase?.salt ?: "",
    +                            key1Info.content?.passphrase?.iterations ?: 0,
    +                            null),
    +                    it
    +            )
    +        }
    +
             mTestHelper.signout(aliceSession)
         }
     
    @@ -338,7 +316,7 @@ class QuadSTests : InstrumentedTest {
             GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) }
             mTestHelper.await(accountDataLock)
     
    -        Assert.assertNotNull("Account Data type:$type should be found", accountData)
    +        assertNotNull("Account Data type:$type should be found", accountData)
     
             return accountData!!
         }
    @@ -346,64 +324,34 @@ class QuadSTests : InstrumentedTest {
         private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
             val quadS = session.sharedSecretStorageService
     
    -        var creationInfo: SsssKeyCreationInfo? = null
    -
    -        val generateLatch = CountDownLatch(1)
    -
    -        quadS.generateKey(keyId, keyId, emptyKeySigner,
    -                object : MatrixCallback {
    -                    override fun onSuccess(data: SsssKeyCreationInfo) {
    -                        creationInfo = data
    -                        generateLatch.countDown()
    -                    }
    -
    -                    override fun onFailure(failure: Throwable) {
    -                        Assert.fail("onFailure " + failure.localizedMessage)
    -                        generateLatch.countDown()
    -                    }
    -                })
    -
    -        mTestHelper.await(generateLatch)
    -
    -        Assert.assertNotNull(creationInfo)
    +        val creationInfo = mTestHelper.doSync {
    +            quadS.generateKey(keyId, keyId, emptyKeySigner, it)
    +        }
     
             assertAccountData(session, "m.secret_storage.key.$keyId")
    +
             if (asDefault) {
    -            val setDefaultLatch = CountDownLatch(1)
    -            quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
    -            mTestHelper.await(setDefaultLatch)
    +            mTestHelper.doSync {
    +                quadS.setDefaultKey(keyId, it)
    +            }
                 assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
             }
     
    -        return creationInfo!!
    +        return creationInfo
         }
     
         private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
             val quadS = session.sharedSecretStorageService
     
    -        var creationInfo: SsssKeyCreationInfo? = null
    -
    -        val generateLatch = CountDownLatch(1)
    -
    -        quadS.generateKeyWithPassphrase(keyId, keyId,
    -                passphrase,
    -                emptyKeySigner,
    -                null,
    -                object : MatrixCallback {
    -                    override fun onSuccess(data: SsssKeyCreationInfo) {
    -                        creationInfo = data
    -                        generateLatch.countDown()
    -                    }
    -
    -                    override fun onFailure(failure: Throwable) {
    -                        Assert.fail("onFailure " + failure.localizedMessage)
    -                        generateLatch.countDown()
    -                    }
    -                })
    -
    -        mTestHelper.await(generateLatch)
    -
    -        Assert.assertNotNull(creationInfo)
    +        val creationInfo = mTestHelper.doSync {
    +            quadS.generateKeyWithPassphrase(
    +                    keyId,
    +                    keyId,
    +                    passphrase,
    +                    emptyKeySigner,
    +                    null,
    +                    it)
    +        }
     
             assertAccountData(session, "m.secret_storage.key.$keyId")
             if (asDefault) {
    @@ -413,6 +361,6 @@ class QuadSTests : InstrumentedTest {
                 assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
             }
     
    -        return creationInfo!!
    +        return creationInfo
         }
     }
    
    From 4c3b754de499955c667962fc8549314ea986235a Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Fri, 14 Feb 2020 18:33:15 +0100
    Subject: [PATCH 033/187] Use const from DefaultSharedSecretStorageService
    
    ---
     .idea/dictionaries/bmarty.xml                             | 1 +
     .../matrix/android/internal/crypto/ssss/QuadSTests.kt     | 8 ++++----
     2 files changed, 5 insertions(+), 4 deletions(-)
    
    diff --git a/.idea/dictionaries/bmarty.xml b/.idea/dictionaries/bmarty.xml
    index 680a1d57cf..f351ec8bfd 100644
    --- a/.idea/dictionaries/bmarty.xml
    +++ b/.idea/dictionaries/bmarty.xml
    @@ -22,6 +22,7 @@
           signin
           signout
           signup
    +      ssss
           threepid
         
       
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    index 82594f2645..33d4e9dbd2 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    @@ -79,10 +79,10 @@ class QuadSTests : InstrumentedTest {
             var accountData: UserAccountDataEvent? = null
     
             val liveAccountData = runBlocking(Dispatchers.Main) {
    -            aliceSession.getLiveAccountDataEvent("m.secret_storage.key.$TEST_KEY_ID")
    +            aliceSession.getLiveAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID")
             }
             val accountDataObserver = Observer?> { t ->
    -            if (t?.getOrNull()?.type == "m.secret_storage.key.$TEST_KEY_ID") {
    +            if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") {
                     accountData = t.getOrNull()
                     accountDataLock.countDown()
                 }
    @@ -328,7 +328,7 @@ class QuadSTests : InstrumentedTest {
                 quadS.generateKey(keyId, keyId, emptyKeySigner, it)
             }
     
    -        assertAccountData(session, "m.secret_storage.key.$keyId")
    +        assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
     
             if (asDefault) {
                 mTestHelper.doSync {
    @@ -353,7 +353,7 @@ class QuadSTests : InstrumentedTest {
                         it)
             }
     
    -        assertAccountData(session, "m.secret_storage.key.$keyId")
    +        assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
             if (asDefault) {
                 val setDefaultLatch = CountDownLatch(1)
                 quadS.setDefaultKey(keyId, TestMatrixCallback(setDefaultLatch))
    
    From 7ddea99fc6f8246c7004b409180266f733c5c3fd Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Fri, 14 Feb 2020 18:51:27 +0100
    Subject: [PATCH 034/187] Move and improve withOlmDecryption() and
     withOlmEncryption()
    
    ---
     .../internal/crypto/ssss/QuadSTests.kt        |  7 +--
     .../DefaultSharedSecretStorageService.kt      | 54 ++++++-------------
     .../android/internal/crypto/tools/Tools.kt    | 38 +++++++++++++
     3 files changed, 58 insertions(+), 41 deletions(-)
     create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tools/Tools.kt
    
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    index 33d4e9dbd2..7802096338 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt
    @@ -35,6 +35,7 @@ import im.vector.matrix.android.common.TestMatrixCallback
     import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_CURVE25519_AES_SHA2
     import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
     import im.vector.matrix.android.internal.crypto.secrets.DefaultSharedSecretStorageService
    +import im.vector.matrix.android.internal.crypto.tools.withOlmDecryption
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
     import kotlinx.coroutines.Dispatchers
     import kotlinx.coroutines.GlobalScope
    @@ -100,10 +101,10 @@ class QuadSTests : InstrumentedTest {
             assertNotNull("Pubkey should be defined", parsed.publicKey)
     
             val privateKeySpec = Curve25519AesSha2KeySpec.fromRecoveryKey(ssssKeyCreationInfo.recoveryKey)
    -        DefaultSharedSecretStorageService.withOlmDecryption { olmPkDecryption ->
    -            val pubKey = olmPkDecryption.setPrivateKey(privateKeySpec!!.privateKey)
    -            assertEquals("Unexpected Public Key", pubKey, parsed.publicKey)
    +        val pubKey = withOlmDecryption { olmPkDecryption ->
    +            olmPkDecryption.setPrivateKey(privateKeySpec!!.privateKey)
             }
    +        assertEquals("Unexpected Public Key", pubKey, parsed.publicKey)
     
             // Set as default key
             quadS.setDefaultKey(TEST_KEY_ID, object : MatrixCallback {})
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
    index f741021e6c..37b29047fc 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
    @@ -25,21 +25,21 @@ import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent
     import im.vector.matrix.android.api.session.securestorage.KeyInfo
     import im.vector.matrix.android.api.session.securestorage.KeyInfoResult
     import im.vector.matrix.android.api.session.securestorage.KeySigner
    -import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
     import im.vector.matrix.android.api.session.securestorage.SSSSKeySpec
     import im.vector.matrix.android.api.session.securestorage.SSSSPassphrase
     import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent
     import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageError
     import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
    +import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
     import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_CURVE25519_AES_SHA2
     import im.vector.matrix.android.internal.crypto.keysbackup.generatePrivateKeyWithPassword
     import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey
    +import im.vector.matrix.android.internal.crypto.tools.withOlmEncryption
     import im.vector.matrix.android.internal.extensions.foldToCallback
     import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
     import kotlinx.coroutines.CoroutineScope
     import kotlinx.coroutines.launch
     import org.matrix.olm.OlmPkDecryption
    -import org.matrix.olm.OlmPkEncryption
     import org.matrix.olm.OlmPkMessage
     import javax.inject.Inject
     
    @@ -198,15 +198,15 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
                         when (key) {
                             is KeyInfoResult.Success -> {
                                 if (key.keyInfo.content.algorithm == SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
    -                                withOlmEncryption { olmEncrypt ->
    +                                val encryptedResult = withOlmEncryption { olmEncrypt ->
                                         olmEncrypt.setRecipientKey(key.keyInfo.content.publicKey)
    -                                    val encryptedResult = olmEncrypt.encrypt(secretBase64)
    -                                    encryptedContents[key.keyInfo.id] = EncryptedSecretContent(
    -                                            ciphertext = encryptedResult.mCipherText,
    -                                            ephemeral = encryptedResult.mEphemeralKey,
    -                                            mac = encryptedResult.mMac
    -                                    )
    +                                    olmEncrypt.encrypt(secretBase64)
                                     }
    +                                encryptedContents[key.keyInfo.id] = EncryptedSecretContent(
    +                                        ciphertext = encryptedResult.mCipherText,
    +                                        ephemeral = encryptedResult.mEphemeralKey,
    +                                        mac = encryptedResult.mMac
    +                                )
                                 } else {
                                     // Unknown algorithm
                                     callback.onFailure(SharedSecretStorageError.UnknownAlgorithm(key.keyInfo.content.algorithm ?: ""))
    @@ -226,15 +226,15 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
                             when (key) {
                                 is KeyInfoResult.Success -> {
                                     if (key.keyInfo.content.algorithm == SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
    -                                    withOlmEncryption { olmEncrypt ->
    +                                    val encryptedResult = withOlmEncryption { olmEncrypt ->
                                             olmEncrypt.setRecipientKey(key.keyInfo.content.publicKey)
    -                                        val encryptedResult = olmEncrypt.encrypt(secretBase64)
    -                                        encryptedContents[keyId] = EncryptedSecretContent(
    -                                                ciphertext = encryptedResult.mCipherText,
    -                                                ephemeral = encryptedResult.mEphemeralKey,
    -                                                mac = encryptedResult.mMac
    -                                        )
    +                                        olmEncrypt.encrypt(secretBase64)
                                         }
    +                                    encryptedContents[keyId] = EncryptedSecretContent(
    +                                            ciphertext = encryptedResult.mCipherText,
    +                                            ephemeral = encryptedResult.mEphemeralKey,
    +                                            mac = encryptedResult.mMac
    +                                    )
                                     } else {
                                         // Unknown algorithm
                                         callback.onFailure(SharedSecretStorageError.UnknownAlgorithm(key.keyInfo.content.algorithm ?: ""))
    @@ -332,27 +332,5 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
             const val KEY_ID_BASE = "m.secret_storage.key"
             const val ENCRYPTED = "encrypted"
             const val DEFAULT_KEY_ID = "m.secret_storage.default_key"
    -
    -        fun withOlmEncryption(block: (OlmPkEncryption) -> Unit) {
    -            val olmPkEncryption = OlmPkEncryption()
    -            try {
    -                block(olmPkEncryption)
    -            } catch (failure: Throwable) {
    -                throw failure
    -            } finally {
    -                olmPkEncryption.releaseEncryption()
    -            }
    -        }
    -
    -        fun withOlmDecryption(block: (OlmPkDecryption) -> Unit) {
    -            val olmPkDecryption = OlmPkDecryption()
    -            try {
    -                block(olmPkDecryption)
    -            } catch (failure: Throwable) {
    -                throw failure
    -            } finally {
    -                olmPkDecryption.releaseDecryption()
    -            }
    -        }
         }
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tools/Tools.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tools/Tools.kt
    new file mode 100644
    index 0000000000..260e6165ba
    --- /dev/null
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tools/Tools.kt
    @@ -0,0 +1,38 @@
    +/*
    + * 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 im.vector.matrix.android.internal.crypto.tools
    +
    +import org.matrix.olm.OlmPkDecryption
    +import org.matrix.olm.OlmPkEncryption
    +
    +fun  withOlmEncryption(block: (OlmPkEncryption) -> T): T {
    +    val olmPkEncryption = OlmPkEncryption()
    +    try {
    +        return block(olmPkEncryption)
    +    } finally {
    +        olmPkEncryption.releaseEncryption()
    +    }
    +}
    +
    +fun  withOlmDecryption(block: (OlmPkDecryption) -> T): T {
    +    val olmPkDecryption = OlmPkDecryption()
    +    try {
    +        return block(olmPkDecryption)
    +    } finally {
    +        olmPkDecryption.releaseDecryption()
    +    }
    +}
    
    From a305ce302eb9cb5387b09573e79821db6e13ce5e Mon Sep 17 00:00:00 2001
    From: Ganard 
    Date: Tue, 11 Feb 2020 12:08:47 +0100
    Subject: [PATCH 035/187] Coroutine sequencer: use semaphore
    
    ---
     .../session/sync/SyncTaskSequencer.kt         |  4 +-
     .../internal/task/CoroutineSequencer.kt       | 63 +++----------------
     .../internal/task/CoroutineSequencersTest.kt  | 10 +--
     3 files changed, 16 insertions(+), 61 deletions(-)
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTaskSequencer.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTaskSequencer.kt
    index bfa49b7af5..d2d393de00 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTaskSequencer.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTaskSequencer.kt
    @@ -17,8 +17,8 @@
     package im.vector.matrix.android.internal.session.sync
     
     import im.vector.matrix.android.internal.session.SessionScope
    -import im.vector.matrix.android.internal.task.ChannelCoroutineSequencer
    +import im.vector.matrix.android.internal.task.SemaphoreCoroutineSequencer
     import javax.inject.Inject
     
     @SessionScope
    -internal class SyncTaskSequencer @Inject constructor() : ChannelCoroutineSequencer()
    +internal class SyncTaskSequencer @Inject constructor() : SemaphoreCoroutineSequencer()
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/CoroutineSequencer.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/CoroutineSequencer.kt
    index 7062c63816..d36b769ef3 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/CoroutineSequencer.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/CoroutineSequencer.kt
    @@ -16,72 +16,27 @@
     
     package im.vector.matrix.android.internal.task
     
    -import kotlinx.coroutines.*
    -import kotlinx.coroutines.channels.Channel
    -import java.util.concurrent.Executors
    +import kotlinx.coroutines.sync.Semaphore
    +import kotlinx.coroutines.sync.withPermit
     
     /**
      * This class intends to be used for ensure suspendable methods are played sequentially all the way long.
      */
    -internal interface CoroutineSequencer {
    +internal interface CoroutineSequencer {
         /**
          * @param block the suspendable block to execute
          * @return the result of the block
          */
    -    suspend fun post(block: suspend () -> T): T
    -
    -    /**
    -     * Cancel all and close, so you won't be able to post anything else after
    -     */
    -    fun close()
    +    suspend fun  post(block: suspend () -> T): T
     }
     
    -internal open class ChannelCoroutineSequencer : CoroutineSequencer {
    +internal open class SemaphoreCoroutineSequencer : CoroutineSequencer {
     
    -    private data class Message(
    -            val block: suspend () -> T,
    -            val deferred: CompletableDeferred
    -    )
    +    private val semaphore = Semaphore(1) // Permits 1 suspend function at a time.
     
    -    private var messageChannel: Channel> = Channel()
    -    private val coroutineScope = CoroutineScope(SupervisorJob())
    -    // This will ensure
    -    private val singleDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
    -
    -    init {
    -        launchCoroutine()
    -    }
    -
    -    private fun launchCoroutine() {
    -        coroutineScope.launch(singleDispatcher) {
    -            for (message in messageChannel) {
    -                try {
    -                    val result = message.block()
    -                    message.deferred.complete(result)
    -                } catch (exception: Throwable) {
    -                    message.deferred.completeExceptionally(exception)
    -                }
    -            }
    -        }
    -    }
    -
    -    override fun close() {
    -        coroutineScope.coroutineContext.cancelChildren()
    -        messageChannel.close()
    -    }
    -
    -    override suspend fun post(block: suspend () -> T): T {
    -        val deferred = CompletableDeferred()
    -        val message = Message(block, deferred)
    -        messageChannel.send(message)
    -        return try {
    -            deferred.await()
    -        } catch (cancellation: CancellationException) {
    -            // In case of cancellation, we stop the current coroutine context
    -            // and relaunch one to consume next messages
    -            coroutineScope.coroutineContext.cancelChildren()
    -            launchCoroutine()
    -            throw cancellation
    +    override suspend fun  post(block: suspend () -> T): T {
    +        return semaphore.withPermit {
    +            block()
             }
         }
     }
    diff --git a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/task/CoroutineSequencersTest.kt b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/task/CoroutineSequencersTest.kt
    index 9591feaa32..a70713c44b 100644
    --- a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/task/CoroutineSequencersTest.kt
    +++ b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/task/CoroutineSequencersTest.kt
    @@ -32,7 +32,7 @@ class CoroutineSequencersTest {
     
         @Test
         fun sequencer_should_run_sequential() {
    -        val sequencer = ChannelCoroutineSequencer()
    +        val sequencer = SemaphoreCoroutineSequencer()
             val results = ArrayList()
     
             val jobs = listOf(
    @@ -63,9 +63,9 @@ class CoroutineSequencersTest {
     
         @Test
         fun sequencer_should_run_parallel() {
    -        val sequencer1 = ChannelCoroutineSequencer()
    -        val sequencer2 = ChannelCoroutineSequencer()
    -        val sequencer3 = ChannelCoroutineSequencer()
    +        val sequencer1 = SemaphoreCoroutineSequencer()
    +        val sequencer2 = SemaphoreCoroutineSequencer()
    +        val sequencer3 = SemaphoreCoroutineSequencer()
             val results = ArrayList()
             val jobs = listOf(
                     GlobalScope.launch(dispatcher) {
    @@ -92,7 +92,7 @@ class CoroutineSequencersTest {
     
         @Test
         fun sequencer_should_jump_to_next_when_current_job_canceled() {
    -        val sequencer = ChannelCoroutineSequencer()
    +        val sequencer = SemaphoreCoroutineSequencer()
             val results = ArrayList()
             val jobs = listOf(
                     GlobalScope.launch(dispatcher) {
    
    From 2d6f57e21456020549f770ff7cb845f73221bf9c Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Fri, 14 Feb 2020 20:50:21 +0100
    Subject: [PATCH 036/187] More cleanup/code lisibility
    
    ---
     .../securestorage/SecretStorageKeyContent.kt  | 33 +++----
     .../securestorage/SharedSecretStorageError.kt |  1 -
     .../SharedSecretStorageService.kt             |  2 +-
     .../api/session/securestorage/SsssKeySpec.kt  |  4 +-
     .../DefaultSharedSecretStorageService.kt      | 86 +++++++++----------
     5 files changed, 61 insertions(+), 65 deletions(-)
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt
    index 02c3e96658..f803011415 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt
    @@ -54,25 +54,28 @@ data class SecretStorageKeyContent(
             /** Currently support m.secret_storage.v1.curve25519-aes-sha2 */
             @Json(name = "algorithm") val algorithm: String? = null,
             @Json(name = "name") val name: String? = null,
    -        @Json(name = "passphrase") val passphrase: SSSSPassphrase? = null,
    +        @Json(name = "passphrase") val passphrase: SsssPassphrase? = null,
             @Json(name = "pubkey") val publicKey: String? = null,
    -        @Json(name = "signatures")
    -        var signatures: Map>? = null
    +        @Json(name = "signatures") val signatures: Map>? = null
     ) {
     
         private fun signalableJSONDictionary(): Map {
    -        val map = HashMap()
    -        algorithm?.let { map["algorithm"] = it }
    -        name?.let { map["name"] = it }
    -        publicKey?.let { map["pubkey"] = it }
    -        passphrase?.let { ssspp ->
    -            map["passphrase"] = mapOf(
    -                    "algorithm" to ssspp.algorithm,
    -                    "iterations" to ssspp.salt,
    -                    "salt" to ssspp.salt
    -            )
    +        return mutableMapOf().apply {
    +            algorithm
    +                    ?.let { this["algorithm"] = it }
    +            name
    +                    ?.let { this["name"] = it }
    +            publicKey
    +                    ?.let { this["pubkey"] = it }
    +            passphrase
    +                    ?.let { ssssPassphrase ->
    +                        this["passphrase"] = mapOf(
    +                                "algorithm" to ssssPassphrase.algorithm,
    +                                "iterations" to ssssPassphrase.salt,
    +                                "salt" to ssssPassphrase.salt
    +                        )
    +                    }
             }
    -        return map
         }
     
         fun canonicalSignable(): String {
    @@ -93,7 +96,7 @@ data class SecretStorageKeyContent(
     }
     
     @JsonClass(generateAdapter = true)
    -data class SSSSPassphrase(
    +data class SsssPassphrase(
             @Json(name = "algorithm") val algorithm: String?,
             @Json(name = "iterations") val iterations: Int,
             @Json(name = "salt") val salt: String?
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt
    index f882375e5c..e1a216ab37 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageError.kt
    @@ -17,7 +17,6 @@
     package im.vector.matrix.android.api.session.securestorage
     
     sealed class SharedSecretStorageError(message: String?) : Throwable(message) {
    -
         data class UnknownSecret(val secretName: String) : SharedSecretStorageError("Unknown Secret $secretName")
         data class UnknownKey(val keyId: String) : SharedSecretStorageError("Unknown key $keyId")
         data class UnknownAlgorithm(val keyId: String) : SharedSecretStorageError("Unknown algorithm $keyId")
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
    index 02ccc11026..35579f756c 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
    @@ -108,5 +108,5 @@ interface SharedSecretStorageService {
          *
          */
         @Throws
    -    fun getSecret(name: String, keyId: String?, secretKey: SSSSKeySpec, callback: MatrixCallback)
    +    fun getSecret(name: String, keyId: String?, secretKey: SsssKeySpec, callback: MatrixCallback)
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeySpec.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeySpec.kt
    index 9e61f7f8ff..90dcb92449 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeySpec.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SsssKeySpec.kt
    @@ -21,11 +21,11 @@ import im.vector.matrix.android.internal.crypto.keysbackup.deriveKey
     import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
     
     /** Tag class */
    -interface SSSSKeySpec
    +interface SsssKeySpec
     
     data class Curve25519AesSha2KeySpec(
             val privateKey: ByteArray
    -) : SSSSKeySpec {
    +) : SsssKeySpec {
     
         companion object {
     
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
    index 37b29047fc..4bc68f86c2 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
    @@ -25,8 +25,8 @@ import im.vector.matrix.android.api.session.securestorage.EncryptedSecretContent
     import im.vector.matrix.android.api.session.securestorage.KeyInfo
     import im.vector.matrix.android.api.session.securestorage.KeyInfoResult
     import im.vector.matrix.android.api.session.securestorage.KeySigner
    -import im.vector.matrix.android.api.session.securestorage.SSSSKeySpec
    -import im.vector.matrix.android.api.session.securestorage.SSSSPassphrase
    +import im.vector.matrix.android.api.session.securestorage.SsssKeySpec
    +import im.vector.matrix.android.api.session.securestorage.SsssPassphrase
     import im.vector.matrix.android.api.session.securestorage.SecretStorageKeyContent
     import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageError
     import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageService
    @@ -34,15 +34,21 @@ import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
     import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_CURVE25519_AES_SHA2
     import im.vector.matrix.android.internal.crypto.keysbackup.generatePrivateKeyWithPassword
     import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey
    +import im.vector.matrix.android.internal.crypto.tools.withOlmDecryption
     import im.vector.matrix.android.internal.crypto.tools.withOlmEncryption
     import im.vector.matrix.android.internal.extensions.foldToCallback
     import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
     import kotlinx.coroutines.CoroutineScope
     import kotlinx.coroutines.launch
    -import org.matrix.olm.OlmPkDecryption
     import org.matrix.olm.OlmPkMessage
     import javax.inject.Inject
     
    +private data class Key(
    +        val publicKey: String,
    +        @Suppress("ArrayInDataClass")
    +        val privateKey: ByteArray
    +)
    +
     internal class DefaultSharedSecretStorageService @Inject constructor(
             private val accountDataService: AccountDataService,
             private val coroutineDispatchers: MatrixCoroutineDispatchers,
    @@ -54,25 +60,22 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
                                  keySigner: KeySigner,
                                  callback: MatrixCallback) {
             cryptoCoroutineScope.launch(coroutineDispatchers.main) {
    -            val pkDecryption = OlmPkDecryption()
    -            val pubKey: String
    -            val privateKey: ByteArray
    -            try {
    -                pubKey = pkDecryption.generateKey()
    -                privateKey = pkDecryption.privateKey()
    -            } catch (failure: Throwable) {
    -                return@launch Unit.also {
    -                    callback.onFailure(failure)
    +            val key = try {
    +                withOlmDecryption { olmPkDecryption ->
    +                    val pubKey = olmPkDecryption.generateKey()
    +                    val privateKey = olmPkDecryption.privateKey()
    +                    Key(pubKey, privateKey)
                     }
    -            } finally {
    -                pkDecryption.releaseDecryption()
    +            } catch (failure: Throwable) {
    +                callback.onFailure(failure)
    +                return@launch
                 }
     
                 val storageKeyContent = SecretStorageKeyContent(
                         name = keyName,
                         algorithm = SSSS_ALGORITHM_CURVE25519_AES_SHA2,
                         passphrase = null,
    -                    publicKey = pubKey
    +                    publicKey = key.publicKey
                 )
     
                 val signedContent = keySigner.sign(storageKeyContent.canonicalSignable())?.let {
    @@ -93,7 +96,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
                                 callback.onSuccess(SsssKeyCreationInfo(
                                         keyId = keyId,
                                         content = storageKeyContent,
    -                                    recoveryKey = computeRecoveryKey(privateKey)
    +                                    recoveryKey = computeRecoveryKey(key.privateKey)
                                 ))
                             }
                         }
    @@ -110,21 +113,18 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
             cryptoCoroutineScope.launch(coroutineDispatchers.main) {
                 val privatePart = generatePrivateKeyWithPassword(passphrase, progressListener)
     
    -            val pkDecryption = OlmPkDecryption()
    -            val pubKey: String
    -            try {
    -                pubKey = pkDecryption.setPrivateKey(privatePart.privateKey)
    -            } catch (failure: Throwable) {
    -                return@launch Unit.also {
    -                    callback.onFailure(failure)
    +            val pubKey = try {
    +                withOlmDecryption { olmPkDecryption ->
    +                    olmPkDecryption.setPrivateKey(privatePart.privateKey)
                     }
    -            } finally {
    -                pkDecryption.releaseDecryption()
    +            } catch (failure: Throwable) {
    +                callback.onFailure(failure)
    +                return@launch
                 }
     
                 val storageKeyContent = SecretStorageKeyContent(
                         algorithm = SSSS_ALGORITHM_CURVE25519_AES_SHA2,
    -                    passphrase = SSSSPassphrase(algorithm = "m.pbkdf2", iterations = privatePart.iterations, salt = privatePart.salt),
    +                    passphrase = SsssPassphrase(algorithm = "m.pbkdf2", iterations = privatePart.iterations, salt = privatePart.salt),
                         publicKey = pubKey
                 )
     
    @@ -192,10 +192,9 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
             cryptoCoroutineScope.launch(coroutineDispatchers.main) {
                 val encryptedContents = HashMap()
                 try {
    -                if (keys == null || keys.isEmpty()) {
    +                if (keys.isNullOrEmpty()) {
                         // use default key
    -                    val key = getDefaultKey()
    -                    when (key) {
    +                    when (val key = getDefaultKey()) {
                             is KeyInfoResult.Success -> {
                                 if (key.keyInfo.content.algorithm == SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
                                     val encryptedResult = withOlmEncryption { olmEncrypt ->
    @@ -222,8 +221,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
                         keys.forEach {
                             val keyId = it
                             // encrypt the content
    -                        val key = getKey(keyId)
    -                        when (key) {
    +                        when (val key = getKey(keyId)) {
                                 is KeyInfoResult.Success -> {
                                     if (key.keyInfo.content.algorithm == SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
                                         val encryptedResult = withOlmEncryption { olmEncrypt ->
    @@ -279,7 +277,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
             return results
         }
     
    -    override fun getSecret(name: String, keyId: String?, secretKey: SSSSKeySpec, callback: MatrixCallback) {
    +    override fun getSecret(name: String, keyId: String?, secretKey: SsssKeySpec, callback: MatrixCallback) {
             val accountData = accountDataService.getAccountDataEvent(name) ?: return Unit.also {
                 callback.onFailure(SharedSecretStorageError.UnknownSecret(name))
             }
    @@ -306,20 +304,16 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
                 }
                 cryptoCoroutineScope.launch(coroutineDispatchers.main) {
                     kotlin.runCatching {
    -                    // decryt from recovery key
    -                    val keyBytes = keySpec.privateKey
    -                    val decryption = OlmPkDecryption()
    -                    try {
    -                        decryption.setPrivateKey(keyBytes)
    -                        decryption.decrypt(OlmPkMessage().apply {
    -                            mCipherText = secretContent.ciphertext
    -                            mEphemeralKey = secretContent.ephemeral
    -                            mMac = secretContent.mac
    -                        })
    -                    } catch (failure: Throwable) {
    -                        throw failure
    -                    } finally {
    -                        decryption.releaseDecryption()
    +                    // decrypt from recovery key
    +                    withOlmDecryption { olmPkDecryption ->
    +                        olmPkDecryption.setPrivateKey(keySpec.privateKey)
    +                        olmPkDecryption.decrypt(OlmPkMessage()
    +                                .apply {
    +                                    mCipherText = secretContent.ciphertext
    +                                    mEphemeralKey = secretContent.ephemeral
    +                                    mMac = secretContent.mac
    +                                }
    +                        )
                         }
                     }.foldToCallback(callback)
                 }
    
    From 446d826dd3643b08d70ff5be8557a50d20f2ca90 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Fri, 14 Feb 2020 20:53:45 +0100
    Subject: [PATCH 037/187] Create tag interface AccountDataContent
    
    ---
     .../securestorage/EncryptedSecretContent.kt   |  3 ++-
     .../sync/model/accountdata/UserAccountData.kt |  3 ++-
     .../user/accountdata/AccountDataContent.kt    | 22 +++++++++++++++++++
     3 files changed, 26 insertions(+), 2 deletions(-)
     create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataContent.kt
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/EncryptedSecretContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/EncryptedSecretContent.kt
    index 4c8b51c668..f88b39fd13 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/EncryptedSecretContent.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/EncryptedSecretContent.kt
    @@ -19,6 +19,7 @@ package im.vector.matrix.android.api.session.securestorage
     import com.squareup.moshi.Json
     import com.squareup.moshi.JsonClass
     import im.vector.matrix.android.internal.di.MoshiProvider
    +import im.vector.matrix.android.internal.session.user.accountdata.AccountDataContent
     
     /**
      * The account_data will have an encrypted property that is a map from key ID to an object.
    @@ -32,7 +33,7 @@ data class EncryptedSecretContent(
             @Json(name = "ciphertext") val ciphertext: String? = null,
             @Json(name = "mac") val mac: String? = null,
             @Json(name = "ephemeral") val ephemeral: String? = null
    -) {
    +) : AccountDataContent {
         companion object {
             /**
              * Facility method to convert from object which must be comprised of maps, lists,
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountData.kt
    index 3ec6c3c7eb..c508413665 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountData.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountData.kt
    @@ -17,8 +17,9 @@
     package im.vector.matrix.android.internal.session.sync.model.accountdata
     
     import com.squareup.moshi.Json
    +import im.vector.matrix.android.internal.session.user.accountdata.AccountDataContent
     
    -abstract class UserAccountData {
    +abstract class UserAccountData : AccountDataContent {
     
         @Json(name = "type") abstract val type: String
     
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataContent.kt
    new file mode 100644
    index 0000000000..5335f8a7f1
    --- /dev/null
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataContent.kt
    @@ -0,0 +1,22 @@
    +/*
    + * 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 im.vector.matrix.android.internal.session.user.accountdata
    +
    +/**
    + * Tag class to identify every account data content
    + */
    +internal interface AccountDataContent
    
    From 76085a42845840865cd97db794b53bc2a611673a Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Fri, 14 Feb 2020 21:26:16 +0100
    Subject: [PATCH 038/187] AccountData cleanup and Javadoc
    
    ---
     .../java/im/vector/matrix/rx/RxSession.kt     |  6 ++--
     .../session/accountdata/AccountDataService.kt | 25 ++++++++++++++---
     .../android/internal/session/SessionModule.kt |  2 +-
     .../accountdata/DefaultAccountDataService.kt  | 28 +++++++++----------
     .../settings/devtools/AccountDataViewModel.kt |  2 +-
     5 files changed, 40 insertions(+), 23 deletions(-)
    
    diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
    index 0417504cb7..960c00bb71 100644
    --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
    +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
    @@ -123,10 +123,10 @@ class RxSession(private val session: Session) {
                     }
         }
     
    -    fun liveAccountData(filter: List): Observable> {
    -        return session.getLiveAccountDataEvents(filter).asObservable()
    +    fun liveAccountData(types: Set): Observable> {
    +        return session.getLiveAccountDataEvents(types).asObservable()
                     .startWithCallable {
    -                    session.getAccountDataEvents(filter)
    +                    session.getAccountDataEvents(types)
                     }
         }
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
    index 7af7fea214..ee13d1f097 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/accountdata/AccountDataService.kt
    @@ -19,18 +19,35 @@ package im.vector.matrix.android.api.session.accountdata
     import androidx.lifecycle.LiveData
     import im.vector.matrix.android.api.MatrixCallback
     import im.vector.matrix.android.api.session.events.model.Content
    +import im.vector.matrix.android.api.util.Cancelable
     import im.vector.matrix.android.api.util.Optional
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
     
     interface AccountDataService {
    -
    +    /**
    +     * Retrieve the account data with the provided type or null if not found
    +     */
         fun getAccountDataEvent(type: String): UserAccountDataEvent?
     
    +    /**
    +     * Observe the account data with the provided type
    +     */
         fun getLiveAccountDataEvent(type: String): LiveData>
     
    -    fun getAccountDataEvents(filterType: List): List
    +    /**
    +     * Retrieve the account data with the provided types. The return list can have a different size that
    +     * the size of the types set, because some AccountData may not exist.
    +     * If an empty set is provided, all the AccountData are retrieved
    +     */
    +    fun getAccountDataEvents(types: Set): List
     
    -    fun getLiveAccountDataEvents(filterType: List): LiveData>
    +    /**
    +     * Observe the account data with the provided types. If an empty set is provided, all the AccountData are observed
    +     */
    +    fun getLiveAccountDataEvents(types: Set): LiveData>
     
    -    fun updateAccountData(type: String, content: Content, callback: MatrixCallback? = null)
    +    /**
    +     * Update the account data with the provided type and the provided account data content
    +     */
    +    fun updateAccountData(type: String, content: Content, callback: MatrixCallback? = null): Cancelable
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
    index 7352b79073..908c610914 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt
    @@ -269,7 +269,7 @@ internal abstract class SessionModule {
         abstract fun bindHomeServerCapabilitiesService(homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService): HomeServerCapabilitiesService
     
         @Binds
    -    abstract fun bindAccountDataService(accountDataService: DefaultAccountDataService): AccountDataService
    +    abstract fun bindAccountDataService(service: DefaultAccountDataService): AccountDataService
     
         @Binds
         abstract fun bindSharedSecretStorageService(service: DefaultSharedSecretStorageService): SharedSecretStorageService
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
    index b40c75992a..7756b22510 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DefaultAccountDataService.kt
    @@ -22,13 +22,13 @@ import com.zhuinden.monarchy.Monarchy
     import im.vector.matrix.android.api.MatrixCallback
     import im.vector.matrix.android.api.session.accountdata.AccountDataService
     import im.vector.matrix.android.api.session.events.model.Content
    +import im.vector.matrix.android.api.util.Cancelable
     import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE
     import im.vector.matrix.android.api.util.Optional
     import im.vector.matrix.android.api.util.toOptional
     import im.vector.matrix.android.internal.database.model.UserAccountDataEntity
     import im.vector.matrix.android.internal.database.model.UserAccountDataEntityFields
     import im.vector.matrix.android.internal.di.MoshiProvider
    -import im.vector.matrix.android.internal.di.SessionId
     import im.vector.matrix.android.internal.session.sync.UserAccountDataSyncHandler
     import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
     import im.vector.matrix.android.internal.task.TaskExecutor
    @@ -37,7 +37,6 @@ import javax.inject.Inject
     
     internal class DefaultAccountDataService @Inject constructor(
             private val monarchy: Monarchy,
    -        @SessionId private val sessionId: String,
             private val updateUserAccountDataTask: UpdateUserAccountDataTask,
             private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
             private val taskExecutor: TaskExecutor
    @@ -47,39 +46,39 @@ internal class DefaultAccountDataService @Inject constructor(
         private val adapter = moshi.adapter>(JSON_DICT_PARAMETERIZED_TYPE)
     
         override fun getAccountDataEvent(type: String): UserAccountDataEvent? {
    -        return getAccountDataEvents(listOf(type)).firstOrNull()
    +        return getAccountDataEvents(setOf(type)).firstOrNull()
         }
     
         override fun getLiveAccountDataEvent(type: String): LiveData> {
    -        return Transformations.map(getLiveAccountDataEvents(listOf(type))) {
    +        return Transformations.map(getLiveAccountDataEvents(setOf(type))) {
                 it.firstOrNull()?.toOptional()
             }
         }
     
    -    override fun getAccountDataEvents(filterType: List): List {
    +    override fun getAccountDataEvents(types: Set): List {
             return monarchy.fetchAllCopiedSync { realm ->
                 realm.where(UserAccountDataEntity::class.java)
                         .apply {
    -                        if (filterType.isNotEmpty()) {
    -                            `in`(UserAccountDataEntityFields.TYPE, filterType.toTypedArray())
    +                        if (types.isNotEmpty()) {
    +                            `in`(UserAccountDataEntityFields.TYPE, types.toTypedArray())
                             }
                         }
    -        }?.mapNotNull { entity ->
    +        }.mapNotNull { entity ->
                 entity.type?.let { type ->
                     UserAccountDataEvent(
                             type = type,
                             content = entity.contentStr?.let { adapter.fromJson(it) } ?: emptyMap()
                     )
                 }
    -        } ?: emptyList()
    +        }
         }
     
    -    override fun getLiveAccountDataEvents(filterType: List): LiveData> {
    +    override fun getLiveAccountDataEvents(types: Set): LiveData> {
             return monarchy.findAllMappedWithChanges({ realm ->
                 realm.where(UserAccountDataEntity::class.java)
                         .apply {
    -                        if (filterType.isNotEmpty()) {
    -                            `in`(UserAccountDataEntityFields.TYPE, filterType.toTypedArray())
    +                        if (types.isNotEmpty()) {
    +                            `in`(UserAccountDataEntityFields.TYPE, types.toTypedArray())
                             }
                         }
             }, { entity ->
    @@ -90,14 +89,15 @@ internal class DefaultAccountDataService @Inject constructor(
             })
         }
     
    -    override fun updateAccountData(type: String, content: Content, callback: MatrixCallback?) {
    -        updateUserAccountDataTask.configureWith(UpdateUserAccountDataTask.AnyParams(
    +    override fun updateAccountData(type: String, content: Content, callback: MatrixCallback?): Cancelable {
    +        return updateUserAccountDataTask.configureWith(UpdateUserAccountDataTask.AnyParams(
                     type = type,
                     any = content
             )) {
                 this.retryCount = 5
                 this.callback = object : MatrixCallback {
                     override fun onSuccess(data: Unit) {
    +                    // TODO Move that to the task (but it created a circular dependencies...)
                         monarchy.runTransactionSync { realm ->
                             userAccountDataSyncHandler.handleGenericAccountData(realm, type, content)
                         }
    diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataViewModel.kt
    index b0b23a62d1..32ce17c660 100644
    --- a/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/AccountDataViewModel.kt
    @@ -40,7 +40,7 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A
         : VectorViewModel(initialState) {
     
         init {
    -        session.rx().liveAccountData(emptyList())
    +        session.rx().liveAccountData(emptySet())
                     .execute {
                         copy(accountData = it)
                     }
    
    From a61f508b5d9230620b3ea0cd819e1f5e3ca743b9 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Fri, 14 Feb 2020 22:00:36 +0100
    Subject: [PATCH 039/187] Fix a nice bug
    
    ---
     .../api/session/securestorage/SecretStorageKeyContent.kt        | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt
    index f803011415..129f4bab9d 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SecretStorageKeyContent.kt
    @@ -71,7 +71,7 @@ data class SecretStorageKeyContent(
                         ?.let { ssssPassphrase ->
                             this["passphrase"] = mapOf(
                                     "algorithm" to ssssPassphrase.algorithm,
    -                                "iterations" to ssssPassphrase.salt,
    +                                "iterations" to ssssPassphrase.iterations,
                                     "salt" to ssssPassphrase.salt
                             )
                         }
    
    From e349a35419ec4f07966470bc888d281b897b3985 Mon Sep 17 00:00:00 2001
    From: ganfra
     
    Date: Sun, 16 Feb 2020 15:40:31 +0100
    Subject: [PATCH 040/187] Crypto: expose cryptoService by a getter, removing
     the session implementation delegation
    
    ---
     .../java/im/vector/matrix/rx/RxSession.kt     |  8 ++--
     .../matrix/android/api/session/Session.kt     |  7 +++-
     .../api/session/crypto/CryptoService.kt       | 12 +++---
     .../internal/crypto/DefaultCryptoService.kt   |  6 +--
     .../internal/session/DefaultSession.kt        |  5 +--
     .../vector/riotx/core/extensions/Session.kt   |  4 +-
     .../features/crypto/keys/KeysExporter.kt      |  2 +-
     .../features/crypto/keys/KeysImporter.kt      |  2 +-
     .../KeysBackupRestoreFromKeyViewModel.kt      |  2 +-
     .../KeysBackupRestoreSharedViewModel.kt       |  2 +-
     ...eysBackupSettingsRecyclerViewController.kt |  4 +-
     .../settings/KeysBackupSettingsViewModel.kt   |  6 +--
     .../setup/KeysBackupSetupSharedViewModel.kt   |  8 ++--
     .../crypto/keysrequest/KeyRequestHandler.kt   | 14 +++----
     .../IncomingVerificationRequestHandler.kt     |  6 +--
     .../VerificationBottomSheetViewModel.kt       | 38 ++++++++++---------
     .../VerificationChooseMethodViewModel.kt      | 10 ++---
     .../emoji/VerificationEmojiCodeViewModel.kt   |  6 +--
     .../riotx/features/home/HomeActivity.kt       |  4 +-
     .../home/room/detail/RoomDetailViewModel.kt   |  8 ++--
     .../edithistory/ViewEditHistoryViewModel.kt   |  2 +-
     .../features/navigation/DefaultNavigator.kt   |  6 +--
     .../notifications/NotifiableEventResolver.kt  |  2 +-
     .../riotx/features/rageshake/BugReporter.kt   |  2 +-
     .../RoomMemberProfileViewModel.kt             |  2 +-
     .../devices/DeviceListBottomSheetViewModel.kt |  2 +-
     .../members/RoomMemberListViewModel.kt        |  4 +-
     .../VectorSettingsHelpAboutFragment.kt        |  2 +-
     .../settings/VectorSettingsLabsFragment.kt    |  2 +-
     .../VectorSettingsSecurityPrivacyFragment.kt  | 14 +++----
     .../CrossSigningSettingsViewModel.kt          |  6 +--
     ...iceVerificationInfoBottomSheetViewModel.kt |  2 +-
     .../settings/devices/DevicesViewModel.kt      | 22 +++++------
     .../workers/signout/SignOutViewModel.kt       | 14 +++----
     34 files changed, 121 insertions(+), 115 deletions(-)
    
    diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
    index 0417504cb7..5146e3f199 100644
    --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
    +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
    @@ -111,15 +111,15 @@ class RxSession(private val session: Session) {
         }
     
         fun liveUserCryptoDevices(userId: String): Observable> {
    -        return session.getLiveCryptoDeviceInfo(userId).asObservable().startWithCallable {
    -            session.getCryptoDeviceInfo(userId)
    +        return session.cryptoService().getLiveCryptoDeviceInfo(userId).asObservable().startWithCallable {
    +            session.cryptoService().getCryptoDeviceInfo(userId)
             }
         }
     
         fun liveCrossSigningInfo(userId: String): Observable> {
    -        return session.getCrossSigningService().getLiveCrossSigningKeys(userId).asObservable()
    +        return session.cryptoService().crossSigningService().getLiveCrossSigningKeys(userId).asObservable()
                     .startWithCallable {
    -                    session.getCrossSigningService().getUserCrossSigningKeys(userId).toOptional()
    +                    session.cryptoService().crossSigningService().getUserCrossSigningKeys(userId).toOptional()
                     }
         }
     
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
    index 4167131c68..de981efdf0 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
    @@ -39,6 +39,7 @@ import im.vector.matrix.android.api.session.signout.SignOutService
     import im.vector.matrix.android.api.session.sync.FilterService
     import im.vector.matrix.android.api.session.sync.SyncState
     import im.vector.matrix.android.api.session.user.UserService
    +import im.vector.matrix.android.internal.crypto.DefaultCryptoService
     
     /**
      * This interface defines interactions with a session.
    @@ -49,7 +50,6 @@ interface Session :
             RoomDirectoryService,
             GroupService,
             UserService,
    -        CryptoService,
             CacheService,
             SignOutService,
             FilterService,
    @@ -139,6 +139,11 @@ interface Session :
          */
         fun contentUploadProgressTracker(): ContentUploadStateTracker
     
    +    /**
    +     * Returns the cryptoService associated with the session
    +     */
    +    fun cryptoService(): CryptoService
    +
         /**
          * Add a listener to the session.
          * @param listener the listener to add.
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt
    index 46539d9029..d277c496e3 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt
    @@ -40,6 +40,12 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
     
     interface CryptoService {
     
    +    fun verificationService(): VerificationService
    +
    +    fun crossSigningService(): CrossSigningService
    +
    +    fun keysBackupService(): KeysBackupService
    +
         fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback)
     
         fun deleteDevice(deviceId: String, callback: MatrixCallback)
    @@ -50,12 +56,6 @@ interface CryptoService {
     
         fun isCryptoEnabled(): Boolean
     
    -    fun getVerificationService(): VerificationService
    -
    -    fun getCrossSigningService(): CrossSigningService
    -
    -    fun getKeysBackupService(): KeysBackupService
    -
         fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean
     
         fun setWarnOnUnknownDevices(warn: Boolean)
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    index 26a35bd919..593cbb493c 100755
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    @@ -340,14 +340,14 @@ internal class DefaultCryptoService @Inject constructor(
         /**
          * @return the Keys backup Service
          */
    -    override fun getKeysBackupService() = keysBackup
    +    override fun keysBackupService() = keysBackup
     
         /**
          * @return the VerificationService
          */
    -    override fun getVerificationService() = verificationService
    +    override fun verificationService() = verificationService
     
    -    override fun getCrossSigningService() = crossSigningService
    +    override fun crossSigningService() = crossSigningService
     
         /**
          * A sync response has been received
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt
    index 46264ceb85..c0141156f9 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt
    @@ -88,7 +88,6 @@ internal class DefaultSession @Inject constructor(
             private val syncThreadProvider: Provider,
             private val contentUrlResolver: ContentUrlResolver,
             private val syncTokenStore: SyncTokenStore,
    -        private val syncTaskSequencer: SyncTaskSequencer,
             private val sessionParamsStore: SessionParamsStore,
             private val contentUploadProgressTracker: ContentUploadStateTracker,
             private val initialSyncProgressService: Lazy,
    @@ -101,7 +100,6 @@ internal class DefaultSession @Inject constructor(
             RoomDirectoryService by roomDirectoryService.get(),
             GroupService by groupService.get(),
             UserService by userService.get(),
    -        CryptoService by cryptoService.get(),
             SignOutService by signOutService.get(),
             FilterService by filterService.get(),
             PushRuleService by pushRuleService.get(),
    @@ -170,7 +168,6 @@ internal class DefaultSession @Inject constructor(
             cryptoService.get().close()
             isOpen = false
             eventBus.unregister(this)
    -        syncTaskSequencer.close()
             shieldTrustUpdater.stop()
         }
     
    @@ -212,6 +209,8 @@ internal class DefaultSession @Inject constructor(
     
         override fun contentUploadProgressTracker() = contentUploadProgressTracker
     
    +    override fun cryptoService(): CryptoService = cryptoService.get()
    +
         override fun addListener(listener: Session.Listener) {
             sessionListeners.addListener(listener)
         }
    diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/Session.kt b/vector/src/main/java/im/vector/riotx/core/extensions/Session.kt
    index 0a8345c650..eb3fca66c6 100644
    --- a/vector/src/main/java/im/vector/riotx/core/extensions/Session.kt
    +++ b/vector/src/main/java/im/vector/riotx/core/extensions/Session.kt
    @@ -66,6 +66,6 @@ fun Session.startSyncing(context: Context) {
      * Tell is the session has unsaved e2e keys in the backup
      */
     fun Session.hasUnsavedKeys(): Boolean {
    -    return inboundGroupSessionsCount(false) > 0
    -            && getKeysBackupService().state != KeysBackupState.ReadyToBackUp
    +    return cryptoService().inboundGroupSessionsCount(false) > 0
    +            && cryptoService().keysBackupService().state != KeysBackupState.ReadyToBackUp
     }
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysExporter.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysExporter.kt
    index 9642c2d8c6..cae8b50523 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysExporter.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysExporter.kt
    @@ -38,7 +38,7 @@ class KeysExporter(private val session: Session) {
         fun export(context: Context, password: String, callback: MatrixCallback) {
             GlobalScope.launch(Dispatchers.Main) {
                 runCatching {
    -                val data = awaitCallback { session.exportRoomKeys(password, it) }
    +                val data = awaitCallback { session.cryptoService().exportRoomKeys(password, it) }
                     withContext(Dispatchers.IO) {
                         val parentDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
                         val file = File(parentDir, "riotx-keys-" + System.currentTimeMillis() + ".txt")
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysImporter.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysImporter.kt
    index b60e25af04..5f550c032a 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysImporter.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysImporter.kt
    @@ -59,7 +59,7 @@ class KeysImporter(private val session: Session) {
                         }
     
                         awaitCallback {
    -                        session.importRoomKeys(data, password, null, it)
    +                        session.cryptoService().importRoomKeys(data, password, null, it)
                         }
                     }
                 }.foldToCallback(callback)
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
    index 7ece88e086..7c95c3f3fe 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
    @@ -47,7 +47,7 @@ class KeysBackupRestoreFromKeyViewModel @Inject constructor() : ViewModel() {
     
         fun recoverKeys(context: Context, sharedViewModel: KeysBackupRestoreSharedViewModel) {
             val session = sharedViewModel.session
    -        val keysBackup = session.getKeysBackupService()
    +        val keysBackup = session.cryptoService().keysBackupService()
     
             recoveryCodeErrorText.value = null
             val recoveryKey = recoveryCode.value!!
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt
    index 83b4daa383..5586d0cf05 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt
    @@ -63,7 +63,7 @@ class KeysBackupRestoreSharedViewModel @Inject constructor() : ViewModel() {
         }
     
         fun getLatestVersion(context: Context) {
    -        val keysBackup = session.getKeysBackupService()
    +        val keysBackup = session.cryptoService().keysBackupService()
     
             loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restore_is_getting_backup_version))
     
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewController.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewController.kt
    index 7b60cb2f9b..4f2d806ce3 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewController.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsRecyclerViewController.kt
    @@ -119,8 +119,8 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(private val s
                         style(GenericItem.STYLE.BIG_TEXT)
                         hasIndeterminateProcess(true)
     
    -                    val totalKeys = session.inboundGroupSessionsCount(false)
    -                    val backedUpKeys = session.inboundGroupSessionsCount(true)
    +                    val totalKeys = session.cryptoService().inboundGroupSessionsCount(false)
    +                    val backedUpKeys = session.cryptoService().inboundGroupSessionsCount(true)
     
                         val remainingKeysToBackup = totalKeys - backedUpKeys
     
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
    index e4bbd39684..cdea487439 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt
    @@ -47,13 +47,13 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS
             }
         }
     
    -    private var keysBackupService: KeysBackupService = session.getKeysBackupService()
    +    private val keysBackupService: KeysBackupService = session.cryptoService().keysBackupService()
     
         init {
             setState {
                 this.copy(
    -                    keysBackupState = session.getKeysBackupService().state,
    -                    keysBackupVersion = session.getKeysBackupService().keysBackupVersion
    +                    keysBackupState = keysBackupService.state,
    +                    keysBackupVersion = keysBackupService.keysBackupVersion
                 )
             }
             keysBackupService.addListener(this)
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt
    index 8d9392a919..d9a90eb457 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt
    @@ -102,7 +102,7 @@ class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() {
             session.let { mxSession ->
                 val requestedId = currentRequestId.value!!
     
    -            mxSession.getKeysBackupService().prepareKeysBackupVersion(withPassphrase,
    +            mxSession.cryptoService().keysBackupService().prepareKeysBackupVersion(withPassphrase,
                         object : ProgressListener {
                             override fun onProgress(progress: Int, total: Int) {
                                 if (requestedId != currentRequestId.value) {
    @@ -125,7 +125,7 @@ class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() {
                                 megolmBackupCreationInfo = data
                                 copyHasBeenMade = false
     
    -                            val keyBackup = session.getKeysBackupService()
    +                            val keyBackup = session.cryptoService().keysBackupService()
                                 createKeysBackup(context, keyBackup)
                             }
     
    @@ -145,14 +145,14 @@ class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() {
         }
     
         fun forceCreateKeyBackup(context: Context) {
    -        val keyBackup = session.getKeysBackupService()
    +        val keyBackup = session.cryptoService().keysBackupService()
             createKeysBackup(context, keyBackup, true)
         }
     
         fun stopAndKeepAfterDetectingExistingOnServer() {
             loadingStatus.value = null
             navigateEvent.value = LiveEvent(NAVIGATE_FINISH)
    -        session.getKeysBackupService().checkAndStartKeysBackup()
    +        session.cryptoService().keysBackupService().checkAndStartKeysBackup()
         }
     
         private fun createKeysBackup(context: Context, keysBackup: KeysBackupService, forceOverride: Boolean = false) {
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt
    index f890aef91b..a6f992a614 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt
    @@ -64,13 +64,13 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
     
         fun start(session: Session) {
             this.session = session
    -        session.getVerificationService().addListener(this)
    -        session.addRoomKeysRequestListener(this)
    +        session.cryptoService().verificationService().addListener(this)
    +        session.cryptoService().addRoomKeysRequestListener(this)
         }
     
         fun stop() {
    -        session?.getVerificationService()?.removeListener(this)
    -        session?.removeRoomKeysRequestListener(this)
    +        session?.cryptoService()?.verificationService()?.removeListener(this)
    +        session?.cryptoService()?.removeRoomKeysRequestListener(this)
             session = null
         }
     
    @@ -100,7 +100,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
             alertsToRequests[mappingKey] = ArrayList().apply { this.add(request) }
     
             // Add a notification for every incoming request
    -        session?.downloadKeys(listOf(userId), false, object : MatrixCallback> {
    +        session?.cryptoService()?.downloadKeys(listOf(userId), false, object : MatrixCallback> {
                 override fun onSuccess(data: MXUsersDevicesMap) {
                     val deviceInfo = data.getObject(userId, deviceId)
     
    @@ -111,12 +111,12 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
                     }
     
                     if (deviceInfo.isUnknown) {
    -                    session?.setDeviceVerification(DeviceTrustLevel(false, false), userId, deviceId)
    +                    session?.cryptoService()?.setDeviceVerification(DeviceTrustLevel(false, false), userId, deviceId)
     
                         deviceInfo.trustLevel = DeviceTrustLevel(false, false)
     
                         // can we get more info on this device?
    -                    session?.getDevicesList(object : MatrixCallback {
    +                    session?.cryptoService()?.getDevicesList(object : MatrixCallback {
                             override fun onSuccess(data: DevicesListResponse) {
                                 data.devices?.find { it.deviceId == deviceId }?.let {
                                     postAlert(context, userId, deviceId, true, deviceInfo, it)
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/IncomingVerificationRequestHandler.kt
    index 8765dbc0d9..1cec40e7f1 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/IncomingVerificationRequestHandler.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/IncomingVerificationRequestHandler.kt
    @@ -40,11 +40,11 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context
     
         fun start(session: Session) {
             this.session = session
    -        session.getVerificationService().addListener(this)
    +        session.cryptoService().verificationService().addListener(this)
         }
     
         fun stop() {
    -        session?.getVerificationService()?.removeListener(this)
    +        session?.cryptoService()?.verificationService()?.removeListener(this)
             this.session = null
         }
     
    @@ -139,7 +139,7 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context
                                 }
                             }
                             dismissedAction = Runnable {
    -                            session?.getVerificationService()?.declineVerificationRequestInDMs(pr.otherUserId,
    +                            session?.cryptoService()?.verificationService()?.declineVerificationRequestInDMs(pr.otherUserId,
                                         pr.transactionId ?: "",
                                         pr.roomId ?: ""
                                 )
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt
    index 85b878fe16..343f58b20c 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt
    @@ -63,7 +63,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
             VerificationService.Listener {
     
         init {
    -        session.getVerificationService().addListener(this)
    +        session.cryptoService().verificationService().addListener(this)
     
             val userItem = session.getUser(args.otherUserId)
     
    @@ -73,7 +73,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
             val pr = if (isWaitingForOtherMode) {
                 // See if active tx for this user and take it
     
    -            session.getVerificationService().getExistingVerificationRequest(args.otherUserId)
    +            session.cryptoService().verificationService().getExistingVerificationRequest(args.otherUserId)
                         ?.lastOrNull { !it.isFinished }
                         ?.also { verificationRequest ->
                             if (verificationRequest.isIncoming && !verificationRequest.isReady) {
    @@ -82,15 +82,15 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
                             }
                         }
             } else {
    -            session.getVerificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
    +            session.cryptoService().verificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
             }
     
             val sasTx = (pr?.transactionId ?: args.verificationId)?.let {
    -            session.getVerificationService().getExistingTransaction(args.otherUserId, it) as? SasVerificationTransaction
    +            session.cryptoService().verificationService().getExistingTransaction(args.otherUserId, it) as? SasVerificationTransaction
             }
     
             val qrTx = (pr?.transactionId ?: args.verificationId)?.let {
    -            session.getVerificationService().getExistingTransaction(args.otherUserId, it) as? QrCodeVerificationTransaction
    +            session.cryptoService().verificationService().getExistingTransaction(args.otherUserId, it) as? QrCodeVerificationTransaction
             }
     
             setState {
    @@ -108,7 +108,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
     
             if (autoReady) {
                 // TODO, can I be here in DM mode? in this case should test if roomID is null?
    -            session.getVerificationService()
    +            session.cryptoService().verificationService()
                         .readyPendingVerification(supportedVerificationMethods,
                                 pr!!.otherUserId,
                                 pr.transactionId ?: "")
    @@ -116,7 +116,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
         }
     
         override fun onCleared() {
    -        session.getVerificationService().removeListener(this)
    +        session.cryptoService().verificationService().removeListener(this)
             super.onCleared()
         }
     
    @@ -164,7 +164,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
                                             roomId = data,
                                             pendingRequest = Success(
                                                     session
    -                                                        .getVerificationService()
    +                                                        .cryptoService()
    +                                                        .verificationService()
                                                             .requestKeyVerificationInDMs(supportedVerificationMethods, otherUserId, data, pendingLocalId)
                                             )
                                     )
    @@ -181,7 +182,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
                         setState {
                             copy(
                                     pendingRequest = Success(session
    -                                        .getVerificationService()
    +                                        .cryptoService()
    +                                        .verificationService()
                                             .requestKeyVerificationInDMs(supportedVerificationMethods, otherUserId, roomId)
                                     )
                             )
    @@ -190,18 +192,18 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
                     Unit
                 }
                 is VerificationAction.StartSASVerification         -> {
    -                val request = session.getVerificationService().getExistingVerificationRequest(otherUserId, action.pendingRequestTransactionId)
    +                val request = session.cryptoService().verificationService().getExistingVerificationRequest(otherUserId, action.pendingRequestTransactionId)
                             ?: return@withState
                     val otherDevice = if (request.isIncoming) request.requestInfo?.fromDevice else request.readyInfo?.fromDevice
                     if (roomId == null) {
    -                    session.getVerificationService().beginKeyVerification(
    +                    session.cryptoService().verificationService().beginKeyVerification(
                                 VerificationMethod.SAS,
                                 otherUserId = request.otherUserId,
                                 otherDeviceId = otherDevice ?: "",
                                 transactionId = action.pendingRequestTransactionId
                         )
                     } else {
    -                    session.getVerificationService().beginKeyVerificationInDMs(
    +                    session.cryptoService().verificationService().beginKeyVerificationInDMs(
                                 VerificationMethod.SAS,
                                 transactionId = action.pendingRequestTransactionId,
                                 roomId = roomId,
    @@ -213,7 +215,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
                     Unit
                 }
                 is VerificationAction.RemoteQrCodeScanned          -> {
    -                val existingTransaction = session.getVerificationService()
    +                val existingTransaction = session.cryptoService().verificationService()
                             .getExistingTransaction(action.otherUserId, action.transactionId) as? QrCodeVerificationTransaction
                     existingTransaction
                             ?.userHasScannedOtherQrCode(action.scannedData)
    @@ -221,7 +223,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
                 is VerificationAction.OtherUserScannedSuccessfully -> {
                     val transactionId = state.transactionId ?: return@withState
     
    -                val existingTransaction = session.getVerificationService()
    +                val existingTransaction = session.cryptoService().verificationService()
                             .getExistingTransaction(otherUserId, transactionId) as? QrCodeVerificationTransaction
                     existingTransaction
                             ?.otherUserScannedMyQrCode()
    @@ -229,18 +231,18 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
                 is VerificationAction.OtherUserDidNotScanned       -> {
                     val transactionId = state.transactionId ?: return@withState
     
    -                val existingTransaction = session.getVerificationService()
    +                val existingTransaction = session.cryptoService().verificationService()
                             .getExistingTransaction(otherUserId, transactionId) as? QrCodeVerificationTransaction
                     existingTransaction
                             ?.otherUserDidNotScannedMyQrCode()
                 }
                 is VerificationAction.SASMatchAction               -> {
    -                (session.getVerificationService()
    +                (session.cryptoService().verificationService()
                             .getExistingTransaction(action.otherUserId, action.sasTransactionId)
                             as? SasVerificationTransaction)?.userHasVerifiedShortCode()
                 }
                 is VerificationAction.SASDoNotMatchAction          -> {
    -                (session.getVerificationService()
    +                (session.cryptoService().verificationService()
                             .getExistingTransaction(action.otherUserId, action.sasTransactionId)
                             as? SasVerificationTransaction)
                             ?.shortCodeDoesNotMatch()
    @@ -312,7 +314,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
                     if (!pr.isReady) {
                         // auto ready in this case, as we are waiting
                         // TODO, can I be here in DM mode? in this case should test if roomID is null?
    -                    session.getVerificationService()
    +                    session.cryptoService().verificationService()
                                 .readyPendingVerification(supportedVerificationMethods,
                                         pr.otherUserId,
                                         pr.transactionId ?: "")
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
    index bdb07ed0dc..1fcdfbf05b 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
    @@ -62,7 +62,7 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
         }
     
         override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state ->
    -        val pvr = session.getVerificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId)
    +        val pvr = session.cryptoService().verificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId)
     
             setState {
                 copy(
    @@ -79,12 +79,12 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
         }
     
         init {
    -        session.getVerificationService().addListener(this)
    +        session.cryptoService().verificationService().addListener(this)
         }
     
         override fun onCleared() {
             super.onCleared()
    -        session.getVerificationService().removeListener(this)
    +        session.cryptoService().verificationService().removeListener(this)
         }
     
         companion object : MvRxViewModelFactory {
    @@ -96,10 +96,10 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
             override fun initialState(viewModelContext: ViewModelContext): VerificationChooseMethodViewState? {
                 val args: VerificationBottomSheet.VerificationArgs = viewModelContext.args()
                 val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
    -            val pvr = session.getVerificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
    +            val pvr = session.cryptoService().verificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
     
                 // Get the QR code now, because transaction is already created, so transactionCreated() will not be called
    -            val qrCodeVerificationTransaction = session.getVerificationService().getExistingTransaction(args.otherUserId, args.verificationId ?: "")
    +            val qrCodeVerificationTransaction = session.cryptoService().verificationService().getExistingTransaction(args.otherUserId, args.verificationId ?: "")
     
                 return VerificationChooseMethodViewState(otherUserId = args.otherUserId,
                         transactionId = args.verificationId ?: "",
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt
    index 637b7d7cc9..ad282f7dc9 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt
    @@ -56,16 +56,16 @@ class VerificationEmojiCodeViewModel @AssistedInject constructor(
     
         init {
             withState { state ->
    -            refreshStateFromTx(session.getVerificationService()
    +            refreshStateFromTx(session.cryptoService().verificationService()
                         .getExistingTransaction(state.otherUser?.id ?: "", state.transactionId
                                 ?: "") as? SasVerificationTransaction)
             }
     
    -        session.getVerificationService().addListener(this)
    +        session.cryptoService().verificationService().addListener(this)
         }
     
         override fun onCleared() {
    -        session.getVerificationService().removeListener(this)
    +        session.cryptoService().verificationService().removeListener(this)
             super.onCleared()
         }
     
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt
    index 94e14ab214..2eac4e946e 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt
    @@ -130,7 +130,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
             if (sharedActionViewModel.hasDisplayedCompleteSecurityPrompt) return
     
             // ensure keys are downloaded
    -        session.downloadKeys(listOf(session.myUserId), true, object : MatrixCallback> {
    +        session.cryptoService().downloadKeys(listOf(session.myUserId), true, object : MatrixCallback> {
                 override fun onSuccess(data: MXUsersDevicesMap) {
                     runOnUiThread {
                         alertCompleteSecurity(session)
    @@ -140,7 +140,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
         }
     
         private fun alertCompleteSecurity(session: Session) {
    -        val myCrossSigningKeys = session.getCrossSigningService()
    +        val myCrossSigningKeys = session.cryptoService().crossSigningService()
                     .getMyCrossSigningKeys()
             val crossSigningEnabledOnAccount = myCrossSigningKeys != null
     
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    index 2ca372707f..47738d3cf4 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    @@ -420,7 +420,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
                                 popDraft()
                             }
                             is ParsedCommand.VerifyUser               -> {
    -                            session.getVerificationService().requestKeyVerificationInDMs(supportedVerificationMethods, slashCommandResult.userId, room.roomId)
    +                            session.cryptoService().verificationService().requestKeyVerificationInDMs(supportedVerificationMethods, slashCommandResult.userId, room.roomId)
                                 _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled())
                                 popDraft()
                             }
    @@ -826,7 +826,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
     
         private fun handleAcceptVerification(action: RoomDetailAction.AcceptVerificationRequest) {
             Timber.v("## SAS handleAcceptVerification ${action.otherUserId},  roomId:${room.roomId}, txId:${action.transactionId}")
    -        if (session.getVerificationService().readyPendingVerificationInDMs(
    +        if (session.cryptoService().verificationService().readyPendingVerificationInDMs(
                             supportedVerificationMethods,
                             action.otherUserId,
                             room.roomId,
    @@ -838,7 +838,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
         }
     
         private fun handleDeclineVerification(action: RoomDetailAction.DeclineVerificationRequest) {
    -        session.getVerificationService().declineVerificationRequestInDMs(
    +        session.cryptoService().verificationService().declineVerificationRequestInDMs(
                     action.otherUserId,
                     action.transactionId,
                     room.roomId)
    @@ -851,7 +851,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
     
         private fun handleResumeRequestVerification(action: RoomDetailAction.ResumeVerification) {
             // Check if this request is still active and handled by me
    -        session.getVerificationService().getExistingVerificationRequestInRoom(room.roomId, action.transactionId)?.let {
    +        session.cryptoService().verificationService().getExistingVerificationRequestInRoom(room.roomId, action.transactionId)?.let {
                 if (it.handledByOtherSession) return
                 if (!it.isFinished) {
                     _viewEvents.post(RoomDetailViewEvents.ActionSuccess(action.copy(
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
    index 3c98d24ccf..f03a1c8704 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
    @@ -100,7 +100,7 @@ class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
                             if (it.isEncrypted() && it.mxDecryptionResult == null) {
                                 // for now decrypt sync
                                 try {
    -                                val result = session.decryptEvent(it, timelineID)
    +                                val result = session.cryptoService().decryptEvent(it, timelineID)
                                     it.mxDecryptionResult = OlmDecryptionResult(
                                             payload = result.clearEvent,
                                             senderKey = result.senderCurve25519Key,
    diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt
    index ecc806c798..027cb85863 100644
    --- a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt
    @@ -66,7 +66,7 @@ class DefaultNavigator @Inject constructor(
     
         override fun performDeviceVerification(context: Context, otherUserId: String, sasTransationId: String) {
             val session = sessionHolder.getSafeActiveSession() ?: return
    -        val tx = session.getVerificationService().getExistingTransaction(otherUserId, sasTransationId) ?: return
    +        val tx = session.cryptoService().verificationService().getExistingTransaction(otherUserId, sasTransationId) ?: return
             (tx as? IncomingSasVerificationTransaction)?.performAccept()
             if (context is VectorBaseActivity) {
                 VerificationBottomSheet.withArgs(
    @@ -79,10 +79,10 @@ class DefaultNavigator @Inject constructor(
     
         override fun requestSessionVerification(context: Context) {
             val session = sessionHolder.getSafeActiveSession() ?: return
    -        val pr = session.getVerificationService().requestKeyVerification(
    +        val pr = session.cryptoService().verificationService().requestKeyVerification(
                     listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW),
                     session.myUserId,
    -                session.getUserDevices(session.myUserId).map { it.deviceId })
    +                session.cryptoService().getUserDevices(session.myUserId).map { it.deviceId })
             if (context is VectorBaseActivity) {
                 VerificationBottomSheet.withArgs(
                         roomId = null,
    diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt
    index 11d770adc4..17339956e5 100644
    --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt
    @@ -114,7 +114,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
                     // TODO use a global event decryptor? attache to session and that listen to new sessionId?
                     // for now decrypt sync
                     try {
    -                    val result = session.decryptEvent(event.root, event.root.roomId + UUID.randomUUID().toString())
    +                    val result = session.cryptoService().decryptEvent(event.root, event.root.roomId + UUID.randomUUID().toString())
                         event.root.mxDecryptionResult = OlmDecryptionResult(
                                 payload = result.clearEvent,
                                 senderKey = result.senderCurve25519Key,
    diff --git a/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt
    index dc353363d5..b41c100003 100755
    --- a/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt
    @@ -211,7 +211,7 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes
                     activeSessionHolder.getSafeActiveSession()?.let { session ->
                         userId = session.myUserId
                         deviceId = session.sessionParams.credentials.deviceId ?: "undefined"
    -                    olmVersion = session.getCryptoVersion(context, true)
    +                    olmVersion = session.cryptoService().getCryptoVersion(context, true)
                     }
     
                     if (!mIsCancelled) {
    diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt
    index e3dd53104c..d6a1018e80 100644
    --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt
    @@ -148,7 +148,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
                     // ok, let's find or create the DM room
                     _viewEvents.post(RoomMemberProfileViewEvents.StartVerification(
                             userId = state.userId,
    -                        canCrossSign = session.getCrossSigningService().canCrossSign()
    +                        canCrossSign = session.cryptoService().crossSigningService().canCrossSign()
                     ))
                 }
             }
    diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
    index b12ed58a73..253a21e3fa 100644
    --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
    @@ -100,7 +100,7 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva
         }
     
         private fun manuallyVerify(action: DeviceListAction.ManuallyVerify) {
    -        session.getVerificationService().beginKeyVerification(VerificationMethod.SAS, userId, action.deviceId, null)?.let { txID ->
    +        session.cryptoService().verificationService().beginKeyVerification(VerificationMethod.SAS, userId, action.deviceId, null)?.let { txID ->
                 _viewEvents.post(DeviceListBottomSheetViewEvents.Verify(userId, txID))
             }
         }
    diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt
    index 72b4af4474..81b2809c4f 100644
    --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt
    @@ -95,7 +95,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState
                 room.rx().liveRoomMembers(roomMemberQueryParams)
                         .observeOn(AndroidSchedulers.mainThread())
                         .switchMap { membersSummary ->
    -                        session.getLiveCryptoDeviceInfo(membersSummary.map { it.userId })
    +                        session.cryptoService().getLiveCryptoDeviceInfo(membersSummary.map { it.userId })
                                     .asObservable()
                                     .doOnError { Timber.e(it) }
                                     .map { deviceList ->
    @@ -104,7 +104,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState
                                             val allDeviceTrusted = it.value.fold(it.value.isNotEmpty()) { prev, next ->
                                                 prev && next.trustLevel?.isCrossSigningVerified().orFalse()
                                             }
    -                                        if (session.getCrossSigningService().getUserCrossSigningKeys(it.key)?.isTrusted().orFalse()) {
    +                                        if (session.cryptoService().crossSigningService().getUserCrossSigningKeys(it.key)?.isTrusted().orFalse()) {
                                                 if (allDeviceTrusted) RoomEncryptionTrustLevel.Trusted else RoomEncryptionTrustLevel.Warning
                                             } else {
                                                 RoomEncryptionTrustLevel.Default
    diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsHelpAboutFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsHelpAboutFragment.kt
    index 6c10b8695d..7b892ec88b 100644
    --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsHelpAboutFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsHelpAboutFragment.kt
    @@ -77,7 +77,7 @@ class VectorSettingsHelpAboutFragment @Inject constructor(
     
             // olm version
             findPreference(VectorPreferences.SETTINGS_OLM_VERSION_PREFERENCE_KEY)!!
    -                .summary = session.getCryptoVersion(requireContext(), false)
    +                .summary = session.cryptoService().getCryptoVersion(requireContext(), false)
     
             // copyright
             findPreference(VectorPreferences.SETTINGS_COPYRIGHT_PREFERENCE_KEY)!!
    diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsLabsFragment.kt
    index 8b1a2dba31..66c56455b9 100644
    --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsLabsFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsLabsFragment.kt
    @@ -37,7 +37,7 @@ class VectorSettingsLabsFragment @Inject constructor(
     //        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.isCryptoEnabled()) {
    +        if (session.cryptoService().isCryptoEnabled()) {
     //            mLabsCategory.removePreference(useCryptoPref)
     //
     //            cryptoIsEnabledPref.isEnabled = false
    diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt
    index 0637d6d545..4324c79dfd 100644
    --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt
    @@ -134,10 +134,10 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
     
         private fun refreshXSigningStatus() {
             if (vectorPreferences.developerMode()) {
    -            val crossSigningKeys = session.getCrossSigningService().getMyCrossSigningKeys()
    +            val crossSigningKeys = session.cryptoService().crossSigningService().getMyCrossSigningKeys()
                 val xSigningIsEnableInAccount = crossSigningKeys != null
    -            val xSigningKeysAreTrusted = session.getCrossSigningService().checkUserTrust(session.myUserId).isVerified()
    -            val xSigningKeyCanSign = session.getCrossSigningService().canCrossSign()
    +            val xSigningKeysAreTrusted = session.cryptoService().crossSigningService().checkUserTrust(session.myUserId).isVerified()
    +            val xSigningKeyCanSign = session.cryptoService().crossSigningService().canCrossSign()
     
                 if (xSigningKeyCanSign) {
                     mCrossSigningStatePreference.setIcon(R.drawable.ic_shield_trusted)
    @@ -412,10 +412,10 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
     
             sendToUnverifiedDevicesPref.isChecked = false
     
    -        sendToUnverifiedDevicesPref.isChecked = session.getGlobalBlacklistUnverifiedDevices()
    +        sendToUnverifiedDevicesPref.isChecked = session.cryptoService().getGlobalBlacklistUnverifiedDevices()
     
             sendToUnverifiedDevicesPref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
    -            session.setGlobalBlacklistUnverifiedDevices(sendToUnverifiedDevicesPref.isChecked)
    +            session.cryptoService().setGlobalBlacklistUnverifiedDevices(sendToUnverifiedDevicesPref.isChecked)
     
                 true
             }
    @@ -426,7 +426,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
         // ==============================================================================================================
     
         private fun refreshMyDevice() {
    -        session.getUserDevices(session.myUserId).map {
    +        session.cryptoService().getUserDevices(session.myUserId).map {
                 DeviceInfo(
                         user_id = session.myUserId,
                         deviceId = it.deviceId,
    @@ -436,7 +436,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
                 refreshCryptographyPreference(it)
             }
             // TODO Move to a ViewModel...
    -        session.getDevicesList(object : MatrixCallback {
    +        session.cryptoService().getDevicesList(object : MatrixCallback {
                 override fun onSuccess(data: DevicesListResponse) {
                     if (isAdded) {
                         refreshCryptographyPreference(data.devices ?: emptyList())
    diff --git a/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsViewModel.kt
    index 2ad2e8c1ca..8ddb1802f3 100644
    --- a/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsViewModel.kt
    @@ -57,8 +57,8 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat
                     .execute {
                         val crossSigningKeys = it.invoke()?.getOrNull()
                         val xSigningIsEnableInAccount = crossSigningKeys != null
    -                    val xSigningKeysAreTrusted = session.getCrossSigningService().checkUserTrust(session.myUserId).isVerified()
    -                    val xSigningKeyCanSign = session.getCrossSigningService().canCrossSign()
    +                    val xSigningKeysAreTrusted = session.cryptoService().crossSigningService().checkUserTrust(session.myUserId).isVerified()
    +                    val xSigningKeyCanSign = session.cryptoService().crossSigningService().canCrossSign()
                         copy(
                                 crossSigningInfo = crossSigningKeys,
                                 xSigningIsEnableInAccount = xSigningIsEnableInAccount,
    @@ -97,7 +97,7 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat
             setState {
                 copy(isUploadingKeys = true)
             }
    -        session.getCrossSigningService().initializeCrossSigning(auth, object : MatrixCallback {
    +        session.cryptoService().crossSigningService().initializeCrossSigning(auth, object : MatrixCallback {
                 override fun onSuccess(data: Unit) {
                     _pendingSession = null
     
    diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt
    index f2625cbc37..7ee79a279f 100644
    --- a/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt
    @@ -63,7 +63,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As
             setState {
                 copy(deviceInfo = Loading())
             }
    -        session.getDeviceInfo(deviceId, object : MatrixCallback {
    +        session.cryptoService().getDeviceInfo(deviceId, object : MatrixCallback {
                 override fun onSuccess(data: DeviceInfo) {
                     setState {
                         copy(deviceInfo = Success(data))
    diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt
    index b931f5d66f..931f7a261f 100644
    --- a/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt
    @@ -74,7 +74,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
     
         init {
             refreshDevicesList()
    -        session.getVerificationService().addListener(this)
    +        session.cryptoService().verificationService().addListener(this)
     
             session.rx().liveUserCryptoDevices(session.myUserId)
                     .execute {
    @@ -85,7 +85,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
         }
     
         override fun onCleared() {
    -        session.getVerificationService().removeListener(this)
    +        session.cryptoService().verificationService().removeListener(this)
             super.onCleared()
         }
     
    @@ -103,7 +103,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
         private fun refreshDevicesList() {
             if (!session.sessionParams.credentials.deviceId.isNullOrEmpty()) {
                 // display something asap
    -            val localKnown = session.getUserDevices(session.myUserId).map {
    +            val localKnown = session.cryptoService().getUserDevices(session.myUserId).map {
                     DeviceInfo(
                             user_id = session.myUserId,
                             deviceId = it.deviceId,
    @@ -118,7 +118,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
                     )
                 }
     
    -            session.getDevicesList(object : MatrixCallback {
    +            session.cryptoService().getDevicesList(object : MatrixCallback {
                     override fun onSuccess(data: DevicesListResponse) {
                         setState {
                             copy(
    @@ -141,16 +141,16 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
                 setState {
                     copy(
                             myDeviceId = session.sessionParams.credentials.deviceId ?: "",
    -                        cryptoDevices = Success(session.getUserDevices(session.myUserId))
    +                        cryptoDevices = Success(session.cryptoService().getUserDevices(session.myUserId))
                     )
                 }
     
                 // then force download
    -            session.downloadKeys(listOf(session.myUserId), true, object : MatrixCallback> {
    +            session.cryptoService().downloadKeys(listOf(session.myUserId), true, object : MatrixCallback> {
                     override fun onSuccess(data: MXUsersDevicesMap) {
                         setState {
                             copy(
    -                                cryptoDevices = Success(session.getUserDevices(session.myUserId))
    +                                cryptoDevices = Success(session.cryptoService().getUserDevices(session.myUserId))
                             )
                         }
                     }
    @@ -172,7 +172,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
         }
     
         private fun handleVerify(action: DevicesAction.VerifyMyDevice) {
    -        val txID = session.getVerificationService().requestKeyVerification(supportedVerificationMethods, session.myUserId, listOf(action.deviceId))
    +        val txID = session.cryptoService().verificationService().requestKeyVerification(supportedVerificationMethods, session.myUserId, listOf(action.deviceId))
             _viewEvents.post(DevicesViewEvents.ShowVerifyDevice(
                     session.myUserId,
                     txID.transactionId
    @@ -187,7 +187,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
         }
     
         private fun handleRename(action: DevicesAction.Rename) {
    -        session.setDeviceName(action.deviceId, action.newName, object : MatrixCallback {
    +        session.cryptoService().setDeviceName(action.deviceId, action.newName, object : MatrixCallback {
                 override fun onSuccess(data: Unit) {
                     setState {
                         copy(
    @@ -222,7 +222,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
                 )
             }
     
    -        session.deleteDevice(deviceId, object : MatrixCallback {
    +        session.cryptoService().deleteDevice(deviceId, object : MatrixCallback {
                 override fun onFailure(failure: Throwable) {
                     var isPasswordRequestFound = false
     
    @@ -284,7 +284,7 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic
                 )
             }
     
    -        session.deleteDeviceWithUserPassword(currentDeviceId, _currentSession, action.password, object : MatrixCallback {
    +        session.cryptoService().deleteDeviceWithUserPassword(currentDeviceId, _currentSession, action.password, object : MatrixCallback {
                 override fun onSuccess(data: Unit) {
                     _currentDeviceId = null
                     _currentSession = null
    diff --git a/vector/src/main/java/im/vector/riotx/features/workers/signout/SignOutViewModel.kt b/vector/src/main/java/im/vector/riotx/features/workers/signout/SignOutViewModel.kt
    index 2526e8cbe3..2f26fdf377 100644
    --- a/vector/src/main/java/im/vector/riotx/features/workers/signout/SignOutViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/workers/signout/SignOutViewModel.kt
    @@ -30,36 +30,36 @@ class SignOutViewModel @Inject constructor(private val session: Session) : ViewM
         var keysBackupState = MutableLiveData()
     
         init {
    -        session.getKeysBackupService().addListener(this)
    +        session.cryptoService().keysBackupService().addListener(this)
     
    -        keysBackupState.value = session.getKeysBackupService().state
    +        keysBackupState.value = session.cryptoService().keysBackupService().state
         }
     
         /**
          * Safe way to get the current KeysBackup version
          */
         fun getCurrentBackupVersion(): String {
    -        return session.getKeysBackupService().currentBackupVersion ?: ""
    +        return session.cryptoService().keysBackupService().currentBackupVersion ?: ""
         }
     
         /**
          * Safe way to get the number of keys to backup
          */
         fun getNumberOfKeysToBackup(): Int {
    -        return session.inboundGroupSessionsCount(false)
    +        return session.cryptoService().inboundGroupSessionsCount(false)
         }
     
         /**
          * Safe way to tell if there are more keys on the server
          */
         fun canRestoreKeys(): Boolean {
    -        return session.getKeysBackupService().canRestoreKeys()
    +        return session.cryptoService().keysBackupService().canRestoreKeys()
         }
     
         override fun onCleared() {
             super.onCleared()
     
    -        session.getKeysBackupService().removeListener(this)
    +        session.cryptoService().keysBackupService().removeListener(this)
         }
     
         override fun onStateChange(newState: KeysBackupState) {
    @@ -68,7 +68,7 @@ class SignOutViewModel @Inject constructor(private val session: Session) : ViewM
     
         fun refreshRemoteStateIfNeeded() {
             if (keysBackupState.value == KeysBackupState.Disabled) {
    -            session.getKeysBackupService().checkAndStartKeysBackup()
    +            session.cryptoService().keysBackupService().checkAndStartKeysBackup()
             }
         }
     }
    
    From ecf07ff64ab49d298834a5bd4e8ee97c11f8e915 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 10:30:54 +0100
    Subject: [PATCH 041/187] Account creation: wrongly hints that an email can be
     used to create an account (#941)
    
    ---
     CHANGES.md                                                | 2 +-
     .../java/im/vector/riotx/features/login/LoginFragment.kt  | 8 +++++++-
     vector/src/main/res/layout/fragment_login.xml             | 4 ++--
     vector/src/main/res/values-bg/strings.xml                 | 2 +-
     vector/src/main/res/values-de/strings.xml                 | 2 +-
     vector/src/main/res/values-eu/strings.xml                 | 2 +-
     vector/src/main/res/values-fi/strings.xml                 | 2 +-
     vector/src/main/res/values-fr/strings.xml                 | 2 +-
     vector/src/main/res/values-hu/strings.xml                 | 2 +-
     vector/src/main/res/values-it/strings.xml                 | 2 +-
     vector/src/main/res/values-sq/strings.xml                 | 2 +-
     vector/src/main/res/values-zh-rTW/strings.xml             | 2 +-
     vector/src/main/res/values/strings.xml                    | 2 +-
     vector/src/main/res/values/strings_riotX.xml              | 4 +---
     14 files changed, 21 insertions(+), 17 deletions(-)
    
    diff --git a/CHANGES.md b/CHANGES.md
    index 3e771a79d4..aa353fe633 100644
    --- a/CHANGES.md
    +++ b/CHANGES.md
    @@ -9,7 +9,7 @@ Improvements 🙌:
      -
     
     Bugfix 🐛:
    - -
    + - Account creation: wrongly hints that an email can be used to create an account (#941)
     
     Translations 🗣:
      -
    diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt
    index 93b1b1b525..3e45eeb406 100644
    --- a/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt
    @@ -40,7 +40,7 @@ import javax.inject.Inject
     
     /**
      * In this screen, in signin mode:
    - * - the user is asked for login and password to sign in to a homeserver.
    + * - the user is asked for login (or email) and password to sign in to a homeserver.
      * - He also can reset his password
      * In signup mode:
      * - the user is asked for login and password
    @@ -97,6 +97,12 @@ class LoginFragment @Inject constructor() : AbstractLoginFragment() {
                 SignMode.SignIn  -> R.string.login_connect_to
             }
     
    +        loginFieldTil.hint = getString(when (state.signMode) {
    +            SignMode.Unknown -> error("developer error")
    +            SignMode.SignUp  -> R.string.login_signup_username_hint
    +            SignMode.SignIn  -> R.string.login_signin_username_hint
    +        })
    +
             when (state.serverType) {
                 ServerType.MatrixOrg -> {
                     loginServerIcon.isVisible = true
    diff --git a/vector/src/main/res/layout/fragment_login.xml b/vector/src/main/res/layout/fragment_login.xml
    index fb445dfb89..3f2443440e 100644
    --- a/vector/src/main/res/layout/fragment_login.xml
    +++ b/vector/src/main/res/layout/fragment_login.xml
    @@ -50,8 +50,8 @@
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:layout_marginTop="32dp"
    -                android:hint="@string/login_signup_username_hint"
    -                app:errorEnabled="true">
    +                app:errorEnabled="true"
    +                tools:hint="@string/login_signin_username_hint">
     
                     Телефонния номер изглежда невалиден. Моля проверете го
     
         Регистрация в %1$s
    -    Потребителско име или имейл
    +    Потребителско име или имейл
         Парола
         Напред
         Това потребителско име е заето
    diff --git a/vector/src/main/res/values-de/strings.xml b/vector/src/main/res/values-de/strings.xml
    index fe059320d3..6c3ad4e84e 100644
    --- a/vector/src/main/res/values-de/strings.xml
    +++ b/vector/src/main/res/values-de/strings.xml
    @@ -1941,7 +1941,7 @@ Wenn du diese neue Wiederherstellungsmethode nicht eingerichtet hast, kann ein A
         Wir haben einen Code an %1$s gesendet. Gib diesen unten ein um dich zu verifizieren.
         Code eingeben
         Erneut senden
    -    Benutzername oder E-Mail-Adresse
    +    Benutzername oder E-Mail-Adresse
         Passwort
         Dieser Benutzername ist bereits belegt
         Dein Benutzerkonto ist noch nicht erstellt.
    diff --git a/vector/src/main/res/values-eu/strings.xml b/vector/src/main/res/values-eu/strings.xml
    index 91432ac66e..86fb1e0e12 100644
    --- a/vector/src/main/res/values-eu/strings.xml
    +++ b/vector/src/main/res/values-eu/strings.xml
    @@ -1909,7 +1909,7 @@ Abisua: Fitxategi hau ezabatu daiteke aplikazioa desinstalatzen bada.
         Telefono zenbakia baliogabea dirudi. Egiaztatu ezazu
     
         Erregistratu %1$s zerbitzarian
    -    Erabiltzaile-izena edo e-maila
    +    Erabiltzaile-izena edo e-maila
         Pasahitza
         Hurrengoa
         Erabiltzaile-izen hori hartuta dago
    diff --git a/vector/src/main/res/values-fi/strings.xml b/vector/src/main/res/values-fi/strings.xml
    index 4966a5ea6a..fef747d546 100644
    --- a/vector/src/main/res/values-fi/strings.xml
    +++ b/vector/src/main/res/values-fi/strings.xml
    @@ -1962,7 +1962,7 @@ Jotta et menetä mitään, automaattiset päivitykset kannattaa pitää käytös
         Puhelinnumero vaikuttaa epäkelvolta. Tarkista numero
     
         Rekisteröidy palvelimelle %1$s
    -    Käyttäjätunnus tai sähköpostiosoite
    +    Käyttäjätunnus tai sähköpostiosoite
         Salasana
         Seuraava
         Käyttäjätunnus on varattu
    diff --git a/vector/src/main/res/values-fr/strings.xml b/vector/src/main/res/values-fr/strings.xml
    index 86547b02fd..959266b9b7 100644
    --- a/vector/src/main/res/values-fr/strings.xml
    +++ b/vector/src/main/res/values-fr/strings.xml
    @@ -1918,7 +1918,7 @@ Si vous n’avez pas configuré de nouvelle méthode de récupération, un attaq
         Le numéro de téléphone n’a pas l’air d’être valide. Veuillez le vérifier
     
         S’inscrire sur %1$s
    -    Nom d’utilisateur ou e-mail
    +    Nom d’utilisateur ou e-mail
         Mot de passe
         Suivant
         Ce nom d’utilisateur est déjà pris
    diff --git a/vector/src/main/res/values-hu/strings.xml b/vector/src/main/res/values-hu/strings.xml
    index 4598e445fa..64eee79075 100644
    --- a/vector/src/main/res/values-hu/strings.xml
    +++ b/vector/src/main/res/values-hu/strings.xml
    @@ -1913,7 +1913,7 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
         A telefonszám érvénytelennek látszik. Kérlek ellenőrizd
     
         Bejelentkezés ide: %1$s
    -    Felhasználónév vagy e-mail
    +    Felhasználónév vagy e-mail
         Jelszó
         Következő
         A felhasználónév már használatban van
    diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml
    index 6c10f7b04f..4d1ac8edaf 100644
    --- a/vector/src/main/res/values-it/strings.xml
    +++ b/vector/src/main/res/values-it/strings.xml
    @@ -1963,7 +1963,7 @@
         Il numero di telefono non sembra valido. Ricontrollalo
     
         Registrati su %1$s
    -    Nome utente o email
    +    Nome utente o email
         Password
         Avanti
         Quel nome utente esiste già
    diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml
    index 397a245289..a8af77d59b 100644
    --- a/vector/src/main/res/values-sq/strings.xml
    +++ b/vector/src/main/res/values-sq/strings.xml
    @@ -1871,7 +1871,7 @@ Që të garantoni se s’ju shpëton gjë, thjesht mbajeni të aktivizuar mekani
         Numri i telefonit duket se është i vlefshëm. Ju lutemi, kontrollojeni
     
         Regjistrohuni te %1$s
    -    Emër përdoruesi ose email
    +    Emër përdoruesi ose email
         Fjalëkalim
         Pasuesi
         Ai emër përdoruesi është i zënë
    diff --git a/vector/src/main/res/values-zh-rTW/strings.xml b/vector/src/main/res/values-zh-rTW/strings.xml
    index 9e36a2ec3a..d285c364b2 100644
    --- a/vector/src/main/res/values-zh-rTW/strings.xml
    +++ b/vector/src/main/res/values-zh-rTW/strings.xml
    @@ -1866,7 +1866,7 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意
         電話號碼似乎無效。請檢查
     
         註冊至 %1$s
    -    使用者名稱或電子郵件
    +    使用者名稱或電子郵件
         密碼
         下一個
         使用者名稱已被使用
    diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
    index 7693bc8cee..29fff11230 100644
    --- a/vector/src/main/res/values/strings.xml
    +++ b/vector/src/main/res/values/strings.xml
    @@ -1912,7 +1912,7 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
     
         
         Sign up to %1$s
    -    Username or email
    +    Username or email
         Password
         Next
         That username is taken
    diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml
    index 15fd002e89..6f2af1e7ac 100644
    --- a/vector/src/main/res/values/strings_riotX.xml
    +++ b/vector/src/main/res/values/strings_riotX.xml
    @@ -23,9 +23,7 @@
     
         
         Remove…
    -    
    -
    -
    +    Username
         
     
         
    
    From b6f5e27155f0af332e5047a9227a94bd8a51f81b Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 13:50:36 +0100
    Subject: [PATCH 042/187] Fix issue after merge of develop
    
    ---
     CHANGES.md | 1 -
     1 file changed, 1 deletion(-)
    
    diff --git a/CHANGES.md b/CHANGES.md
    index 3b0fa6a2a7..0f100e19fe 100644
    --- a/CHANGES.md
    +++ b/CHANGES.md
    @@ -3,7 +3,6 @@ Changes in RiotX 0.17.0 (2020-XX-XX)
     
     Features ✨:
      - Secured Shared Storage Support (#984, #936)
    - - Polls and Bot Buttons (MSC 2192 matrix-org/matrix-doc#2192)
      - It's now possible to select several rooms (with a possible mix of clear/encrypted rooms) when sharing elements to RiotX (#1010)
      - Media preview: media are previewed before being sent to a room (#1010)
      - Image edition: it's now possible to edit image before sending: crop, rotate, and delete actions are supported (#1010)
    
    From 31d5578063037ac687db009ef848e887ffb7a5ae Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 14:35:57 +0100
    Subject: [PATCH 043/187] Media preview: do not propose to edit Gif
    
    ---
     .../preview/AttachmentsPreviewFragment.kt     | 12 ++++++++
     .../attachments/preview/Extensions.kt         | 28 +++++++++++++++++++
     2 files changed, 40 insertions(+)
     create mode 100644 vector/src/main/java/im/vector/riotx/features/attachments/preview/Extensions.kt
    
    diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    index d05dcc5593..062295f586 100644
    --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    @@ -23,6 +23,7 @@ import android.content.Intent
     import android.graphics.Color
     import android.os.Bundle
     import android.os.Parcelable
    +import android.view.Menu
     import android.view.MenuItem
     import android.view.View
     import android.view.ViewGroup
    @@ -107,6 +108,16 @@ class AttachmentsPreviewFragment @Inject constructor(
             }
         }
     
    +    override fun onPrepareOptionsMenu(menu: Menu) {
    +        withState(viewModel) { state ->
    +            val editMenuItem = menu.findItem(R.id.attachmentsPreviewEditAction)
    +            val showEditMenuItem = state.attachments[state.currentAttachmentIndex].isEditable()
    +            editMenuItem.setVisible(showEditMenuItem)
    +        }
    +
    +        super.onPrepareOptionsMenu(menu)
    +    }
    +
         override fun getMenuRes() = R.menu.vector_attachments_preview
     
         override fun onDestroyView() {
    @@ -116,6 +127,7 @@ class AttachmentsPreviewFragment @Inject constructor(
         }
     
         override fun invalidate() = withState(viewModel) { state ->
    +        requireActivity().invalidateOptionsMenu()
             if (state.attachments.isEmpty()) {
                 requireActivity().setResult(RESULT_CANCELED)
                 requireActivity().finish()
    diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/Extensions.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/Extensions.kt
    new file mode 100644
    index 0000000000..3bd47baa89
    --- /dev/null
    +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/Extensions.kt
    @@ -0,0 +1,28 @@
    +/*
    + * 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 im.vector.riotx.features.attachments.preview
    +
    +import im.vector.matrix.android.api.session.content.ContentAttachmentData
    +
    +/**
    + * All images are editable, expect Gif
    + */
    +fun ContentAttachmentData.isEditable(): Boolean {
    +    return type == ContentAttachmentData.Type.IMAGE
    +            && mimeType?.startsWith("image/") == true
    +            && mimeType != "image/gif"
    +}
    
    From ca0cd9e97d0bb9416f39ac68721294adae9d20f6 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 14:38:56 +0100
    Subject: [PATCH 044/187] Better code
    
    ---
     .../java/im/vector/riotx/core/platform/VectorBaseFragment.kt   | 3 +++
     .../features/attachments/preview/AttachmentsPreviewFragment.kt | 2 +-
     .../createdirect/CreateDirectRoomKnownUsersFragment.kt         | 2 +-
     .../vector/riotx/features/home/room/list/RoomListFragment.kt   | 2 +-
     4 files changed, 6 insertions(+), 3 deletions(-)
    
    diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt
    index 8261b5991b..cbb0e904e4 100644
    --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt
    @@ -263,6 +263,9 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
             }
         }
     
    +    // This should be provided by the framework
    +    protected fun invalidateOptionsMenu() = requireActivity().invalidateOptionsMenu()
    +
         /* ==========================================================================================
          * Common Dialogs
          * ========================================================================================== */
    diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    index 062295f586..25f3ebc2d9 100644
    --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    @@ -127,7 +127,7 @@ class AttachmentsPreviewFragment @Inject constructor(
         }
     
         override fun invalidate() = withState(viewModel) { state ->
    -        requireActivity().invalidateOptionsMenu()
    +        invalidateOptionsMenu()
             if (state.attachments.isEmpty()) {
                 requireActivity().setResult(RESULT_CANCELED)
                 requireActivity().finish()
    diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt
    index e06df21be5..fc1a4b9e65 100644
    --- a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt
    @@ -149,7 +149,7 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor(
         }
     
         private fun renderSelectedUsers(selectedUsers: Set) {
    -        vectorBaseActivity.invalidateOptionsMenu()
    +        invalidateOptionsMenu()
             if (selectedUsers.isNotEmpty() && chipGroup.size == 0) {
                 selectedUsers.forEach { addChipToGroup(it, chipGroup) }
             }
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt
    index 96af6e5908..e5141975f3 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt
    @@ -257,7 +257,7 @@ class RoomListFragment @Inject constructor(
                     val newValue = state.hasUnread
                     if (hasUnreadRooms != newValue) {
                         hasUnreadRooms = newValue
    -                    requireActivity().invalidateOptionsMenu()
    +                    invalidateOptionsMenu()
                     }
                 }
                 else                      -> Unit
    
    From a3b205b310fa10c8a82b22c2bd3cbdd348ce92f3 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 14:48:12 +0100
    Subject: [PATCH 045/187] No preview for elements sent from the keyboard
    
    ---
     .../home/room/detail/RoomDetailFragment.kt    | 24 ++++++++++++-------
     .../home/room/detail/RoomDetailViewModel.kt   |  2 ++
     2 files changed, 18 insertions(+), 8 deletions(-)
    
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    index 9885e40eef..c4e7dae61c 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    @@ -650,9 +650,11 @@ class RoomDetailFragment @Inject constructor(
         }
     
         private fun sendUri(uri: Uri): Boolean {
    +        roomDetailViewModel.preventAttachmentPreview = true
             val shareIntent = Intent(Intent.ACTION_SEND, uri)
             val isHandled = attachmentsHelper.handleShareIntent(shareIntent)
             if (!isHandled) {
    +            roomDetailViewModel.preventAttachmentPreview = false
                 Toast.makeText(requireContext(), R.string.error_handling_incoming_share, Toast.LENGTH_SHORT).show()
             }
             return isHandled
    @@ -1347,18 +1349,24 @@ class RoomDetailFragment @Inject constructor(
         // AttachmentsHelper.Callback
     
         override fun onContentAttachmentsReady(attachments: List) {
    -        val grouped = attachments.toGroupedContentAttachmentData()
    -        if (grouped.notPreviewables.isNotEmpty()) {
    -            // Send the not previewable attachments right now (?)
    -            roomDetailViewModel.handle(RoomDetailAction.SendMedia(grouped.notPreviewables, false))
    -        }
    -        if (grouped.previewables.isNotEmpty()) {
    -            val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(grouped.previewables))
    -            startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE)
    +        if (roomDetailViewModel.preventAttachmentPreview) {
    +            roomDetailViewModel.preventAttachmentPreview = false
    +            roomDetailViewModel.handle(RoomDetailAction.SendMedia(attachments, false))
    +        } else {
    +            val grouped = attachments.toGroupedContentAttachmentData()
    +            if (grouped.notPreviewables.isNotEmpty()) {
    +                // Send the not previewable attachments right now (?)
    +                roomDetailViewModel.handle(RoomDetailAction.SendMedia(grouped.notPreviewables, false))
    +            }
    +            if (grouped.previewables.isNotEmpty()) {
    +                val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(grouped.previewables))
    +                startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE)
    +            }
             }
         }
     
         override fun onAttachmentsProcessFailed() {
    +        roomDetailViewModel.preventAttachmentPreview = false
             Toast.makeText(requireContext(), R.string.error_attachment, Toast.LENGTH_SHORT).show()
         }
     
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    index 1a4d3d0783..957c4fb7e7 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    @@ -115,6 +115,8 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
         var pendingAction: RoomDetailAction? = null
         // Slot to keep a pending uri during permission request
         var pendingUri: Uri? = null
    +    // Slot to store if we want to prevent preview of attachment
    +    var preventAttachmentPreview = false
     
         private var trackUnreadMessages = AtomicBoolean(false)
         private var mostRecentDisplayedEvent: TimelineEvent? = null
    
    From 48a033b3bd80122218ff24ccae483df87e5a1929 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 14:57:41 +0100
    Subject: [PATCH 046/187] Fix navigation issue when sharing text to multiple
     rooms
    
    ---
     .../riotx/features/home/room/detail/RoomDetailFragment.kt     | 4 ++--
     .../im/vector/riotx/features/share/IncomingShareViewModel.kt  | 4 +++-
     2 files changed, 5 insertions(+), 3 deletions(-)
    
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    index c4e7dae61c..46d2c24853 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    @@ -309,8 +309,8 @@ class RoomDetailFragment @Inject constructor(
                         // open share edition
                         onContentAttachmentsReady(sharedData.attachmentData)
                     }
    -                null                      -> Timber.v("No share data to process")
    -            }
    +                null -> Timber.v("No share data to process")
    +            }.exhaustive
             }
         }
     
    diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    index b7dfcb26bb..3988643f02 100644
    --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    @@ -122,11 +122,13 @@ class IncomingShareViewModel @AssistedInject constructor(
                             val room = session.getRoom(roomId)
                             room?.sendTextMessage(sharedData.text)
                         }
    +                    // This is it, pass the first roomId to let the screen open it
    +                    _viewEvents.post(IncomingShareViewEvents.MultipleRoomsShareDone(state.selectedRoomIds.first()))
                     }
                     is SharedData.Attachments -> {
                         shareAttachments(sharedData.attachmentData, state.selectedRoomIds, proposeMediaEdition = true, compressMediaBeforeSending = false)
                     }
    -            }
    +            }.exhaustive
             }
         }
     
    
    From fd0dceb5979ffef6779e56ccdb1401d9760c15ae Mon Sep 17 00:00:00 2001
    From: ganfra
     
    Date: Mon, 17 Feb 2020 15:23:24 +0100
    Subject: [PATCH 047/187] Clean code
    
    ---
     .../java/im/vector/matrix/android/api/session/Session.kt     | 1 -
     .../vector/matrix/android/internal/session/DefaultSession.kt | 1 -
     .../verification/choose/VerificationChooseMethodViewModel.kt | 5 +++--
     .../riotx/features/home/room/detail/RoomDetailViewModel.kt   | 5 ++++-
     4 files changed, 7 insertions(+), 5 deletions(-)
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
    index de981efdf0..c2fa7d2d32 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
    @@ -39,7 +39,6 @@ import im.vector.matrix.android.api.session.signout.SignOutService
     import im.vector.matrix.android.api.session.sync.FilterService
     import im.vector.matrix.android.api.session.sync.SyncState
     import im.vector.matrix.android.api.session.user.UserService
    -import im.vector.matrix.android.internal.crypto.DefaultCryptoService
     
     /**
      * This interface defines interactions with a session.
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt
    index c0141156f9..e7cb963c5f 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt
    @@ -49,7 +49,6 @@ import im.vector.matrix.android.internal.crypto.crosssigning.ShieldTrustUpdater
     import im.vector.matrix.android.internal.database.LiveEntityObserver
     import im.vector.matrix.android.internal.di.SessionId
     import im.vector.matrix.android.internal.di.WorkManagerProvider
    -import im.vector.matrix.android.internal.session.sync.SyncTaskSequencer
     import im.vector.matrix.android.internal.session.sync.SyncTokenStore
     import im.vector.matrix.android.internal.session.sync.job.SyncThread
     import im.vector.matrix.android.internal.session.sync.job.SyncWorker
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
    index 1fcdfbf05b..d7ee17a397 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
    @@ -96,10 +96,11 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
             override fun initialState(viewModelContext: ViewModelContext): VerificationChooseMethodViewState? {
                 val args: VerificationBottomSheet.VerificationArgs = viewModelContext.args()
                 val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
    -            val pvr = session.cryptoService().verificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId)
    +            val verificationService = session.cryptoService().verificationService()
    +            val pvr = verificationService.getExistingVerificationRequest(args.otherUserId, args.verificationId)
     
                 // Get the QR code now, because transaction is already created, so transactionCreated() will not be called
    -            val qrCodeVerificationTransaction = session.cryptoService().verificationService().getExistingTransaction(args.otherUserId, args.verificationId ?: "")
    +            val qrCodeVerificationTransaction = verificationService.getExistingTransaction(args.otherUserId, args.verificationId ?: "")
     
                 return VerificationChooseMethodViewState(otherUserId = args.otherUserId,
                         transactionId = args.verificationId ?: "",
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    index 47738d3cf4..4cbf16e49c 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    @@ -420,7 +420,10 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
                                 popDraft()
                             }
                             is ParsedCommand.VerifyUser               -> {
    -                            session.cryptoService().verificationService().requestKeyVerificationInDMs(supportedVerificationMethods, slashCommandResult.userId, room.roomId)
    +                            session
    +                                    .cryptoService()
    +                                    .verificationService()
    +                                    .requestKeyVerificationInDMs(supportedVerificationMethods, slashCommandResult.userId, room.roomId)
                                 _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled())
                                 popDraft()
                             }
    
    From 91cbcebf73efd1a46b425063d64b2dac9edade61 Mon Sep 17 00:00:00 2001
    From: ganfra
     
    Date: Mon, 17 Feb 2020 15:50:42 +0100
    Subject: [PATCH 048/187] Make test compile
    
    ---
     .../matrix/android/common/CryptoTestHelper.kt |   4 +-
     .../crypto/crosssigning/XSigningTest.kt       |  38 ++--
     .../crypto/keysbackup/KeysBackupTest.kt       | 194 +++++++++---------
     .../internal/crypto/verification/SASTest.kt   |  44 ++--
     .../verification/qrcode/VerificationTest.kt   |   8 +-
     5 files changed, 144 insertions(+), 144 deletions(-)
    
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt
    index f94c8455c2..7368c87252 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt
    @@ -230,9 +230,9 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) {
             val aliceRoomId = cryptoTestData.roomId
             val bobSession = cryptoTestData.secondSession!!
     
    -        bobSession.setWarnOnUnknownDevices(false)
    +        bobSession.cryptoService().setWarnOnUnknownDevices(false)
     
    -        aliceSession.setWarnOnUnknownDevices(false)
    +        aliceSession.cryptoService().setWarnOnUnknownDevices(false)
     
             val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
             val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt
    index c8e7355d7a..069d32acaf 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt
    @@ -35,7 +35,7 @@ class XSigningTest : InstrumentedTest {
             val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
     
             val aliceLatch = CountDownLatch(1)
    -        aliceSession.getCrossSigningService()
    +        aliceSession.cryptoService().crossSigningService()
                     .initializeCrossSigning(UserPasswordAuth(
                             user = aliceSession.myUserId,
                             password = TestConstants.PASSWORD
    @@ -43,7 +43,7 @@ class XSigningTest : InstrumentedTest {
     
             mTestHelper.await(aliceLatch)
     
    -        val myCrossSigningKeys = aliceSession.getCrossSigningService().getMyCrossSigningKeys()
    +        val myCrossSigningKeys = aliceSession.cryptoService().crossSigningService().getMyCrossSigningKeys()
             val masterPubKey = myCrossSigningKeys?.masterKey()
             assertNotNull("Master key should be stored", masterPubKey?.unpaddedBase64PublicKey)
             val selfSigningKey = myCrossSigningKeys?.selfSigningKey()
    @@ -53,7 +53,7 @@ class XSigningTest : InstrumentedTest {
     
             assertTrue("Signing Keys should be trusted", myCrossSigningKeys?.isTrusted() == true)
     
    -        assertTrue("Signing Keys should be trusted", aliceSession.getCrossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
    +        assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
     
             mTestHelper.signout(aliceSession)
         }
    @@ -76,23 +76,23 @@ class XSigningTest : InstrumentedTest {
     
             val latch = CountDownLatch(2)
     
    -        aliceSession.getCrossSigningService().initializeCrossSigning(aliceAuthParams, TestMatrixCallback(latch))
    -        bobSession.getCrossSigningService().initializeCrossSigning(bobAuthParams, TestMatrixCallback(latch))
    +        aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, TestMatrixCallback(latch))
    +        bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, TestMatrixCallback(latch))
     
             mTestHelper.await(latch)
     
             // Check that alice can see bob keys
             val downloadLatch = CountDownLatch(1)
    -        aliceSession.downloadKeys(listOf(bobSession.myUserId), true, TestMatrixCallback(downloadLatch))
    +        aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, TestMatrixCallback(downloadLatch))
             mTestHelper.await(downloadLatch)
     
    -        val bobKeysFromAlicePOV = aliceSession.getCrossSigningService().getUserCrossSigningKeys(bobSession.myUserId)
    +        val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId)
             assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV!!.masterKey())
             assertNull("Alice should not see bob User key", bobKeysFromAlicePOV.userKey())
             assertNotNull("Alice can see bob SelfSigned key", bobKeysFromAlicePOV.selfSigningKey())
     
    -        assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV.masterKey()?.unpaddedBase64PublicKey, bobSession.getCrossSigningService().getMyCrossSigningKeys()?.masterKey()?.unpaddedBase64PublicKey)
    -        assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV.selfSigningKey()?.unpaddedBase64PublicKey, bobSession.getCrossSigningService().getMyCrossSigningKeys()?.selfSigningKey()?.unpaddedBase64PublicKey)
    +        assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV.masterKey()?.unpaddedBase64PublicKey, bobSession.cryptoService().crossSigningService().getMyCrossSigningKeys()?.masterKey()?.unpaddedBase64PublicKey)
    +        assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV.selfSigningKey()?.unpaddedBase64PublicKey, bobSession.cryptoService().crossSigningService().getMyCrossSigningKeys()?.selfSigningKey()?.unpaddedBase64PublicKey)
     
             assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted())
     
    @@ -118,22 +118,22 @@ class XSigningTest : InstrumentedTest {
     
             val latch = CountDownLatch(2)
     
    -        aliceSession.getCrossSigningService().initializeCrossSigning(aliceAuthParams, TestMatrixCallback(latch))
    -        bobSession.getCrossSigningService().initializeCrossSigning(bobAuthParams, TestMatrixCallback(latch))
    +        aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, TestMatrixCallback(latch))
    +        bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, TestMatrixCallback(latch))
     
             mTestHelper.await(latch)
     
             // Check that alice can see bob keys
             val downloadLatch = CountDownLatch(1)
             val bobUserId = bobSession.myUserId
    -        aliceSession.downloadKeys(listOf(bobUserId), true, TestMatrixCallback(downloadLatch))
    +        aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, TestMatrixCallback(downloadLatch))
             mTestHelper.await(downloadLatch)
     
    -        val bobKeysFromAlicePOV = aliceSession.getCrossSigningService().getUserCrossSigningKeys(bobUserId)
    +        val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId)
             assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false)
     
             val trustLatch = CountDownLatch(1)
    -        aliceSession.getCrossSigningService().trustUser(bobUserId, object : MatrixCallback {
    +        aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, object : MatrixCallback {
                 override fun onSuccess(data: Unit) {
                     trustLatch.countDown()
                 }
    @@ -152,7 +152,7 @@ class XSigningTest : InstrumentedTest {
     
             // Check that bob first session sees the new login
             val bobKeysLatch = CountDownLatch(1)
    -        bobSession.downloadKeys(listOf(bobUserId), true, object : MatrixCallback> {
    +        bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, object : MatrixCallback> {
                 override fun onFailure(failure: Throwable) {
                     fail("Failed to get device")
                 }
    @@ -166,12 +166,12 @@ class XSigningTest : InstrumentedTest {
             })
             mTestHelper.await(bobKeysLatch)
     
    -        val bobSecondDevicePOVFirstDevice = bobSession.getDeviceInfo(bobUserId, bobSecondDeviceId)
    +        val bobSecondDevicePOVFirstDevice = bobSession.cryptoService().getDeviceInfo(bobUserId, bobSecondDeviceId)
             assertNotNull("Bob Second device should be known and persisted from first", bobSecondDevicePOVFirstDevice)
     
             // Manually mark it as trusted from first session
             val bobSignLatch = CountDownLatch(1)
    -        bobSession.getCrossSigningService().signDevice(bobSecondDeviceId!!, object : MatrixCallback {
    +        bobSession.cryptoService().crossSigningService().signDevice(bobSecondDeviceId!!, object : MatrixCallback {
                 override fun onSuccess(data: Unit) {
                     bobSignLatch.countDown()
                 }
    @@ -184,7 +184,7 @@ class XSigningTest : InstrumentedTest {
     
             // Now alice should cross trust bob's second device
             val aliceKeysLatch = CountDownLatch(1)
    -        aliceSession.downloadKeys(listOf(bobUserId), true, object : MatrixCallback> {
    +        aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, object : MatrixCallback> {
                 override fun onFailure(failure: Throwable) {
                     fail("Failed to get device")
                 }
    @@ -199,7 +199,7 @@ class XSigningTest : InstrumentedTest {
             })
             mTestHelper.await(aliceKeysLatch)
     
    -        val result = aliceSession.getCrossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
    +        val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
             assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
     
             mTestHelper.signout(aliceSession)
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
    index 312ad03a06..66ce44ad4f 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
    @@ -77,21 +77,21 @@ class KeysBackupTest : InstrumentedTest {
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
             // From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
    -        val cryptoStore = (cryptoTestData.firstSession.getKeysBackupService() as KeysBackup).store
    +        val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as KeysBackup).store
             val sessions = cryptoStore.inboundGroupSessionsToBackup(100)
             val sessionsCount = sessions.size
     
             assertFalse(sessions.isEmpty())
    -        assertEquals(sessionsCount, cryptoTestData.firstSession.inboundGroupSessionsCount(false))
    -        assertEquals(0, cryptoTestData.firstSession.inboundGroupSessionsCount(true))
    +        assertEquals(sessionsCount, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false))
    +        assertEquals(0, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true))
     
             // - Check backup keys after having marked one as backed up
             val session = sessions[0]
     
             cryptoStore.markBackupDoneForInboundGroupSessions(Collections.singletonList(session))
     
    -        assertEquals(sessionsCount, cryptoTestData.firstSession.inboundGroupSessionsCount(false))
    -        assertEquals(1, cryptoTestData.firstSession.inboundGroupSessionsCount(true))
    +        assertEquals(sessionsCount, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false))
    +        assertEquals(1, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true))
     
             val sessions2 = cryptoStore.inboundGroupSessionsToBackup(100)
             assertEquals(sessionsCount - 1, sessions2.size)
    @@ -101,8 +101,8 @@ class KeysBackupTest : InstrumentedTest {
     
             val sessions3 = cryptoStore.inboundGroupSessionsToBackup(100)
             assertEquals(sessionsCount, sessions3.size)
    -        assertEquals(sessionsCount, cryptoTestData.firstSession.inboundGroupSessionsCount(false))
    -        assertEquals(0, cryptoTestData.firstSession.inboundGroupSessionsCount(true))
    +        assertEquals(sessionsCount, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false))
    +        assertEquals(0, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true))
         }
     
         /**
    @@ -112,9 +112,9 @@ class KeysBackupTest : InstrumentedTest {
         fun prepareKeysBackupVersionTest() {
             val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams)
     
    -        assertNotNull(bobSession.getKeysBackupService())
    +        assertNotNull(bobSession.cryptoService().keysBackupService())
     
    -        val keysBackup = bobSession.getKeysBackupService()
    +        val keysBackup = bobSession.cryptoService().keysBackupService()
     
             val stateObserver = StateObserver(keysBackup)
     
    @@ -154,7 +154,7 @@ class KeysBackupTest : InstrumentedTest {
         fun createKeysBackupVersionTest() {
             val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams)
     
    -        val keysBackup = bobSession.getKeysBackupService()
    +        val keysBackup = bobSession.cryptoService().keysBackupService()
     
             val stateObserver = StateObserver(keysBackup)
     
    @@ -209,12 +209,12 @@ class KeysBackupTest : InstrumentedTest {
         fun backupAfterCreateKeysBackupVersionTest() {
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
    -        val keysBackup = cryptoTestData.firstSession.getKeysBackupService()
    +        val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
     
             val latch = CountDownLatch(1)
     
    -        assertEquals(2, cryptoTestData.firstSession.inboundGroupSessionsCount(false))
    -        assertEquals(0, cryptoTestData.firstSession.inboundGroupSessionsCount(true))
    +        assertEquals(2, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false))
    +        assertEquals(0, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true))
     
             val stateObserver = StateObserver(keysBackup, latch, 5)
     
    @@ -222,8 +222,8 @@ class KeysBackupTest : InstrumentedTest {
     
             mTestHelper.await(latch)
     
    -        val nbOfKeys = cryptoTestData.firstSession.inboundGroupSessionsCount(false)
    -        val backedUpKeys = cryptoTestData.firstSession.inboundGroupSessionsCount(true)
    +        val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)
    +        val backedUpKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true)
     
             assertEquals(2, nbOfKeys)
             assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys)
    @@ -248,14 +248,14 @@ class KeysBackupTest : InstrumentedTest {
         fun backupAllGroupSessionsTest() {
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
    -        val keysBackup = cryptoTestData.firstSession.getKeysBackupService()
    +        val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
     
             val stateObserver = StateObserver(keysBackup)
     
             prepareAndCreateKeysBackupData(keysBackup)
     
             // Check that backupAllGroupSessions returns valid data
    -        val nbOfKeys = cryptoTestData.firstSession.inboundGroupSessionsCount(false)
    +        val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)
     
             assertEquals(2, nbOfKeys)
     
    @@ -273,7 +273,7 @@ class KeysBackupTest : InstrumentedTest {
             mTestHelper.await(latch)
             assertEquals(nbOfKeys, lastBackedUpKeysProgress)
     
    -        val backedUpKeys = cryptoTestData.firstSession.inboundGroupSessionsCount(true)
    +        val backedUpKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true)
     
             assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys)
     
    @@ -293,7 +293,7 @@ class KeysBackupTest : InstrumentedTest {
         fun testEncryptAndDecryptKeysBackupData() {
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
    -        val keysBackup = cryptoTestData.firstSession.getKeysBackupService() as KeysBackup
    +        val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() as KeysBackup
     
             val stateObserver = StateObserver(keysBackup)
     
    @@ -337,7 +337,7 @@ class KeysBackupTest : InstrumentedTest {
             // - Restore the e2e backup from the homeserver
             val latch2 = CountDownLatch(1)
             var importRoomKeysResult: ImportRoomKeysResult? = null
    -        testData.aliceSession2.getKeysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
                     null,
                     null,
    @@ -373,7 +373,7 @@ class KeysBackupTest : InstrumentedTest {
             val testData = createKeysBackupScenarioWithPassword(null)
     
             // - Check the SDK sent key share requests
    -        val cryptoStore2 = (testData.aliceSession2.getKeysBackupService() as KeysBackup).store
    +        val cryptoStore2 = (testData.aliceSession2.cryptoService().keysBackupService() as KeysBackup).store
             val unsentRequest = cryptoStore2
                     .getOutgoingRoomKeyRequestByState(setOf(OutgoingRoomKeyRequest.RequestState.UNSENT))
             val sentRequest = cryptoStore2
    @@ -385,7 +385,7 @@ class KeysBackupTest : InstrumentedTest {
             // - Restore the e2e backup from the homeserver
             val latch2 = CountDownLatch(1)
             var importRoomKeysResult: ImportRoomKeysResult? = null
    -        testData.aliceSession2.getKeysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
                     null,
                     null,
    @@ -429,17 +429,17 @@ class KeysBackupTest : InstrumentedTest {
             // - And log Alice on a new device
             val testData = createKeysBackupScenarioWithPassword(null)
     
    -        val stateObserver = StateObserver(testData.aliceSession2.getKeysBackupService())
    +        val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
     
             // - The new device must see the previous backup as not trusted
    -        assertNotNull(testData.aliceSession2.getKeysBackupService().keysBackupVersion)
    -        assertFalse(testData.aliceSession2.getKeysBackupService().isEnabled)
    -        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.getKeysBackupService().state)
    +        assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
    +        assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
    +        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
     
             // - Trust the backup from the new device
             val latch = CountDownLatch(1)
    -        testData.aliceSession2.getKeysBackupService().trustKeysBackupVersion(
    -                testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersion(
    +                testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     true,
                     TestMatrixCallback(latch)
             )
    @@ -449,13 +449,13 @@ class KeysBackupTest : InstrumentedTest {
             waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
     
             // - Backup must be enabled on the new device, on the same version
    -        assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.getKeysBackupService().keysBackupVersion?.version)
    -        assertTrue(testData.aliceSession2.getKeysBackupService().isEnabled)
    +        assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
    +        assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
     
             // - Retrieve the last version from the server
             val latch2 = CountDownLatch(1)
             var keysVersionResult: KeysVersionResult? = null
    -        testData.aliceSession2.getKeysBackupService().getCurrentVersion(
    +        testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(
                     object : TestMatrixCallback(latch2) {
                         override fun onSuccess(data: KeysVersionResult?) {
                             keysVersionResult = data
    @@ -470,7 +470,7 @@ class KeysBackupTest : InstrumentedTest {
     
             val latch3 = CountDownLatch(1)
             var keysBackupVersionTrust: KeysBackupVersionTrust? = null
    -        testData.aliceSession2.getKeysBackupService().getKeysBackupTrust(keysVersionResult!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult!!,
                     object : TestMatrixCallback(latch3) {
                         override fun onSuccess(data: KeysBackupVersionTrust) {
                             keysBackupVersionTrust = data
    @@ -503,17 +503,17 @@ class KeysBackupTest : InstrumentedTest {
             // - And log Alice on a new device
             val testData = createKeysBackupScenarioWithPassword(null)
     
    -        val stateObserver = StateObserver(testData.aliceSession2.getKeysBackupService())
    +        val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
     
             // - The new device must see the previous backup as not trusted
    -        assertNotNull(testData.aliceSession2.getKeysBackupService().keysBackupVersion)
    -        assertFalse(testData.aliceSession2.getKeysBackupService().isEnabled)
    -        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.getKeysBackupService().state)
    +        assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
    +        assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
    +        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
     
             // - Trust the backup from the new device with the recovery key
             val latch = CountDownLatch(1)
    -        testData.aliceSession2.getKeysBackupService().trustKeysBackupVersionWithRecoveryKey(
    -                testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey(
    +                testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
                     TestMatrixCallback(latch)
             )
    @@ -523,13 +523,13 @@ class KeysBackupTest : InstrumentedTest {
             waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
     
             // - Backup must be enabled on the new device, on the same version
    -        assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.getKeysBackupService().keysBackupVersion?.version)
    -        assertTrue(testData.aliceSession2.getKeysBackupService().isEnabled)
    +        assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
    +        assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
     
             // - Retrieve the last version from the server
             val latch2 = CountDownLatch(1)
             var keysVersionResult: KeysVersionResult? = null
    -        testData.aliceSession2.getKeysBackupService().getCurrentVersion(
    +        testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(
                     object : TestMatrixCallback(latch2) {
                         override fun onSuccess(data: KeysVersionResult?) {
                             keysVersionResult = data
    @@ -544,7 +544,7 @@ class KeysBackupTest : InstrumentedTest {
     
             val latch3 = CountDownLatch(1)
             var keysBackupVersionTrust: KeysBackupVersionTrust? = null
    -        testData.aliceSession2.getKeysBackupService().getKeysBackupTrust(keysVersionResult!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult!!,
                     object : TestMatrixCallback(latch3) {
                         override fun onSuccess(data: KeysBackupVersionTrust) {
                             keysBackupVersionTrust = data
    @@ -575,26 +575,26 @@ class KeysBackupTest : InstrumentedTest {
             // - And log Alice on a new device
             val testData = createKeysBackupScenarioWithPassword(null)
     
    -        val stateObserver = StateObserver(testData.aliceSession2.getKeysBackupService())
    +        val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
     
             // - The new device must see the previous backup as not trusted
    -        assertNotNull(testData.aliceSession2.getKeysBackupService().keysBackupVersion)
    -        assertFalse(testData.aliceSession2.getKeysBackupService().isEnabled)
    -        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.getKeysBackupService().state)
    +        assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
    +        assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
    +        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
     
             // - Try to trust the backup from the new device with a wrong recovery key
             val latch = CountDownLatch(1)
    -        testData.aliceSession2.getKeysBackupService().trustKeysBackupVersionWithRecoveryKey(
    -                testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey(
    +                testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     "Bad recovery key",
                     TestMatrixCallback(latch, false)
             )
             mTestHelper.await(latch)
     
             // - The new device must still see the previous backup as not trusted
    -        assertNotNull(testData.aliceSession2.getKeysBackupService().keysBackupVersion)
    -        assertFalse(testData.aliceSession2.getKeysBackupService().isEnabled)
    -        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.getKeysBackupService().state)
    +        assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
    +        assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
    +        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
     
             stateObserver.stopAndCheckStates(null)
             testData.cryptoTestData.close()
    @@ -618,17 +618,17 @@ class KeysBackupTest : InstrumentedTest {
             // - And log Alice on a new device
             val testData = createKeysBackupScenarioWithPassword(password)
     
    -        val stateObserver = StateObserver(testData.aliceSession2.getKeysBackupService())
    +        val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
     
             // - The new device must see the previous backup as not trusted
    -        assertNotNull(testData.aliceSession2.getKeysBackupService().keysBackupVersion)
    -        assertFalse(testData.aliceSession2.getKeysBackupService().isEnabled)
    -        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.getKeysBackupService().state)
    +        assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
    +        assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
    +        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
     
             // - Trust the backup from the new device with the password
             val latch = CountDownLatch(1)
    -        testData.aliceSession2.getKeysBackupService().trustKeysBackupVersionWithPassphrase(
    -                testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithPassphrase(
    +                testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     password,
                     TestMatrixCallback(latch)
             )
    @@ -638,13 +638,13 @@ class KeysBackupTest : InstrumentedTest {
             waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp)
     
             // - Backup must be enabled on the new device, on the same version
    -        assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.getKeysBackupService().keysBackupVersion?.version)
    -        assertTrue(testData.aliceSession2.getKeysBackupService().isEnabled)
    +        assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
    +        assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
     
             // - Retrieve the last version from the server
             val latch2 = CountDownLatch(1)
             var keysVersionResult: KeysVersionResult? = null
    -        testData.aliceSession2.getKeysBackupService().getCurrentVersion(
    +        testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(
                     object : TestMatrixCallback(latch2) {
                         override fun onSuccess(data: KeysVersionResult?) {
                             keysVersionResult = data
    @@ -659,7 +659,7 @@ class KeysBackupTest : InstrumentedTest {
     
             val latch3 = CountDownLatch(1)
             var keysBackupVersionTrust: KeysBackupVersionTrust? = null
    -        testData.aliceSession2.getKeysBackupService().getKeysBackupTrust(keysVersionResult!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult!!,
                     object : TestMatrixCallback(latch3) {
                         override fun onSuccess(data: KeysBackupVersionTrust) {
                             keysBackupVersionTrust = data
    @@ -693,26 +693,26 @@ class KeysBackupTest : InstrumentedTest {
             // - And log Alice on a new device
             val testData = createKeysBackupScenarioWithPassword(password)
     
    -        val stateObserver = StateObserver(testData.aliceSession2.getKeysBackupService())
    +        val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
     
             // - The new device must see the previous backup as not trusted
    -        assertNotNull(testData.aliceSession2.getKeysBackupService().keysBackupVersion)
    -        assertFalse(testData.aliceSession2.getKeysBackupService().isEnabled)
    -        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.getKeysBackupService().state)
    +        assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
    +        assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
    +        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
     
             // - Try to trust the backup from the new device with a wrong password
             val latch = CountDownLatch(1)
    -        testData.aliceSession2.getKeysBackupService().trustKeysBackupVersionWithPassphrase(
    -                testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithPassphrase(
    +                testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     badPassword,
                     TestMatrixCallback(latch, false)
             )
             mTestHelper.await(latch)
     
             // - The new device must still see the previous backup as not trusted
    -        assertNotNull(testData.aliceSession2.getKeysBackupService().keysBackupVersion)
    -        assertFalse(testData.aliceSession2.getKeysBackupService().isEnabled)
    -        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.getKeysBackupService().state)
    +        assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
    +        assertFalse(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
    +        assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
     
             stateObserver.stopAndCheckStates(null)
             testData.cryptoTestData.close()
    @@ -731,7 +731,7 @@ class KeysBackupTest : InstrumentedTest {
             // - Try to restore the e2e backup with a wrong recovery key
             val latch2 = CountDownLatch(1)
             var importRoomKeysResult: ImportRoomKeysResult? = null
    -        testData.aliceSession2.getKeysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     "EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d",
                     null,
                     null,
    @@ -768,7 +768,7 @@ class KeysBackupTest : InstrumentedTest {
             var importRoomKeysResult: ImportRoomKeysResult? = null
             val steps = ArrayList()
     
    -        testData.aliceSession2.getKeysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     password,
                     null,
                     null,
    @@ -828,7 +828,7 @@ class KeysBackupTest : InstrumentedTest {
             // - Try to restore the e2e backup with a wrong password
             val latch2 = CountDownLatch(1)
             var importRoomKeysResult: ImportRoomKeysResult? = null
    -        testData.aliceSession2.getKeysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     wrongPassword,
                     null,
                     null,
    @@ -863,7 +863,7 @@ class KeysBackupTest : InstrumentedTest {
             // - Restore the e2e backup with the recovery key.
             val latch2 = CountDownLatch(1)
             var importRoomKeysResult: ImportRoomKeysResult? = null
    -        testData.aliceSession2.getKeysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
                     null,
                     null,
    @@ -895,7 +895,7 @@ class KeysBackupTest : InstrumentedTest {
             // - Try to restore the e2e backup with a password
             val latch2 = CountDownLatch(1)
             var importRoomKeysResult: ImportRoomKeysResult? = null
    -        testData.aliceSession2.getKeysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.getKeysBackupService().keysBackupVersion!!,
    +        testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
                     "password",
                     null,
                     null,
    @@ -924,7 +924,7 @@ class KeysBackupTest : InstrumentedTest {
             // - Create a backup version
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
    -        val keysBackup = cryptoTestData.firstSession.getKeysBackupService()
    +        val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
     
             val stateObserver = StateObserver(keysBackup)
     
    @@ -967,7 +967,7 @@ class KeysBackupTest : InstrumentedTest {
             val signature = keysBackupVersionTrust!!.signatures[0]
             assertTrue(signature.valid)
             assertNotNull(signature.device)
    -        assertEquals(cryptoTestData.firstSession.getMyDevice().deviceId, signature.deviceId)
    +        assertEquals(cryptoTestData.firstSession.cryptoService().getMyDevice().deviceId, signature.deviceId)
             assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.credentials.deviceId)
     
             stateObserver.stopAndCheckStates(null)
    @@ -986,7 +986,7 @@ class KeysBackupTest : InstrumentedTest {
             // - Create a backup version
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
    -        val keysBackup = cryptoTestData.firstSession.getKeysBackupService()
    +        val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
     
             val stateObserver = StateObserver(keysBackup)
     
    @@ -1002,7 +1002,7 @@ class KeysBackupTest : InstrumentedTest {
     
             cryptoTestData.close()
     
    -        val keysBackup2 = aliceSession2.getKeysBackupService()
    +        val keysBackup2 = aliceSession2.cryptoService().keysBackupService()
     
             val stateObserver2 = StateObserver(keysBackup2)
     
    @@ -1046,7 +1046,7 @@ class KeysBackupTest : InstrumentedTest {
             // - Create a backup version
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
    -        val keysBackup = cryptoTestData.firstSession.getKeysBackupService()
    +        val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
     
             val stateObserver = StateObserver(keysBackup)
     
    @@ -1086,7 +1086,7 @@ class KeysBackupTest : InstrumentedTest {
             mTestHelper.await(latch)
     
             // Reset the store backup status for keys
    -        (cryptoTestData.firstSession.getKeysBackupService() as KeysBackup).store.resetBackupMarkers()
    +        (cryptoTestData.firstSession.cryptoService().keysBackupService() as KeysBackup).store.resetBackupMarkers()
     
             // - Make alice back up all her keys again
             val latch2 = CountDownLatch(1)
    @@ -1121,7 +1121,7 @@ class KeysBackupTest : InstrumentedTest {
             // - Create a backup version
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
    -        val keysBackup = cryptoTestData.firstSession.getKeysBackupService()
    +        val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
     
             val stateObserver = StateObserver(keysBackup)
     
    @@ -1147,14 +1147,14 @@ class KeysBackupTest : InstrumentedTest {
             val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, defaultSessionParamsWithInitialSync)
     
             // - Post a message to have a new megolm session
    -        aliceSession2.setWarnOnUnknownDevices(false)
    +        aliceSession2.cryptoService().setWarnOnUnknownDevices(false)
     
             val room2 = aliceSession2.getRoom(cryptoTestData.roomId)!!
     
             mTestHelper.sendTextMessage(room2, "New key", 1)
     
             // - Try to backup all in aliceSession2, it must fail
    -        val keysBackup2 = aliceSession2.getKeysBackupService()
    +        val keysBackup2 = aliceSession2.cryptoService().keysBackupService()
     
             val stateObserver2 = StateObserver(keysBackup2)
     
    @@ -1178,7 +1178,7 @@ class KeysBackupTest : InstrumentedTest {
             assertFalse(keysBackup2.isEnabled)
     
             // - Validate the old device from the new one
    -        aliceSession2.setDeviceVerification(DeviceTrustLevel(false, true), aliceSession2.myUserId, oldDeviceId)
    +        aliceSession2.cryptoService().setDeviceVerification(DeviceTrustLevel(false, true), aliceSession2.myUserId, oldDeviceId)
     
             // -> Backup should automatically enable on the new device
             val latch4 = CountDownLatch(1)
    @@ -1196,14 +1196,14 @@ class KeysBackupTest : InstrumentedTest {
             mTestHelper.await(latch4)
     
             // -> It must use the same backup version
    -        assertEquals(oldKeyBackupVersion, aliceSession2.getKeysBackupService().currentBackupVersion)
    +        assertEquals(oldKeyBackupVersion, aliceSession2.cryptoService().keysBackupService().currentBackupVersion)
     
             val latch5 = CountDownLatch(1)
    -        aliceSession2.getKeysBackupService().backupAllGroupSessions(null, TestMatrixCallback(latch5))
    +        aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, TestMatrixCallback(latch5))
             mTestHelper.await(latch5)
     
             // -> It must success
    -        assertTrue(aliceSession2.getKeysBackupService().isEnabled)
    +        assertTrue(aliceSession2.cryptoService().keysBackupService().isEnabled)
     
             stateObserver.stopAndCheckStates(null)
             stateObserver2.stopAndCheckStates(null)
    @@ -1220,7 +1220,7 @@ class KeysBackupTest : InstrumentedTest {
             // - Create a backup version
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
    -        val keysBackup = cryptoTestData.firstSession.getKeysBackupService()
    +        val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
     
             val stateObserver = StateObserver(keysBackup)
     
    @@ -1254,17 +1254,17 @@ class KeysBackupTest : InstrumentedTest {
          */
         private fun waitForKeysBackupToBeInState(session: Session, state: KeysBackupState) {
             // If already in the wanted state, return
    -        if (session.getKeysBackupService().state == state) {
    +        if (session.cryptoService().keysBackupService().state == state) {
                 return
             }
     
             // Else observe state changes
             val latch = CountDownLatch(1)
     
    -        session.getKeysBackupService().addListener(object : KeysBackupStateListener {
    +        session.cryptoService().keysBackupService().addListener(object : KeysBackupStateListener {
                 override fun onStateChange(newState: KeysBackupState) {
                     if (newState == state) {
    -                    session.getKeysBackupService().removeListener(this)
    +                    session.cryptoService().keysBackupService().removeListener(this)
                         latch.countDown()
                     }
                 }
    @@ -1359,8 +1359,8 @@ class KeysBackupTest : InstrumentedTest {
         private fun createKeysBackupScenarioWithPassword(password: String?): KeysBackupScenarioData {
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
    -        val cryptoStore = (cryptoTestData.firstSession.getKeysBackupService() as KeysBackup).store
    -        val keysBackup = cryptoTestData.firstSession.getKeysBackupService()
    +        val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as KeysBackup).store
    +        val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
     
             val stateObserver = StateObserver(keysBackup)
     
    @@ -1394,7 +1394,7 @@ class KeysBackupTest : InstrumentedTest {
             val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, defaultSessionParamsWithInitialSync)
     
             // Test check: aliceSession2 has no keys at login
    -        assertEquals(0, aliceSession2.inboundGroupSessionsCount(false))
    +        assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false))
     
             // Wait for backup state to be NotTrusted
             waitForKeysBackupToBeInState(aliceSession2, KeysBackupState.NotTrusted)
    @@ -1421,11 +1421,11 @@ class KeysBackupTest : InstrumentedTest {
             assertEquals(total, imported)
     
             // - The new device must have the same count of megolm keys
    -        assertEquals(testData.aliceKeys.size, testData.aliceSession2.inboundGroupSessionsCount(false))
    +        assertEquals(testData.aliceKeys.size, testData.aliceSession2.cryptoService().inboundGroupSessionsCount(false))
     
             // - Alice must have the same keys on both devices
             for (aliceKey1 in testData.aliceKeys) {
    -            val aliceKey2 = (testData.aliceSession2.getKeysBackupService() as KeysBackup).store
    +            val aliceKey2 = (testData.aliceSession2.cryptoService().keysBackupService() as KeysBackup).store
                         .getInboundGroupSession(aliceKey1.olmInboundGroupSession!!.sessionIdentifier(), aliceKey1.senderKey!!)
                 assertNotNull(aliceKey2)
                 assertKeysEquals(aliceKey1.exportKeys(), aliceKey2!!.exportKeys())
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt
    index 79670bb21e..7fad87b82d 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt
    @@ -62,8 +62,8 @@ class SASTest : InstrumentedTest {
             val aliceSession = cryptoTestData.firstSession
             val bobSession = cryptoTestData.secondSession
     
    -        val aliceVerificationService = aliceSession.getVerificationService()
    -        val bobVerificationService = bobSession!!.getVerificationService()
    +        val aliceVerificationService = aliceSession.cryptoService().verificationService()
    +        val bobVerificationService = bobSession!!.cryptoService().verificationService()
     
             val bobTxCreatedLatch = CountDownLatch(1)
             val bobListener = object : VerificationService.Listener {
    @@ -75,7 +75,7 @@ class SASTest : InstrumentedTest {
     
             val txID = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS,
                     bobSession.myUserId,
    -                bobSession.getMyDevice().deviceId,
    +                bobSession.cryptoService().getMyDevice().deviceId,
                     null)
             assertNotNull("Alice should have a started transaction", txID)
     
    @@ -157,7 +157,7 @@ class SASTest : InstrumentedTest {
                     }
                 }
             }
    -        bobSession.getVerificationService().addListener(bobListener)
    +        bobSession.cryptoService().verificationService().addListener(bobListener)
     
             // TODO bobSession!!.dataHandler.addListener(object : MXEventListener() {
             // TODO     override fun onToDeviceEvent(event: Event?) {
    @@ -172,7 +172,7 @@ class SASTest : InstrumentedTest {
     
             val aliceSession = cryptoTestData.firstSession
             val aliceUserID = aliceSession.myUserId
    -        val aliceDevice = aliceSession.getMyDevice().deviceId
    +        val aliceDevice = aliceSession.cryptoService().getMyDevice().deviceId
     
             val aliceListener = object : VerificationService.Listener {
                 override fun transactionUpdated(tx: VerificationTransaction) {
    @@ -181,7 +181,7 @@ class SASTest : InstrumentedTest {
                     }
                 }
             }
    -        aliceSession.getVerificationService().addListener(aliceListener)
    +        aliceSession.cryptoService().verificationService().addListener(aliceListener)
     
             fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols)
     
    @@ -218,7 +218,7 @@ class SASTest : InstrumentedTest {
     
             val aliceSession = cryptoTestData.firstSession
             val aliceUserID = aliceSession.myUserId
    -        val aliceDevice = aliceSession.getMyDevice().deviceId
    +        val aliceDevice = aliceSession.cryptoService().getMyDevice().deviceId
     
             fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac)
     
    @@ -256,7 +256,7 @@ class SASTest : InstrumentedTest {
     
             val aliceSession = cryptoTestData.firstSession
             val aliceUserID = aliceSession.myUserId
    -        val aliceDevice = aliceSession.getMyDevice().deviceId
    +        val aliceDevice = aliceSession.cryptoService().getMyDevice().deviceId
     
             fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes)
     
    @@ -277,7 +277,7 @@ class SASTest : InstrumentedTest {
                                  mac: List = SASDefaultVerificationTransaction.KNOWN_MACS,
                                  codes: List = SASDefaultVerificationTransaction.KNOWN_SHORT_CODES) {
             val startMessage = KeyVerificationStart(
    -                fromDevice = bobSession.getMyDevice().deviceId,
    +                fromDevice = bobSession.cryptoService().getMyDevice().deviceId,
                     method = VerificationMethod.SAS.toValue(),
                     transactionID = tid,
                     keyAgreementProtocols = protocols,
    @@ -307,7 +307,7 @@ class SASTest : InstrumentedTest {
             val aliceSession = cryptoTestData.firstSession
             val bobSession = cryptoTestData.secondSession
     
    -        val aliceVerificationService = aliceSession.getVerificationService()
    +        val aliceVerificationService = aliceSession.cryptoService().verificationService()
     
             val aliceCreatedLatch = CountDownLatch(2)
             val aliceCancelledLatch = CountDownLatch(2)
    @@ -327,7 +327,7 @@ class SASTest : InstrumentedTest {
             aliceVerificationService.addListener(aliceListener)
     
             val bobUserId = bobSession!!.myUserId
    -        val bobDeviceId = bobSession.getMyDevice().deviceId
    +        val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
             aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
             aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
     
    @@ -347,8 +347,8 @@ class SASTest : InstrumentedTest {
             val aliceSession = cryptoTestData.firstSession
             val bobSession = cryptoTestData.secondSession
     
    -        val aliceVerificationService = aliceSession.getVerificationService()
    -        val bobVerificationService = bobSession!!.getVerificationService()
    +        val aliceVerificationService = aliceSession.cryptoService().verificationService()
    +        val bobVerificationService = bobSession!!.cryptoService().verificationService()
     
             var accepted: KeyVerificationAccept? = null
             var startReq: KeyVerificationStart? = null
    @@ -377,7 +377,7 @@ class SASTest : InstrumentedTest {
             bobVerificationService.addListener(bobListener)
     
             val bobUserId = bobSession.myUserId
    -        val bobDeviceId = bobSession.getMyDevice().deviceId
    +        val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
             aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
             mTestHelper.await(aliceAcceptedLatch)
     
    @@ -403,8 +403,8 @@ class SASTest : InstrumentedTest {
             val aliceSession = cryptoTestData.firstSession
             val bobSession = cryptoTestData.secondSession
     
    -        val aliceVerificationService = aliceSession.getVerificationService()
    -        val bobVerificationService = bobSession!!.getVerificationService()
    +        val aliceVerificationService = aliceSession.cryptoService().verificationService()
    +        val bobVerificationService = bobSession!!.cryptoService().verificationService()
     
             val aliceSASLatch = CountDownLatch(1)
             val aliceListener = object : VerificationService.Listener {
    @@ -438,7 +438,7 @@ class SASTest : InstrumentedTest {
             bobVerificationService.addListener(bobListener)
     
             val bobUserId = bobSession.myUserId
    -        val bobDeviceId = bobSession.getMyDevice().deviceId
    +        val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
             val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
             mTestHelper.await(aliceSASLatch)
             mTestHelper.await(bobSASLatch)
    @@ -459,8 +459,8 @@ class SASTest : InstrumentedTest {
             val aliceSession = cryptoTestData.firstSession
             val bobSession = cryptoTestData.secondSession
     
    -        val aliceVerificationService = aliceSession.getVerificationService()
    -        val bobVerificationService = bobSession!!.getVerificationService()
    +        val aliceVerificationService = aliceSession.cryptoService().verificationService()
    +        val bobVerificationService = bobSession!!.cryptoService().verificationService()
     
             val aliceSASLatch = CountDownLatch(1)
             val aliceListener = object : VerificationService.Listener {
    @@ -500,14 +500,14 @@ class SASTest : InstrumentedTest {
             bobVerificationService.addListener(bobListener)
     
             val bobUserId = bobSession.myUserId
    -        val bobDeviceId = bobSession.getMyDevice().deviceId
    +        val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
             aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
             mTestHelper.await(aliceSASLatch)
             mTestHelper.await(bobSASLatch)
     
             // Assert that devices are verified
    -        val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.getDeviceInfo(bobUserId, bobDeviceId)
    -        val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = bobSession.getDeviceInfo(aliceSession.myUserId, aliceSession.getMyDevice().deviceId)
    +        val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId)
    +        val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = bobSession.cryptoService().getDeviceInfo(aliceSession.myUserId, aliceSession.cryptoService().getMyDevice().deviceId)
     
             // latch wait a bit again
             Thread.sleep(1000)
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/VerificationTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/VerificationTest.kt
    index 61ea0f35b4..d62aef077e 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/VerificationTest.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/VerificationTest.kt
    @@ -156,7 +156,7 @@ class VerificationTest : InstrumentedTest {
             val bobSession = cryptoTestData.secondSession!!
     
             mTestHelper.doSync { callback ->
    -            aliceSession.getCrossSigningService()
    +            aliceSession.cryptoService().crossSigningService()
                         .initializeCrossSigning(UserPasswordAuth(
                                 user = aliceSession.myUserId,
                                 password = TestConstants.PASSWORD
    @@ -164,15 +164,15 @@ class VerificationTest : InstrumentedTest {
             }
     
             mTestHelper.doSync { callback ->
    -            bobSession.getCrossSigningService()
    +            bobSession.cryptoService().crossSigningService()
                         .initializeCrossSigning(UserPasswordAuth(
                                 user = bobSession.myUserId,
                                 password = TestConstants.PASSWORD
                         ), callback)
             }
     
    -        val aliceVerificationService = aliceSession.getVerificationService()
    -        val bobVerificationService = bobSession.getVerificationService()
    +        val aliceVerificationService = aliceSession.cryptoService().verificationService()
    +        val bobVerificationService = bobSession.cryptoService().verificationService()
     
             var aliceReadyPendingVerificationRequest: PendingVerificationRequest? = null
             var bobReadyPendingVerificationRequest: PendingVerificationRequest? = null
    
    From 6d2025b61ad12de2e4c1b3308951315533565c08 Mon Sep 17 00:00:00 2001
    From: ganfra
     
    Date: Mon, 17 Feb 2020 15:54:17 +0100
    Subject: [PATCH 049/187] Update CHANGES
    
    ---
     CHANGES.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/CHANGES.md b/CHANGES.md
    index 3e771a79d4..91d3c5d7e3 100644
    --- a/CHANGES.md
    +++ b/CHANGES.md
    @@ -15,7 +15,7 @@ Translations 🗣:
      -
     
     SDK API changes ⚠️:
    - -
    + - Get crypto methods through Session.cryptoService()
     
     Build 🧱:
      -
    
    From d67cd2cbef241c714bb2efc7da524500d779a838 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 16:21:10 +0100
    Subject: [PATCH 050/187] Small iteration on UI
    
    ---
     .../src/main/res/layout/fragment_attachments_preview.xml  | 8 +++++---
     1 file changed, 5 insertions(+), 3 deletions(-)
    
    diff --git a/vector/src/main/res/layout/fragment_attachments_preview.xml b/vector/src/main/res/layout/fragment_attachments_preview.xml
    index 18009cd1a6..ec99cce9b3 100644
    --- a/vector/src/main/res/layout/fragment_attachments_preview.xml
    +++ b/vector/src/main/res/layout/fragment_attachments_preview.xml
    @@ -20,16 +20,18 @@
             style="@style/VectorToolbarStyle"
             android:layout_width="match_parent"
             android:layout_height="?attr/actionBarSize"
    -        android:background="@android:color/transparent"
    +        android:background="#40000000"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
    -        app:layout_constraintTop_toTopOf="parent" />
    +        app:layout_constraintTop_toTopOf="parent"
    +        tools:title="Title"
    +        tools:titleTextColor="@color/white" />
     
         
    
    From 82bd8a2e2ac311b160b1883190236b06ba37820c Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 16:29:10 +0100
    Subject: [PATCH 051/187] Ask for write permission before editing media
    
    ---
     .../riotx/core/utils/PermissionsTools.kt      |  1 +
     .../preview/AttachmentsPreviewFragment.kt     | 21 ++++++++++++++++++-
     2 files changed, 21 insertions(+), 1 deletion(-)
    
    diff --git a/vector/src/main/java/im/vector/riotx/core/utils/PermissionsTools.kt b/vector/src/main/java/im/vector/riotx/core/utils/PermissionsTools.kt
    index f8cdeb3de6..4790b26ad0 100644
    --- a/vector/src/main/java/im/vector/riotx/core/utils/PermissionsTools.kt
    +++ b/vector/src/main/java/im/vector/riotx/core/utils/PermissionsTools.kt
    @@ -68,6 +68,7 @@ const val PERMISSION_REQUEST_CODE_CHANGE_AVATAR = 574
     const val PERMISSION_REQUEST_CODE_DOWNLOAD_FILE = 575
     const val PERMISSION_REQUEST_CODE_PICK_ATTACHMENT = 576
     const val PERMISSION_REQUEST_CODE_INCOMING_URI = 577
    +const val PERMISSION_REQUEST_CODE_PREVIEW_FRAGMENT = 578
     
     /**
      * Log the used permissions statuses.
    diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    index 25f3ebc2d9..60765c495d 100644
    --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    @@ -45,8 +45,12 @@ import im.vector.riotx.core.extensions.cleanup
     import im.vector.riotx.core.platform.VectorBaseFragment
     import im.vector.riotx.core.resources.ColorProvider
     import im.vector.riotx.core.utils.OnSnapPositionChangeListener
    +import im.vector.riotx.core.utils.PERMISSIONS_FOR_WRITING_FILES
    +import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_PREVIEW_FRAGMENT
     import im.vector.riotx.core.utils.SnapOnScrollListener
    +import im.vector.riotx.core.utils.allGranted
     import im.vector.riotx.core.utils.attachSnapHelperWithListener
    +import im.vector.riotx.core.utils.checkPermissions
     import kotlinx.android.parcel.Parcelize
     import kotlinx.android.synthetic.main.fragment_attachments_preview.*
     import timber.log.Timber
    @@ -178,7 +182,22 @@ class AttachmentsPreviewFragment @Inject constructor(
             viewModel.handle(AttachmentsPreviewAction.RemoveCurrentAttachment)
         }
     
    -    private fun handleEditAction() = withState(viewModel) {
    +    private fun handleEditAction() {
    +        // check permissions
    +        if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this, PERMISSION_REQUEST_CODE_PREVIEW_FRAGMENT)) {
    +            doHandleEditAction()
    +        }
    +    }
    +
    +    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
    +        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    +
    +        if (requestCode == PERMISSION_REQUEST_CODE_PREVIEW_FRAGMENT && allGranted(grantResults)) {
    +            doHandleEditAction()
    +        }
    +    }
    +
    +    private fun doHandleEditAction() = withState(viewModel) {
             val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState
             val destinationFile = File(requireContext().cacheDir, "${currentAttachment.name}_edited_image_${System.currentTimeMillis()}")
             UCrop.of(currentAttachment.queryUri.toUri(), destinationFile.toUri())
    
    From 51bbee297c04e966b6fd499890fe91d053a00327 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 17:06:15 +0100
    Subject: [PATCH 052/187] Remove useless confirmation when shared data are
     previewables
    
    ---
     .../vector/riotx/features/share/IncomingShareViewModel.kt   | 6 +++++-
     1 file changed, 5 insertions(+), 1 deletion(-)
    
    diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    index 3988643f02..7a0ce8bc47 100644
    --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    @@ -22,6 +22,7 @@ import com.airbnb.mvrx.ViewModelContext
     import com.jakewharton.rxrelay2.BehaviorRelay
     import com.squareup.inject.assisted.Assisted
     import com.squareup.inject.assisted.AssistedInject
    +import im.vector.matrix.android.api.extensions.orFalse
     import im.vector.matrix.android.api.query.QueryStringValue
     import im.vector.matrix.android.api.session.Session
     import im.vector.matrix.android.api.session.content.ContentAttachmentData
    @@ -30,6 +31,7 @@ import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
     import im.vector.matrix.rx.rx
     import im.vector.riotx.core.extensions.exhaustive
     import im.vector.riotx.core.platform.VectorViewModel
    +import im.vector.riotx.features.attachments.isPreviewable
     import im.vector.riotx.features.attachments.toGroupedContentAttachmentData
     import im.vector.riotx.features.home.room.list.BreadcrumbsRoomComparator
     import java.util.concurrent.TimeUnit
    @@ -188,7 +190,9 @@ class IncomingShareViewModel @AssistedInject constructor(
                 setState { copy(isInMultiSelectionMode = true, selectedRoomIds = setOf(action.roomSummary.roomId)) }
             } else {
                 val sharedData = state.sharedData ?: return@withState
    -            _viewEvents.post(IncomingShareViewEvents.ShareToRoom(action.roomSummary, sharedData, showAlert = true))
    +            // Do not show alert if the shared data contains only previewable attachments, because the user will get another chance to cancel the share
    +            val doNotShowAlert = (sharedData as? SharedData.Attachments)?.attachmentData?.all { it.isPreviewable() }.orFalse()
    +            _viewEvents.post(IncomingShareViewEvents.ShareToRoom(action.roomSummary, sharedData, !doNotShowAlert))
             }
         }
     }
    
    From 002e881704ebb5c128f9f4aa5727759ac7f111bc Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 17:26:19 +0100
    Subject: [PATCH 053/187] Fix multiple share intent issue
    
    ---
     .../features/share/IncomingShareFragment.kt   | 22 ++++++++++---------
     1 file changed, 12 insertions(+), 10 deletions(-)
    
    diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt
    index 1ba00ad50c..eb0f5128ba 100644
    --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt
    @@ -76,17 +76,19 @@ class IncomingShareFragment @Inject constructor(
             attachmentsHelper = AttachmentsHelper.create(this, this).register()
     
             val intent = vectorBaseActivity.intent
    -        if (intent?.action == Intent.ACTION_SEND || intent?.action == Intent.ACTION_SEND_MULTIPLE) {
    -            var isShareManaged = attachmentsHelper.handleShareIntent(
    -                    IntentUtils.getPickerIntentForSharing(intent)
    -            )
    -            if (!isShareManaged) {
    -                isShareManaged = handleTextShare(intent)
    +        val isShareManaged = when (intent?.action) {
    +            Intent.ACTION_SEND          -> {
    +                var isShareManaged = attachmentsHelper.handleShareIntent(IntentUtils.getPickerIntentForSharing(intent))
    +                if (!isShareManaged) {
    +                    isShareManaged = handleTextShare(intent)
    +                }
    +                isShareManaged
                 }
    -            if (!isShareManaged) {
    -                cannotManageShare(R.string.error_handling_incoming_share)
    -            }
    -        } else {
    +            Intent.ACTION_SEND_MULTIPLE -> attachmentsHelper.handleShareIntent(intent)
    +            else                        -> false
    +        }
    +
    +        if (!isShareManaged) {
                 cannotManageShare(R.string.error_handling_incoming_share)
             }
     
    
    From 4995c14f69765c1cd69d3b7c8f82b95484a31e45 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 17:46:15 +0100
    Subject: [PATCH 054/187] Add log
    
    ---
     .../session/room/send/DefaultSendService.kt   | 27 +++++++++++++------
     1 file changed, 19 insertions(+), 8 deletions(-)
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt
    index 05dc3098f1..15494728c3 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt
    @@ -233,17 +233,19 @@ internal class DefaultSendService @AssistedInject constructor(
     
                 val dispatcherWork = createMultipleEventDispatcherWork(true)
     
    -            val operation = workManagerProvider.workManager
    +            workManagerProvider.workManager
                         .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
                         .then(dispatcherWork)
                         .enqueue()
    -            operation.result.addListener(Runnable {
    -                if (operation.result.isCancelled) {
    -                    Timber.e("CHAIN WAS CANCELLED")
    -                } else if (operation.state.value is Operation.State.FAILURE) {
    -                    Timber.e("CHAIN DID FAIL")
    -                }
    -            }, workerFutureListenerExecutor)
    +                    .also { operation ->
    +                        operation.result.addListener(Runnable {
    +                            if (operation.result.isCancelled) {
    +                                Timber.e("CHAIN WAS CANCELLED")
    +                            } else if (operation.state.value is Operation.State.FAILURE) {
    +                                Timber.e("CHAIN DID FAIL")
    +                            }
    +                        }, workerFutureListenerExecutor)
    +                    }
     
                 cancelableBag.add(CancelableWork(workManagerProvider.workManager, dispatcherWork.id))
             }
    @@ -256,6 +258,15 @@ internal class DefaultSendService @AssistedInject constructor(
                         .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
                         .then(dispatcherWork)
                         .enqueue()
    +                    .also { operation ->
    +                        operation.result.addListener(Runnable {
    +                            if (operation.result.isCancelled) {
    +                                Timber.e("CHAIN WAS CANCELLED")
    +                            } else if (operation.state.value is Operation.State.FAILURE) {
    +                                Timber.e("CHAIN DID FAIL")
    +                            }
    +                        }, workerFutureListenerExecutor)
    +                    }
     
                 cancelableBag.add(CancelableWork(workManagerProvider.workManager, dispatcherWork.id))
             }
    
    From f2f94c4a936649f4b72b35c1cba12255ab84ea3f Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 17:56:11 +0100
    Subject: [PATCH 055/187] Reduce code duplication
    
    ---
     .../session/room/send/DefaultSendService.kt   | 67 +++++++------------
     1 file changed, 23 insertions(+), 44 deletions(-)
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt
    index 15494728c3..a99337695a 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt
    @@ -221,55 +221,34 @@ internal class DefaultSendService @AssistedInject constructor(
          * We use the roomId of the local echo event
          */
         private fun internalSendMedia(allLocalEchoes: List, attachment: ContentAttachmentData, compressBeforeSending: Boolean): Cancelable {
    -        val splitLocalEchoes = allLocalEchoes.groupBy { cryptoService.isRoomEncrypted(it.roomId!!) }
    -
    -        val encryptedLocalEchoes = splitLocalEchoes[true].orEmpty()
    -        val clearLocalEchoes = splitLocalEchoes[false].orEmpty()
    -
             val cancelableBag = CancelableBag()
     
    -        if (encryptedLocalEchoes.isNotEmpty()) {
    -            val uploadWork = createUploadMediaWork(encryptedLocalEchoes, attachment, true, compressBeforeSending, startChain = true)
    +        allLocalEchoes.groupBy { cryptoService.isRoomEncrypted(it.roomId!!) }
    +                .apply {
    +                    keys.forEach { isRoomEncrypted ->
    +                        // Should never be empty
    +                        val localEchoes = get(isRoomEncrypted).orEmpty()
    +                        val uploadWork = createUploadMediaWork(localEchoes, attachment, isRoomEncrypted, compressBeforeSending, startChain = true)
     
    -            val dispatcherWork = createMultipleEventDispatcherWork(true)
    +                        val dispatcherWork = createMultipleEventDispatcherWork(isRoomEncrypted)
     
    -            workManagerProvider.workManager
    -                    .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
    -                    .then(dispatcherWork)
    -                    .enqueue()
    -                    .also { operation ->
    -                        operation.result.addListener(Runnable {
    -                            if (operation.result.isCancelled) {
    -                                Timber.e("CHAIN WAS CANCELLED")
    -                            } else if (operation.state.value is Operation.State.FAILURE) {
    -                                Timber.e("CHAIN DID FAIL")
    -                            }
    -                        }, workerFutureListenerExecutor)
    +                        workManagerProvider.workManager
    +                                .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
    +                                .then(dispatcherWork)
    +                                .enqueue()
    +                                .also { operation ->
    +                                    operation.result.addListener(Runnable {
    +                                        if (operation.result.isCancelled) {
    +                                            Timber.e("CHAIN WAS CANCELLED")
    +                                        } else if (operation.state.value is Operation.State.FAILURE) {
    +                                            Timber.e("CHAIN DID FAIL")
    +                                        }
    +                                    }, workerFutureListenerExecutor)
    +                                }
    +
    +                        cancelableBag.add(CancelableWork(workManagerProvider.workManager, dispatcherWork.id))
                         }
    -
    -            cancelableBag.add(CancelableWork(workManagerProvider.workManager, dispatcherWork.id))
    -        }
    -
    -        if (clearLocalEchoes.isNotEmpty()) {
    -            val uploadWork = createUploadMediaWork(clearLocalEchoes, attachment, false, compressBeforeSending, startChain = true)
    -            val dispatcherWork = createMultipleEventDispatcherWork(false)
    -
    -            workManagerProvider.workManager
    -                    .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
    -                    .then(dispatcherWork)
    -                    .enqueue()
    -                    .also { operation ->
    -                        operation.result.addListener(Runnable {
    -                            if (operation.result.isCancelled) {
    -                                Timber.e("CHAIN WAS CANCELLED")
    -                            } else if (operation.state.value is Operation.State.FAILURE) {
    -                                Timber.e("CHAIN DID FAIL")
    -                            }
    -                        }, workerFutureListenerExecutor)
    -                    }
    -
    -            cancelableBag.add(CancelableWork(workManagerProvider.workManager, dispatcherWork.id))
    -        }
    +                }
     
             return cancelableBag
         }
    
    From 7495533edc160a3574344de749876fdebafc2978 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 18:16:37 +0100
    Subject: [PATCH 056/187] Use let
    
    ---
     .../preview/AttachmentsPreviewFragment.kt     | 34 +++++++++++--------
     1 file changed, 20 insertions(+), 14 deletions(-)
    
    diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    index 60765c495d..e78cc09083 100644
    --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt
    @@ -128,6 +128,7 @@ class AttachmentsPreviewFragment @Inject constructor(
             super.onDestroyView()
             attachmentPreviewerMiniatureList.cleanup()
             attachmentPreviewerBigList.cleanup()
    +        attachmentMiniaturePreviewController.callback = null
         }
     
         override fun invalidate() = withState(viewModel) { state ->
    @@ -229,21 +230,26 @@ class AttachmentsPreviewFragment @Inject constructor(
         }
     
         private fun setupRecyclerViews() {
    -        attachmentPreviewerMiniatureList.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
    -        attachmentPreviewerMiniatureList.setHasFixedSize(true)
    -        attachmentPreviewerMiniatureList.adapter = attachmentMiniaturePreviewController.adapter
             attachmentMiniaturePreviewController.callback = this
     
    -        attachmentPreviewerBigList.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
    -        attachmentPreviewerBigList.attachSnapHelperWithListener(
    -                PagerSnapHelper(),
    -                SnapOnScrollListener.Behavior.NOTIFY_ON_SCROLL_STATE_IDLE,
    -                object : OnSnapPositionChangeListener {
    -                    override fun onSnapPositionChange(position: Int) {
    -                        viewModel.handle(AttachmentsPreviewAction.SetCurrentAttachment(position))
    -                    }
    -                })
    -        attachmentPreviewerBigList.setHasFixedSize(true)
    -        attachmentPreviewerBigList.adapter = attachmentBigPreviewController.adapter
    +        attachmentPreviewerMiniatureList.let {
    +            it.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
    +            it.setHasFixedSize(true)
    +            it.adapter = attachmentMiniaturePreviewController.adapter
    +        }
    +
    +        attachmentPreviewerBigList.let {
    +            it.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
    +            it.attachSnapHelperWithListener(
    +                    PagerSnapHelper(),
    +                    SnapOnScrollListener.Behavior.NOTIFY_ON_SCROLL_STATE_IDLE,
    +                    object : OnSnapPositionChangeListener {
    +                        override fun onSnapPositionChange(position: Int) {
    +                            viewModel.handle(AttachmentsPreviewAction.SetCurrentAttachment(position))
    +                        }
    +                    })
    +            it.setHasFixedSize(true)
    +            it.adapter = attachmentBigPreviewController.adapter
    +        }
         }
     }
    
    From 141c7d6af02f34ad48eed5a56ce4a089720dbbc8 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 18:43:04 +0100
    Subject: [PATCH 057/187] Update comment
    
    ---
     .../matrix/android/internal/task/CoroutineSequencer.kt       | 5 +++--
     1 file changed, 3 insertions(+), 2 deletions(-)
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/CoroutineSequencer.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/CoroutineSequencer.kt
    index d36b769ef3..46cc446b87 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/CoroutineSequencer.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/CoroutineSequencer.kt
    @@ -20,7 +20,7 @@ import kotlinx.coroutines.sync.Semaphore
     import kotlinx.coroutines.sync.withPermit
     
     /**
    - * This class intends to be used for ensure suspendable methods are played sequentially all the way long.
    + * This class intends to be used to ensure suspendable methods are played sequentially all the way long.
      */
     internal interface CoroutineSequencer {
         /**
    @@ -32,7 +32,8 @@ internal interface CoroutineSequencer {
     
     internal open class SemaphoreCoroutineSequencer : CoroutineSequencer {
     
    -    private val semaphore = Semaphore(1) // Permits 1 suspend function at a time.
    +    // Permits 1 suspend function at a time.
    +    private val semaphore = Semaphore(1)
     
         override suspend fun  post(block: suspend () -> T): T {
             return semaphore.withPermit {
    
    From f716e9d7890baf8dd7d0a0d5865331f444795a6e Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 18:49:45 +0100
    Subject: [PATCH 058/187] Fix compilation issue
    
    ---
     .../restore/KeysBackupRestoreFromPassphraseViewModel.kt         | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
    index 4e86f60909..92a3504b5c 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
    @@ -49,7 +49,7 @@ class KeysBackupRestoreFromPassphraseViewModel @Inject constructor() : ViewModel
         }
     
         fun recoverKeys(context: Context, sharedViewModel: KeysBackupRestoreSharedViewModel) {
    -        val keysBackup = sharedViewModel.session.getKeysBackupService()
    +        val keysBackup = sharedViewModel.session.cryptoService().keysBackupService()
     
             passphraseErrorText.value = null
     
    
    From 67ed86fee5c305b53c8e8fc18c68b4bf8d93dd32 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 18:51:06 +0100
    Subject: [PATCH 059/187] Rename KeysBackup to DefaultKeysBackupService
    
    ---
     .../internal/crypto/keysbackup/KeysBackupTest.kt   | 14 +++++++-------
     .../internal/crypto/DefaultCryptoService.kt        | 10 +++++-----
     .../crypto/actions/SetDeviceVerificationAction.kt  |  6 +++---
     .../internal/crypto/algorithms/IMXDecrypting.kt    |  4 ++--
     .../crypto/algorithms/megolm/MXMegolmDecryption.kt |  6 +++---
     .../crypto/algorithms/megolm/MXMegolmEncryption.kt |  6 +++---
     .../algorithms/megolm/MXMegolmEncryptionFactory.kt |  6 +++---
     .../{KeysBackup.kt => DefaultKeysBackupService.kt} |  5 ++---
     8 files changed, 28 insertions(+), 29 deletions(-)
     rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/{KeysBackup.kt => DefaultKeysBackupService.kt} (99%)
    
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
    index 66ce44ad4f..2edc0c3976 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
    @@ -77,7 +77,7 @@ class KeysBackupTest : InstrumentedTest {
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
             // From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
    -        val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as KeysBackup).store
    +        val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
             val sessions = cryptoStore.inboundGroupSessionsToBackup(100)
             val sessionsCount = sessions.size
     
    @@ -293,7 +293,7 @@ class KeysBackupTest : InstrumentedTest {
         fun testEncryptAndDecryptKeysBackupData() {
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
    -        val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() as KeysBackup
    +        val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService
     
             val stateObserver = StateObserver(keysBackup)
     
    @@ -373,7 +373,7 @@ class KeysBackupTest : InstrumentedTest {
             val testData = createKeysBackupScenarioWithPassword(null)
     
             // - Check the SDK sent key share requests
    -        val cryptoStore2 = (testData.aliceSession2.cryptoService().keysBackupService() as KeysBackup).store
    +        val cryptoStore2 = (testData.aliceSession2.cryptoService().keysBackupService() as DefaultKeysBackupService).store
             val unsentRequest = cryptoStore2
                     .getOutgoingRoomKeyRequestByState(setOf(OutgoingRoomKeyRequest.RequestState.UNSENT))
             val sentRequest = cryptoStore2
    @@ -1082,11 +1082,11 @@ class KeysBackupTest : InstrumentedTest {
             val latch = CountDownLatch(1)
     
             val megolmBackupCreationInfo = mCryptoTestHelper.createFakeMegolmBackupCreationInfo()
    -        (keysBackup as KeysBackup).createFakeKeysBackupVersion(megolmBackupCreationInfo, TestMatrixCallback(latch))
    +        (keysBackup as DefaultKeysBackupService).createFakeKeysBackupVersion(megolmBackupCreationInfo, TestMatrixCallback(latch))
             mTestHelper.await(latch)
     
             // Reset the store backup status for keys
    -        (cryptoTestData.firstSession.cryptoService().keysBackupService() as KeysBackup).store.resetBackupMarkers()
    +        (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store.resetBackupMarkers()
     
             // - Make alice back up all her keys again
             val latch2 = CountDownLatch(1)
    @@ -1359,7 +1359,7 @@ class KeysBackupTest : InstrumentedTest {
         private fun createKeysBackupScenarioWithPassword(password: String?): KeysBackupScenarioData {
             val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
     
    -        val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as KeysBackup).store
    +        val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
             val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
     
             val stateObserver = StateObserver(keysBackup)
    @@ -1425,7 +1425,7 @@ class KeysBackupTest : InstrumentedTest {
     
             // - Alice must have the same keys on both devices
             for (aliceKey1 in testData.aliceKeys) {
    -            val aliceKey2 = (testData.aliceSession2.cryptoService().keysBackupService() as KeysBackup).store
    +            val aliceKey2 = (testData.aliceSession2.cryptoService().keysBackupService() as DefaultKeysBackupService).store
                         .getInboundGroupSession(aliceKey1.olmInboundGroupSession!!.sessionIdentifier(), aliceKey1.senderKey!!)
                 assertNotNull(aliceKey2)
                 assertKeysEquals(aliceKey1.exportKeys(), aliceKey2!!.exportKeys())
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    index 593cbb493c..b0acb031fc 100755
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    @@ -49,7 +49,7 @@ import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryp
     import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFactory
     import im.vector.matrix.android.internal.crypto.crosssigning.DefaultCrossSigningService
     import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
    -import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
    +import im.vector.matrix.android.internal.crypto.keysbackup.DefaultKeysBackupService
     import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
     import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
     import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
    @@ -122,7 +122,7 @@ internal class DefaultCryptoService @Inject constructor(
             // Device list manager
             private val deviceListManager: DeviceListManager,
             // The key backup service.
    -        private val keysBackup: KeysBackup,
    +        private val keysBackupService: DefaultKeysBackupService,
             //
             private val objectSigner: ObjectSigner,
             //
    @@ -301,7 +301,7 @@ internal class DefaultCryptoService @Inject constructor(
                 uploadDeviceKeys()
                 oneTimeKeysUploader.maybeUploadOneTimeKeys()
                 outgoingRoomKeyRequestManager.start()
    -            keysBackup.checkAndStartKeysBackup()
    +            keysBackupService.checkAndStartKeysBackup()
                 if (isInitialSync) {
                     // refresh the devices list for each known room members
                     deviceListManager.invalidateAllDeviceLists()
    @@ -340,7 +340,7 @@ internal class DefaultCryptoService @Inject constructor(
         /**
          * @return the Keys backup Service
          */
    -    override fun keysBackupService() = keysBackup
    +    override fun keysBackupService() = keysBackupService
     
         /**
          * @return the VerificationService
    @@ -721,7 +721,7 @@ internal class DefaultCryptoService @Inject constructor(
                 Timber.e("## onRoomKeyEvent() : Unable to handle keys for ${roomKeyContent.algorithm}")
                 return
             }
    -        alg.onRoomKeyEvent(event, keysBackup)
    +        alg.onRoomKeyEvent(event, keysBackupService)
         }
     
         /**
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt
    index 8dad832617..d6538f041d 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt
    @@ -17,7 +17,7 @@
     package im.vector.matrix.android.internal.crypto.actions
     
     import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
    -import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
    +import im.vector.matrix.android.internal.crypto.keysbackup.DefaultKeysBackupService
     import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
     import im.vector.matrix.android.internal.di.UserId
     import timber.log.Timber
    @@ -26,7 +26,7 @@ import javax.inject.Inject
     internal class SetDeviceVerificationAction @Inject constructor(
             private val cryptoStore: IMXCryptoStore,
             @UserId private val userId: String,
    -        private val keysBackup: KeysBackup) {
    +        private val defaultKeysBackupService: DefaultKeysBackupService) {
     
         fun handle(trustLevel: DeviceTrustLevel, userId: String, deviceId: String) {
             val device = cryptoStore.getUserDevice(userId, deviceId)
    @@ -42,7 +42,7 @@ internal class SetDeviceVerificationAction @Inject constructor(
                     // If one of the user's own devices is being marked as verified / unverified,
                     // check the key backup status, since whether or not we use this depends on
                     // whether it has a signature from a verified device
    -                keysBackup.checkAndStartKeysBackup()
    +                defaultKeysBackupService.checkAndStartKeysBackup()
                 }
             }
     
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt
    index f63eaa93b3..e8fb6be6ff 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt
    @@ -20,7 +20,7 @@ package im.vector.matrix.android.internal.crypto.algorithms
     import im.vector.matrix.android.api.session.events.model.Event
     import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
     import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
    -import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
    +import im.vector.matrix.android.internal.crypto.keysbackup.DefaultKeysBackupService
     
     /**
      * An interface for decrypting data
    @@ -41,7 +41,7 @@ internal interface IMXDecrypting {
          *
          * @param event the key event.
          */
    -    fun onRoomKeyEvent(event: Event, keysBackup: KeysBackup) {}
    +    fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {}
     
         /**
          * Check if the some messages can be decrypted with a new session
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt
    index 9121ce3fcb..054b38ad06 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt
    @@ -30,7 +30,7 @@ import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequestManager
     import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
     import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
     import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting
    -import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
    +import im.vector.matrix.android.internal.crypto.keysbackup.DefaultKeysBackupService
     import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
     import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
     import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent
    @@ -198,7 +198,7 @@ internal class MXMegolmDecryption(private val userId: String,
          *
          * @param event the key event.
          */
    -    override fun onRoomKeyEvent(event: Event, keysBackup: KeysBackup) {
    +    override fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {
             var exportFormat = false
             val roomKeyContent = event.getClearContent().toModel() ?: return
     
    @@ -262,7 +262,7 @@ internal class MXMegolmDecryption(private val userId: String,
                     exportFormat)
     
             if (added) {
    -            keysBackup.maybeBackupKeys()
    +            defaultKeysBackupService.maybeBackupKeys()
     
                 val content = RoomKeyRequestBody()
     
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt
    index ee35810763..a2d21c4f89 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt
    @@ -28,7 +28,7 @@ import im.vector.matrix.android.internal.crypto.MXOlmDevice
     import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
     import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
     import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting
    -import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
    +import im.vector.matrix.android.internal.crypto.keysbackup.DefaultKeysBackupService
     import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
     import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
     import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
    @@ -42,7 +42,7 @@ internal class MXMegolmEncryption(
             // The id of the room we will be sending to.
             private var roomId: String,
             private val olmDevice: MXOlmDevice,
    -        private val keysBackup: KeysBackup,
    +        private val defaultKeysBackupService: DefaultKeysBackupService,
             private val cryptoStore: IMXCryptoStore,
             private val deviceListManager: DeviceListManager,
             private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
    @@ -85,7 +85,7 @@ internal class MXMegolmEncryption(
             olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
                     emptyList(), keysClaimedMap, false)
     
    -        keysBackup.maybeBackupKeys()
    +        defaultKeysBackupService.maybeBackupKeys()
     
             return MXOutboundSessionInfo(sessionId)
         }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt
    index dadd810a4b..e9fe902f1f 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt
    @@ -21,7 +21,7 @@ import im.vector.matrix.android.internal.crypto.DeviceListManager
     import im.vector.matrix.android.internal.crypto.MXOlmDevice
     import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
     import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
    -import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
    +import im.vector.matrix.android.internal.crypto.keysbackup.DefaultKeysBackupService
     import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
     import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
     import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
    @@ -29,7 +29,7 @@ import javax.inject.Inject
     
     internal class MXMegolmEncryptionFactory @Inject constructor(
             private val olmDevice: MXOlmDevice,
    -        private val keysBackup: KeysBackup,
    +        private val defaultKeysBackupService: DefaultKeysBackupService,
             private val cryptoStore: IMXCryptoStore,
             private val deviceListManager: DeviceListManager,
             private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
    @@ -42,7 +42,7 @@ internal class MXMegolmEncryptionFactory @Inject constructor(
             return MXMegolmEncryption(
                     roomId,
                     olmDevice,
    -                keysBackup,
    +                defaultKeysBackupService,
                     cryptoStore,
                     deviceListManager,
                     ensureOlmSessionsForDevicesAction,
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt
    similarity index 99%
    rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt
    rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt
    index 595b55a7a6..a630467d4c 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt
    @@ -92,12 +92,11 @@ import javax.inject.Inject
     import kotlin.random.Random
     
     /**
    - * A KeysBackup class instance manage incremental backup of e2e keys (megolm keys)
    + * A DefaultKeysBackupService class instance manage incremental backup of e2e keys (megolm keys)
      * to the user's homeserver.
      */
    -
     @SessionScope
    -internal class KeysBackup @Inject constructor(
    +internal class DefaultKeysBackupService @Inject constructor(
             @UserId private val userId: String,
             private val credentials: Credentials,
             private val cryptoStore: IMXCryptoStore,
    
    From b811bf9e7f6ac80520a32c1ab17d21ae12e6a9e9 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Mon, 17 Feb 2020 23:21:08 +0100
    Subject: [PATCH 060/187] Fix issue after merge
    
    ---
     .../im/vector/riotx/features/navigation/DefaultNavigator.kt     | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt
    index 6486e79cc8..2925c3cefe 100644
    --- a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt
    @@ -66,7 +66,7 @@ class DefaultNavigator @Inject constructor(
     
         override fun performDeviceVerification(context: Context, otherUserId: String, sasTransactionId: String) {
             val session = sessionHolder.getSafeActiveSession() ?: return
    -        val tx = session.cryptoService().verificationService().getExistingTransaction(otherUserId, sasTransationId) ?: return
    +        val tx = session.cryptoService().verificationService().getExistingTransaction(otherUserId, sasTransactionId) ?: return
             (tx as? IncomingSasVerificationTransaction)?.performAccept()
             if (context is VectorBaseActivity) {
                 VerificationBottomSheet.withArgs(
    
    From 7133d513b491ee9ba0703ec8cc06bebf39529b05 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Tue, 18 Feb 2020 10:40:30 +0100
    Subject: [PATCH 061/187] Fix crash in the room directory, when public room has
     no name (#1023)
    
    ---
     CHANGES.md                                             |  1 +
     .../room/model/roomdirectory/PublicRoomsResponse.kt    |  8 ++++----
     .../im/vector/matrix/android/api/util/MatrixItem.kt    | 10 ++++++++--
     .../vector/riotx/core/platform/EllipsizingTextView.kt  |  2 +-
     4 files changed, 14 insertions(+), 7 deletions(-)
    
    diff --git a/CHANGES.md b/CHANGES.md
    index 0397e27312..4f729ba0a8 100644
    --- a/CHANGES.md
    +++ b/CHANGES.md
    @@ -13,6 +13,7 @@ Improvements 🙌:
     
     Bugfix 🐛:
      - Account creation: wrongly hints that an email can be used to create an account (#941)
    + - Fix crash in the room directory, when public room has no name (#1023)
     
     Translations 🗣:
      -
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsResponse.kt
    index 3799d097fc..b83fa51491 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsResponse.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsResponse.kt
    @@ -27,24 +27,24 @@ data class PublicRoomsResponse(
              * A pagination token for the response. The absence of this token means there are no more results to fetch and the client should stop paginating.
              */
             @Json(name = "next_batch")
    -        var nextBatch: String? = null,
    +        val nextBatch: String? = null,
     
             /**
              * A pagination token that allows fetching previous results. The absence of this token means there are no results before this batch,
              * i.e. this is the first batch.
              */
             @Json(name = "prev_batch")
    -        var prevBatch: String? = null,
    +        val prevBatch: String? = null,
     
             /**
              * A paginated chunk of public rooms.
              */
             @Json(name = "chunk")
    -        var chunk: List? = null,
    +        val chunk: List? = null,
     
             /**
              * An estimate on the total number of public rooms, if the server has an estimate.
              */
             @Json(name = "total_room_count_estimate")
    -        var totalRoomCountEstimate: Int? = null
    +        val totalRoomCountEstimate: Int? = null
     )
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt
    index 5b3ca234ac..784256503b 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt
    @@ -22,7 +22,7 @@ import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
     import im.vector.matrix.android.api.session.room.model.RoomSummary
     import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
     import im.vector.matrix.android.api.session.user.model.User
    -import java.util.*
    +import java.util.Locale
     
     sealed class MatrixItem(
             open val id: String,
    @@ -143,8 +143,14 @@ sealed class MatrixItem(
      * ========================================================================================== */
     
     fun User.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
    +
     fun GroupSummary.toMatrixItem() = MatrixItem.GroupItem(groupId, displayName, avatarUrl)
    +
     fun RoomSummary.toMatrixItem() = MatrixItem.RoomItem(roomId, displayName, avatarUrl)
    +
     fun RoomSummary.toRoomAliasMatrixItem() = MatrixItem.RoomAliasItem(canonicalAlias ?: roomId, displayName, avatarUrl)
    -fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name, avatarUrl)
    +
    +// If no name is available, use room alias as Riot-Web does
    +fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name ?: canonicalAlias, avatarUrl)
    +
     fun RoomMemberSummary.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
    diff --git a/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt b/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt
    index 9d119f4063..2178921733 100644
    --- a/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt
    +++ b/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt
    @@ -116,7 +116,7 @@ class EllipsizingTextView @JvmOverloads constructor(context: Context, attrs: Att
             super.setLineSpacing(add, mult)
         }
     
    -    override fun setText(text: CharSequence, type: BufferType) {
    +    override fun setText(text: CharSequence?, type: BufferType) {
             if (!programmaticChange) {
                 fullText = if (text is Spanned) text else text
                 isStale = true
    
    From 7b3fcb7798e96369df7180dd580901e241d58b2f Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Tue, 18 Feb 2020 12:11:51 +0100
    Subject: [PATCH 062/187] When sharing to a room save a draft to pre-fill the
     composer
    
    ---
     .../riotx/features/home/room/detail/RoomDetailFragment.kt  | 7 ++++---
     .../riotx/features/home/room/detail/RoomDetailViewModel.kt | 2 +-
     2 files changed, 5 insertions(+), 4 deletions(-)
    
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    index 46d2c24853..c2eb61b3ca 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    @@ -303,13 +303,14 @@ class RoomDetailFragment @Inject constructor(
             if (savedInstanceState == null) {
                 when (val sharedData = roomDetailArgs.sharedData) {
                     is SharedData.Text        -> {
    -                    roomDetailViewModel.handle(RoomDetailAction.ExitSpecialMode(composerLayout.text.toString()))
    +                    // Save a draft to set the shared text to the composer
    +                    roomDetailViewModel.handle(RoomDetailAction.SaveDraft(sharedData.text))
                     }
                     is SharedData.Attachments -> {
                         // open share edition
                         onContentAttachmentsReady(sharedData.attachmentData)
                     }
    -                null -> Timber.v("No share data to process")
    +                null                      -> Timber.v("No share data to process")
                 }.exhaustive
             }
         }
    @@ -435,7 +436,7 @@ class RoomDetailFragment @Inject constructor(
             composerLayout.collapse()
     
             updateComposerText(text)
    -        composerLayout.sendButton.setContentDescription(getString(R.string.send))
    +        composerLayout.sendButton.contentDescription = getString(R.string.send)
         }
     
         private fun renderSpecialMode(event: TimelineEvent,
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    index 78f2b9a577..1eccba7fa9 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
    @@ -243,7 +243,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
                     is SendMode.REPLY   -> room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
                     is SendMode.QUOTE   -> room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
                     is SendMode.EDIT    -> room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback())
    -            }
    +            }.exhaustive
             }
         }
     
    
    From d730f96c41d89a4e691fb0e03b486215172152f8 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Tue, 18 Feb 2020 12:24:27 +0100
    Subject: [PATCH 063/187] Do not show alert when sharing text to a single room
    
    ---
     .../features/share/IncomingShareViewModel.kt     | 16 +++++++++++++---
     1 file changed, 13 insertions(+), 3 deletions(-)
    
    diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    index 7a0ce8bc47..8a425d70d4 100644
    --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    @@ -22,7 +22,6 @@ import com.airbnb.mvrx.ViewModelContext
     import com.jakewharton.rxrelay2.BehaviorRelay
     import com.squareup.inject.assisted.Assisted
     import com.squareup.inject.assisted.AssistedInject
    -import im.vector.matrix.android.api.extensions.orFalse
     import im.vector.matrix.android.api.query.QueryStringValue
     import im.vector.matrix.android.api.session.Session
     import im.vector.matrix.android.api.session.content.ContentAttachmentData
    @@ -179,6 +178,7 @@ class IncomingShareViewModel @AssistedInject constructor(
     
         private fun handleSelectRoom(action: IncomingShareAction.SelectRoom) = withState { state ->
             if (state.isInMultiSelectionMode) {
    +            // One room is clicked (or long clicked) while in multi selection mode -> toggle this room
                 val selectedRooms = state.selectedRoomIds
                 val newSelectedRooms = if (selectedRooms.contains(action.roomSummary.roomId)) {
                     selectedRooms.minus(action.roomSummary.roomId)
    @@ -187,11 +187,21 @@ class IncomingShareViewModel @AssistedInject constructor(
                 }
                 setState { copy(isInMultiSelectionMode = newSelectedRooms.isNotEmpty(), selectedRoomIds = newSelectedRooms) }
             } else if (action.enableMultiSelect) {
    +            // One room is long clicked, not in multi selection mode -> enable multi selection mode
                 setState { copy(isInMultiSelectionMode = true, selectedRoomIds = setOf(action.roomSummary.roomId)) }
             } else {
    +            // One room is clicked, not in multi selection mode -> direct share
                 val sharedData = state.sharedData ?: return@withState
    -            // Do not show alert if the shared data contains only previewable attachments, because the user will get another chance to cancel the share
    -            val doNotShowAlert = (sharedData as? SharedData.Attachments)?.attachmentData?.all { it.isPreviewable() }.orFalse()
    +            val doNotShowAlert = when (sharedData) {
    +                is SharedData.Attachments -> {
    +                    // Do not show alert if the shared data contains only previewable attachments, because the user will get another chance to cancel the share
    +                    sharedData.attachmentData.all { it.isPreviewable() }
    +                }
    +                is SharedData.Text        -> {
    +                    // Do not show alert when sharing text to one room, because it will just fill the composer
    +                    true
    +                }
    +            }
                 _viewEvents.post(IncomingShareViewEvents.ShareToRoom(action.roomSummary, sharedData, !doNotShowAlert))
             }
         }
    
    From 86c38ebd2d1687cb0a2498fe35914c4b7d235873 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Tue, 18 Feb 2020 13:33:49 +0100
    Subject: [PATCH 064/187] Create Set.toggle() method
    
    ---
     .../im/vector/riotx/core/extensions/Set.kt    | 26 +++++++++++++++++++
     .../features/share/IncomingShareViewModel.kt  |  7 ++---
     2 files changed, 28 insertions(+), 5 deletions(-)
     create mode 100644 vector/src/main/java/im/vector/riotx/core/extensions/Set.kt
    
    diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/Set.kt b/vector/src/main/java/im/vector/riotx/core/extensions/Set.kt
    new file mode 100644
    index 0000000000..43eb1b0d7c
    --- /dev/null
    +++ b/vector/src/main/java/im/vector/riotx/core/extensions/Set.kt
    @@ -0,0 +1,26 @@
    +/*
    + * 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 im.vector.riotx.core.extensions
    +
    +// Create a new Set including the provided element if not already present, or removing the element if already present
    +fun  Set.toggle(element: T): Set {
    +    return if (contains(element)) {
    +        minus(element)
    +    } else {
    +        plus(element)
    +    }
    +}
    diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    index 8a425d70d4..c135460ee4 100644
    --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt
    @@ -29,6 +29,7 @@ import im.vector.matrix.android.api.session.room.model.Membership
     import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
     import im.vector.matrix.rx.rx
     import im.vector.riotx.core.extensions.exhaustive
    +import im.vector.riotx.core.extensions.toggle
     import im.vector.riotx.core.platform.VectorViewModel
     import im.vector.riotx.features.attachments.isPreviewable
     import im.vector.riotx.features.attachments.toGroupedContentAttachmentData
    @@ -180,11 +181,7 @@ class IncomingShareViewModel @AssistedInject constructor(
             if (state.isInMultiSelectionMode) {
                 // One room is clicked (or long clicked) while in multi selection mode -> toggle this room
                 val selectedRooms = state.selectedRoomIds
    -            val newSelectedRooms = if (selectedRooms.contains(action.roomSummary.roomId)) {
    -                selectedRooms.minus(action.roomSummary.roomId)
    -            } else {
    -                selectedRooms.plus(action.roomSummary.roomId)
    -            }
    +            val newSelectedRooms = selectedRooms.toggle(action.roomSummary.roomId)
                 setState { copy(isInMultiSelectionMode = newSelectedRooms.isNotEmpty(), selectedRoomIds = newSelectedRooms) }
             } else if (action.enableMultiSelect) {
                 // One room is long clicked, not in multi selection mode -> enable multi selection mode
    
    From f8276e48e3b44118e4b9f67d5736bdf8af40f334 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Thu, 6 Feb 2020 23:22:07 +0100
    Subject: [PATCH 065/187] add kluent for test
    
    ---
     vector/build.gradle | 3 +++
     1 file changed, 3 insertions(+)
    
    diff --git a/vector/build.gradle b/vector/build.gradle
    index 59dd75348f..483cd53831 100644
    --- a/vector/build.gradle
    +++ b/vector/build.gradle
    @@ -378,8 +378,11 @@ dependencies {
     
         // TESTS
         testImplementation 'junit:junit:4.12'
    +    testImplementation 'org.amshove.kluent:kluent-android:1.44'
    +
         androidTestImplementation 'androidx.test:runner:1.2.0'
         androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    +    androidTestImplementation 'org.amshove.kluent:kluent-android:1.44'
     }
     
     if (getGradle().getStartParameter().getTaskRequests().toString().contains("Gplay")) {
    
    From 53410789c0dbd10ddaf3e4438c1970f04039d418 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Thu, 6 Feb 2020 23:23:54 +0100
    Subject: [PATCH 066/187] Cleanup
    
    ---
     .../room/EventRelationsAggregationTask.kt     | 39 +++++++------------
     1 file changed, 14 insertions(+), 25 deletions(-)
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt
    index bd0a2e1b2e..49ffe29c29 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt
    @@ -555,29 +555,17 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor(
                 // i.e. never change state if already canceled/done
                 val currentState = VerificationState.values().firstOrNull { data.verificationSummary == it.name }
                 val newState = when (event.getClearType()) {
    -                EventType.KEY_VERIFICATION_START  -> {
    -                    updateVerificationState(currentState, VerificationState.WAITING)
    -                }
    -                EventType.KEY_VERIFICATION_ACCEPT -> {
    -                    updateVerificationState(currentState, VerificationState.WAITING)
    -                }
    -                EventType.KEY_VERIFICATION_READY  -> {
    -                    updateVerificationState(currentState, VerificationState.WAITING)
    -                }
    -                EventType.KEY_VERIFICATION_KEY    -> {
    -                    updateVerificationState(currentState, VerificationState.WAITING)
    -                }
    -                EventType.KEY_VERIFICATION_MAC    -> {
    -                    updateVerificationState(currentState, VerificationState.WAITING)
    -                }
    -                EventType.KEY_VERIFICATION_CANCEL -> {
    -                    updateVerificationState(currentState, if (event.senderId == userId) {
    -                        VerificationState.CANCELED_BY_ME
    -                    } else VerificationState.CANCELED_BY_OTHER)
    -                }
    -                EventType.KEY_VERIFICATION_DONE   -> {
    -                    updateVerificationState(currentState, VerificationState.DONE)
    -                }
    +                EventType.KEY_VERIFICATION_START,
    +                EventType.KEY_VERIFICATION_ACCEPT,
    +                EventType.KEY_VERIFICATION_READY,
    +                EventType.KEY_VERIFICATION_KEY,
    +                EventType.KEY_VERIFICATION_MAC    -> updateVerificationState(currentState, VerificationState.WAITING)
    +                EventType.KEY_VERIFICATION_CANCEL -> updateVerificationState(currentState, if (event.senderId == userId) {
    +                    VerificationState.CANCELED_BY_ME
    +                } else {
    +                    VerificationState.CANCELED_BY_OTHER
    +                })
    +                EventType.KEY_VERIFICATION_DONE   -> updateVerificationState(currentState, VerificationState.DONE)
                     else                              -> VerificationState.REQUEST
                 }
     
    @@ -593,15 +581,16 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor(
             }
         }
     
    +    // TODO Ben refacto -> fun VerifcationState?.toState(state): State
         private fun updateVerificationState(oldState: VerificationState?, newState: VerificationState): VerificationState {
             // Cancel is always prioritary ?
             // Eg id i found that mac or keys mismatch and send a cancel and the other send a done, i have to
             // consider as canceled
    -        if (newState == VerificationState.CANCELED_BY_OTHER || newState == VerificationState.CANCELED_BY_ME) {
    +        if (newState.isCanceled()) {
                 return newState
             }
             // never move out of cancel
    -        if (oldState == VerificationState.CANCELED_BY_OTHER || oldState == VerificationState.CANCELED_BY_ME) {
    +        if (oldState?.isCanceled() == true) {
                 return oldState
             }
             return newState
    
    From c6b231c0b150280acdb3def60d40611757f2bf39 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Thu, 6 Feb 2020 23:43:24 +0100
    Subject: [PATCH 067/187] Create toState() fun and use the enum
    
    ---
     .../room/model/ReferencesAggregatedContent.kt |  3 +-
     .../room/EventRelationsAggregationTask.kt     | 42 +++++++++----------
     .../helper/MessageInformationDataFactory.kt   |  8 ++--
     .../timeline/item/VerificationRequestItem.kt  |  8 +---
     4 files changed, 28 insertions(+), 33 deletions(-)
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/ReferencesAggregatedContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/ReferencesAggregatedContent.kt
    index ae6e52a091..31a5694682 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/ReferencesAggregatedContent.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/ReferencesAggregatedContent.kt
    @@ -17,6 +17,7 @@ package im.vector.matrix.android.api.session.room.model
     
     import com.squareup.moshi.Json
     import com.squareup.moshi.JsonClass
    +import im.vector.matrix.android.internal.session.room.VerificationState
     
     /**
      * Contains an aggregated summary info of the references.
    @@ -26,6 +27,6 @@ import com.squareup.moshi.JsonClass
     @JsonClass(generateAdapter = true)
     data class ReferencesAggregatedContent(
             // Verification status info for m.key.verification.request msgType events
    -        @Json(name = "verif_sum") val verificationSummary: String
    +        @Json(name = "verif_sum") val verificationState: VerificationState
             // Add more fields for future summary info.
     )
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt
    index 49ffe29c29..98046a4a36 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt
    @@ -73,6 +73,21 @@ fun VerificationState.isCanceled(): Boolean {
         return this == VerificationState.CANCELED_BY_ME || this == VerificationState.CANCELED_BY_OTHER
     }
     
    +// State transition with control
    +private fun VerificationState?.toState(newState: VerificationState): VerificationState {
    +    // Cancel is always prioritary ?
    +    // Eg id i found that mac or keys mismatch and send a cancel and the other send a done, i have to
    +    // consider as canceled
    +    if (newState.isCanceled()) {
    +        return newState
    +    }
    +    // never move out of cancel
    +    if (this?.isCanceled() == true) {
    +        return this
    +    }
    +    return newState
    +}
    +
     /**
      * Called by EventRelationAggregationUpdater, when new events that can affect relations are inserted in base.
      */
    @@ -550,26 +565,26 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor(
             } else {
                 ContentMapper.map(verifSummary.content)?.toModel()
                 var data = ContentMapper.map(verifSummary.content)?.toModel()
    -                    ?: ReferencesAggregatedContent(VerificationState.REQUEST.name)
    +                    ?: ReferencesAggregatedContent(VerificationState.REQUEST)
                 // TODO ignore invalid messages? e.g a START after a CANCEL?
                 // i.e. never change state if already canceled/done
    -            val currentState = VerificationState.values().firstOrNull { data.verificationSummary == it.name }
    +            val currentState = data.verificationState
                 val newState = when (event.getClearType()) {
                     EventType.KEY_VERIFICATION_START,
                     EventType.KEY_VERIFICATION_ACCEPT,
                     EventType.KEY_VERIFICATION_READY,
                     EventType.KEY_VERIFICATION_KEY,
    -                EventType.KEY_VERIFICATION_MAC    -> updateVerificationState(currentState, VerificationState.WAITING)
    -                EventType.KEY_VERIFICATION_CANCEL -> updateVerificationState(currentState, if (event.senderId == userId) {
    +                EventType.KEY_VERIFICATION_MAC    -> currentState.toState(VerificationState.WAITING)
    +                EventType.KEY_VERIFICATION_CANCEL -> currentState.toState(if (event.senderId == userId) {
                         VerificationState.CANCELED_BY_ME
                     } else {
                         VerificationState.CANCELED_BY_OTHER
                     })
    -                EventType.KEY_VERIFICATION_DONE   -> updateVerificationState(currentState, VerificationState.DONE)
    +                EventType.KEY_VERIFICATION_DONE   -> currentState.toState(VerificationState.DONE)
                     else                              -> VerificationState.REQUEST
                 }
     
    -            data = data.copy(verificationSummary = newState.name)
    +            data = data.copy(verificationState = newState)
                 verifSummary.content = ContentMapper.map(data.toContent())
             }
     
    @@ -580,19 +595,4 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor(
                 verifSummary.sourceEvents.add(event.eventId)
             }
         }
    -
    -    // TODO Ben refacto -> fun VerifcationState?.toState(state): State
    -    private fun updateVerificationState(oldState: VerificationState?, newState: VerificationState): VerificationState {
    -        // Cancel is always prioritary ?
    -        // Eg id i found that mac or keys mismatch and send a cancel and the other send a done, i have to
    -        // consider as canceled
    -        if (newState.isCanceled()) {
    -            return newState
    -        }
    -        // never move out of cancel
    -        if (oldState?.isCanceled() == true) {
    -            return oldState
    -        }
    -        return newState
    -    }
     }
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
    index c300b8e1c3..2a7261665a 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
    @@ -104,11 +104,9 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
                             }
                             .toList(),
                     referencesInfoData = event.annotations?.referencesAggregatedSummary?.let { referencesAggregatedSummary ->
    -                    val stateStr = referencesAggregatedSummary.content.toModel()?.verificationSummary
    -                    ReferencesInfoData(
    -                            VerificationState.values().firstOrNull { stateStr == it.name }
    -                                    ?: VerificationState.REQUEST
    -                    )
    +                    val verificationState = referencesAggregatedSummary.content.toModel()?.verificationState
    +                            ?: VerificationState.REQUEST
    +                    ReferencesInfoData(verificationState)
                     },
                     sentByMe = event.root.senderId == session.myUserId
             )
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/VerificationRequestItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/VerificationRequestItem.kt
    index 853e40c516..5c3cf03aa8 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/VerificationRequestItem.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/VerificationRequestItem.kt
    @@ -31,6 +31,7 @@ import com.airbnb.epoxy.EpoxyModelClass
     import im.vector.matrix.android.api.session.crypto.sas.VerificationService
     import im.vector.matrix.android.internal.session.room.VerificationState
     import im.vector.riotx.R
    +import im.vector.riotx.core.extensions.exhaustive
     import im.vector.riotx.core.resources.ColorProvider
     import im.vector.riotx.core.utils.DebouncedClickListener
     import im.vector.riotx.features.home.AvatarRenderer
    @@ -102,12 +103,7 @@ abstract class VerificationRequestItem : AbsBaseMessageItem {
    -                holder.buttonBar.isVisible = false
    -                holder.statusTextView.text = null
    -                holder.statusTextView.isVisible = false
    -            }
    -        }
    +        }.exhaustive
     
             // Always hide buttons if request is too old
             if (!VerificationService.isValidRequest(attributes.informationData.ageLocalTS)) {
    
    From 66a2958c39c6e3d3a0cd5060b9ddbda8b39a7444 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Wed, 19 Feb 2020 14:21:41 +0100
    Subject: [PATCH 068/187] Add unit test to check byte array to string
     conversion
    
    ---
     .../verification/qrcode/BinaryStringTest.kt   | 52 +++++++++++++++++++
     1 file changed, 52 insertions(+)
     create mode 100644 matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/BinaryStringTest.kt
    
    diff --git a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/BinaryStringTest.kt b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/BinaryStringTest.kt
    new file mode 100644
    index 0000000000..df5499cb6f
    --- /dev/null
    +++ b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/BinaryStringTest.kt
    @@ -0,0 +1,52 @@
    +/*
    + * 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 im.vector.matrix.android.internal.crypto.verification.qrcode
    +
    +import org.amshove.kluent.shouldEqualTo
    +import org.junit.FixMethodOrder
    +import org.junit.Test
    +import org.junit.runners.MethodSorters
    +
    +@FixMethodOrder(MethodSorters.JVM)
    +class BinaryStringTest {
    +
    +    /**
    +     * I want to put bytes to a String, and vice versa
    +     */
    +    @Test
    +    fun testNominalCase() {
    +        val byteArray = ByteArray(256)
    +        for (i in byteArray.indices) {
    +            byteArray[i] = i.toByte() // Random.nextInt(255).toByte()
    +        }
    +
    +        val str = byteArray.toString(Charsets.ISO_8859_1)
    +
    +        str.length shouldEqualTo 256
    +
    +        // Ok convert back to bytearray
    +
    +        val result = str.toByteArray(Charsets.ISO_8859_1)
    +
    +        result.size shouldEqualTo 256
    +
    +        for (i in 0..255) {
    +            result[i] shouldEqualTo i.toByte()
    +            result[i] shouldEqualTo byteArray[i]
    +        }
    +    }
    +}
    
    From 35b10daef17000d442945f7ec374ae4f8a650782 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Wed, 19 Feb 2020 14:26:45 +0100
    Subject: [PATCH 069/187] Ensure RiotX is able to scan binary QrCodes
    
    ---
     .../riotx/features/debug/DebugMenuActivity.kt | 29 +++++++++++++++++--
     .../features/qrcode/QrCodeScannerActivity.kt  | 25 ++++++++++++++--
     2 files changed, 49 insertions(+), 5 deletions(-)
    
    diff --git a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt
    index 81708182b7..7c73e85ae0 100644
    --- a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt
    +++ b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt
    @@ -37,6 +37,7 @@ import im.vector.riotx.core.utils.toast
     import im.vector.riotx.features.debug.sas.DebugSasEmojiActivity
     import im.vector.riotx.features.qrcode.QrCodeScannerActivity
     import kotlinx.android.synthetic.debug.activity_debug_menu.*
    +import timber.log.Timber
     import javax.inject.Inject
     
     class DebugMenuActivity : VectorBaseActivity() {
    @@ -50,8 +51,19 @@ class DebugMenuActivity : VectorBaseActivity() {
             injector.inject(this)
         }
     
    +    private lateinit var buffer: ByteArray
    +
         override fun initUiAndData() {
    -        renderQrCode("https://www.example.org")
    +        // renderQrCode("https://www.example.org")
    +
    +        buffer = ByteArray(256)
    +        for (i in buffer.indices) {
    +            buffer[i] = i.toByte()
    +        }
    +
    +        val string = buffer.toString(Charsets.ISO_8859_1)
    +
    +        renderQrCode(string)
         }
     
         private fun renderQrCode(text: String) {
    @@ -194,7 +206,20 @@ class DebugMenuActivity : VectorBaseActivity() {
                         toast("QrCode: " + QrCodeScannerActivity.getResultText(data) + " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(data))
     
                         // Also update the current QR Code (reverse operation)
    -                    renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "")
    +                    // renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "")
    +                    val result = QrCodeScannerActivity.getResultText(data)!!
    +
    +                    if (result.length != buffer.size) {
    +                        Timber.e("Error, length are not the same")
    +                    } else {
    +                        // Convert to ByteArray
    +                        val byteArrayResult = result.toByteArray(Charsets.ISO_8859_1)
    +                        for (i in byteArrayResult.indices) {
    +                            if (buffer[i] != byteArrayResult[i]) {
    +                                Timber.e("Error for byte $i, expecting ${buffer[i]} and get ${byteArrayResult[i]}")
    +                            }
    +                        }
    +                    }
                     }
                 }
             }
    diff --git a/vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerActivity.kt b/vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerActivity.kt
    index bb6f1be03b..f9c92ba3ad 100644
    --- a/vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerActivity.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerActivity.kt
    @@ -22,6 +22,7 @@ import android.os.Bundle
     import androidx.fragment.app.Fragment
     import com.google.zxing.BarcodeFormat
     import com.google.zxing.Result
    +import com.google.zxing.ResultMetadataType
     import im.vector.riotx.R
     import im.vector.riotx.core.di.ScreenComponent
     import im.vector.riotx.core.extensions.replaceFragment
    @@ -43,15 +44,33 @@ class QrCodeScannerActivity : VectorBaseActivity() {
         }
     
         fun setResultAndFinish(result: Result?) {
    -        result?.let {
    +        if (result != null) {
    +            val rawBytes = getRawBytes(result)
    +            val rawBytesStr = rawBytes?.toString(Charsets.ISO_8859_1)
    +
                 setResult(RESULT_OK, Intent().apply {
    -                putExtra(EXTRA_OUT_TEXT, it.text)
    -                putExtra(EXTRA_OUT_IS_QR_CODE, it.barcodeFormat == BarcodeFormat.QR_CODE)
    +                putExtra(EXTRA_OUT_TEXT, rawBytesStr ?: result.text)
    +                putExtra(EXTRA_OUT_IS_QR_CODE, result.barcodeFormat == BarcodeFormat.QR_CODE)
                 })
             }
             finish()
         }
     
    +    // Copied from https://github.com/markusfisch/BinaryEye/blob/
    +    // 9d57889b810dcaa1a91d7278fc45c262afba1284/app/src/main/kotlin/de/markusfisch/android/binaryeye/activity/CameraActivity.kt#L434
    +    private fun getRawBytes(result: Result): ByteArray? {
    +        val metadata = result.resultMetadata ?: return null
    +        val segments = metadata[ResultMetadataType.BYTE_SEGMENTS] ?: return null
    +        var bytes = ByteArray(0)
    +        @Suppress("UNCHECKED_CAST")
    +        for (seg in segments as Iterable) {
    +            bytes += seg
    +        }
    +        // byte segments can never be shorter than the text.
    +        // Zxing cuts off content prefixes like "WIFI:"
    +        return if (bytes.size >= result.text.length) bytes else null
    +    }
    +
         companion object {
             private const val EXTRA_OUT_TEXT = "EXTRA_OUT_TEXT"
             private const val EXTRA_OUT_IS_QR_CODE = "EXTRA_OUT_IS_QR_CODE"
    
    From a97971dd8417ffe555906c8d8809d13ef77082d2 Mon Sep 17 00:00:00 2001
    From: onurays 
    Date: Wed, 19 Feb 2020 18:30:12 +0300
    Subject: [PATCH 070/187] Fix threading issues while restoring keys backup.
    
    ---
     CHANGES.md                                    |  1 +
     .../internal/crypto/DefaultCryptoService.kt   |  2 +-
     .../actions/MegolmSessionDataImporter.kt      | 18 ++++-----------
     .../keysbackup/DefaultKeysBackupService.kt    |  2 +-
     .../restore/KeysBackupRestoreActivity.kt      |  2 --
     .../KeysBackupRestoreFromKeyViewModel.kt      | 14 +++++------
     ...eysBackupRestoreFromPassphraseViewModel.kt | 17 +++++++-------
     .../KeysBackupRestoreSuccessFragment.kt       | 23 +++++++++++--------
     .../fragment_keys_backup_restore_from_key.xml |  3 ++-
     ...nt_keys_backup_restore_from_passphrase.xml |  5 ++--
     .../fragment_keys_backup_restore_success.xml  |  3 ++-
     .../layout/fragment_keys_backup_settings.xml  |  1 +
     .../fragment_keys_backup_setup_step1.xml      |  1 +
     .../fragment_keys_backup_setup_step2.xml      |  1 +
     .../fragment_keys_backup_setup_step3.xml      |  1 +
     vector/src/main/res/values/strings_riotX.xml  |  2 ++
     16 files changed, 51 insertions(+), 45 deletions(-)
    
    diff --git a/CHANGES.md b/CHANGES.md
    index 4f729ba0a8..897f8deebf 100644
    --- a/CHANGES.md
    +++ b/CHANGES.md
    @@ -14,6 +14,7 @@ Improvements 🙌:
     Bugfix 🐛:
      - Account creation: wrongly hints that an email can be used to create an account (#941)
      - Fix crash in the room directory, when public room has no name (#1023)
    + - Fix restoring keys backup with passphrase (#526)
     
     Translations 🗣:
      -
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    index b0acb031fc..c4d115b10c 100755
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    @@ -887,7 +887,7 @@ internal class DefaultCryptoService @Inject constructor(
                             throw Exception("Error")
                         }
     
    -                    megolmSessionDataImporter.handle(importedSessions, true, uiHandler, progressListener)
    +                    megolmSessionDataImporter.handle(importedSessions, true, progressListener)
                     }
                 }.foldToCallback(callback)
             }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt
    index 62b4f1b851..ca6cfad0f9 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt
    @@ -16,7 +16,6 @@
     
     package im.vector.matrix.android.internal.crypto.actions
     
    -import android.os.Handler
     import androidx.annotation.WorkerThread
     import im.vector.matrix.android.api.listeners.ProgressListener
     import im.vector.matrix.android.internal.crypto.MXOlmDevice
    @@ -46,7 +45,6 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
         @WorkerThread
         fun handle(megolmSessionsData: List,
                    fromBackup: Boolean,
    -               uiHandler: Handler,
                    progressListener: ProgressListener?): ImportRoomKeysResult {
             val t0 = System.currentTimeMillis()
     
    @@ -54,11 +52,7 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
             var lastProgress = 0
             var totalNumbersOfImportedKeys = 0
     
    -        if (progressListener != null) {
    -            uiHandler.post {
    -                progressListener.onProgress(0, 100)
    -            }
    -        }
    +        progressListener?.onProgress(0, 100)
             val olmInboundGroupSessionWrappers = olmDevice.importInboundGroupSessions(megolmSessionsData)
     
             megolmSessionsData.forEachIndexed { cpt, megolmSessionData ->
    @@ -89,14 +83,12 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
                 }
     
                 if (progressListener != null) {
    -                uiHandler.post {
    -                    val progress = 100 * cpt / totalNumbersOfKeys
    +                val progress = 100 * cpt / totalNumbersOfKeys
     
    -                    if (lastProgress != progress) {
    -                        lastProgress = progress
    +                if (lastProgress != progress) {
    +                    lastProgress = progress
     
    -                        progressListener.onProgress(progress, 100)
    -                    }
    +                    progressListener.onProgress(progress, 100)
                     }
                 }
             }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt
    index a630467d4c..aa291e8206 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt
    @@ -692,7 +692,7 @@ internal class DefaultKeysBackupService @Inject constructor(
                             null
                         }
     
    -                    val result = megolmSessionDataImporter.handle(sessionsData, !backUp, uiHandler, progressListener)
    +                    val result = megolmSessionDataImporter.handle(sessionsData, !backUp, progressListener)
     
                         // Do not back up the key if it comes from a backup recovery
                         if (backUp) {
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
    index a12a43d06f..e6d303b3aa 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
    @@ -19,7 +19,6 @@ import android.app.Activity
     import android.content.Context
     import android.content.Intent
     import androidx.appcompat.app.AlertDialog
    -import androidx.fragment.app.FragmentManager
     import androidx.lifecycle.Observer
     import im.vector.riotx.R
     import im.vector.riotx.core.extensions.addFragmentToBackstack
    @@ -79,7 +78,6 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
                         addFragmentToBackstack(R.id.container, KeysBackupRestoreFromKeyFragment::class.java)
                     }
                     KeysBackupRestoreSharedViewModel.NAVIGATE_TO_SUCCESS          -> {
    -                    supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
                         replaceFragment(R.id.container, KeysBackupRestoreSuccessFragment::class.java)
                     }
                 }
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
    index 7c95c3f3fe..0cf297f7f1 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
    @@ -62,21 +62,21 @@ class KeysBackupRestoreFromKeyViewModel @Inject constructor() : ViewModel() {
                         override fun onStepProgress(step: StepProgressListener.Step) {
                             when (step) {
                                 is StepProgressListener.Step.DownloadingKey -> {
    -                                sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
    +                                sharedViewModel.loadingEvent.postValue(WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
                                             + "\n" + context.getString(R.string.keys_backup_restoring_downloading_backup_waiting_message),
    -                                        isIndeterminate = true)
    +                                        isIndeterminate = true))
                                 }
    -                            is StepProgressListener.Step.ImportingKey -> {
    +                            is StepProgressListener.Step.ImportingKey   -> {
                                     // Progress 0 can take a while, display an indeterminate progress in this case
                                     if (step.progress == 0) {
    -                                    sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
    +                                    sharedViewModel.loadingEvent.postValue(WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
                                                 + "\n" + context.getString(R.string.keys_backup_restoring_importing_keys_waiting_message),
    -                                            isIndeterminate = true)
    +                                            isIndeterminate = true))
                                     } else {
    -                                    sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
    +                                    sharedViewModel.loadingEvent.postValue(WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
                                                 + "\n" + context.getString(R.string.keys_backup_restoring_importing_keys_waiting_message),
                                                 step.progress,
    -                                            step.total)
    +                                            step.total))
                                     }
                                 }
                             }
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
    index 92a3504b5c..c6ffd730ca 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
    @@ -63,27 +63,28 @@ class KeysBackupRestoreFromPassphraseViewModel @Inject constructor() : ViewModel
                         override fun onStepProgress(step: StepProgressListener.Step) {
                             when (step) {
                                 is StepProgressListener.Step.ComputingKey -> {
    -                                sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
    +                                sharedViewModel.loadingEvent.postValue(WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
                                             + "\n" + context.getString(R.string.keys_backup_restoring_computing_key_waiting_message),
                                             step.progress,
    -                                        step.total)
    +                                        step.total))
                                 }
                                 is StepProgressListener.Step.DownloadingKey -> {
    -                                sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
    +                                sharedViewModel.loadingEvent.postValue(WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
                                             + "\n" + context.getString(R.string.keys_backup_restoring_downloading_backup_waiting_message),
    -                                        isIndeterminate = true)
    +                                        isIndeterminate = true))
                                 }
                                 is StepProgressListener.Step.ImportingKey -> {
    +                                Timber.d("backupKeys.ImportingKey.progress: " + step.progress)
                                     // Progress 0 can take a while, display an indeterminate progress in this case
                                     if (step.progress == 0) {
    -                                    sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
    +                                    sharedViewModel.loadingEvent.postValue(WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
                                                 + "\n" + context.getString(R.string.keys_backup_restoring_importing_keys_waiting_message),
    -                                            isIndeterminate = true)
    +                                            isIndeterminate = true))
                                     } else {
    -                                    sharedViewModel.loadingEvent.value = WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
    +                                    sharedViewModel.loadingEvent.postValue(WaitingViewData(context.getString(R.string.keys_backup_restoring_waiting_message)
                                                 + "\n" + context.getString(R.string.keys_backup_restoring_importing_keys_waiting_message),
                                                 step.progress,
    -                                            step.total)
    +                                            step.total))
                                     }
                                 }
                             }
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt
    index 0f681af737..def8e4bf65 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt
    @@ -17,6 +17,7 @@ package im.vector.riotx.features.crypto.keysbackup.restore
     
     import android.os.Bundle
     import android.widget.TextView
    +import androidx.core.view.isVisible
     import butterknife.BindView
     import butterknife.OnClick
     import im.vector.riotx.R
    @@ -39,16 +40,20 @@ class KeysBackupRestoreSuccessFragment @Inject constructor() : VectorBaseFragmen
             super.onActivityCreated(savedInstanceState)
             sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
     
    -        sharedViewModel.importKeyResult?.let {
    -            val part1 = resources.getQuantityString(R.plurals.keys_backup_restore_success_description_part1,
    -                    it.totalNumberOfKeys, it.totalNumberOfKeys)
    -            val part2 = resources.getQuantityString(R.plurals.keys_backup_restore_success_description_part2,
    -                    it.successfullyNumberOfImportedKeys, it.successfullyNumberOfImportedKeys)
    -            mSuccessDetailsText.text = String.format("%s\n%s", part1, part2)
    +        if (compareValues(sharedViewModel.importKeyResult?.totalNumberOfKeys, 0) > 0) {
    +            sharedViewModel.importKeyResult?.let {
    +                val part1 = resources.getQuantityString(R.plurals.keys_backup_restore_success_description_part1,
    +                        it.totalNumberOfKeys, it.totalNumberOfKeys)
    +                val part2 = resources.getQuantityString(R.plurals.keys_backup_restore_success_description_part2,
    +                        it.successfullyNumberOfImportedKeys, it.successfullyNumberOfImportedKeys)
    +                mSuccessDetailsText.text = String.format("%s\n%s", part1, part2)
    +            }
    +            // We don't put emoji in string xml as it will crash on old devices
    +            mSuccessText.text = context?.getString(R.string.keys_backup_restore_success_title, "🎉")
    +        } else {
    +            mSuccessText.text = context?.getString(R.string.keys_backup_restore_success_title_already_up_to_date)
    +            mSuccessDetailsText.isVisible = false
             }
    -
    -        // We don't put emoji in string xml as it will crash on old devices
    -        mSuccessText.text = context?.getString(R.string.keys_backup_restore_success_title, "🎉")
         }
     
         @OnClick(R.id.keys_backup_setup_done_button)
    diff --git a/vector/src/main/res/layout/fragment_keys_backup_restore_from_key.xml b/vector/src/main/res/layout/fragment_keys_backup_restore_from_key.xml
    index cf24ab6af1..3592ff074d 100644
    --- a/vector/src/main/res/layout/fragment_keys_backup_restore_from_key.xml
    +++ b/vector/src/main/res/layout/fragment_keys_backup_restore_from_key.xml
    @@ -4,7 +4,8 @@
         xmlns:tools="http://schemas.android.com/tools"
         android:id="@+id/keys_backup_root"
         android:layout_width="match_parent"
    -    android:layout_height="match_parent">
    +    android:layout_height="match_parent"
    +    android:background="?riotx_background">
     
         
    +    android:layout_height="match_parent"
    +    android:background="?riotx_background">
     
         
    +    android:layout_height="match_parent"
    +    android:background="?riotx_background">
     
         
     
         
     
         
     
         Event deleted by user, reason: %1$s
         Event moderated by room admin, reason: %1$s
    +
    +    Keys are already up to date!
         
     
     
    
    From e00d3ef63d7f12e7226cc543325f525cca09e92f Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Wed, 19 Feb 2020 17:13:51 +0100
    Subject: [PATCH 071/187] QrCodeV2 WIP (al tests passing)
    
    ---
     .../verification/qrcode/QrCodeV2Test.kt       | 293 ++++++++++++++++++
     .../verification/qrcode/SharedSecretV2Test.kt |  46 +++
     .../crypto/verification/qrcode/Extensions.kt  |   2 +
     .../verification/qrcode/ExtensionsV2.kt       | 123 ++++++++
     .../crypto/verification/qrcode/QrCodeData.kt  |   1 +
     .../verification/qrcode/QrCodeDataV2.kt       | 102 ++++++
     .../verification/qrcode/SharedSecret.kt       |   9 +
     7 files changed, 576 insertions(+)
     create mode 100644 matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeV2Test.kt
     create mode 100644 matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretV2Test.kt
     create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/ExtensionsV2.kt
     create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeDataV2.kt
    
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeV2Test.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeV2Test.kt
    new file mode 100644
    index 0000000000..3bcb5c660f
    --- /dev/null
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeV2Test.kt
    @@ -0,0 +1,293 @@
    +/*
    + * 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 im.vector.matrix.android.internal.crypto.verification.qrcode
    +
    +import androidx.test.ext.junit.runners.AndroidJUnit4
    +import im.vector.matrix.android.InstrumentedTest
    +import org.amshove.kluent.shouldBeNull
    +import org.amshove.kluent.shouldEqual
    +import org.amshove.kluent.shouldEqualTo
    +import org.amshove.kluent.shouldNotBeNull
    +import org.junit.FixMethodOrder
    +import org.junit.Test
    +import org.junit.runner.RunWith
    +import org.junit.runners.MethodSorters
    +
    +@RunWith(AndroidJUnit4::class)
    +@FixMethodOrder(MethodSorters.JVM)
    +class QrCodeV2Test : InstrumentedTest {
    +
    +    private val qrCode1 = QrCodeDataV2.VerifyingAnotherUser(
    +            transactionId = "MaTransaction",
    +            userMasterCrossSigningPublicKey = "ktEwcUP6su1xh+GuE+CYkQ3H6W/DIl+ybHFdaEOrolU",
    +            otherUserMasterCrossSigningPublicKey = "TXluZKTZLvSRWOTPlOqLq534bA+/K4zLFKSu9cGLQaU",
    +            sharedSecret = "MTIzNDU2Nzg"
    +    )
    +
    +    private val value1 = "MATRIX\u0002\u0000\u0000\u000DMaTransaction\u0092Ñ0qCú²íq\u0087á®\u0013à\u0098\u0091\u000DÇéoÃ\"_²lq]hC«¢UMynd¤Ù.ô\u0091XäÏ\u0094ê\u008B«\u009Døl\u000F¿+\u008CË\u0014¤®õÁ\u008BA¥12345678"
    +
    +    private val qrCode2 = QrCodeDataV2.SelfVerifyingMasterKeyTrusted(
    +            transactionId = "MaTransaction",
    +            userMasterCrossSigningPublicKey = "ktEwcUP6su1xh+GuE+CYkQ3H6W/DIl+ybHFdaEOrolU",
    +            otherDeviceKey = "TXluZKTZLvSRWOTPlOqLq534bA+/K4zLFKSu9cGLQaU",
    +            sharedSecret = "MTIzNDU2Nzg"
    +    )
    +
    +    private val value2 = "MATRIX\u0002\u0001\u0000\u000DMaTransaction\u0092Ñ0qCú²íq\u0087á®\u0013à\u0098\u0091\u000DÇéoÃ\"_²lq]hC«¢UMynd¤Ù.ô\u0091XäÏ\u0094ê\u008B«\u009Døl\u000F¿+\u008CË\u0014¤®õÁ\u008BA¥12345678"
    +
    +    private val qrCode3 = QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted(
    +            transactionId = "MaTransaction",
    +            deviceKey = "TXluZKTZLvSRWOTPlOqLq534bA+/K4zLFKSu9cGLQaU",
    +            userMasterCrossSigningPublicKey = "ktEwcUP6su1xh+GuE+CYkQ3H6W/DIl+ybHFdaEOrolU",
    +            sharedSecret = "MTIzNDU2Nzg"
    +    )
    +
    +    private val value3 = "MATRIX\u0002\u0002\u0000\u000DMaTransactionMynd¤Ù.ô\u0091XäÏ\u0094ê\u008B«\u009Døl\u000F¿+\u008CË\u0014¤®õÁ\u008BA¥\u0092Ñ0qCú²íq\u0087á®\u0013à\u0098\u0091\u000DÇéoÃ\"_²lq]hC«¢U12345678"
    +
    +    private val sharedSecretByteArray = "12345678".toByteArray(Charsets.ISO_8859_1)
    +
    +    // 4d 79 6e 64 a4 d9 2e f4 91 58 e4 cf 94 ea 8b ab 9d f8 6c 0f bf 2b 8c cb 14 a4 ae f5 c1 8b 41 a5
    +    private val tlx_byteArray = ByteArray(32) {
    +        when (it) {
    +            0    -> 0x4D.toByte()
    +            1    -> 0x79.toByte()
    +            2    -> 0x6E.toByte()
    +            3    -> 0x64.toByte()
    +            4    -> 0xA4.toByte()
    +            5    -> 0xD9.toByte()
    +            6    -> 0x2E.toByte()
    +            7    -> 0xF4.toByte()
    +            8    -> 0x91.toByte()
    +            9    -> 0x58.toByte()
    +            10   -> 0xE4.toByte()
    +            11   -> 0xCF.toByte()
    +            12   -> 0x94.toByte()
    +            13   -> 0xEA.toByte()
    +            14   -> 0x8B.toByte()
    +            15   -> 0xAB.toByte()
    +            16   -> 0x9D.toByte()
    +            17   -> 0xF8.toByte()
    +            18   -> 0x6C.toByte()
    +            19   -> 0x0F.toByte()
    +            20   -> 0xBF.toByte()
    +            21   -> 0x2B.toByte()
    +            22   -> 0x8C.toByte()
    +            23   -> 0xCB.toByte()
    +            24   -> 0x14.toByte()
    +            25   -> 0xA4.toByte()
    +            26   -> 0xAE.toByte()
    +            27   -> 0xF5.toByte()
    +            28   -> 0xC1.toByte()
    +            29   -> 0x8B.toByte()
    +            30   -> 0x41.toByte()
    +            else -> 0xA5.toByte()
    +        }
    +    }
    +
    +    // 92 d1 30 71 43 fa b2 ed 71 87 e1 ae 13 e0 98 91 0d c7 e9 6f c3 22 5f b2 6c 71 5d 68 43 ab a2 55
    +    private val kte_byteArray = ByteArray(32) {
    +        when (it) {
    +            0    -> 0x92.toByte()
    +            1    -> 0xd1.toByte()
    +            2    -> 0x30.toByte()
    +            3    -> 0x71.toByte()
    +            4    -> 0x43.toByte()
    +            5    -> 0xfa.toByte()
    +            6    -> 0xb2.toByte()
    +            7    -> 0xed.toByte()
    +            8    -> 0x71.toByte()
    +            9    -> 0x87.toByte()
    +            10   -> 0xe1.toByte()
    +            11   -> 0xae.toByte()
    +            12   -> 0x13.toByte()
    +            13   -> 0xe0.toByte()
    +            14   -> 0x98.toByte()
    +            15   -> 0x91.toByte()
    +            16   -> 0x0d.toByte()
    +            17   -> 0xc7.toByte()
    +            18   -> 0xe9.toByte()
    +            19   -> 0x6f.toByte()
    +            20   -> 0xc3.toByte()
    +            21   -> 0x22.toByte()
    +            22   -> 0x5f.toByte()
    +            23   -> 0xb2.toByte()
    +            24   -> 0x6c.toByte()
    +            25   -> 0x71.toByte()
    +            26   -> 0x5d.toByte()
    +            27   -> 0x68.toByte()
    +            28   -> 0x43.toByte()
    +            29   -> 0xab.toByte()
    +            30   -> 0xa2.toByte()
    +            else -> 0x55.toByte()
    +        }
    +    }
    +
    +    @Test
    +    fun testEncoding1() {
    +        qrCode1.toEncodedString() shouldEqual value1
    +    }
    +
    +    @Test
    +    fun testEncoding2() {
    +        qrCode2.toEncodedString() shouldEqual value2
    +    }
    +
    +    @Test
    +    fun testEncoding3() {
    +        qrCode3.toEncodedString() shouldEqual value3
    +    }
    +
    +    @Test
    +    fun testSymmetry1() {
    +        qrCode1.toEncodedString().toQrCodeDataV2() shouldEqual qrCode1
    +    }
    +
    +    @Test
    +    fun testSymmetry2() {
    +        qrCode2.toEncodedString().toQrCodeDataV2() shouldEqual qrCode2
    +    }
    +
    +    @Test
    +    fun testSymmetry3() {
    +        qrCode3.toEncodedString().toQrCodeDataV2() shouldEqual qrCode3
    +    }
    +
    +    @Test
    +    fun testCase1() {
    +        val url = qrCode1.toEncodedString()
    +
    +        val byteArray = url.toByteArray(Charsets.ISO_8859_1)
    +        checkHeader(byteArray)
    +
    +        // Mode
    +        byteArray[7] shouldEqualTo 0
    +
    +        checkSizeAndTransaction(byteArray)
    +
    +        compareArray(byteArray.copyOfRange(23, 23 + 32), kte_byteArray)
    +        compareArray(byteArray.copyOfRange(23 + 32, 23 + 64), tlx_byteArray)
    +
    +        compareArray(byteArray.copyOfRange(23 + 64, byteArray.size), sharedSecretByteArray)
    +    }
    +
    +    @Test
    +    fun testCase2() {
    +        val url = qrCode2.toEncodedString()
    +
    +        val byteArray = url.toByteArray(Charsets.ISO_8859_1)
    +        checkHeader(byteArray)
    +
    +        // Mode
    +        byteArray[7] shouldEqualTo 1
    +
    +        checkSizeAndTransaction(byteArray)
    +        compareArray(byteArray.copyOfRange(23, 23 + 32), kte_byteArray)
    +        compareArray(byteArray.copyOfRange(23 + 32, 23 + 64), tlx_byteArray)
    +
    +        compareArray(byteArray.copyOfRange(23 + 64, byteArray.size), sharedSecretByteArray)
    +    }
    +
    +    @Test
    +    fun testCase3() {
    +        val url = qrCode3.toEncodedString()
    +
    +        val byteArray = url.toByteArray(Charsets.ISO_8859_1)
    +        checkHeader(byteArray)
    +
    +        // Mode
    +        byteArray[7] shouldEqualTo 2
    +
    +        checkSizeAndTransaction(byteArray)
    +        compareArray(byteArray.copyOfRange(23, 23 + 32), tlx_byteArray)
    +        compareArray(byteArray.copyOfRange(23 + 32, 23 + 64), kte_byteArray)
    +
    +        compareArray(byteArray.copyOfRange(23 + 64, byteArray.size), sharedSecretByteArray)
    +    }
    +
    +    // Error cases
    +    @Test
    +    fun testErrorHeader() {
    +        value1.replace("MATRIX", "MOTRIX").toQrCodeDataV2().shouldBeNull()
    +        value1.replace("MATRIX", "MATRI").toQrCodeDataV2().shouldBeNull()
    +        value1.replace("MATRIX", "").toQrCodeDataV2().shouldBeNull()
    +    }
    +
    +    @Test
    +    fun testErrorVersion() {
    +        value1.replace("MATRIX\u0002", "MATRIX\u0000").toQrCodeDataV2().shouldBeNull()
    +        value1.replace("MATRIX\u0002", "MATRIX\u0001").toQrCodeDataV2().shouldBeNull()
    +        value1.replace("MATRIX\u0002", "MATRIX\u0003").toQrCodeDataV2().shouldBeNull()
    +        value1.replace("MATRIX\u0002", "MATRIX").toQrCodeDataV2().shouldBeNull()
    +    }
    +
    +    @Test
    +    fun testErrorSecretTooShort() {
    +        value1.replace("12345678", "1234567").toQrCodeDataV2().shouldBeNull()
    +    }
    +
    +    @Test
    +    fun testErrorNoTransactionNoKeyNoSecret() {
    +        // But keep transaction length
    +        "MATRIX\u0002\u0000\u0000\u000D".toQrCodeDataV2().shouldBeNull()
    +    }
    +
    +    @Test
    +    fun testErrorNoKeyNoSecret() {
    +        "MATRIX\u0002\u0000\u0000\u000DMaTransaction".toQrCodeDataV2().shouldBeNull()
    +    }
    +
    +    @Test
    +    fun testErrorTransactionLengthTooShort() {
    +        // In this case, the secret will be longer, so this is not an error, but it will lead to keys mismatch
    +        value1.replace("\u000DMaTransaction", "\u000CMaTransaction").toQrCodeDataV2().shouldNotBeNull()
    +    }
    +
    +    @Test
    +    fun testErrorTransactionLengthTooBig() {
    +        value1.replace("\u000DMaTransaction", "\u000EMaTransaction").toQrCodeDataV2().shouldBeNull()
    +    }
    +
    +    private fun compareArray(actual: ByteArray, expected: ByteArray) {
    +        actual.size shouldEqual expected.size
    +
    +        for (i in actual.indices) {
    +            actual[i] shouldEqualTo expected[i]
    +        }
    +    }
    +
    +    private fun checkHeader(byteArray: ByteArray) {
    +        // MATRIX
    +        byteArray[0] shouldEqualTo 'M'.toByte()
    +        byteArray[1] shouldEqualTo 'A'.toByte()
    +        byteArray[2] shouldEqualTo 'T'.toByte()
    +        byteArray[3] shouldEqualTo 'R'.toByte()
    +        byteArray[4] shouldEqualTo 'I'.toByte()
    +        byteArray[5] shouldEqualTo 'X'.toByte()
    +
    +        // Version
    +        byteArray[6] shouldEqualTo 2
    +    }
    +
    +    private fun checkSizeAndTransaction(byteArray: ByteArray) {
    +        // Size
    +        byteArray[8] shouldEqualTo 0
    +        byteArray[9] shouldEqualTo 13
    +
    +        // Transaction
    +        byteArray.copyOfRange(10, 10 + "MaTransaction".length).toString(Charsets.ISO_8859_1) shouldEqual "MaTransaction"
    +    }
    +}
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretV2Test.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretV2Test.kt
    new file mode 100644
    index 0000000000..6c46fcd6a9
    --- /dev/null
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretV2Test.kt
    @@ -0,0 +1,46 @@
    +/*
    + * 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 im.vector.matrix.android.internal.crypto.verification.qrcode
    +
    +import androidx.test.ext.junit.runners.AndroidJUnit4
    +import im.vector.matrix.android.InstrumentedTest
    +import org.amshove.kluent.shouldBe
    +import org.amshove.kluent.shouldNotBeEqualTo
    +import org.junit.FixMethodOrder
    +import org.junit.Test
    +import org.junit.runner.RunWith
    +import org.junit.runners.MethodSorters
    +
    +@RunWith(AndroidJUnit4::class)
    +@FixMethodOrder(MethodSorters.JVM)
    +class SharedSecretV2Test : InstrumentedTest {
    +
    +    @Test
    +    fun testSharedSecretLengthCase() {
    +        repeat(100) {
    +            generateSharedSecretV2().length shouldBe 11
    +        }
    +    }
    +
    +    @Test
    +    fun testSharedDiffCase() {
    +        val sharedSecret1 = generateSharedSecretV2()
    +        val sharedSecret2 = generateSharedSecretV2()
    +
    +        sharedSecret1 shouldNotBeEqualTo sharedSecret2
    +    }
    +}
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt
    index d539152135..a6f2927418 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt
    @@ -45,6 +45,7 @@ private const val ENCODING = "utf-8"
      *     &other_device_key=WqSVLkBREDACTEDBsfszdvsdBEvefqsdcsfBvsfcsFb
      * 
    */ +// @Deprecated(message = "Use QrCodeDataV2") fun QrCodeData.toUrl(): String { return buildString { append(PermalinkFactory.createPermalink(userId)) @@ -72,6 +73,7 @@ fun QrCodeData.toUrl(): String { } } +// @Deprecated(message = "Use QrCodeDataV2") fun String.toQrCodeData(): QrCodeData? { if (!startsWith(PermalinkFactory.MATRIX_TO_URL_BASE)) { return null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/ExtensionsV2.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/ExtensionsV2.kt new file mode 100644 index 0000000000..5e6c984990 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/ExtensionsV2.kt @@ -0,0 +1,123 @@ +/* + * 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 im.vector.matrix.android.internal.crypto.verification.qrcode + +import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64NoPadding +import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding + +// MATRIX +private val prefix = "MATRIX".toByteArray(Charsets.ISO_8859_1) + +fun QrCodeDataV2.toEncodedString(): String { + var result = ByteArray(0) + + // MATRIX + for (i in prefix.indices) { + result += prefix[i] + } + + // Version + result += 2 + + // Mode + result += when (this) { + is QrCodeDataV2.VerifyingAnotherUser -> 0 + is QrCodeDataV2.SelfVerifyingMasterKeyTrusted -> 1 + is QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted -> 2 + }.toByte() + + // TransactionId length + val length = transactionId.length + result += ((length and 0xFF00) shr 8).toByte() + result += length.toByte() + + // TransactionId + transactionId.forEach { + result += it.toByte() + } + + // Keys + firstKey.fromBase64NoPadding().forEach { + result += it + } + secondKey.fromBase64NoPadding().forEach { + result += it + } + + // Secret + sharedSecret.fromBase64NoPadding().forEach { + result += it + } + + return result.toString(Charsets.ISO_8859_1) +} + +fun String.toQrCodeDataV2(): QrCodeDataV2? { + val byteArray = toByteArray(Charsets.ISO_8859_1) + + // Size should be min 6 + 1 + 1 + 2 + ? + 32 + 32 + ? = 74 + transactionLength + secretLength + + // Check header + // MATRIX + if (byteArray.size < 10) return null + + for (i in prefix.indices) { + if (byteArray[i] != prefix[i]) { + return null + } + } + + var cursor = prefix.size // 6 + + // Version + if (byteArray[cursor] != 2.toByte()) { + return null + } + cursor++ + + // Get mode + val mode = byteArray[cursor].toInt() + cursor++ + + // Get transaction length + val transactionLength = (byteArray[cursor].toInt() shr 8) + byteArray[cursor + 1].toInt() + + cursor++ + cursor++ + + val secretLength = byteArray.size - 74 - transactionLength + + // ensure the secret length is 8 bytes min + if (secretLength < 8) { + return null + } + + val transactionId = byteArray.copyOfRange(cursor, cursor + transactionLength).toString(Charsets.ISO_8859_1) + cursor += transactionLength + val key1 = byteArray.copyOfRange(cursor, cursor + 32).toBase64NoPadding() + cursor += 32 + val key2 = byteArray.copyOfRange(cursor, cursor + 32).toBase64NoPadding() + cursor += 32 + val secret = byteArray.copyOfRange(cursor, byteArray.size).toBase64NoPadding() + + return when (mode) { + 0 -> QrCodeDataV2.VerifyingAnotherUser(transactionId, key1, key2, secret) + 1 -> QrCodeDataV2.SelfVerifyingMasterKeyTrusted(transactionId, key1, key2, secret) + 2 -> QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted(transactionId, key1, key2, secret) + else -> null + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt index 0f9a31ab32..07a11699ba 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt @@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.crypto.verification.qrcode /** * Ref: https://github.com/uhoreg/matrix-doc/blob/qr_key_verification/proposals/1543-qr_code_key_verification.md#qr-code-format */ +//@Deprecated(message = "Use QrCodeDataV2") data class QrCodeData( val userId: String, // Request Id. Can be an arbitrary value. In DM, it will be the event ID of the associated verification request event. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeDataV2.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeDataV2.kt new file mode 100644 index 0000000000..d3b23b30de --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeDataV2.kt @@ -0,0 +1,102 @@ +/* + * 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 im.vector.matrix.android.internal.crypto.verification.qrcode + +/** + * Ref: https://github.com/uhoreg/matrix-doc/blob/qr_key_verification/proposals/1543-qr_code_key_verification.md#qr-code-format + */ +sealed class QrCodeDataV2( + /** + * the event ID or transaction_id of the associated verification + */ + open val transactionId: String, + /** + * First key (32 bytes, in base64 no padding) + */ + val firstKey: String, + /** + * Second key (32 bytes, in base64 no padding) + */ + val secondKey: String, + /** + * a random shared secret (in base64 no padding) + */ + open val sharedSecret: String +) { + /** + * verifying another user with cross-signing + * QR code verification mode: 0x00 + */ + data class VerifyingAnotherUser( + override val transactionId: String, + /** + * the user's own master cross-signing public key + */ + val userMasterCrossSigningPublicKey: String, + /** + * what the device thinks the other user's master cross-signing key is + */ + val otherUserMasterCrossSigningPublicKey: String, + override val sharedSecret: String + ) : QrCodeDataV2( + transactionId, + userMasterCrossSigningPublicKey, + otherUserMasterCrossSigningPublicKey, + sharedSecret) + + /** + * self-verifying in which the current device does trust the master key + * QR code verification mode: 0x01 + */ + data class SelfVerifyingMasterKeyTrusted( + override val transactionId: String, + /** + * the user's own master cross-signing public key + */ + val userMasterCrossSigningPublicKey: String, + /** + * what the device thinks the other device's device key is + */ + val otherDeviceKey: String, + override val sharedSecret: String + ) : QrCodeDataV2( + transactionId, + userMasterCrossSigningPublicKey, + otherDeviceKey, + sharedSecret) + + /** + * self-verifying in which the current device does not yet trust the master key + * QR code verification mode: 0x02 + */ + data class SelfVerifyingMasterKeyNotTrusted( + override val transactionId: String, + /** + * the current device's device key + */ + val deviceKey: String, + /** + * what the device thinks the user's master cross-signing key is + */ + val userMasterCrossSigningPublicKey: String, + override val sharedSecret: String + ) : QrCodeDataV2( + transactionId, + deviceKey, + userMasterCrossSigningPublicKey, + sharedSecret) +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt index d319ebd88c..b34ff85346 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt @@ -27,3 +27,12 @@ fun generateSharedSecret(): String { secureRandom.nextBytes(secretBytes) return secretBytes.toBase64NoPadding() } + +fun generateSharedSecretV2(): String { + val secureRandom = SecureRandom() + + // 8 bytes long + val secretBytes = ByteArray(8) + secureRandom.nextBytes(secretBytes) + return secretBytes.toBase64NoPadding() +} From 859b9e4f8edc90ec3e3de90ee19fc0d39bc76e96 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 19 Feb 2020 17:50:30 +0100 Subject: [PATCH 072/187] Migrate to QrCode v2 - TODO: cleanup --- .../DefaultVerificationService.kt | 71 ++++----- .../DefaultQrCodeVerificationTransaction.kt | 144 +++++++----------- .../android/internal/util/Exhaustive.kt | 20 +++ 3 files changed, 110 insertions(+), 125 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Exhaustive.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt index 00ac4a6986..8771c7a303 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt @@ -65,8 +65,9 @@ import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_S import im.vector.matrix.android.internal.crypto.model.rest.toValue import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.verification.qrcode.DefaultQrCodeVerificationTransaction -import im.vector.matrix.android.internal.crypto.verification.qrcode.QrCodeData +import im.vector.matrix.android.internal.crypto.verification.qrcode.QrCodeDataV2 import im.vector.matrix.android.internal.crypto.verification.qrcode.generateSharedSecret +import im.vector.matrix.android.internal.crypto.verification.qrcode.generateSharedSecretV2 import im.vector.matrix.android.internal.di.DeviceId import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.session.SessionScope @@ -788,7 +789,7 @@ internal class DefaultVerificationService @Inject constructor( )) } - private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeData? { + private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeDataV2? { requestId ?: run { Timber.w("## Unknown requestId") return null @@ -796,17 +797,17 @@ internal class DefaultVerificationService @Inject constructor( return when { userId != otherUserId -> - createQrCodeDataForDistinctUser(requestId, otherUserId, otherDeviceId) + createQrCodeDataForDistinctUser(requestId, otherUserId /*, otherDeviceId*/) crossSigningService.isCrossSigningVerified() -> // This is a self verification and I am the old device (Osborne2) createQrCodeDataForVerifiedDevice(requestId, otherDeviceId) else -> // This is a self verification and I am the new device (Dynabook) - createQrCodeDataForUnVerifiedDevice(requestId, otherDeviceId) + createQrCodeDataForUnVerifiedDevice(requestId/*, otherDeviceId*/) } } - private fun createQrCodeDataForDistinctUser(requestId: String, otherUserId: String, otherDeviceId: String?): QrCodeData? { + private fun createQrCodeDataForDistinctUser(requestId: String, otherUserId: String /*, otherDeviceId: String?*/): QrCodeDataV2.VerifyingAnotherUser? { val myMasterKey = crossSigningService.getMyCrossSigningKeys() ?.masterKey() ?.unpaddedBase64PublicKey @@ -823,6 +824,7 @@ internal class DefaultVerificationService @Inject constructor( return null } + /* TODO Cleanup val myDeviceId = deviceId ?: run { Timber.w("## Unable to get my deviceId") @@ -839,23 +841,18 @@ internal class DefaultVerificationService @Inject constructor( ?.let { cryptoStore.getUserDevice(userId, otherDeviceId)?.fingerprint() } + */ - return QrCodeData( - userId = userId, - requestId = requestId, - action = QrCodeData.ACTION_VERIFY, - keys = hashMapOf( - myMasterKey to myMasterKey, - myDeviceId to myDeviceKey - ), - sharedSecret = generateSharedSecret(), - otherUserKey = otherUserMasterKey, - otherDeviceKey = otherDeviceKey + return QrCodeDataV2.VerifyingAnotherUser( + transactionId = requestId, + userMasterCrossSigningPublicKey = myMasterKey, + otherUserMasterCrossSigningPublicKey = otherUserMasterKey, + sharedSecret = generateSharedSecretV2() ) } // Create a QR code to display on the old device (Osborne2) - private fun createQrCodeDataForVerifiedDevice(requestId: String, otherDeviceId: String?): QrCodeData? { + private fun createQrCodeDataForVerifiedDevice(requestId: String, otherDeviceId: String?): QrCodeDataV2.SelfVerifyingMasterKeyTrusted? { val myMasterKey = crossSigningService.getMyCrossSigningKeys() ?.masterKey() ?.unpaddedBase64PublicKey @@ -873,6 +870,7 @@ internal class DefaultVerificationService @Inject constructor( return null } + /* TODO Cleanup val myDeviceId = deviceId ?: run { Timber.w("## Unable to get my deviceId") @@ -884,23 +882,18 @@ internal class DefaultVerificationService @Inject constructor( Timber.w("## Unable to get my fingerprint") return null } + */ - return QrCodeData( - userId = userId, - requestId = requestId, - action = QrCodeData.ACTION_VERIFY, - keys = hashMapOf( - myMasterKey to myMasterKey, - myDeviceId to myDeviceKey - ), - sharedSecret = generateSharedSecret(), - otherUserKey = null, - otherDeviceKey = otherDeviceKey + return QrCodeDataV2.SelfVerifyingMasterKeyTrusted( + transactionId = requestId, + userMasterCrossSigningPublicKey = myMasterKey, + otherDeviceKey = otherDeviceKey, + sharedSecret = generateSharedSecretV2() ) } // Create a QR code to display on the new device (Dynabook) - private fun createQrCodeDataForUnVerifiedDevice(requestId: String, otherDeviceId: String?): QrCodeData? { + private fun createQrCodeDataForUnVerifiedDevice(requestId: String/*, otherDeviceId: String?*/): QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted? { val myMasterKey = crossSigningService.getMyCrossSigningKeys() ?.masterKey() ?.unpaddedBase64PublicKey @@ -909,11 +902,13 @@ internal class DefaultVerificationService @Inject constructor( return null } + /* TODO Cleanup val myDeviceId = deviceId ?: run { Timber.w("## Unable to get my deviceId") return null } + */ val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint() ?: run { @@ -921,22 +916,18 @@ internal class DefaultVerificationService @Inject constructor( return null } + /* TODO Cleanup val otherDeviceKey = otherDeviceId ?.let { cryptoStore.getUserDevice(userId, otherDeviceId)?.fingerprint() } + */ - return QrCodeData( - userId = userId, - requestId = requestId, - action = QrCodeData.ACTION_VERIFY, - keys = hashMapOf( - // Note: no master key here - myDeviceId to myDeviceKey - ), - sharedSecret = generateSharedSecret(), - otherUserKey = myMasterKey, - otherDeviceKey = otherDeviceKey + return QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted( + transactionId = requestId, + deviceKey = myDeviceKey, + userMasterCrossSigningPublicKey = myMasterKey, + sharedSecret = generateSharedSecretV2() ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt index d1b72f54c6..c6b84c60b0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt @@ -28,7 +28,7 @@ import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.verification.DefaultVerificationTransaction import im.vector.matrix.android.internal.crypto.verification.VerificationInfo import im.vector.matrix.android.internal.crypto.verification.VerificationInfoStart -import im.vector.matrix.android.internal.util.withoutPrefix +import im.vector.matrix.android.internal.util.exhaustive import timber.log.Timber internal class DefaultQrCodeVerificationTransaction( @@ -39,14 +39,14 @@ internal class DefaultQrCodeVerificationTransaction( private val crossSigningService: CrossSigningService, private val cryptoStore: IMXCryptoStore, // Not null only if other user is able to scan QR code - private val qrCodeData: QrCodeData?, + private val qrCodeData: QrCodeDataV2?, val userId: String, val deviceId: String, override val isIncoming: Boolean ) : DefaultVerificationTransaction(transactionId, otherUserId, otherDeviceId, isIncoming), QrCodeVerificationTransaction { override val qrCodeText: String? - get() = qrCodeData?.toUrl() + get() = qrCodeData?.toEncodedString() override var state: VerificationTxState = VerificationTxState.None set(newState) { @@ -62,96 +62,77 @@ internal class DefaultQrCodeVerificationTransaction( } override fun userHasScannedOtherQrCode(otherQrCodeText: String) { - val otherQrCodeData = otherQrCodeText.toQrCodeData() ?: run { + val otherQrCodeData = otherQrCodeText.toQrCodeDataV2() ?: run { Timber.d("## Verification QR: Invalid QR Code Data") cancel(CancelCode.QrCodeInvalid) return } // Perform some checks - if (otherQrCodeData.action != QrCodeData.ACTION_VERIFY) { - Timber.d("## Verification QR: Invalid action ${otherQrCodeData.action}") - cancel(CancelCode.QrCodeInvalid) - return - } - - if (otherQrCodeData.userId != otherUserId) { - Timber.d("## Verification QR: Mismatched user ${otherQrCodeData.userId}") - cancel(CancelCode.MismatchedUser) - return - } - - if (otherQrCodeData.requestId != transactionId) { - Timber.d("## Verification QR: Invalid transaction actual ${otherQrCodeData.requestId} expected:$transactionId") + if (otherQrCodeData.transactionId != transactionId) { + Timber.d("## Verification QR: Invalid transaction actual ${otherQrCodeData.transactionId} expected:$transactionId") cancel(CancelCode.QrCodeInvalid) return } // check master key - if (otherQrCodeData.userId != userId - && otherQrCodeData.otherUserKey == null) { - // Verification with other user, other_user_key is mandatory in this case - Timber.d("## Verification QR: Invalid, missing other_user_key") - cancel(CancelCode.QrCodeInvalid) - return - } - - if (otherQrCodeData.otherUserKey != null - && otherQrCodeData.otherUserKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { - Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.otherUserKey}") - cancel(CancelCode.MismatchedKeys) - return - } - - // Check device key if available - if (otherQrCodeData.otherDeviceKey != null - && otherQrCodeData.otherDeviceKey != cryptoStore.getUserDevice(userId, deviceId)?.fingerprint()) { - Timber.d("## Verification QR: Invalid other device key") - cancel(CancelCode.MismatchedKeys) - return - } + when (otherQrCodeData) { + is QrCodeDataV2.VerifyingAnotherUser -> { + if (otherQrCodeData.otherUserMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { + Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.otherUserMasterCrossSigningPublicKey}") + cancel(CancelCode.MismatchedKeys) + return + } else Unit + } + is QrCodeDataV2.SelfVerifyingMasterKeyTrusted -> { + if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { + Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}") + cancel(CancelCode.MismatchedKeys) + return + } else Unit + } + is QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted -> { + if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { + Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}") + cancel(CancelCode.MismatchedKeys) + return + } else Unit + } + }.exhaustive val toVerifyDeviceIds = mutableListOf() var canTrustOtherUserMasterKey = false - val otherDevices = cryptoStore.getUserDevices(otherUserId) - otherQrCodeData.keys.keys.forEach { key -> - Timber.w("## Verification QR: Checking key $key") - - when (val keyNoPrefix = key.withoutPrefix("ed25519:")) { - otherQrCodeData.keys[key] -> { - // Maybe master key? - if (otherQrCodeData.keys[key] == crossSigningService.getUserCrossSigningKeys(otherUserId)?.masterKey()?.unpaddedBase64PublicKey) { - canTrustOtherUserMasterKey = true - } else { - cancel(CancelCode.MismatchedKeys) - return - } - } - else -> { - when (val otherDevice = otherDevices?.get(keyNoPrefix)) { - null -> { - // Unknown device, ignore - } - else -> { - when (otherDevice.fingerprint()) { - null -> { - // Ignore - } - otherQrCodeData.keys[key] -> { - // Store the deviceId to verify after - toVerifyDeviceIds.add(key) - } - else -> { - cancel(CancelCode.MismatchedKeys) - return - } - } - } - } + // Check device key if available + when (otherQrCodeData) { + is QrCodeDataV2.VerifyingAnotherUser -> { + if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(otherUserId)?.masterKey()?.unpaddedBase64PublicKey) { + Timber.d("## Verification QR: Invalid user master key ${otherQrCodeData.userMasterCrossSigningPublicKey}") + cancel(CancelCode.MismatchedKeys) + return + } else { + canTrustOtherUserMasterKey = true + Unit } } - } + is QrCodeDataV2.SelfVerifyingMasterKeyTrusted -> { + if (otherQrCodeData.otherDeviceKey != cryptoStore.getUserDevice(userId, deviceId)?.fingerprint()) { + Timber.d("## Verification QR: Invalid other device key ${otherQrCodeData.otherDeviceKey}") + cancel(CancelCode.MismatchedKeys) + return + } else Unit + } + is QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted -> { + if (otherQrCodeData.deviceKey != cryptoStore.getUserDevice(otherUserId, otherDeviceId ?: "")?.fingerprint()) { + Timber.d("## Verification QR: Invalid device key ${otherQrCodeData.deviceKey}") + cancel(CancelCode.MismatchedKeys) + return + } else { + toVerifyDeviceIds.add(otherQrCodeData.deviceKey) + Unit + } + } + }.exhaustive if (!canTrustOtherUserMasterKey && toVerifyDeviceIds.isEmpty()) { // Nothing to verify @@ -164,13 +145,6 @@ internal class DefaultQrCodeVerificationTransaction( // qrCodeData.sharedSecret will be used to send the start request start(otherQrCodeData.sharedSecret) - val safeOtherDeviceId = otherDeviceId - if (!otherQrCodeData.otherDeviceKey.isNullOrBlank() - && safeOtherDeviceId != null) { - // Locally verify the device - toVerifyDeviceIds.add(safeOtherDeviceId) - } - // Trust the other user trust(canTrustOtherUserMasterKey, toVerifyDeviceIds.distinct()) } @@ -264,8 +238,8 @@ internal class DefaultQrCodeVerificationTransaction( // TODO what if the otherDevice is not in this list? and should we toVerifyDeviceIds.forEach { - setDeviceVerified(otherUserId, it) - } + setDeviceVerified(otherUserId, it) + } transport.done(transactionId) state = VerificationTxState.Verified } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Exhaustive.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Exhaustive.kt new file mode 100644 index 0000000000..8f6beea92d --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Exhaustive.kt @@ -0,0 +1,20 @@ +/* + * 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 im.vector.matrix.android.internal.util + +// Trick to ensure that when block is exhaustive +internal val T.exhaustive: T get() = this From f81eb298cb35f4b7803a22703d19415bae68d229 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 19 Feb 2020 18:05:47 +0100 Subject: [PATCH 073/187] Cleanup QRCode v1 --- .../verification/qrcode/SharedSecretTest.kt | 46 ---- .../DefaultVerificationService.kt | 57 +--- .../crypto/verification/qrcode/Extensions.kt | 133 ---------- .../crypto/verification/qrcode/QrCodeData.kt | 46 ---- .../verification/qrcode/SharedSecret.kt | 9 - .../crypto/verification/qrcode/QrCodeTest.kt | 246 ------------------ 6 files changed, 4 insertions(+), 533 deletions(-) delete mode 100644 matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt delete mode 100644 matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt deleted file mode 100644 index 7a07c16d14..0000000000 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 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 im.vector.matrix.android.internal.crypto.verification.qrcode - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import im.vector.matrix.android.InstrumentedTest -import org.amshove.kluent.shouldBe -import org.amshove.kluent.shouldNotBeEqualTo -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters - -@RunWith(AndroidJUnit4::class) -@FixMethodOrder(MethodSorters.JVM) -class SharedSecretTest : InstrumentedTest { - - @Test - fun testSharedSecretLengthCase() { - repeat(100) { - generateSharedSecret().length shouldBe 43 - } - } - - @Test - fun testSharedDiffCase() { - val sharedSecret1 = generateSharedSecret() - val sharedSecret2 = generateSharedSecret() - - sharedSecret1 shouldNotBeEqualTo sharedSecret2 - } -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt index 8771c7a303..317bbe6415 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt @@ -66,7 +66,6 @@ import im.vector.matrix.android.internal.crypto.model.rest.toValue import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.verification.qrcode.DefaultQrCodeVerificationTransaction import im.vector.matrix.android.internal.crypto.verification.qrcode.QrCodeDataV2 -import im.vector.matrix.android.internal.crypto.verification.qrcode.generateSharedSecret import im.vector.matrix.android.internal.crypto.verification.qrcode.generateSharedSecretV2 import im.vector.matrix.android.internal.di.DeviceId import im.vector.matrix.android.internal.di.UserId @@ -797,17 +796,17 @@ internal class DefaultVerificationService @Inject constructor( return when { userId != otherUserId -> - createQrCodeDataForDistinctUser(requestId, otherUserId /*, otherDeviceId*/) + createQrCodeDataForDistinctUser(requestId, otherUserId) crossSigningService.isCrossSigningVerified() -> // This is a self verification and I am the old device (Osborne2) createQrCodeDataForVerifiedDevice(requestId, otherDeviceId) else -> // This is a self verification and I am the new device (Dynabook) - createQrCodeDataForUnVerifiedDevice(requestId/*, otherDeviceId*/) + createQrCodeDataForUnVerifiedDevice(requestId) } } - private fun createQrCodeDataForDistinctUser(requestId: String, otherUserId: String /*, otherDeviceId: String?*/): QrCodeDataV2.VerifyingAnotherUser? { + private fun createQrCodeDataForDistinctUser(requestId: String, otherUserId: String): QrCodeDataV2.VerifyingAnotherUser? { val myMasterKey = crossSigningService.getMyCrossSigningKeys() ?.masterKey() ?.unpaddedBase64PublicKey @@ -824,25 +823,6 @@ internal class DefaultVerificationService @Inject constructor( return null } - /* TODO Cleanup - val myDeviceId = deviceId - ?: run { - Timber.w("## Unable to get my deviceId") - return null - } - - val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint() - ?: run { - Timber.w("## Unable to get my fingerprint") - return null - } - - val otherDeviceKey = otherDeviceId - ?.let { - cryptoStore.getUserDevice(userId, otherDeviceId)?.fingerprint() - } - */ - return QrCodeDataV2.VerifyingAnotherUser( transactionId = requestId, userMasterCrossSigningPublicKey = myMasterKey, @@ -870,20 +850,6 @@ internal class DefaultVerificationService @Inject constructor( return null } - /* TODO Cleanup - val myDeviceId = deviceId - ?: run { - Timber.w("## Unable to get my deviceId") - return null - } - - val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint() - ?: run { - Timber.w("## Unable to get my fingerprint") - return null - } - */ - return QrCodeDataV2.SelfVerifyingMasterKeyTrusted( transactionId = requestId, userMasterCrossSigningPublicKey = myMasterKey, @@ -893,7 +859,7 @@ internal class DefaultVerificationService @Inject constructor( } // Create a QR code to display on the new device (Dynabook) - private fun createQrCodeDataForUnVerifiedDevice(requestId: String/*, otherDeviceId: String?*/): QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted? { + private fun createQrCodeDataForUnVerifiedDevice(requestId: String): QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted? { val myMasterKey = crossSigningService.getMyCrossSigningKeys() ?.masterKey() ?.unpaddedBase64PublicKey @@ -902,27 +868,12 @@ internal class DefaultVerificationService @Inject constructor( return null } - /* TODO Cleanup - val myDeviceId = deviceId - ?: run { - Timber.w("## Unable to get my deviceId") - return null - } - */ - val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint() ?: run { Timber.w("## Unable to get my fingerprint") return null } - /* TODO Cleanup - val otherDeviceKey = otherDeviceId - ?.let { - cryptoStore.getUserDevice(userId, otherDeviceId)?.fingerprint() - } - */ - return QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted( transactionId = requestId, deviceKey = myDeviceKey, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt deleted file mode 100644 index a6f2927418..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 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 im.vector.matrix.android.internal.crypto.verification.qrcode - -import im.vector.matrix.android.api.MatrixPatterns -import im.vector.matrix.android.api.permalinks.PermalinkFactory -import java.net.URLDecoder -import java.net.URLEncoder - -private const val ENCODING = "utf-8" - -/** - * Generate an URL to generate a QR code of the form: - *
    - * https://matrix.to/#/?
    - *     request=
    - *     &action=verify
    - *     &key_=...
    - *     &secret=
    - *     &other_user_key=
    - *     &other_device_key=
    - *
    - * Example:
    - * https://matrix.to/#/@user:matrix.org?
    - *     request=%24pBeIfm7REDACTEDSQJbgqvi-yYiwmPB8_H_W_O974
    - *     &action=verify
    - *     &key_VJEDVKUYTQ=DL7LWIw7Qp%2B4AREDACTEDOwy2BjygumSWAGfzaWY
    - *     &key_fsh%2FfQ08N3xvh4ySXsINB%2BJ2hREDACTEDVcVOG4qqo=fsh%2FfQ08N3xvh4ySXsINB%2BJ2hREDACTEDVcVOG4qqo
    - *     &secret=AjQqw51Fp6UBuPolZ2FAD5WnXc22ZhJG6iGslrVvIdw%3D
    - *     &other_user_key=WqSVLkBCS%2Fi5NqRREDACTEDRPxBIuqK8Usl6Y3big
    - *     &other_device_key=WqSVLkBREDACTEDBsfszdvsdBEvefqsdcsfBvsfcsFb
    - * 
    - */ -// @Deprecated(message = "Use QrCodeDataV2") -fun QrCodeData.toUrl(): String { - return buildString { - append(PermalinkFactory.createPermalink(userId)) - append("?request=") - append(URLEncoder.encode(requestId, ENCODING)) - append("&action=") - append(URLEncoder.encode(action, ENCODING)) - - for ((keyId, key) in keys) { - append("&key_${URLEncoder.encode(keyId, ENCODING)}=") - append(URLEncoder.encode(key, ENCODING)) - } - - append("&secret=") - append(URLEncoder.encode(sharedSecret, ENCODING)) - - if (!otherUserKey.isNullOrBlank()) { - append("&other_user_key=") - append(URLEncoder.encode(otherUserKey, ENCODING)) - } - if (!otherDeviceKey.isNullOrBlank()) { - append("&other_device_key=") - append(URLEncoder.encode(otherDeviceKey, ENCODING)) - } - } -} - -// @Deprecated(message = "Use QrCodeDataV2") -fun String.toQrCodeData(): QrCodeData? { - if (!startsWith(PermalinkFactory.MATRIX_TO_URL_BASE)) { - return null - } - - val fragment = substringAfter("#") - if (fragment.isEmpty()) { - return null - } - - val safeFragment = fragment.substringBefore("?") - - // we are limiting to 2 params - val params = safeFragment - .split(MatrixPatterns.SEP_REGEX.toRegex()) - .filter { it.isNotEmpty() } - - if (params.size != 1) { - return null - } - - val userId = params.getOrNull(0) - ?.let { PermalinkFactory.unescape(it) } - ?.takeIf { MatrixPatterns.isUserId(it) } ?: return null - - val urlParams = fragment.substringAfter("?") - .split("&".toRegex()) - .filter { it.isNotEmpty() } - - val keyValues = urlParams.map { - (it.substringBefore("=") to it.substringAfter("=").let { value -> URLDecoder.decode(value, ENCODING) }) - }.toMap() - - val action = keyValues["action"]?.takeIf { it.isNotBlank() } ?: return null - - val requestEventId = keyValues["request"]?.takeIf { it.isNotBlank() } ?: return null - val sharedSecret = keyValues["secret"]?.takeIf { it.isNotBlank() } ?: return null - val otherUserKey = keyValues["other_user_key"] - val otherDeviceKey = keyValues["other_device_key"] - - val keys = keyValues.keys - .filter { it.startsWith("key_") } - .map { - URLDecoder.decode(it.substringAfter("key_"), ENCODING) to (keyValues[it] ?: return null) - } - .toMap() - - return QrCodeData( - userId, - requestEventId, - action, - keys, - sharedSecret, - otherUserKey, - otherDeviceKey - ) -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt deleted file mode 100644 index 07a11699ba..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 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 im.vector.matrix.android.internal.crypto.verification.qrcode - -/** - * Ref: https://github.com/uhoreg/matrix-doc/blob/qr_key_verification/proposals/1543-qr_code_key_verification.md#qr-code-format - */ -//@Deprecated(message = "Use QrCodeDataV2") -data class QrCodeData( - val userId: String, - // Request Id. Can be an arbitrary value. In DM, it will be the event ID of the associated verification request event. - val requestId: String, - // The action - val action: String, - // key_: each key that the user wants verified will have an entry of this form, where the value is the key in unpadded base64. - // The QR code should contain at least the user's master cross-signing key. In the case where a device does not have a cross-signing key - // (as in the case where a user logs in to a new device, and is verifying against another device), thin the QR code should contain at - // least the device's key. - val keys: Map, - // random single-use shared secret in unpadded base64. It must be at least 256-bits long (43 characters when base64-encoded). - val sharedSecret: String, - // the other user's master cross-signing key, in unpadded base64. In other words, if Alice is displaying the QR code, - // this would be the copy of Bob's master cross-signing key that Alice has. - val otherUserKey: String?, - // The other device's key, in unpadded base64 - // This is only needed when a user is verifying their own devices, where the other device has not yet been signed with the cross-signing key. - val otherDeviceKey: String? -) { - companion object { - const val ACTION_VERIFY = "verify" - } -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt index b34ff85346..397cad0c53 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt @@ -19,15 +19,6 @@ package im.vector.matrix.android.internal.crypto.verification.qrcode import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding import java.security.SecureRandom -fun generateSharedSecret(): String { - val secureRandom = SecureRandom() - - // 256 bits long - val secretBytes = ByteArray(32) - secureRandom.nextBytes(secretBytes) - return secretBytes.toBase64NoPadding() -} - fun generateSharedSecretV2(): String { val secureRandom = SecureRandom() diff --git a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt deleted file mode 100644 index 2dd0649be1..0000000000 --- a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 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 im.vector.matrix.android.internal.crypto.verification.qrcode - -import org.amshove.kluent.shouldBe -import org.amshove.kluent.shouldBeEqualTo -import org.amshove.kluent.shouldBeNull -import org.amshove.kluent.shouldNotBeNull -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runners.MethodSorters - -@Suppress("SpellCheckingInspection") -@FixMethodOrder(MethodSorters.JVM) -class QrCodeTest { - - private val basicQrCodeData = QrCodeData( - userId = "@benoit:matrix.org", - requestId = "\$azertyazerty", - action = QrCodeData.ACTION_VERIFY, - keys = mapOf( - "1" to "abcdef", - "2" to "ghijql" - ), - sharedSecret = "sharedSecret", - otherUserKey = "otherUserKey", - otherDeviceKey = "otherDeviceKey" - ) - - private val basicUrl = "https://matrix.to/#/@benoit:matrix.org" + - "?request=%24azertyazerty" + - "&action=verify" + - "&key_1=abcdef" + - "&key_2=ghijql" + - "&secret=sharedSecret" + - "&other_user_key=otherUserKey" + - "&other_device_key=otherDeviceKey" - - @Test - fun testNominalCase() { - val url = basicQrCodeData.toUrl() - - url shouldBeEqualTo basicUrl - - val decodedData = url.toQrCodeData() - - decodedData.shouldNotBeNull() - - decodedData.userId shouldBeEqualTo "@benoit:matrix.org" - decodedData.requestId shouldBeEqualTo "\$azertyazerty" - decodedData.keys["1"]?.shouldBeEqualTo("abcdef") - decodedData.keys["2"]?.shouldBeEqualTo("ghijql") - decodedData.sharedSecret shouldBeEqualTo "sharedSecret" - decodedData.otherUserKey?.shouldBeEqualTo("otherUserKey") - decodedData.otherDeviceKey?.shouldBeEqualTo("otherDeviceKey") - } - - @Test - fun testSlashCase() { - val url = basicQrCodeData - .copy( - userId = "@benoit/foo:matrix.org", - requestId = "\$azertyazerty/bar" - ) - .toUrl() - - url shouldBeEqualTo basicUrl - .replace("@benoit", "@benoit%2Ffoo") - .replace("azertyazerty", "azertyazerty%2Fbar") - - val decodedData = url.toQrCodeData() - - decodedData.shouldNotBeNull() - - decodedData.userId shouldBeEqualTo "@benoit/foo:matrix.org" - decodedData.requestId shouldBeEqualTo "\$azertyazerty/bar" - decodedData.keys["1"]?.shouldBeEqualTo("abcdef") - decodedData.keys["2"]?.shouldBeEqualTo("ghijql") - decodedData.sharedSecret shouldBeEqualTo "sharedSecret" - decodedData.otherUserKey!! shouldBeEqualTo "otherUserKey" - decodedData.otherDeviceKey!! shouldBeEqualTo "otherDeviceKey" - } - - @Test - fun testNoOtherUserKey() { - val url = basicQrCodeData - .copy( - otherUserKey = null - ) - .toUrl() - - url shouldBeEqualTo basicUrl - .replace("&other_user_key=otherUserKey", "") - - val decodedData = url.toQrCodeData() - - decodedData.shouldNotBeNull() - - decodedData.userId shouldBeEqualTo "@benoit:matrix.org" - decodedData.requestId shouldBeEqualTo "\$azertyazerty" - decodedData.keys["1"]?.shouldBeEqualTo("abcdef") - decodedData.keys["2"]?.shouldBeEqualTo("ghijql") - decodedData.sharedSecret shouldBeEqualTo "sharedSecret" - decodedData.otherUserKey shouldBe null - decodedData.otherDeviceKey?.shouldBeEqualTo("otherDeviceKey") - } - - @Test - fun testNoOtherDeviceKey() { - val url = basicQrCodeData - .copy( - otherDeviceKey = null - ) - .toUrl() - - url shouldBeEqualTo basicUrl - .replace("&other_device_key=otherDeviceKey", "") - - val decodedData = url.toQrCodeData() - - decodedData.shouldNotBeNull() - - decodedData.userId shouldBeEqualTo "@benoit:matrix.org" - decodedData.requestId shouldBeEqualTo "\$azertyazerty" - decodedData.keys["1"]?.shouldBeEqualTo("abcdef") - decodedData.keys["2"]?.shouldBeEqualTo("ghijql") - decodedData.sharedSecret shouldBeEqualTo "sharedSecret" - decodedData.otherUserKey?.shouldBeEqualTo("otherUserKey") - decodedData.otherDeviceKey shouldBe null - } - - @Test - fun testUrlCharInKeys() { - val url = basicQrCodeData - .copy( - keys = mapOf( - "/=" to "abcdef", - "&?" to "ghijql" - ) - ) - .toUrl() - - url shouldBeEqualTo basicUrl - .replace("key_1=abcdef", "key_%2F%3D=abcdef") - .replace("key_2=ghijql", "key_%26%3F=ghijql") - - val decodedData = url.toQrCodeData() - - decodedData.shouldNotBeNull() - - decodedData.keys["/="]?.shouldBeEqualTo("abcdef") - decodedData.keys["&&"]?.shouldBeEqualTo("ghijql") - } - - @Test - fun testMissingActionCase() { - basicUrl.replace("&action=verify", "") - .toQrCodeData() - .shouldBeNull() - } - - @Test - fun testEmptyActionCase() { - basicUrl.replace("&action=verify", "&action=") - .toQrCodeData() - .shouldBeNull() - } - - @Test - fun testOtherActionCase() { - basicUrl.replace("&action=verify", "&action=confirm") - .toQrCodeData() - ?.action - ?.shouldBeEqualTo("confirm") - } - - @Test - fun testMissingRequestId() { - basicUrl.replace("request=%24azertyazerty", "") - .toQrCodeData() - .shouldBeNull() - } - - @Test - fun testEmptyRequestId() { - basicUrl.replace("request=%24azertyazerty", "request=") - .toQrCodeData() - .shouldBeNull() - } - - @Test - fun testMissingUserId() { - basicUrl.replace("@benoit:matrix.org", "") - .toQrCodeData() - .shouldBeNull() - } - - @Test - fun testBadUserId() { - basicUrl.replace("@benoit:matrix.org", "@benoit") - .toQrCodeData() - .shouldBeNull() - } - - @Test - fun testMissingSecret() { - basicUrl.replace("&secret=sharedSecret", "") - .toQrCodeData() - .shouldBeNull() - } - - @Test - fun testEmptySecret() { - basicUrl.replace("&secret=sharedSecret", "&secret=") - .toQrCodeData() - .shouldBeNull() - } - - @Test - fun testSelfSigning() { - // request is not an eventId in this case - val url = "https://matrix.to/#/@benoit0815:matrix.org" + - "?request=local.4dff40e1-7bf1-4e80-81ed-c6090d43bf20" + - "&action=verify" + - "&key_utbSRFcFjFDYf0KcNv3FoBHFSbvUPXtCYutuOg6WQ%2Bs=utbSRFcFjFDYf0KcNv3FoBHFSbvUPXtCYutuOg6WQ%2Bs" + - "&key_YSOXZVBXIZ=F0XWqgUePgwm5HMYG3yhBNneHmscrAxxlooLHjy8YQc" + - "&secret=LYVcEQmfdorbJ3vbQnq7nbNZc%2BGmDxUen1rByV9hRM4" + - "&other_device_key=eGoUqZqAroCYpjp7FLGIkTEzYHBFED4uUAfJ267gqQQ" - - url.toQrCodeData()!!.requestId shouldBeEqualTo "local.4dff40e1-7bf1-4e80-81ed-c6090d43bf20" - } -} From e81439d57ba67c0d62dca8f691a9a90134d97412 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 19 Feb 2020 18:09:33 +0100 Subject: [PATCH 074/187] Remove v2 suffix --- .../qrcode/{QrCodeV2Test.kt => QrCodeTest.kt} | 38 +++++++++---------- ...redSecretV2Test.kt => SharedSecretTest.kt} | 2 +- .../DefaultVerificationService.kt | 16 ++++---- .../DefaultQrCodeVerificationTransaction.kt | 16 ++++---- .../qrcode/{ExtensionsV2.kt => Extensions.kt} | 16 ++++---- .../qrcode/{QrCodeDataV2.kt => QrCodeData.kt} | 8 ++-- 6 files changed, 48 insertions(+), 48 deletions(-) rename matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/{QrCodeV2Test.kt => QrCodeTest.kt} (89%) rename matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/{SharedSecretV2Test.kt => SharedSecretTest.kt} (96%) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/{ExtensionsV2.kt => Extensions.kt} (84%) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/{QrCodeDataV2.kt => QrCodeData.kt} (97%) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeV2Test.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt similarity index 89% rename from matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeV2Test.kt rename to matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt index 3bcb5c660f..1cbc1a17df 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeV2Test.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt @@ -29,9 +29,9 @@ import org.junit.runners.MethodSorters @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) -class QrCodeV2Test : InstrumentedTest { +class QrCodeTest : InstrumentedTest { - private val qrCode1 = QrCodeDataV2.VerifyingAnotherUser( + private val qrCode1 = QrCodeData.VerifyingAnotherUser( transactionId = "MaTransaction", userMasterCrossSigningPublicKey = "ktEwcUP6su1xh+GuE+CYkQ3H6W/DIl+ybHFdaEOrolU", otherUserMasterCrossSigningPublicKey = "TXluZKTZLvSRWOTPlOqLq534bA+/K4zLFKSu9cGLQaU", @@ -40,7 +40,7 @@ class QrCodeV2Test : InstrumentedTest { private val value1 = "MATRIX\u0002\u0000\u0000\u000DMaTransaction\u0092Ñ0qCú²íq\u0087á®\u0013à\u0098\u0091\u000DÇéoÃ\"_²lq]hC«¢UMynd¤Ù.ô\u0091XäÏ\u0094ê\u008B«\u009Døl\u000F¿+\u008CË\u0014¤®õÁ\u008BA¥12345678" - private val qrCode2 = QrCodeDataV2.SelfVerifyingMasterKeyTrusted( + private val qrCode2 = QrCodeData.SelfVerifyingMasterKeyTrusted( transactionId = "MaTransaction", userMasterCrossSigningPublicKey = "ktEwcUP6su1xh+GuE+CYkQ3H6W/DIl+ybHFdaEOrolU", otherDeviceKey = "TXluZKTZLvSRWOTPlOqLq534bA+/K4zLFKSu9cGLQaU", @@ -49,7 +49,7 @@ class QrCodeV2Test : InstrumentedTest { private val value2 = "MATRIX\u0002\u0001\u0000\u000DMaTransaction\u0092Ñ0qCú²íq\u0087á®\u0013à\u0098\u0091\u000DÇéoÃ\"_²lq]hC«¢UMynd¤Ù.ô\u0091XäÏ\u0094ê\u008B«\u009Døl\u000F¿+\u008CË\u0014¤®õÁ\u008BA¥12345678" - private val qrCode3 = QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted( + private val qrCode3 = QrCodeData.SelfVerifyingMasterKeyNotTrusted( transactionId = "MaTransaction", deviceKey = "TXluZKTZLvSRWOTPlOqLq534bA+/K4zLFKSu9cGLQaU", userMasterCrossSigningPublicKey = "ktEwcUP6su1xh+GuE+CYkQ3H6W/DIl+ybHFdaEOrolU", @@ -153,17 +153,17 @@ class QrCodeV2Test : InstrumentedTest { @Test fun testSymmetry1() { - qrCode1.toEncodedString().toQrCodeDataV2() shouldEqual qrCode1 + qrCode1.toEncodedString().toQrCodeData() shouldEqual qrCode1 } @Test fun testSymmetry2() { - qrCode2.toEncodedString().toQrCodeDataV2() shouldEqual qrCode2 + qrCode2.toEncodedString().toQrCodeData() shouldEqual qrCode2 } @Test fun testSymmetry3() { - qrCode3.toEncodedString().toQrCodeDataV2() shouldEqual qrCode3 + qrCode3.toEncodedString().toQrCodeData() shouldEqual qrCode3 } @Test @@ -221,44 +221,44 @@ class QrCodeV2Test : InstrumentedTest { // Error cases @Test fun testErrorHeader() { - value1.replace("MATRIX", "MOTRIX").toQrCodeDataV2().shouldBeNull() - value1.replace("MATRIX", "MATRI").toQrCodeDataV2().shouldBeNull() - value1.replace("MATRIX", "").toQrCodeDataV2().shouldBeNull() + value1.replace("MATRIX", "MOTRIX").toQrCodeData().shouldBeNull() + value1.replace("MATRIX", "MATRI").toQrCodeData().shouldBeNull() + value1.replace("MATRIX", "").toQrCodeData().shouldBeNull() } @Test fun testErrorVersion() { - value1.replace("MATRIX\u0002", "MATRIX\u0000").toQrCodeDataV2().shouldBeNull() - value1.replace("MATRIX\u0002", "MATRIX\u0001").toQrCodeDataV2().shouldBeNull() - value1.replace("MATRIX\u0002", "MATRIX\u0003").toQrCodeDataV2().shouldBeNull() - value1.replace("MATRIX\u0002", "MATRIX").toQrCodeDataV2().shouldBeNull() + value1.replace("MATRIX\u0002", "MATRIX\u0000").toQrCodeData().shouldBeNull() + value1.replace("MATRIX\u0002", "MATRIX\u0001").toQrCodeData().shouldBeNull() + value1.replace("MATRIX\u0002", "MATRIX\u0003").toQrCodeData().shouldBeNull() + value1.replace("MATRIX\u0002", "MATRIX").toQrCodeData().shouldBeNull() } @Test fun testErrorSecretTooShort() { - value1.replace("12345678", "1234567").toQrCodeDataV2().shouldBeNull() + value1.replace("12345678", "1234567").toQrCodeData().shouldBeNull() } @Test fun testErrorNoTransactionNoKeyNoSecret() { // But keep transaction length - "MATRIX\u0002\u0000\u0000\u000D".toQrCodeDataV2().shouldBeNull() + "MATRIX\u0002\u0000\u0000\u000D".toQrCodeData().shouldBeNull() } @Test fun testErrorNoKeyNoSecret() { - "MATRIX\u0002\u0000\u0000\u000DMaTransaction".toQrCodeDataV2().shouldBeNull() + "MATRIX\u0002\u0000\u0000\u000DMaTransaction".toQrCodeData().shouldBeNull() } @Test fun testErrorTransactionLengthTooShort() { // In this case, the secret will be longer, so this is not an error, but it will lead to keys mismatch - value1.replace("\u000DMaTransaction", "\u000CMaTransaction").toQrCodeDataV2().shouldNotBeNull() + value1.replace("\u000DMaTransaction", "\u000CMaTransaction").toQrCodeData().shouldNotBeNull() } @Test fun testErrorTransactionLengthTooBig() { - value1.replace("\u000DMaTransaction", "\u000EMaTransaction").toQrCodeDataV2().shouldBeNull() + value1.replace("\u000DMaTransaction", "\u000EMaTransaction").toQrCodeData().shouldBeNull() } private fun compareArray(actual: ByteArray, expected: ByteArray) { diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretV2Test.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt similarity index 96% rename from matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretV2Test.kt rename to matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt index 6c46fcd6a9..4ab79be18b 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretV2Test.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt @@ -27,7 +27,7 @@ import org.junit.runners.MethodSorters @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) -class SharedSecretV2Test : InstrumentedTest { +class SharedSecretTest : InstrumentedTest { @Test fun testSharedSecretLengthCase() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt index 317bbe6415..fa1bfb3b98 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt @@ -65,7 +65,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_S import im.vector.matrix.android.internal.crypto.model.rest.toValue import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.verification.qrcode.DefaultQrCodeVerificationTransaction -import im.vector.matrix.android.internal.crypto.verification.qrcode.QrCodeDataV2 +import im.vector.matrix.android.internal.crypto.verification.qrcode.QrCodeData import im.vector.matrix.android.internal.crypto.verification.qrcode.generateSharedSecretV2 import im.vector.matrix.android.internal.di.DeviceId import im.vector.matrix.android.internal.di.UserId @@ -788,7 +788,7 @@ internal class DefaultVerificationService @Inject constructor( )) } - private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeDataV2? { + private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeData? { requestId ?: run { Timber.w("## Unknown requestId") return null @@ -806,7 +806,7 @@ internal class DefaultVerificationService @Inject constructor( } } - private fun createQrCodeDataForDistinctUser(requestId: String, otherUserId: String): QrCodeDataV2.VerifyingAnotherUser? { + private fun createQrCodeDataForDistinctUser(requestId: String, otherUserId: String): QrCodeData.VerifyingAnotherUser? { val myMasterKey = crossSigningService.getMyCrossSigningKeys() ?.masterKey() ?.unpaddedBase64PublicKey @@ -823,7 +823,7 @@ internal class DefaultVerificationService @Inject constructor( return null } - return QrCodeDataV2.VerifyingAnotherUser( + return QrCodeData.VerifyingAnotherUser( transactionId = requestId, userMasterCrossSigningPublicKey = myMasterKey, otherUserMasterCrossSigningPublicKey = otherUserMasterKey, @@ -832,7 +832,7 @@ internal class DefaultVerificationService @Inject constructor( } // Create a QR code to display on the old device (Osborne2) - private fun createQrCodeDataForVerifiedDevice(requestId: String, otherDeviceId: String?): QrCodeDataV2.SelfVerifyingMasterKeyTrusted? { + private fun createQrCodeDataForVerifiedDevice(requestId: String, otherDeviceId: String?): QrCodeData.SelfVerifyingMasterKeyTrusted? { val myMasterKey = crossSigningService.getMyCrossSigningKeys() ?.masterKey() ?.unpaddedBase64PublicKey @@ -850,7 +850,7 @@ internal class DefaultVerificationService @Inject constructor( return null } - return QrCodeDataV2.SelfVerifyingMasterKeyTrusted( + return QrCodeData.SelfVerifyingMasterKeyTrusted( transactionId = requestId, userMasterCrossSigningPublicKey = myMasterKey, otherDeviceKey = otherDeviceKey, @@ -859,7 +859,7 @@ internal class DefaultVerificationService @Inject constructor( } // Create a QR code to display on the new device (Dynabook) - private fun createQrCodeDataForUnVerifiedDevice(requestId: String): QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted? { + private fun createQrCodeDataForUnVerifiedDevice(requestId: String): QrCodeData.SelfVerifyingMasterKeyNotTrusted? { val myMasterKey = crossSigningService.getMyCrossSigningKeys() ?.masterKey() ?.unpaddedBase64PublicKey @@ -874,7 +874,7 @@ internal class DefaultVerificationService @Inject constructor( return null } - return QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted( + return QrCodeData.SelfVerifyingMasterKeyNotTrusted( transactionId = requestId, deviceKey = myDeviceKey, userMasterCrossSigningPublicKey = myMasterKey, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt index c6b84c60b0..3954092f8f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt @@ -39,7 +39,7 @@ internal class DefaultQrCodeVerificationTransaction( private val crossSigningService: CrossSigningService, private val cryptoStore: IMXCryptoStore, // Not null only if other user is able to scan QR code - private val qrCodeData: QrCodeDataV2?, + private val qrCodeData: QrCodeData?, val userId: String, val deviceId: String, override val isIncoming: Boolean @@ -62,7 +62,7 @@ internal class DefaultQrCodeVerificationTransaction( } override fun userHasScannedOtherQrCode(otherQrCodeText: String) { - val otherQrCodeData = otherQrCodeText.toQrCodeDataV2() ?: run { + val otherQrCodeData = otherQrCodeText.toQrCodeData() ?: run { Timber.d("## Verification QR: Invalid QR Code Data") cancel(CancelCode.QrCodeInvalid) return @@ -77,21 +77,21 @@ internal class DefaultQrCodeVerificationTransaction( // check master key when (otherQrCodeData) { - is QrCodeDataV2.VerifyingAnotherUser -> { + is QrCodeData.VerifyingAnotherUser -> { if (otherQrCodeData.otherUserMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.otherUserMasterCrossSigningPublicKey}") cancel(CancelCode.MismatchedKeys) return } else Unit } - is QrCodeDataV2.SelfVerifyingMasterKeyTrusted -> { + is QrCodeData.SelfVerifyingMasterKeyTrusted -> { if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}") cancel(CancelCode.MismatchedKeys) return } else Unit } - is QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted -> { + is QrCodeData.SelfVerifyingMasterKeyNotTrusted -> { if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}") cancel(CancelCode.MismatchedKeys) @@ -105,7 +105,7 @@ internal class DefaultQrCodeVerificationTransaction( // Check device key if available when (otherQrCodeData) { - is QrCodeDataV2.VerifyingAnotherUser -> { + is QrCodeData.VerifyingAnotherUser -> { if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(otherUserId)?.masterKey()?.unpaddedBase64PublicKey) { Timber.d("## Verification QR: Invalid user master key ${otherQrCodeData.userMasterCrossSigningPublicKey}") cancel(CancelCode.MismatchedKeys) @@ -115,14 +115,14 @@ internal class DefaultQrCodeVerificationTransaction( Unit } } - is QrCodeDataV2.SelfVerifyingMasterKeyTrusted -> { + is QrCodeData.SelfVerifyingMasterKeyTrusted -> { if (otherQrCodeData.otherDeviceKey != cryptoStore.getUserDevice(userId, deviceId)?.fingerprint()) { Timber.d("## Verification QR: Invalid other device key ${otherQrCodeData.otherDeviceKey}") cancel(CancelCode.MismatchedKeys) return } else Unit } - is QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted -> { + is QrCodeData.SelfVerifyingMasterKeyNotTrusted -> { if (otherQrCodeData.deviceKey != cryptoStore.getUserDevice(otherUserId, otherDeviceId ?: "")?.fingerprint()) { Timber.d("## Verification QR: Invalid device key ${otherQrCodeData.deviceKey}") cancel(CancelCode.MismatchedKeys) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/ExtensionsV2.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt similarity index 84% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/ExtensionsV2.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt index 5e6c984990..cc16c2d725 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/ExtensionsV2.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt @@ -22,7 +22,7 @@ import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding // MATRIX private val prefix = "MATRIX".toByteArray(Charsets.ISO_8859_1) -fun QrCodeDataV2.toEncodedString(): String { +fun QrCodeData.toEncodedString(): String { var result = ByteArray(0) // MATRIX @@ -35,9 +35,9 @@ fun QrCodeDataV2.toEncodedString(): String { // Mode result += when (this) { - is QrCodeDataV2.VerifyingAnotherUser -> 0 - is QrCodeDataV2.SelfVerifyingMasterKeyTrusted -> 1 - is QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted -> 2 + is QrCodeData.VerifyingAnotherUser -> 0 + is QrCodeData.SelfVerifyingMasterKeyTrusted -> 1 + is QrCodeData.SelfVerifyingMasterKeyNotTrusted -> 2 }.toByte() // TransactionId length @@ -66,7 +66,7 @@ fun QrCodeDataV2.toEncodedString(): String { return result.toString(Charsets.ISO_8859_1) } -fun String.toQrCodeDataV2(): QrCodeDataV2? { +fun String.toQrCodeData(): QrCodeData? { val byteArray = toByteArray(Charsets.ISO_8859_1) // Size should be min 6 + 1 + 1 + 2 + ? + 32 + 32 + ? = 74 + transactionLength + secretLength @@ -115,9 +115,9 @@ fun String.toQrCodeDataV2(): QrCodeDataV2? { val secret = byteArray.copyOfRange(cursor, byteArray.size).toBase64NoPadding() return when (mode) { - 0 -> QrCodeDataV2.VerifyingAnotherUser(transactionId, key1, key2, secret) - 1 -> QrCodeDataV2.SelfVerifyingMasterKeyTrusted(transactionId, key1, key2, secret) - 2 -> QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted(transactionId, key1, key2, secret) + 0 -> QrCodeData.VerifyingAnotherUser(transactionId, key1, key2, secret) + 1 -> QrCodeData.SelfVerifyingMasterKeyTrusted(transactionId, key1, key2, secret) + 2 -> QrCodeData.SelfVerifyingMasterKeyNotTrusted(transactionId, key1, key2, secret) else -> null } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeDataV2.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt similarity index 97% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeDataV2.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt index d3b23b30de..934c0c82b9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeDataV2.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt @@ -19,7 +19,7 @@ package im.vector.matrix.android.internal.crypto.verification.qrcode /** * Ref: https://github.com/uhoreg/matrix-doc/blob/qr_key_verification/proposals/1543-qr_code_key_verification.md#qr-code-format */ -sealed class QrCodeDataV2( +sealed class QrCodeData( /** * the event ID or transaction_id of the associated verification */ @@ -52,7 +52,7 @@ sealed class QrCodeDataV2( */ val otherUserMasterCrossSigningPublicKey: String, override val sharedSecret: String - ) : QrCodeDataV2( + ) : QrCodeData( transactionId, userMasterCrossSigningPublicKey, otherUserMasterCrossSigningPublicKey, @@ -73,7 +73,7 @@ sealed class QrCodeDataV2( */ val otherDeviceKey: String, override val sharedSecret: String - ) : QrCodeDataV2( + ) : QrCodeData( transactionId, userMasterCrossSigningPublicKey, otherDeviceKey, @@ -94,7 +94,7 @@ sealed class QrCodeDataV2( */ val userMasterCrossSigningPublicKey: String, override val sharedSecret: String - ) : QrCodeDataV2( + ) : QrCodeData( transactionId, deviceKey, userMasterCrossSigningPublicKey, From 932c478ed5346c8297dc6eec8d461e37897d8e7b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 19 Feb 2020 18:11:23 +0100 Subject: [PATCH 075/187] Change log --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 4f729ba0a8..e2958c9fc1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,7 +9,7 @@ Features ✨: - Sending image: image are sent to rooms with a reduced size. It's still possible to send original image file (#1010) Improvements 🙌: - - + - Migrate to binary QR code verification (#994) Bugfix 🐛: - Account creation: wrongly hints that an email can be used to create an account (#941) From 382fc6f05c7ea2a5783891aa924b9e0f27269fb3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 19 Feb 2020 18:59:39 +0100 Subject: [PATCH 076/187] Fix issue with long transactionId --- .../crypto/verification/qrcode/QrCodeTest.kt | 16 ++++++++++++++++ .../crypto/verification/qrcode/Extensions.kt | 13 ++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt index 1cbc1a17df..7e835385cc 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt @@ -218,6 +218,22 @@ class QrCodeTest : InstrumentedTest { compareArray(byteArray.copyOfRange(23 + 64, byteArray.size), sharedSecretByteArray) } + @Test + fun testLongTransactionId() { + // Size on two bytes (2_000 = 0x07D0) + val longTransactionId = "PatternId_".repeat(200) + + val qrCode = qrCode1.copy(transactionId = longTransactionId) + + val result = qrCode.toEncodedString() + val expected = value1.replace("\u0000\u000DMaTransaction", "\u0007\u00D0$longTransactionId") + + result shouldEqual expected + + // Reverse operation + expected.toQrCodeData() shouldEqual qrCode + } + // Error cases @Test fun testErrorHeader() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt index cc16c2d725..b1da4a228c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt @@ -94,7 +94,10 @@ fun String.toQrCodeData(): QrCodeData? { cursor++ // Get transaction length - val transactionLength = (byteArray[cursor].toInt() shr 8) + byteArray[cursor + 1].toInt() + val bigEndian1 = ensurePositive(byteArray[cursor]) + val bigEndian2 = ensurePositive(byteArray[cursor + 1]) + + val transactionLength = bigEndian1 * 0x0100 + bigEndian2 cursor++ cursor++ @@ -121,3 +124,11 @@ fun String.toQrCodeData(): QrCodeData? { else -> null } } + +fun ensurePositive(byte: Byte): Int { + return if (byte < 0) { + 256 + byte + } else { + byte.toInt() + } +} From 74175ddfa03077cadda720b409f7c5c0a5756ecc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 10:12:07 +0100 Subject: [PATCH 077/187] Add a test and create extension for Byte to avoid using UByte (still experimental) --- .../crypto/verification/qrcode/QrCodeTest.kt | 12 +++++++++ .../crypto/verification/qrcode/Extensions.kt | 13 +++------- .../matrix/android/internal/util/Byte.kt | 25 +++++++++++++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Byte.kt diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt index 7e835385cc..ced7896aa3 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt @@ -234,6 +234,18 @@ class QrCodeTest : InstrumentedTest { expected.toQrCodeData() shouldEqual qrCode } + @Test + fun testAnyTransactionId() { + for (qty in 0 until 0x1FFF step 200) { + val longTransactionId = "a".repeat(qty) + + val qrCode = qrCode1.copy(transactionId = longTransactionId) + + // Symmetric operation + qrCode.toEncodedString().toQrCodeData() shouldEqual qrCode + } + } + // Error cases @Test fun testErrorHeader() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt index b1da4a228c..f6cc5b1be1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt @@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.crypto.verification.qrcode import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64NoPadding import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding +import im.vector.matrix.android.internal.util.toPositiveInt // MATRIX private val prefix = "MATRIX".toByteArray(Charsets.ISO_8859_1) @@ -94,8 +95,8 @@ fun String.toQrCodeData(): QrCodeData? { cursor++ // Get transaction length - val bigEndian1 = ensurePositive(byteArray[cursor]) - val bigEndian2 = ensurePositive(byteArray[cursor + 1]) + val bigEndian1 = byteArray[cursor].toPositiveInt() + val bigEndian2 = byteArray[cursor + 1].toPositiveInt() val transactionLength = bigEndian1 * 0x0100 + bigEndian2 @@ -124,11 +125,3 @@ fun String.toQrCodeData(): QrCodeData? { else -> null } } - -fun ensurePositive(byte: Byte): Int { - return if (byte < 0) { - 256 + byte - } else { - byte.toInt() - } -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Byte.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Byte.kt new file mode 100644 index 0000000000..77f28e17e5 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Byte.kt @@ -0,0 +1,25 @@ +/* + * 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 im.vector.matrix.android.internal.util + +fun Byte.toPositiveInt(): Int { + return if (this < 0) { + 256 + this + } else { + toInt() + } +} From e61d56358901c97d42f2c60c9ccaf0a322a139c0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 10:16:09 +0100 Subject: [PATCH 078/187] Split long lines --- .../DefaultQrCodeVerificationTransaction.kt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt index 3954092f8f..743100ee45 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt @@ -78,21 +78,24 @@ internal class DefaultQrCodeVerificationTransaction( // check master key when (otherQrCodeData) { is QrCodeData.VerifyingAnotherUser -> { - if (otherQrCodeData.otherUserMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { + if (otherQrCodeData.otherUserMasterCrossSigningPublicKey + != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.otherUserMasterCrossSigningPublicKey}") cancel(CancelCode.MismatchedKeys) return } else Unit } is QrCodeData.SelfVerifyingMasterKeyTrusted -> { - if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { + if (otherQrCodeData.userMasterCrossSigningPublicKey + != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}") cancel(CancelCode.MismatchedKeys) return } else Unit } is QrCodeData.SelfVerifyingMasterKeyNotTrusted -> { - if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { + if (otherQrCodeData.userMasterCrossSigningPublicKey + != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) { Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}") cancel(CancelCode.MismatchedKeys) return @@ -106,7 +109,8 @@ internal class DefaultQrCodeVerificationTransaction( // Check device key if available when (otherQrCodeData) { is QrCodeData.VerifyingAnotherUser -> { - if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(otherUserId)?.masterKey()?.unpaddedBase64PublicKey) { + if (otherQrCodeData.userMasterCrossSigningPublicKey + != crossSigningService.getUserCrossSigningKeys(otherUserId)?.masterKey()?.unpaddedBase64PublicKey) { Timber.d("## Verification QR: Invalid user master key ${otherQrCodeData.userMasterCrossSigningPublicKey}") cancel(CancelCode.MismatchedKeys) return @@ -116,14 +120,16 @@ internal class DefaultQrCodeVerificationTransaction( } } is QrCodeData.SelfVerifyingMasterKeyTrusted -> { - if (otherQrCodeData.otherDeviceKey != cryptoStore.getUserDevice(userId, deviceId)?.fingerprint()) { + if (otherQrCodeData.otherDeviceKey + != cryptoStore.getUserDevice(userId, deviceId)?.fingerprint()) { Timber.d("## Verification QR: Invalid other device key ${otherQrCodeData.otherDeviceKey}") cancel(CancelCode.MismatchedKeys) return } else Unit } is QrCodeData.SelfVerifyingMasterKeyNotTrusted -> { - if (otherQrCodeData.deviceKey != cryptoStore.getUserDevice(otherUserId, otherDeviceId ?: "")?.fingerprint()) { + if (otherQrCodeData.deviceKey + != cryptoStore.getUserDevice(otherUserId, otherDeviceId ?: "")?.fingerprint()) { Timber.d("## Verification QR: Invalid device key ${otherQrCodeData.deviceKey}") cancel(CancelCode.MismatchedKeys) return From 7d232527c880f94cd48eda216309c0ffb105489b Mon Sep 17 00:00:00 2001 From: onurays Date: Thu, 20 Feb 2020 16:39:48 +0300 Subject: [PATCH 079/187] Documentation of the API change is added. --- CHANGES.md | 1 + .../im/vector/matrix/android/api/listeners/ProgressListener.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 897f8deebf..b402dc0522 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ Translations 🗣: SDK API changes ⚠️: - Get crypto methods through Session.cryptoService() + - ProgressListener.onProgress() function will be invoked on the background thread instead of UI thread Build 🧱: - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt index ad47260f86..ab91110061 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt @@ -20,7 +20,7 @@ package im.vector.matrix.android.api.listeners * Interface to send a progress info */ interface ProgressListener { - /** + /** Will be invoked on the background thread, not in UI thread. * @param progress from 0 to total by contract * @param total */ From d867b1345f73a1911cf33755c7a88a18e0d4176e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 15:15:38 +0100 Subject: [PATCH 080/187] Format --- .../im/vector/matrix/android/api/listeners/ProgressListener.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt index ab91110061..d672434046 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/listeners/ProgressListener.kt @@ -20,7 +20,8 @@ package im.vector.matrix.android.api.listeners * Interface to send a progress info */ interface ProgressListener { - /** Will be invoked on the background thread, not in UI thread. + /** + * Will be invoked on the background thread, not in UI thread. * @param progress from 0 to total by contract * @param total */ From b85c76b172a819e9b3857dbd9fc6cfc49514562d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 18:05:53 +0100 Subject: [PATCH 081/187] Simplify code --- .../vector/matrix/android/api/MatrixPatterns.kt | 16 ++++------------ .../home/room/detail/RoomDetailViewModel.kt | 12 ++++-------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt index e30d139cd3..bba925b89f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt @@ -122,9 +122,9 @@ object MatrixPatterns { */ fun isEventId(str: String?): Boolean { return str != null - && (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER - || str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 - || str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4) + && (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER + || str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 + || str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4) } /** @@ -144,14 +144,6 @@ object MatrixPatterns { * @return null if not found or if matrixId is null */ fun extractServerNameFromId(matrixId: String?): String? { - if (matrixId == null) { - return null - } - - val index = matrixId.indexOf(":") - - return if (index == -1) { - null - } else matrixId.substring(index + 1) + return matrixId?.substringAfter(":", missingDelimiterValue = "")?.takeIf { it.isNotEmpty() } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 1eccba7fa9..15f7bc0694 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -297,15 +297,11 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro if (isRoomJoined) { setState { copy(tombstoneEventHandling = Success(roomId)) } } else { - val viaServer = MatrixPatterns.extractServerNameFromId(action.event.senderId).let { - if (it.isNullOrBlank()) { - emptyList() - } else { - listOf(it) - } - } + val viaServers = MatrixPatterns.extractServerNameFromId(action.event.senderId) + ?.let { listOf(it) } + .orEmpty() session.rx() - .joinRoom(roomId, viaServers = viaServer) + .joinRoom(roomId, viaServers = viaServers) .map { roomId } .execute { copy(tombstoneEventHandling = it) From 87021dd6ecc6dec12a7353e7be7b058eef5834df Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 18:16:10 +0100 Subject: [PATCH 082/187] Rename field --- .../session/room/model/tombstone/RoomTombstoneContent.kt | 9 ++++++++- .../room/tombstone/RoomTombstoneEventLiveObserver.kt | 2 +- .../features/home/room/detail/RoomDetailViewModel.kt | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/tombstone/RoomTombstoneContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/tombstone/RoomTombstoneContent.kt index 035e76d10f..4dfcdd86f5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/tombstone/RoomTombstoneContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/tombstone/RoomTombstoneContent.kt @@ -23,6 +23,13 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) data class RoomTombstoneContent( + /** + * Required. A server-defined message. + */ @Json(name = "body") val body: String? = null, - @Json(name = "replacement_room") val replacementRoom: String? + + /** + * Required. The new room the client should be visiting. + */ + @Json(name = "replacement_room") val replacementRoomId: String? ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/tombstone/RoomTombstoneEventLiveObserver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/tombstone/RoomTombstoneEventLiveObserver.kt index 12f7f92794..6bfa54059e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/tombstone/RoomTombstoneEventLiveObserver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/tombstone/RoomTombstoneEventLiveObserver.kt @@ -62,7 +62,7 @@ internal class RoomTombstoneEventLiveObserver @Inject constructor(@SessionDataba for (event in tombstoneEvents) { if (event.roomId == null) continue val createRoomContent = event.getClearContent().toModel() - if (createRoomContent?.replacementRoom == null) continue + if (createRoomContent?.replacementRoomId == null) continue val predecessorRoomSummary = RoomSummaryEntity.where(realm, event.roomId).findFirst() ?: RoomSummaryEntity(event.roomId) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 15f7bc0694..4e112ab6dc 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -292,7 +292,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro private fun handleTombstoneEvent(action: RoomDetailAction.HandleTombstoneEvent) { val tombstoneContent = action.event.getClearContent().toModel() ?: return - val roomId = tombstoneContent.replacementRoom ?: "" + val roomId = tombstoneContent.replacementRoomId ?: "" val isRoomJoined = session.getRoom(roomId)?.roomSummary()?.membership == Membership.JOIN if (isRoomJoined) { setState { copy(tombstoneEventHandling = Success(roomId)) } From eec65fb622cb6bea6f3fd8c765e970ce73b5486d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 18:17:57 +0100 Subject: [PATCH 083/187] Join room by roomId or room alias --- .../src/main/java/im/vector/matrix/rx/RxSession.kt | 4 ++-- .../android/api/session/room/RoomDirectoryService.kt | 4 ++-- .../matrix/android/api/session/room/RoomService.kt | 4 ++-- .../session/room/model/create/CreateRoomResponse.kt | 7 ++++++- .../session/room/DefaultRoomDirectoryService.kt | 4 ++-- .../internal/session/room/DefaultRoomService.kt | 4 ++-- .../matrix/android/internal/session/room/RoomAPI.kt | 5 +++-- .../internal/session/room/create/CreateRoomTask.kt | 2 +- .../session/room/membership/joining/JoinRoomTask.kt | 12 +++++++----- 9 files changed, 27 insertions(+), 19 deletions(-) diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt index 82e1d71f44..87ff6f0390 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt @@ -95,10 +95,10 @@ class RxSession(private val session: Session) { session.searchUsersDirectory(search, limit, excludedUserIds, it) } - fun joinRoom(roomId: String, + fun joinRoom(roomIdOrAlias: String, reason: String? = null, viaServers: List = emptyList()): Single = singleBuilder { - session.joinRoom(roomId, reason, viaServers, it) + session.joinRoom(roomIdOrAlias, reason, viaServers, it) } fun getRoomIdByAlias(roomAlias: String, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomDirectoryService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomDirectoryService.kt index c0e413f83b..0273c789dd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomDirectoryService.kt @@ -35,9 +35,9 @@ interface RoomDirectoryService { callback: MatrixCallback): Cancelable /** - * Join a room by id + * Join a room by id, or room alias */ - fun joinRoom(roomId: String, + fun joinRoom(roomIdOrAlias: String, reason: String? = null, callback: MatrixCallback): Cancelable diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt index 9fec605bd9..93761dfd26 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt @@ -36,11 +36,11 @@ interface RoomService { /** * Join a room by id - * @param roomId the roomId of the room to join + * @param roomIdOrAlias the roomId or the room alias of the room to join * @param reason optional reason for joining the room * @param viaServers the servers to attempt to join the room through. One of the servers must be participating in the room. */ - fun joinRoom(roomId: String, + fun joinRoom(roomIdOrAlias: String, reason: String? = null, viaServers: List = emptyList(), callback: MatrixCallback): Cancelable diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt index c437bcfbf1..29c8cb830d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt @@ -21,5 +21,10 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) internal data class CreateRoomResponse( - @Json(name = "room_id") var roomId: String? = null + /** + * Required. The created room's ID. + */ + @Json(name = "room_id") var roomId: String ) + +internal typealias JoinRoomResponse = CreateRoomResponse diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt index 711e2bd97c..ef55702de6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt @@ -44,9 +44,9 @@ internal class DefaultRoomDirectoryService @Inject constructor(private val getPu .executeBy(taskExecutor) } - override fun joinRoom(roomId: String, reason: String?, callback: MatrixCallback): Cancelable { + override fun joinRoom(roomIdOrAlias: String, reason: String?, callback: MatrixCallback): Cancelable { return joinRoomTask - .configureWith(JoinRoomTask.Params(roomId, reason)) { + .configureWith(JoinRoomTask.Params(roomIdOrAlias, reason)) { this.callback = callback } .executeBy(taskExecutor) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt index e049c1869d..84fc357160 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt @@ -139,9 +139,9 @@ internal class DefaultRoomService @Inject constructor( .executeBy(taskExecutor) } - override fun joinRoom(roomId: String, reason: String?, viaServers: List, callback: MatrixCallback): Cancelable { + override fun joinRoom(roomIdOrAlias: String, reason: String?, viaServers: List, callback: MatrixCallback): Cancelable { return joinRoomTask - .configureWith(JoinRoomTask.Params(roomId, reason, viaServers)) { + .configureWith(JoinRoomTask.Params(roomIdOrAlias, reason, viaServers)) { this.callback = callback } .executeBy(taskExecutor) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt index 9be63565fe..f5ddf6ae4b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt @@ -20,6 +20,7 @@ import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse +import im.vector.matrix.android.api.session.room.model.create.JoinRoomResponse import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol @@ -223,13 +224,13 @@ internal interface RoomAPI { * Join the given room. * * @param roomIdOrAlias the room id or alias - * @param server_name the servers to attempt to join the room through + * @param viaServers the servers to attempt to join the room through * @param params the request body */ @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "join/{roomIdOrAlias}") fun join(@Path("roomIdOrAlias") roomIdOrAlias: String, @Query("server_name") viaServers: List, - @Body params: Map): Call + @Body params: Map): Call /** * Leave the given room. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt index f120130739..7314f55961 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt @@ -66,7 +66,7 @@ internal class DefaultCreateRoomTask @Inject constructor( val createRoomResponse = executeRequest(eventBus) { apiCall = roomAPI.createRoom(createRoomParams) } - val roomId = createRoomResponse.roomId!! + val roomId = createRoomResponse.roomId // Wait for room to come back from the sync (but it can maybe be in the DB if the sync response is received before) try { awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt index 0153930226..635f3955c2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.session.room.membership.joining import im.vector.matrix.android.api.session.room.failure.JoinRoomFailure +import im.vector.matrix.android.api.session.room.model.create.JoinRoomResponse import im.vector.matrix.android.internal.database.awaitNotEmptyResult import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomEntityFields @@ -33,7 +34,7 @@ import javax.inject.Inject internal interface JoinRoomTask : Task { data class Params( - val roomId: String, + val roomIdOrAlias: String, val reason: String?, val viaServers: List = emptyList() ) @@ -48,19 +49,20 @@ internal class DefaultJoinRoomTask @Inject constructor( ) : JoinRoomTask { override suspend fun execute(params: JoinRoomTask.Params) { - executeRequest(eventBus) { - apiCall = roomAPI.join(params.roomId, params.viaServers, mapOf("reason" to params.reason)) + val joinRoomResponse = executeRequest(eventBus) { + apiCall = roomAPI.join(params.roomIdOrAlias, params.viaServers, mapOf("reason" to params.reason)) } // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) + val roomId = joinRoomResponse.roomId try { awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> realm.where(RoomEntity::class.java) - .equalTo(RoomEntityFields.ROOM_ID, params.roomId) + .equalTo(RoomEntityFields.ROOM_ID, roomId) } } catch (exception: TimeoutCancellationException) { throw JoinRoomFailure.JoinedWithTimeout } - setReadMarkers(params.roomId) + setReadMarkers(roomId) } private suspend fun setReadMarkers(roomId: String) { From b72698d63c1c0b8716f6ab8a545624a288e72121 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 18:47:07 +0100 Subject: [PATCH 084/187] Fix another issue when there is no name and no canonical alias on a public room --- .../room/model/roomdirectory/PublicRoom.kt | 31 +++++++++++-------- .../matrix/android/api/util/MatrixItem.kt | 2 +- .../core/platform/EllipsizingTextView.kt | 2 +- .../roomdirectory/PublicRoomsController.kt | 2 +- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoom.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoom.kt index 508cbb55b4..5a9d6dab36 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoom.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoom.kt @@ -23,66 +23,71 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) data class PublicRoom( - /** * Aliases of the room. May be empty. */ @Json(name = "aliases") - var aliases: List? = null, + val aliases: List? = null, /** * The canonical alias of the room, if any. */ @Json(name = "canonical_alias") - var canonicalAlias: String? = null, + val canonicalAlias: String? = null, /** * The name of the room, if any. */ @Json(name = "name") - var name: String? = null, + val name: String? = null, /** * Required. The number of members joined to the room. */ @Json(name = "num_joined_members") - var numJoinedMembers: Int = 0, + val numJoinedMembers: Int = 0, /** * Required. The ID of the room. */ @Json(name = "room_id") - var roomId: String, + val roomId: String, /** * The topic of the room, if any. */ @Json(name = "topic") - var topic: String? = null, + val topic: String? = null, /** * Required. Whether the room may be viewed by guest users without joining. */ @Json(name = "world_readable") - var worldReadable: Boolean = false, + val worldReadable: Boolean = false, /** * Required. Whether guest users may join the room and participate in it. If they can, * they will be subject to ordinary power level rules like any other user. */ @Json(name = "guest_can_join") - var guestCanJoin: Boolean = false, + val guestCanJoin: Boolean = false, /** * The URL for the room's avatar, if one is set. */ @Json(name = "avatar_url") - var avatarUrl: String? = null, + val avatarUrl: String? = null, /** * Undocumented item */ @Json(name = "m.federate") - var isFederated: Boolean = false - -) + val isFederated: Boolean = false +) { + /** + * Return the canonical alias, or the first alias from the list of alias, or null + */ + fun getPrimaryAlias(): String? { + return canonicalAlias ?: aliases?.firstOrNull() + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt index 784256503b..d5aa897c7d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt @@ -151,6 +151,6 @@ fun RoomSummary.toMatrixItem() = MatrixItem.RoomItem(roomId, displayName, avatar fun RoomSummary.toRoomAliasMatrixItem() = MatrixItem.RoomAliasItem(canonicalAlias ?: roomId, displayName, avatarUrl) // If no name is available, use room alias as Riot-Web does -fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name ?: canonicalAlias, avatarUrl) +fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name ?: getPrimaryAlias() ?: "", avatarUrl) fun RoomMemberSummary.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl) diff --git a/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt b/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt index 2178921733..70af607753 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt @@ -242,7 +242,7 @@ class EllipsizingTextView @JvmOverloads constructor(context: Context, attrs: Att @Suppress("DEPRECATION") protected fun createWorkingLayout(workingText: CharSequence?): Layout { return StaticLayout( - workingText, + workingText ?: "", paint, width - compoundPaddingLeft - compoundPaddingRight, Layout.Alignment.ALIGN_NORMAL, diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsController.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsController.kt index 83a1768843..aaacb2a170 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsController.kt @@ -85,7 +85,7 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri avatarRenderer(avatarRenderer) id(publicRoom.roomId) matrixItem(publicRoom.toMatrixItem()) - roomAlias(publicRoom.canonicalAlias) + roomAlias(publicRoom.getPrimaryAlias()) roomTopic(publicRoom.topic) nbOfMembers(publicRoom.numJoinedMembers) From fd05fe36f56d3d85c62d907ca7be8d1f7d386600 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 18:57:29 +0100 Subject: [PATCH 085/187] Fix joining rooms from directory via federation isn't working. (#808) --- CHANGES.md | 1 + .../vector/riotx/features/roomdirectory/PublicRoomsFragment.kt | 2 +- .../vector/riotx/features/roomdirectory/RoomDirectoryAction.kt | 2 +- .../riotx/features/roomdirectory/RoomDirectoryViewModel.kt | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b402dc0522..76a576099a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ Bugfix 🐛: - Account creation: wrongly hints that an email can be used to create an account (#941) - Fix crash in the room directory, when public room has no name (#1023) - Fix restoring keys backup with passphrase (#526) + - Fix joining rooms from directory via federation isn't working. (#808) Translations 🗣: - diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt index 0cb2a7feca..e466c2311f 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt @@ -134,7 +134,7 @@ class PublicRoomsFragment @Inject constructor( override fun onPublicRoomJoin(publicRoom: PublicRoom) { Timber.v("PublicRoomJoinClicked: $publicRoom") - viewModel.handle(RoomDirectoryAction.JoinRoom(publicRoom.roomId)) + viewModel.handle(RoomDirectoryAction.JoinRoom(publicRoom.getPrimaryAlias(), publicRoom.roomId)) } override fun loadMore() { diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryAction.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryAction.kt index 8b32726370..598f26fc3b 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryAction.kt @@ -23,5 +23,5 @@ sealed class RoomDirectoryAction : VectorViewModelAction { data class SetRoomDirectoryData(val roomDirectoryData: RoomDirectoryData) : RoomDirectoryAction() data class FilterWith(val filter: String) : RoomDirectoryAction() object LoadMore : RoomDirectoryAction() - data class JoinRoom(val roomId: String) : RoomDirectoryAction() + data class JoinRoom(val roomAlias: String?, val roomId: String) : RoomDirectoryAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt index 9f81e8b076..53661b075a 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt @@ -216,7 +216,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: ) } - session.joinRoom(action.roomId, callback = object : MatrixCallback { + session.joinRoom(action.roomAlias ?: action.roomId, 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 From aefffc0c05bc709b853255674e619c2027a2c8f0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 19:19:31 +0100 Subject: [PATCH 086/187] RoomPreviewNoPreviewFragment use room alias if there is no name --- .../features/roomdirectory/roompreview/RoomPreviewActivity.kt | 4 +++- .../roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewActivity.kt index 0fdb504c23..a7cb8f2f68 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewActivity.kt @@ -32,12 +32,13 @@ import kotlinx.android.parcel.Parcelize data class RoomPreviewData( val roomId: String, val roomName: String?, + val roomAlias: String?, val topic: String?, val worldReadable: Boolean, val avatarUrl: String? ) : Parcelable { val matrixItem: MatrixItem - get() = MatrixItem.RoomItem(roomId, roomName, avatarUrl) + get() = MatrixItem.RoomItem(roomId, roomName ?: roomAlias, avatarUrl) } class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { @@ -50,6 +51,7 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { putExtra(ARG, RoomPreviewData( roomId = publicRoom.roomId, roomName = publicRoom.name, + roomAlias = publicRoom.getPrimaryAlias(), topic = publicRoom.topic, worldReadable = publicRoom.worldReadable, avatarUrl = publicRoom.avatarUrl diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt index 8999b88aba..ee01e8f7fe 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt @@ -50,11 +50,11 @@ class RoomPreviewNoPreviewFragment @Inject constructor( setupToolbar(roomPreviewNoPreviewToolbar) // Toolbar avatarRenderer.render(roomPreviewData.matrixItem, roomPreviewNoPreviewToolbarAvatar) - roomPreviewNoPreviewToolbarTitle.text = roomPreviewData.roomName + roomPreviewNoPreviewToolbarTitle.text = roomPreviewData.roomName ?: roomPreviewData.roomAlias // Screen avatarRenderer.render(roomPreviewData.matrixItem, roomPreviewNoPreviewAvatar) - roomPreviewNoPreviewName.text = roomPreviewData.roomName + roomPreviewNoPreviewName.text = roomPreviewData.roomName ?: roomPreviewData.roomAlias roomPreviewNoPreviewTopic.setTextOrHide(roomPreviewData.topic) if (roomPreviewData.worldReadable) { From 198e23c2043410d3f8fb808286df370d394a2f54 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 19:23:32 +0100 Subject: [PATCH 087/187] Second fix for #808 --- .../roomdirectory/roompreview/RoomPreviewAction.kt | 2 +- .../roompreview/RoomPreviewNoPreviewFragment.kt | 2 +- .../roomdirectory/roompreview/RoomPreviewViewModel.kt | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewAction.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewAction.kt index 426078fa3d..6b83ada90e 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewAction.kt @@ -19,5 +19,5 @@ package im.vector.riotx.features.roomdirectory.roompreview import im.vector.riotx.core.platform.VectorViewModelAction sealed class RoomPreviewAction : VectorViewModelAction { - object Join : RoomPreviewAction() + data class Join(val roomAlias: String?) : RoomPreviewAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt index ee01e8f7fe..04ecdb2305 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt @@ -65,7 +65,7 @@ class RoomPreviewNoPreviewFragment @Inject constructor( roomPreviewNoPreviewJoin.callback = object : ButtonStateView.Callback { override fun onButtonClicked() { - roomPreviewViewModel.handle(RoomPreviewAction.Join) + roomPreviewViewModel.handle(RoomPreviewAction.Join(roomPreviewData.roomAlias)) } override fun onRetryClicked() { diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index b4aae0d116..3f8ae03029 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -26,6 +26,7 @@ import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.roomSummaryQueryParams import im.vector.matrix.rx.rx +import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.platform.EmptyViewEvents import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.features.roomdirectory.JoinState @@ -82,11 +83,11 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: R override fun handle(action: RoomPreviewAction) { when (action) { - RoomPreviewAction.Join -> joinRoom() - } + is RoomPreviewAction.Join -> handleJoinRoom(action) + }.exhaustive } - private fun joinRoom() = withState { state -> + private fun handleJoinRoom(action: RoomPreviewAction.Join) = withState { state -> if (state.roomJoinState == JoinState.JOINING) { // Request already sent, should not happen Timber.w("Try to join an already joining room. Should not happen") @@ -100,7 +101,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: R ) } - session.joinRoom(state.roomId, callback = object : MatrixCallback { + session.joinRoom(action.roomAlias ?: state.roomId, 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 From 3f9a5a4e54b7f86dc582699b85194f85e5837cce Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 19:31:33 +0100 Subject: [PATCH 088/187] Leaving a room creates a stuck "leaving room" loading screen. (#1041) --- CHANGES.md | 1 + .../vector/riotx/features/home/room/list/RoomListFragment.kt | 1 + .../riotx/features/home/room/list/RoomListViewEvents.kt | 1 + .../vector/riotx/features/home/room/list/RoomListViewModel.kt | 4 ++++ 4 files changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 76a576099a..1cf2746182 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ Bugfix 🐛: - Fix crash in the room directory, when public room has no name (#1023) - Fix restoring keys backup with passphrase (#526) - Fix joining rooms from directory via federation isn't working. (#808) + - Leaving a room creates a stuck "leaving room" loading screen. (#1041) Translations 🗣: - diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index e5141975f3..9c606de3b4 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -105,6 +105,7 @@ class RoomListFragment @Inject constructor( is RoomListViewEvents.Loading -> showLoading(it.message) is RoomListViewEvents.Failure -> showFailure(it.throwable) is RoomListViewEvents.SelectRoom -> handleSelectRoom(it) + is RoomListViewEvents.Done -> Unit }.exhaustive } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt index 1798174ef0..ea7393b2fc 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewEvents.kt @@ -28,4 +28,5 @@ sealed class RoomListViewEvents : VectorViewEvents { data class Failure(val throwable: Throwable) : RoomListViewEvents() data class SelectRoom(val roomSummary: RoomSummary) : RoomListViewEvents() + object Done : RoomListViewEvents() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt index 455365602f..7886bf844b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt @@ -197,6 +197,10 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, private fun handleLeaveRoom(action: RoomListAction.LeaveRoom) { _viewEvents.post(RoomListViewEvents.Loading(null)) session.getRoom(action.roomId)?.leave(null, object : MatrixCallback { + override fun onSuccess(data: Unit) { + _viewEvents.post(RoomListViewEvents.Done) + } + override fun onFailure(failure: Throwable) { _viewEvents.post(RoomListViewEvents.Failure(failure)) } From a9909b37ba8dc23ce856c5d66f250ce1bc65cd7f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Feb 2020 19:54:35 +0100 Subject: [PATCH 089/187] Update after Valere's review --- .../crypto/verification/qrcode/HexParser.kt | 35 +++++++++ .../crypto/verification/qrcode/QrCodeTest.kt | 76 +------------------ .../crypto/verification/qrcode/Extensions.kt | 6 +- .../matrix/android/internal/util/Byte.kt | 25 ------ 4 files changed, 40 insertions(+), 102 deletions(-) create mode 100644 matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/HexParser.kt delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Byte.kt diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/HexParser.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/HexParser.kt new file mode 100644 index 0000000000..387b627713 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/HexParser.kt @@ -0,0 +1,35 @@ +/* + * 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 im.vector.matrix.android.internal.crypto.verification.qrcode + +fun hexToByteArray(hex: String): ByteArray { + // Remove all spaces + return hex.replace(" ", "") + .let { + if (it.length % 2 != 0) "0$it" else it + } + .let { + ByteArray(it.length / 2) + .apply { + for (i in this.indices) { + val index = i * 2 + val v = it.substring(index, index + 2).toInt(16) + this[i] = v.toByte() + } + } + } +} diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt index ced7896aa3..d19fad4b59 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt @@ -60,81 +60,9 @@ class QrCodeTest : InstrumentedTest { private val sharedSecretByteArray = "12345678".toByteArray(Charsets.ISO_8859_1) - // 4d 79 6e 64 a4 d9 2e f4 91 58 e4 cf 94 ea 8b ab 9d f8 6c 0f bf 2b 8c cb 14 a4 ae f5 c1 8b 41 a5 - private val tlx_byteArray = ByteArray(32) { - when (it) { - 0 -> 0x4D.toByte() - 1 -> 0x79.toByte() - 2 -> 0x6E.toByte() - 3 -> 0x64.toByte() - 4 -> 0xA4.toByte() - 5 -> 0xD9.toByte() - 6 -> 0x2E.toByte() - 7 -> 0xF4.toByte() - 8 -> 0x91.toByte() - 9 -> 0x58.toByte() - 10 -> 0xE4.toByte() - 11 -> 0xCF.toByte() - 12 -> 0x94.toByte() - 13 -> 0xEA.toByte() - 14 -> 0x8B.toByte() - 15 -> 0xAB.toByte() - 16 -> 0x9D.toByte() - 17 -> 0xF8.toByte() - 18 -> 0x6C.toByte() - 19 -> 0x0F.toByte() - 20 -> 0xBF.toByte() - 21 -> 0x2B.toByte() - 22 -> 0x8C.toByte() - 23 -> 0xCB.toByte() - 24 -> 0x14.toByte() - 25 -> 0xA4.toByte() - 26 -> 0xAE.toByte() - 27 -> 0xF5.toByte() - 28 -> 0xC1.toByte() - 29 -> 0x8B.toByte() - 30 -> 0x41.toByte() - else -> 0xA5.toByte() - } - } + private val tlx_byteArray = hexToByteArray("4d 79 6e 64 a4 d9 2e f4 91 58 e4 cf 94 ea 8b ab 9d f8 6c 0f bf 2b 8c cb 14 a4 ae f5 c1 8b 41 a5") - // 92 d1 30 71 43 fa b2 ed 71 87 e1 ae 13 e0 98 91 0d c7 e9 6f c3 22 5f b2 6c 71 5d 68 43 ab a2 55 - private val kte_byteArray = ByteArray(32) { - when (it) { - 0 -> 0x92.toByte() - 1 -> 0xd1.toByte() - 2 -> 0x30.toByte() - 3 -> 0x71.toByte() - 4 -> 0x43.toByte() - 5 -> 0xfa.toByte() - 6 -> 0xb2.toByte() - 7 -> 0xed.toByte() - 8 -> 0x71.toByte() - 9 -> 0x87.toByte() - 10 -> 0xe1.toByte() - 11 -> 0xae.toByte() - 12 -> 0x13.toByte() - 13 -> 0xe0.toByte() - 14 -> 0x98.toByte() - 15 -> 0x91.toByte() - 16 -> 0x0d.toByte() - 17 -> 0xc7.toByte() - 18 -> 0xe9.toByte() - 19 -> 0x6f.toByte() - 20 -> 0xc3.toByte() - 21 -> 0x22.toByte() - 22 -> 0x5f.toByte() - 23 -> 0xb2.toByte() - 24 -> 0x6c.toByte() - 25 -> 0x71.toByte() - 26 -> 0x5d.toByte() - 27 -> 0x68.toByte() - 28 -> 0x43.toByte() - 29 -> 0xab.toByte() - 30 -> 0xa2.toByte() - else -> 0x55.toByte() - } - } + private val kte_byteArray = hexToByteArray("92 d1 30 71 43 fa b2 ed 71 87 e1 ae 13 e0 98 91 0d c7 e9 6f c3 22 5f b2 6c 71 5d 68 43 ab a2 55") @Test fun testEncoding1() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt index f6cc5b1be1..da926a0e10 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt @@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.crypto.verification.qrcode import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64NoPadding import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding -import im.vector.matrix.android.internal.util.toPositiveInt +import im.vector.matrix.android.internal.extensions.toUnsignedInt // MATRIX private val prefix = "MATRIX".toByteArray(Charsets.ISO_8859_1) @@ -95,8 +95,8 @@ fun String.toQrCodeData(): QrCodeData? { cursor++ // Get transaction length - val bigEndian1 = byteArray[cursor].toPositiveInt() - val bigEndian2 = byteArray[cursor + 1].toPositiveInt() + val bigEndian1 = byteArray[cursor].toUnsignedInt() + val bigEndian2 = byteArray[cursor + 1].toUnsignedInt() val transactionLength = bigEndian1 * 0x0100 + bigEndian2 diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Byte.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Byte.kt deleted file mode 100644 index 77f28e17e5..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Byte.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 im.vector.matrix.android.internal.util - -fun Byte.toPositiveInt(): Int { - return if (this < 0) { - 256 + this - } else { - toInt() - } -} From a11d70f173f1f62b3f9f1e7f4428b4ccd0ff33c5 Mon Sep 17 00:00:00 2001 From: onurays Date: Fri, 21 Feb 2020 00:51:22 +0300 Subject: [PATCH 090/187] Use exif info of the image for correct rotation. --- CHANGES.md | 1 + .../vector/riotx/features/media/ImageContentRenderer.kt | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index b402dc0522..29ccfb29fc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ Bugfix 🐛: - Account creation: wrongly hints that an email can be used to create an account (#941) - Fix crash in the room directory, when public room has no name (#1023) - Fix restoring keys backup with passphrase (#526) + - Fix rotation of full-size image (#647) Translations 🗣: - diff --git a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt index 909fd5b8eb..cbdb19d132 100644 --- a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt +++ b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt @@ -25,6 +25,7 @@ import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target +import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.ORIENTATION_USE_EXIF import com.github.piasy.biv.view.BigImageView import im.vector.matrix.android.api.session.content.ContentUrlResolver import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt @@ -36,6 +37,7 @@ import im.vector.riotx.core.utils.DimensionConverter import im.vector.riotx.core.utils.isLocalFile import kotlinx.android.parcel.Parcelize import timber.log.Timber +import java.io.File import javax.inject.Inject import kotlin.math.min @@ -142,6 +144,13 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: return } + imageView.setImageLoaderCallback(object: DefaultImageLoaderCallback { + override fun onSuccess(image: File?) { + super.onSuccess(image) + imageView.ssiv.orientation = ORIENTATION_USE_EXIF + } + }) + imageView.showImage( Uri.parse(thumbnail), Uri.parse(fullSize) From d7de072155061f57e3fc0e0dc3ce9691542dcf12 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Feb 2020 09:58:56 +0100 Subject: [PATCH 091/187] typo --- .../android/api/session/room/model/roomdirectory/PublicRoom.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoom.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoom.kt index 5a9d6dab36..efe2debeab 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoom.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoom.kt @@ -85,7 +85,7 @@ data class PublicRoom( val isFederated: Boolean = false ) { /** - * Return the canonical alias, or the first alias from the list of alias, or null + * Return the canonical alias, or the first alias from the list of aliases, or null */ fun getPrimaryAlias(): String? { return canonicalAlias ?: aliases?.firstOrNull() From f650e29ddd211ffa74679ed4484697f8dea7c9c1 Mon Sep 17 00:00:00 2001 From: onurays Date: Fri, 21 Feb 2020 15:30:01 +0300 Subject: [PATCH 092/187] Share action is added to room profile and room member profile Fixes #858 --- CHANGES.md | 1 + .../RoomMemberProfileAction.kt | 1 + .../RoomMemberProfileFragment.kt | 30 ++++++++++++++++--- .../RoomMemberProfileViewEvents.kt | 2 ++ .../RoomMemberProfileViewModel.kt | 11 +++++-- .../features/roomprofile/RoomProfileAction.kt | 1 + .../roomprofile/RoomProfileFragment.kt | 22 ++++++++++++++ .../roomprofile/RoomProfileViewEvents.kt | 1 + .../roomprofile/RoomProfileViewModel.kt | 5 ++++ .../main/res/drawable/ic_material_share.xml | 5 ++++ .../res/menu/vector_room_member_profile.xml | 9 ++++++ .../src/main/res/menu/vector_room_profile.xml | 10 +++++++ 12 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 vector/src/main/res/drawable/ic_material_share.xml create mode 100644 vector/src/main/res/menu/vector_room_member_profile.xml create mode 100644 vector/src/main/res/menu/vector_room_profile.xml diff --git a/CHANGES.md b/CHANGES.md index 6d72ad4c2a..dafb6cf110 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,7 @@ Features ✨: Improvements 🙌: - Migrate to binary QR code verification (#994) + - Share action is added to room profile and room member profile (#858) Bugfix 🐛: - Account creation: wrongly hints that an email can be used to create an account (#941) diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt index e352683841..1dc2459538 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt @@ -23,4 +23,5 @@ sealed class RoomMemberProfileAction : VectorViewModelAction { object RetryFetchingInfo : RoomMemberProfileAction() object IgnoreUser : RoomMemberProfileAction() object VerifyUser : RoomMemberProfileAction() + object ShareRoomMemberProfile : RoomMemberProfileAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt index 89b37400f1..6b51f78881 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -19,6 +19,7 @@ package im.vector.riotx.features.roommemberprofile import android.os.Bundle import android.os.Parcelable +import android.view.MenuItem import android.view.View import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible @@ -28,6 +29,7 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import im.vector.matrix.android.api.permalinks.PermalinkFactory import im.vector.matrix.android.api.util.MatrixItem import im.vector.riotx.R import im.vector.riotx.core.animations.AppBarStateChangeListener @@ -38,6 +40,7 @@ import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.platform.StateView import im.vector.riotx.core.platform.VectorBaseFragment +import im.vector.riotx.core.utils.startSharePlainTextIntent import im.vector.riotx.features.crypto.verification.VerificationBottomSheet import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.roommemberprofile.devices.DeviceListBottomSheet @@ -65,6 +68,8 @@ class RoomMemberProfileFragment @Inject constructor( override fun getLayoutResId() = R.layout.fragment_matrix_profile + override fun getMenuRes() = R.menu.vector_room_member_profile + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setupToolbar(matrixProfileToolbar) @@ -90,14 +95,25 @@ class RoomMemberProfileFragment @Inject constructor( matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener) viewModel.observeViewEvents { when (it) { - is RoomMemberProfileViewEvents.Loading -> showLoading(it.message) - is RoomMemberProfileViewEvents.Failure -> showFailure(it.throwable) - is RoomMemberProfileViewEvents.OnIgnoreActionSuccess -> Unit - is RoomMemberProfileViewEvents.StartVerification -> handleStartVerification(it) + is RoomMemberProfileViewEvents.Loading -> showLoading(it.message) + is RoomMemberProfileViewEvents.Failure -> showFailure(it.throwable) + is RoomMemberProfileViewEvents.OnIgnoreActionSuccess -> Unit + is RoomMemberProfileViewEvents.StartVerification -> handleStartVerification(it) + is RoomMemberProfileViewEvents.ShareRoomMemberProfile -> handleShareRoomMemberProfile() }.exhaustive } } + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.roomMemberProfileShareAction -> { + viewModel.handle(RoomMemberProfileAction.ShareRoomMemberProfile) + return true + } + } + return super.onOptionsItemSelected(item) + } + private fun handleStartVerification(startVerification: RoomMemberProfileViewEvents.StartVerification) { if (startVerification.canCrossSign) { VerificationBottomSheet @@ -208,4 +224,10 @@ class RoomMemberProfileFragment @Inject constructor( override fun onMentionClicked() { vectorBaseActivity.notImplemented("Mention") } + + private fun handleShareRoomMemberProfile() = withState(viewModel) { + PermalinkFactory.createPermalink(it.userId)?.let { permalink -> + startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) + } + } } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt index 5b7f5bf54c..d590040180 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt @@ -31,4 +31,6 @@ sealed class RoomMemberProfileViewEvents : VectorViewEvents { val userId: String, val canCrossSign: Boolean ) : RoomMemberProfileViewEvents() + + object ShareRoomMemberProfile : RoomMemberProfileViewEvents() } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index d6a1018e80..eaad2c52a2 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -135,9 +135,10 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v override fun handle(action: RoomMemberProfileAction) { when (action) { - is RoomMemberProfileAction.RetryFetchingInfo -> fetchProfileInfo() - is RoomMemberProfileAction.IgnoreUser -> handleIgnoreAction() - is RoomMemberProfileAction.VerifyUser -> prepareVerification() + is RoomMemberProfileAction.RetryFetchingInfo -> fetchProfileInfo() + is RoomMemberProfileAction.IgnoreUser -> handleIgnoreAction() + is RoomMemberProfileAction.VerifyUser -> prepareVerification() + is RoomMemberProfileAction.ShareRoomMemberProfile -> handleShareRoomMemberProfile() } } @@ -234,4 +235,8 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v session.ignoreUserIds(listOf(state.userId), ignoreActionCallback) } } + + private fun handleShareRoomMemberProfile() { + _viewEvents.post(RoomMemberProfileViewEvents.ShareRoomMemberProfile) + } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt index d3852e34c8..545d67c314 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileAction.kt @@ -23,4 +23,5 @@ import im.vector.riotx.core.platform.VectorViewModelAction sealed class RoomProfileAction: VectorViewModelAction { object LeaveRoom: RoomProfileAction() data class ChangeRoomNotificationState(val notificationState: RoomNotificationState) : RoomProfileAction() + object ShareRoomProfile : RoomProfileAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index 1eafc8af3f..ec0a8e51e1 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -19,12 +19,14 @@ package im.vector.riotx.features.roomprofile import android.os.Bundle import android.os.Parcelable +import android.view.MenuItem import android.view.View import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import im.vector.matrix.android.api.permalinks.PermalinkFactory import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.R @@ -35,6 +37,7 @@ import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.platform.VectorBaseFragment +import im.vector.riotx.core.utils.startSharePlainTextIntent import im.vector.riotx.features.crypto.util.toImageRes import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.room.list.actions.RoomListActionsArgs @@ -67,6 +70,8 @@ class RoomProfileFragment @Inject constructor( override fun getLayoutResId() = R.layout.fragment_matrix_profile + override fun getMenuRes() = R.menu.vector_room_profile + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) roomListQuickActionsSharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) @@ -89,6 +94,7 @@ class RoomProfileFragment @Inject constructor( is RoomProfileViewEvents.Loading -> showLoading(it.message) is RoomProfileViewEvents.Failure -> showFailure(it.throwable) is RoomProfileViewEvents.OnLeaveRoomSuccess -> onLeaveRoom() + is RoomProfileViewEvents.ShareRoomProfile -> onShareRoomProfile() }.exhaustive } roomListQuickActionsSharedActionViewModel @@ -97,6 +103,16 @@ class RoomProfileFragment @Inject constructor( .disposeOnDestroyView() } + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.roomProfileShareAction -> { + roomProfileViewModel.handle(RoomProfileAction.ShareRoomProfile) + return true + } + } + return super.onOptionsItemSelected(item) + } + private fun handleQuickActions(action: RoomListQuickActionsSharedAction) = when (action) { is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> { roomProfileViewModel.handle(RoomProfileAction.ChangeRoomNotificationState(RoomNotificationState.ALL_MESSAGES_NOISY)) @@ -187,4 +203,10 @@ class RoomProfileFragment @Inject constructor( .setNegativeButton(R.string.cancel, null) .show() } + + private fun onShareRoomProfile() { + PermalinkFactory.createPermalink(roomProfileArgs.roomId)?.let { permalink -> + startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) + } + } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt index 50b3c136e5..50e4775252 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt @@ -26,4 +26,5 @@ sealed class RoomProfileViewEvents : VectorViewEvents { data class Failure(val throwable: Throwable) : RoomProfileViewEvents() object OnLeaveRoomSuccess : RoomProfileViewEvents() + object ShareRoomProfile : RoomProfileViewEvents() } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt index 6c66ac67b2..e291556bd4 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt @@ -66,6 +66,7 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R override fun handle(action: RoomProfileAction) = when (action) { RoomProfileAction.LeaveRoom -> handleLeaveRoom() is RoomProfileAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action) + is RoomProfileAction.ShareRoomProfile -> handleShareRoomProfile() } private fun handleChangeNotificationMode(action: RoomProfileAction.ChangeRoomNotificationState) { @@ -88,4 +89,8 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R } }) } + + private fun handleShareRoomProfile() { + _viewEvents.post(RoomProfileViewEvents.ShareRoomProfile) + } } diff --git a/vector/src/main/res/drawable/ic_material_share.xml b/vector/src/main/res/drawable/ic_material_share.xml new file mode 100644 index 0000000000..fd72fa1c6d --- /dev/null +++ b/vector/src/main/res/drawable/ic_material_share.xml @@ -0,0 +1,5 @@ + + + diff --git a/vector/src/main/res/menu/vector_room_member_profile.xml b/vector/src/main/res/menu/vector_room_member_profile.xml new file mode 100644 index 0000000000..dc560ced3e --- /dev/null +++ b/vector/src/main/res/menu/vector_room_member_profile.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/vector/src/main/res/menu/vector_room_profile.xml b/vector/src/main/res/menu/vector_room_profile.xml new file mode 100644 index 0000000000..637cb9ef11 --- /dev/null +++ b/vector/src/main/res/menu/vector_room_profile.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file From ccfa59ad3198b5769816919819f68cce3cee1b12 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Feb 2020 15:05:43 +0100 Subject: [PATCH 093/187] Cleanup var -> val --- .../matrix/android/common/CryptoTestHelper.kt | 14 +- .../matrix/android/api/auth/data/WellKnown.kt | 6 +- .../room/model/create/CreateRoomResponse.kt | 2 +- .../model/roomdirectory/PublicRoomsFilter.kt | 2 +- .../model/roomdirectory/PublicRoomsParams.kt | 10 +- .../model/thirdparty/ThirdPartyProtocol.kt | 12 +- .../thirdparty/ThirdPartyProtocolInstance.kt | 12 +- .../registration/RegistrationFlowResponse.kt | 8 +- .../internal/crypto/DefaultCryptoService.kt | 12 +- .../internal/crypto/IncomingRoomKeyRequest.kt | 86 +++++----- .../IncomingRoomKeyRequestCancellation.kt | 36 ++++- .../crypto/IncomingRoomKeyRequestCommon.kt | 34 ++++ .../crypto/IncomingRoomKeyRequestManager.kt | 4 +- .../internal/crypto/MegolmSessionData.kt | 16 +- .../crypto/OutgoingRoomKeyRequestManager.kt | 16 +- .../actions/MegolmSessionDataImporter.kt | 12 +- .../algorithms/megolm/MXMegolmDecryption.kt | 35 ++--- .../keysbackup/DefaultKeysBackupService.kt | 148 +++++++++--------- .../keysbackup/model/MegolmBackupAuthData.kt | 9 +- .../model/MegolmBackupCreationInfo.kt | 29 ++-- .../model/rest/CreateKeysBackupVersionBody.kt | 16 +- .../keysbackup/model/rest/KeyBackupData.kt | 8 +- .../model/rest/KeysAlgorithmAndData.kt | 9 +- .../keysbackup/model/rest/KeysBackupData.kt | 4 +- .../model/rest/KeysVersionResult.kt | 25 ++- .../model/rest/RoomKeysBackupData.kt | 3 +- .../model/rest/UpdateKeysBackupVersionBody.kt | 17 +- .../internal/crypto/model/MXDeviceInfo.kt | 19 ++- .../model/OlmInboundGroupSessionWrapper.kt | 20 +-- .../model/event/EncryptionEventContent.kt | 7 +- .../crypto/model/event/NewDeviceContent.kt | 5 +- .../crypto/model/event/OlmEventContent.kt | 4 +- .../internal/crypto/model/rest/DeviceInfo.kt | 10 +- .../crypto/model/rest/DevicesListResponse.kt | 2 +- .../crypto/model/rest/EncryptedFileInfo.kt | 12 +- .../crypto/model/rest/EncryptedFileKey.kt | 12 +- .../crypto/model/rest/EncryptedMessage.kt | 7 +- .../crypto/model/rest/KeyChangesResponse.kt | 4 +- .../crypto/model/rest/KeyVerificationDone.kt | 2 +- .../model/rest/KeyVerificationRequest.kt | 2 +- .../crypto/model/rest/KeysClaimBody.kt | 5 +- .../crypto/model/rest/KeysClaimResponse.kt | 3 +- .../crypto/model/rest/KeysQueryBody.kt | 6 +- .../crypto/model/rest/KeysUploadResponse.kt | 4 +- .../crypto/model/rest/RoomKeyRequestBody.kt | 8 +- .../crypto/model/rest/RoomKeyShare.kt | 14 +- .../model/rest/RoomKeyShareCancellation.kt | 19 ++- .../crypto/model/rest/RoomKeyShareRequest.kt | 21 ++- .../crypto/model/rest/UpdateDeviceInfoBody.kt | 2 +- .../internal/crypto/store/IMXCryptoStore.kt | 3 +- .../crypto/store/db/RealmCryptoStore.kt | 3 +- .../db/model/IncomingRoomKeyRequestEntity.kt | 22 +-- .../db/model/OutgoingRoomKeyRequestEntity.kt | 12 +- .../crypto/tasks/DownloadKeysForUsersTask.kt | 10 +- .../android/internal/session/filter/Filter.kt | 14 +- .../internal/session/filter/FilterBody.kt | 10 +- .../internal/session/filter/FilterFactory.kt | 38 +++-- .../internal/session/filter/FilterResponse.kt | 2 +- .../internal/session/filter/FilterUtil.kt | 37 ++--- .../session/filter/RoomEventFilter.kt | 16 +- .../internal/session/filter/RoomFilter.kt | 14 +- .../session/sync/model/GroupSyncProfile.kt | 4 +- 62 files changed, 529 insertions(+), 429 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCommon.kt diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt index 7368c87252..765fd65db7 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt @@ -340,18 +340,14 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) { fun createFakeMegolmBackupAuthData(): MegolmBackupAuthData { return MegolmBackupAuthData( publicKey = "abcdefg", - signatures = HashMap>().apply { - this["something"] = HashMap().apply { - this["ed25519:something"] = "hijklmnop" - } - } + signatures = mapOf("something" to mapOf("ed25519:something" to "hijklmnop")) ) } fun createFakeMegolmBackupCreationInfo(): MegolmBackupCreationInfo { - return MegolmBackupCreationInfo().apply { - algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP - authData = createFakeMegolmBackupAuthData() - } + return MegolmBackupCreationInfo( + algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP, + authData = createFakeMegolmBackupAuthData() + ) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/WellKnown.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/WellKnown.kt index 6285e866cc..bdad4702b7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/WellKnown.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/WellKnown.kt @@ -46,13 +46,13 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class WellKnown( @Json(name = "m.homeserver") - var homeServer: WellKnownBaseConfig? = null, + val homeServer: WellKnownBaseConfig? = null, @Json(name = "m.identity_server") - var identityServer: WellKnownBaseConfig? = null, + val identityServer: WellKnownBaseConfig? = null, @Json(name = "m.integrations") - var integrations: Map? = null + val integrations: Map? = null ) { /** * Returns the list of integration managers proposed diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt index 29c8cb830d..da54b344a2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt @@ -24,7 +24,7 @@ internal data class CreateRoomResponse( /** * Required. The created room's ID. */ - @Json(name = "room_id") var roomId: String + @Json(name = "room_id") val roomId: String ) internal typealias JoinRoomResponse = CreateRoomResponse diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsFilter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsFilter.kt index b4de72e41a..c519d054f2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsFilter.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsFilter.kt @@ -27,5 +27,5 @@ data class PublicRoomsFilter( * A string to search for in the room metadata, e.g. name, topic, canonical alias etc. (Optional). */ @Json(name = "generic_search_term") - var searchTerm: String? = null + val searchTerm: String? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsParams.kt index e2af1c3ccb..467968cd2a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsParams.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/roomdirectory/PublicRoomsParams.kt @@ -28,30 +28,30 @@ data class PublicRoomsParams( * Limit the number of results returned. */ @Json(name = "limit") - var limit: Int? = null, + val limit: Int? = null, /** * A pagination token from a previous request, allowing clients to get the next (or previous) batch of rooms. * The direction of pagination is specified solely by which token is supplied, rather than via an explicit flag. */ @Json(name = "since") - var since: String? = null, + val since: String? = null, /** * Filter to apply to the results. */ @Json(name = "filter") - var filter: PublicRoomsFilter? = null, + val filter: PublicRoomsFilter? = null, /** * Whether or not to include all known networks/protocols from application services on the homeserver. Defaults to false. */ @Json(name = "include_all_networks") - var includeAllNetworks: Boolean = false, + val includeAllNetworks: Boolean = false, /** * The specific third party network/protocol to request from the homeserver. Can only be used if include_all_networks is false. */ @Json(name = "third_party_instance_id") - var thirdPartyInstanceId: String? = null + val thirdPartyInstanceId: String? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/thirdparty/ThirdPartyProtocol.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/thirdparty/ThirdPartyProtocol.kt index b066cff164..b4ed1f1a8e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/thirdparty/ThirdPartyProtocol.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/thirdparty/ThirdPartyProtocol.kt @@ -26,7 +26,7 @@ data class ThirdPartyProtocol( * where higher groupings are ordered first. For example, the name of a network should be searched before the nickname of a user. */ @Json(name = "user_fields") - var userFields: List? = null, + val userFields: List? = null, /** * Required. Fields which may be used to identify a third party location. These should be ordered to suggest the way that @@ -34,15 +34,15 @@ data class ThirdPartyProtocol( * searched before the name of a channel. */ @Json(name = "location_fields") - var locationFields: List? = null, + val locationFields: List? = null, /** * Required. A content URI representing an icon for the third party protocol. * - * FIXDOC: This field was not present in legacy Riot, and it is sometimes sent by the server (no not Required?) + * FIXDOC: This field was not present in legacy Riot, and it is sometimes sent by the server (so not Required?) */ @Json(name = "icon") - var icon: String? = null, + val icon: String? = null, /** * Required. The type definitions for the fields defined in the user_fields and location_fields. Each entry in those arrays MUST have an entry here. @@ -51,12 +51,12 @@ data class ThirdPartyProtocol( * May be an empty object if no fields are defined. */ @Json(name = "field_types") - var fieldTypes: Map? = null, + val fieldTypes: Map? = null, /** * Required. A list of objects representing independent instances of configuration. For example, multiple networks on IRC * if multiple are provided by the same application service. */ @Json(name = "instances") - var instances: List? = null + val instances: List? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/thirdparty/ThirdPartyProtocolInstance.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/thirdparty/ThirdPartyProtocolInstance.kt index 50f92356fb..f5d59f9282 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/thirdparty/ThirdPartyProtocolInstance.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/thirdparty/ThirdPartyProtocolInstance.kt @@ -25,35 +25,35 @@ data class ThirdPartyProtocolInstance( * Required. A human-readable description for the protocol, such as the name. */ @Json(name = "desc") - var desc: String? = null, + val desc: String? = null, /** * An optional content URI representing the protocol. Overrides the one provided at the higher level Protocol object. */ @Json(name = "icon") - var icon: String? = null, + val icon: String? = null, /** * Required. Preset values for fields the client may use to search by. */ @Json(name = "fields") - var fields: Map? = null, + val fields: Map? = null, /** * Required. A unique identifier across all instances. */ @Json(name = "network_id") - var networkId: String? = null, + val networkId: String? = null, /** * FIXDOC Not documented on matrix.org doc */ @Json(name = "instance_id") - var instanceId: String? = null, + val instanceId: String? = null, /** * FIXDOC Not documented on matrix.org doc */ @Json(name = "bot_user_id") - var botUserId: String? = null + val botUserId: String? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/registration/RegistrationFlowResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/registration/RegistrationFlowResponse.kt index 2d3d25e538..7512454052 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/registration/RegistrationFlowResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/registration/RegistrationFlowResponse.kt @@ -32,20 +32,20 @@ data class RegistrationFlowResponse( * The list of flows. */ @Json(name = "flows") - var flows: List? = null, + val flows: List? = null, /** * The list of stages the client has completed successfully. */ @Json(name = "completed") - var completedStages: List? = null, + val completedStages: List? = null, /** * The session identifier that the client must pass back to the home server, if one is provided, * in subsequent attempts to authenticate in the same API call. */ @Json(name = "session") - var session: String? = null, + val session: String? = null, /** * The information that the client will need to know in order to use a given type of authentication. @@ -53,7 +53,7 @@ data class RegistrationFlowResponse( * For example, the public key of reCAPTCHA stage could be given here. */ @Json(name = "params") - var params: JsonDict? = null + val params: JsonDict? = null /** * WARNING, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt index c4d115b10c..1db774fd2d 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt @@ -1021,12 +1021,12 @@ internal class DefaultCryptoService @Inject constructor( return } - val requestBody = RoomKeyRequestBody() - - requestBody.roomId = event.roomId - requestBody.algorithm = wireContent["algorithm"]?.toString() - requestBody.senderKey = wireContent["sender_key"]?.toString() - requestBody.sessionId = wireContent["session_id"]?.toString() + val requestBody = RoomKeyRequestBody( + algorithm = wireContent["algorithm"]?.toString(), + roomId = event.roomId, + senderKey = wireContent["sender_key"]?.toString(), + sessionId = wireContent["session_id"]?.toString() + ) outgoingRoomKeyRequestManager.resendRoomKeyRequest(requestBody) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt index fe1f69f904..93ba5aafe5 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt @@ -25,54 +25,54 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareRequest /** * IncomingRoomKeyRequest class defines the incoming room keys request. */ -open class IncomingRoomKeyRequest { - /** - * The user id - */ - var userId: String? = null +data class IncomingRoomKeyRequest( + /** + * The user id + */ + override val userId: String? = null, - /** - * The device id - */ - var deviceId: String? = null + /** + * The device id + */ + override val deviceId: String? = null, - /** - * The request id - */ - var requestId: String? = null + /** + * The request id + */ + override val requestId: String? = null, - /** - * The request body - */ - var requestBody: RoomKeyRequestBody? = null + /** + * The request body + */ + val requestBody: RoomKeyRequestBody? = null, - /** - * The runnable to call to accept to share the keys - */ - @Transient - var share: Runnable? = null + /** + * The runnable to call to accept to share the keys + */ + @Transient + var share: Runnable? = null, - /** - * The runnable to call to ignore the key share request. - */ - @Transient - var ignore: Runnable? = null + /** + * The runnable to call to ignore the key share request. + */ + @Transient + var ignore: Runnable? = null +) : IncomingRoomKeyRequestCommon { + companion object { + /** + * Factory + * + * @param event the event + */ + fun fromEvent(event: Event): IncomingRoomKeyRequest { + val roomKeyShareRequest = event.getClearContent().toModel()!! - /** - * Constructor - * - * @param event the event - */ - constructor(event: Event) { - userId = event.senderId - val roomKeyShareRequest = event.getClearContent().toModel()!! - deviceId = roomKeyShareRequest.requestingDeviceId - requestId = roomKeyShareRequest.requestId - requestBody = if (null != roomKeyShareRequest.body) roomKeyShareRequest.body else RoomKeyRequestBody() + return IncomingRoomKeyRequest( + userId = event.senderId, + deviceId = roomKeyShareRequest.requestingDeviceId, + requestId = roomKeyShareRequest.requestId, + requestBody = roomKeyShareRequest.body ?: RoomKeyRequestBody() + ) + } } - - /** - * Constructor for object creation from crypto store - */ - constructor() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt index 0b22a0e28b..a33828505e 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt @@ -17,13 +17,43 @@ package im.vector.matrix.android.internal.crypto import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareCancellation /** * IncomingRoomKeyRequestCancellation describes the incoming room key cancellation. */ -class IncomingRoomKeyRequestCancellation(event: Event) : IncomingRoomKeyRequest(event) { +data class IncomingRoomKeyRequestCancellation( + /** + * The user id + */ + override val userId: String? = null, - init { - requestBody = null + /** + * The device id + */ + override val deviceId: String? = null, + + /** + * The request id + */ + override val requestId: String? = null +) : IncomingRoomKeyRequestCommon { + companion object { + + /** + * Factory + * + * @param event the event + */ + fun fromEvent(event: Event): IncomingRoomKeyRequestCancellation { + val roomKeyShareRequestCancellation = event.getClearContent().toModel()!! + + return IncomingRoomKeyRequestCancellation( + userId = event.senderId, + deviceId = roomKeyShareRequestCancellation.requestingDeviceId, + requestId = roomKeyShareRequestCancellation.requestId + ) + } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCommon.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCommon.kt new file mode 100644 index 0000000000..a7b1c6b117 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCommon.kt @@ -0,0 +1,34 @@ +/* + * 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 im.vector.matrix.android.internal.crypto + +interface IncomingRoomKeyRequestCommon { + /** + * The user id + */ + val userId: String? + + /** + * The device id + */ + val deviceId: String? + + /** + * The request id + */ + val requestId: String? +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt index 814d9d5a7c..9a6458cf37 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt @@ -53,8 +53,8 @@ internal class IncomingRoomKeyRequestManager @Inject constructor( fun onRoomKeyRequestEvent(event: Event) { val roomKeyShare = event.getClearContent().toModel() when (roomKeyShare?.action) { - RoomKeyShare.ACTION_SHARE_REQUEST -> receivedRoomKeyRequests.add(IncomingRoomKeyRequest(event)) - RoomKeyShare.ACTION_SHARE_CANCELLATION -> receivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation(event)) + RoomKeyShare.ACTION_SHARE_REQUEST -> receivedRoomKeyRequests.add(IncomingRoomKeyRequest.fromEvent(event)) + RoomKeyShare.ACTION_SHARE_CANCELLATION -> receivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation.fromEvent(event)) else -> Timber.e("## onRoomKeyRequestEvent() : unsupported action ${roomKeyShare?.action}") } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MegolmSessionData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MegolmSessionData.kt index 821ed0a553..d6dae1a865 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MegolmSessionData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MegolmSessionData.kt @@ -28,46 +28,46 @@ data class MegolmSessionData( * The algorithm used. */ @Json(name = "algorithm") - var algorithm: String? = null, + val algorithm: String? = null, /** * Unique id for the session. */ @Json(name = "session_id") - var sessionId: String? = null, + val sessionId: String? = null, /** * Sender's Curve25519 device key. */ @Json(name = "sender_key") - var senderKey: String? = null, + val senderKey: String? = null, /** * Room this session is used in. */ @Json(name = "room_id") - var roomId: String? = null, + val roomId: String? = null, /** * Base64'ed key data. */ @Json(name = "session_key") - var sessionKey: String? = null, + val sessionKey: String? = null, /** * Other keys the sender claims. */ @Json(name = "sender_claimed_keys") - var senderClaimedKeys: Map? = null, + val senderClaimedKeys: Map? = null, // This is a shortcut for sender_claimed_keys.get("ed25519") // Keep it for compatibility reason. @Json(name = "sender_claimed_ed25519_key") - var senderClaimedEd25519Key: String? = null, + val senderClaimedEd25519Key: String? = null, /** * Devices which forwarded this session to us (normally empty). */ @Json(name = "forwarding_curve25519_key_chain") - var forwardingCurve25519KeyChain: List? = null + val forwardingCurve25519KeyChain: List? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt index 5320b84b0e..b59c93ba83 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt @@ -213,10 +213,11 @@ internal class OutgoingRoomKeyRequestManager @Inject constructor( Timber.v("## sendOutgoingRoomKeyRequest() : Requesting keys " + request.requestBody + " from " + request.recipients + " id " + request.requestId) - val requestMessage = RoomKeyShareRequest() - requestMessage.requestingDeviceId = cryptoStore.getDeviceId() - requestMessage.requestId = request.requestId - requestMessage.body = request.requestBody + val requestMessage = RoomKeyShareRequest( + requestingDeviceId = cryptoStore.getDeviceId(), + requestId = request.requestId, + body = request.requestBody + ) sendMessageToDevices(requestMessage, request.recipients, request.requestId, object : MatrixCallback { private fun onDone(state: OutgoingRoomKeyRequest.RequestState) { @@ -253,9 +254,10 @@ internal class OutgoingRoomKeyRequestManager @Inject constructor( + " to " + request.recipients + " cancellation id " + request.cancellationTxnId) - val roomKeyShareCancellation = RoomKeyShareCancellation() - roomKeyShareCancellation.requestingDeviceId = cryptoStore.getDeviceId() - roomKeyShareCancellation.requestId = request.cancellationTxnId + val roomKeyShareCancellation = RoomKeyShareCancellation( + requestingDeviceId = cryptoStore.getDeviceId(), + requestId = request.cancellationTxnId + ) sendMessageToDevices(roomKeyShareCancellation, request.recipients, request.cancellationTxnId, object : MatrixCallback { private fun onDone() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt index ca6cfad0f9..e0d6d72c0d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt @@ -66,12 +66,12 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi totalNumbersOfImportedKeys++ // cancel any outstanding room key requests for this session - val roomKeyRequestBody = RoomKeyRequestBody() - - roomKeyRequestBody.algorithm = megolmSessionData.algorithm - roomKeyRequestBody.roomId = megolmSessionData.roomId - roomKeyRequestBody.senderKey = megolmSessionData.senderKey - roomKeyRequestBody.sessionId = megolmSessionData.sessionId + val roomKeyRequestBody = RoomKeyRequestBody( + algorithm = megolmSessionData.algorithm, + roomId = megolmSessionData.roomId, + senderKey = megolmSessionData.senderKey, + sessionId = megolmSessionData.sessionId + ) outgoingRoomKeyRequestManager.cancelRoomKeyRequest(roomKeyRequestBody) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 054b38ad06..90100fcc48 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -163,12 +163,12 @@ internal class MXMegolmDecryption(private val userId: String, recipients.add(senderMap) } - val requestBody = RoomKeyRequestBody() - - requestBody.roomId = event.roomId - requestBody.algorithm = encryptedEventContent.algorithm - requestBody.senderKey = encryptedEventContent.senderKey - requestBody.sessionId = encryptedEventContent.sessionId + val requestBody = RoomKeyRequestBody( + roomId = event.roomId, + algorithm = encryptedEventContent.algorithm, + senderKey = encryptedEventContent.senderKey, + sessionId = encryptedEventContent.sessionId + ) outgoingRoomKeyRequestManager.sendRoomKeyRequest(requestBody, recipients) } @@ -264,12 +264,12 @@ internal class MXMegolmDecryption(private val userId: String, if (added) { defaultKeysBackupService.maybeBackupKeys() - val content = RoomKeyRequestBody() - - content.algorithm = roomKeyContent.algorithm - content.roomId = roomKeyContent.roomId - content.sessionId = roomKeyContent.sessionId - content.senderKey = senderKey + val content = RoomKeyRequestBody( + algorithm = roomKeyContent.algorithm, + roomId = roomKeyContent.roomId, + sessionId = roomKeyContent.sessionId, + senderKey = senderKey + ) outgoingRoomKeyRequestManager.cancelRoomKeyRequest(content) @@ -290,8 +290,8 @@ internal class MXMegolmDecryption(private val userId: String, override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean { val roomId = request.requestBody?.roomId ?: return false - val senderKey = request.requestBody?.senderKey ?: return false - val sessionId = request.requestBody?.sessionId ?: return false + val senderKey = request.requestBody.senderKey ?: return false + val sessionId = request.requestBody.sessionId ?: return false return olmDevice.hasInboundSessionKeys(roomId, senderKey, sessionId) } @@ -319,15 +319,14 @@ internal class MXMegolmDecryption(private val userId: String, return@mapCatching } Timber.v("## shareKeysWithDevice() : sharing keys for session" + - " ${body?.senderKey}|${body?.sessionId} with device $userId:$deviceId") + " ${body.senderKey}|${body.sessionId} with device $userId:$deviceId") val payloadJson = mutableMapOf("type" to EventType.FORWARDED_ROOM_KEY) - runCatching { olmDevice.getInboundGroupSession(body?.sessionId, body?.senderKey, body?.roomId) } + runCatching { olmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId) } .fold( { // TODO - payloadJson["content"] = it.exportKeys() - ?: "" + payloadJson["content"] = it.exportKeys() ?: "" }, { // TODO diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt index aa291e8206..5206aeedc2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -167,9 +167,7 @@ internal class DefaultKeysBackupService @Inject constructor( runCatching { withContext(coroutineDispatchers.crypto) { val olmPkDecryption = OlmPkDecryption() - val megolmBackupAuthData = MegolmBackupAuthData() - - if (password != null) { + val megolmBackupAuthData = if (password != null) { // Generate a private key from the password val backgroundProgressListener = if (progressListener == null) { null @@ -188,25 +186,30 @@ internal class DefaultKeysBackupService @Inject constructor( } val generatePrivateKeyResult = generatePrivateKeyWithPassword(password, backgroundProgressListener) - megolmBackupAuthData.publicKey = olmPkDecryption.setPrivateKey(generatePrivateKeyResult.privateKey) - megolmBackupAuthData.privateKeySalt = generatePrivateKeyResult.salt - megolmBackupAuthData.privateKeyIterations = generatePrivateKeyResult.iterations + MegolmBackupAuthData( + publicKey = olmPkDecryption.setPrivateKey(generatePrivateKeyResult.privateKey), + privateKeySalt = generatePrivateKeyResult.salt, + privateKeyIterations = generatePrivateKeyResult.iterations + ) } else { val publicKey = olmPkDecryption.generateKey() - megolmBackupAuthData.publicKey = publicKey + MegolmBackupAuthData( + publicKey = publicKey + ) } val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, megolmBackupAuthData.signalableJSONDictionary()) - megolmBackupAuthData.signatures = objectSigner.signObject(canonicalJson) + val signedMegolmBackupAuthData = megolmBackupAuthData.copy( + signatures = objectSigner.signObject(canonicalJson) + ) - val megolmBackupCreationInfo = MegolmBackupCreationInfo() - megolmBackupCreationInfo.algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP - megolmBackupCreationInfo.authData = megolmBackupAuthData - megolmBackupCreationInfo.recoveryKey = computeRecoveryKey(olmPkDecryption.privateKey()) - - megolmBackupCreationInfo + MegolmBackupCreationInfo( + algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP, + authData = signedMegolmBackupAuthData, + recoveryKey = computeRecoveryKey(olmPkDecryption.privateKey()) + ) } }.foldToCallback(callback) } @@ -214,11 +217,12 @@ internal class DefaultKeysBackupService @Inject constructor( override fun createKeysBackupVersion(keysBackupCreationInfo: MegolmBackupCreationInfo, callback: MatrixCallback) { - val createKeysBackupVersionBody = CreateKeysBackupVersionBody() - createKeysBackupVersionBody.algorithm = keysBackupCreationInfo.algorithm @Suppress("UNCHECKED_CAST") - createKeysBackupVersionBody.authData = MoshiProvider.providesMoshi().adapter(Map::class.java) - .fromJson(keysBackupCreationInfo.authData?.toJsonString() ?: "") as JsonDict? + val createKeysBackupVersionBody = CreateKeysBackupVersionBody( + algorithm = keysBackupCreationInfo.algorithm, + authData = MoshiProvider.providesMoshi().adapter(Map::class.java) + .fromJson(keysBackupCreationInfo.authData?.toJsonString() ?: "") as JsonDict? + ) keysBackupStateManager.state = KeysBackupState.Enabling @@ -229,14 +233,14 @@ internal class DefaultKeysBackupService @Inject constructor( // Reset backup markers. cryptoStore.resetBackupMarkers() - val keyBackupVersion = KeysVersionResult() - keyBackupVersion.algorithm = createKeysBackupVersionBody.algorithm - keyBackupVersion.authData = createKeysBackupVersionBody.authData - keyBackupVersion.version = data.version - - // We can consider that the server does not have keys yet - keyBackupVersion.count = 0 - keyBackupVersion.hash = null + val keyBackupVersion = KeysVersionResult( + algorithm = createKeysBackupVersionBody.algorithm, + authData = createKeysBackupVersionBody.authData, + version = data.version, + // We can consider that the server does not have keys yet + count = 0, + hash = null + ) enableKeysBackup(keyBackupVersion) @@ -406,7 +410,7 @@ internal class DefaultKeysBackupService @Inject constructor( return keysBackupVersionTrust } - val mySigs = authData.signatures?.get(userId) + val mySigs = authData.signatures[userId] if (mySigs.isNullOrEmpty()) { Timber.v("getKeysBackupTrust: Ignoring key backup because it lacks any signatures from this user") return keysBackupVersionTrust @@ -469,8 +473,7 @@ internal class DefaultKeysBackupService @Inject constructor( cryptoCoroutineScope.launch(coroutineDispatchers.main) { val updateKeysBackupVersionBody = withContext(coroutineDispatchers.crypto) { // Get current signatures, or create an empty set - val myUserSignatures = authData.signatures?.get(userId)?.toMutableMap() - ?: HashMap() + val myUserSignatures = authData.signatures?.get(userId)?.toMutableMap() ?: HashMap() if (trust) { // Add current device signature @@ -487,24 +490,23 @@ internal class DefaultKeysBackupService @Inject constructor( } // Create an updated version of KeysVersionResult - val updateKeysBackupVersionBody = UpdateKeysBackupVersionBody(keysBackupVersion.version!!) - - updateKeysBackupVersionBody.algorithm = keysBackupVersion.algorithm - val newMegolmBackupAuthData = authData.copy() val newSignatures = newMegolmBackupAuthData.signatures!!.toMutableMap() newSignatures[userId] = myUserSignatures - newMegolmBackupAuthData.signatures = newSignatures + val newMegolmBackupAuthDataWithNewSignature = newMegolmBackupAuthData.copy( + signatures = newSignatures + ) val moshi = MoshiProvider.providesMoshi() val adapter = moshi.adapter(Map::class.java) @Suppress("UNCHECKED_CAST") - updateKeysBackupVersionBody.authData = adapter.fromJson(newMegolmBackupAuthData.toJsonString()) as Map? - - updateKeysBackupVersionBody + UpdateKeysBackupVersionBody( + algorithm = keysBackupVersion.algorithm, + authData = adapter.fromJson(newMegolmBackupAuthDataWithNewSignature.toJsonString()) as Map?, + version = keysBackupVersion.version!!) } // And send it to the homeserver @@ -513,13 +515,13 @@ internal class DefaultKeysBackupService @Inject constructor( this.callback = object : MatrixCallback { override fun onSuccess(data: Unit) { // Relaunch the state machine on this updated backup version - val newKeysBackupVersion = KeysVersionResult() - - newKeysBackupVersion.version = keysBackupVersion.version - newKeysBackupVersion.algorithm = keysBackupVersion.algorithm - newKeysBackupVersion.count = keysBackupVersion.count - newKeysBackupVersion.hash = keysBackupVersion.hash - newKeysBackupVersion.authData = updateKeysBackupVersionBody.authData + val newKeysBackupVersion = KeysVersionResult( + algorithm = keysBackupVersion.algorithm, + authData = updateKeysBackupVersionBody.authData, + version = keysBackupVersion.version, + hash = keysBackupVersion.hash, + count = keysBackupVersion.count + ) checkAndStartWithKeysBackupVersion(newKeysBackupVersion) @@ -1024,7 +1026,7 @@ internal class DefaultKeysBackupService @Inject constructor( } // Extract the recovery key from the passphrase - val data = retrievePrivateKeyWithPassword(password, authData.privateKeySalt!!, authData.privateKeyIterations!!, progressListener) + val data = retrievePrivateKeyWithPassword(password, authData.privateKeySalt, authData.privateKeyIterations, progressListener) return computeRecoveryKey(data) } @@ -1178,14 +1180,16 @@ internal class DefaultKeysBackupService @Inject constructor( // Gather data to send to the homeserver // roomId -> sessionId -> MXKeyBackupData - val keysBackupData = KeysBackupData() - keysBackupData.roomIdToRoomKeysBackupData = HashMap() + val keysBackupData = KeysBackupData( + roomIdToRoomKeysBackupData = HashMap() + ) for (olmInboundGroupSessionWrapper in olmInboundGroupSessionWrappers) { val keyBackupData = encryptGroupSession(olmInboundGroupSessionWrapper) if (keysBackupData.roomIdToRoomKeysBackupData[olmInboundGroupSessionWrapper.roomId] == null) { - val roomKeysBackupData = RoomKeysBackupData() - roomKeysBackupData.sessionIdToKeyBackupData = HashMap() + val roomKeysBackupData = RoomKeysBackupData( + sessionIdToKeyBackupData = HashMap() + ) keysBackupData.roomIdToRoomKeysBackupData[olmInboundGroupSessionWrapper.roomId!!] = roomKeysBackupData } @@ -1301,24 +1305,21 @@ internal class DefaultKeysBackupService @Inject constructor( } // Build backup data for that key - val keyBackupData = KeyBackupData() - try { - keyBackupData.firstMessageIndex = olmInboundGroupSessionWrapper.olmInboundGroupSession!!.firstKnownIndex - } catch (e: OlmException) { - Timber.e(e, "OlmException") - } + return KeyBackupData( + firstMessageIndex = try { + olmInboundGroupSessionWrapper.olmInboundGroupSession!!.firstKnownIndex + } catch (e: OlmException) { + Timber.e(e, "OlmException") + 0L + }, + forwardedCount = olmInboundGroupSessionWrapper.forwardingCurve25519KeyChain!!.size, + isVerified = device?.isVerified == true, - keyBackupData.forwardedCount = olmInboundGroupSessionWrapper.forwardingCurve25519KeyChain!!.size - keyBackupData.isVerified = device?.isVerified == true - - val data = mapOf( - "ciphertext" to encryptedSessionBackupData!!.mCipherText, - "mac" to encryptedSessionBackupData.mMac, - "ephemeral" to encryptedSessionBackupData.mEphemeralKey) - - keyBackupData.sessionData = data - - return keyBackupData + sessionData = mapOf( + "ciphertext" to encryptedSessionBackupData!!.mCipherText, + "mac" to encryptedSessionBackupData.mMac, + "ephemeral" to encryptedSessionBackupData.mEphemeralKey) + ) } @VisibleForTesting @@ -1350,8 +1351,10 @@ internal class DefaultKeysBackupService @Inject constructor( } if (sessionBackupData != null) { - sessionBackupData.sessionId = sessionId - sessionBackupData.roomId = roomId + sessionBackupData = sessionBackupData.copy( + sessionId = sessionId, + roomId = roomId + ) } } @@ -1370,11 +1373,12 @@ internal class DefaultKeysBackupService @Inject constructor( @VisibleForTesting fun createFakeKeysBackupVersion(keysBackupCreationInfo: MegolmBackupCreationInfo, callback: MatrixCallback) { - val createKeysBackupVersionBody = CreateKeysBackupVersionBody() - createKeysBackupVersionBody.algorithm = keysBackupCreationInfo.algorithm @Suppress("UNCHECKED_CAST") - createKeysBackupVersionBody.authData = MoshiProvider.providesMoshi().adapter(Map::class.java) - .fromJson(keysBackupCreationInfo.authData?.toJsonString() ?: "") as JsonDict? + val createKeysBackupVersionBody = CreateKeysBackupVersionBody( + algorithm = keysBackupCreationInfo.algorithm, + authData = MoshiProvider.providesMoshi().adapter(Map::class.java) + .fromJson(keysBackupCreationInfo.authData?.toJsonString() ?: "") as JsonDict? + ) createKeysBackupVersionTask .configureWith(createKeysBackupVersionBody) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupAuthData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupAuthData.kt index 442b1f081c..48015a98dd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupAuthData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupAuthData.kt @@ -30,26 +30,27 @@ data class MegolmBackupAuthData( * The curve25519 public key used to encrypt the backups. */ @Json(name = "public_key") - var publicKey: String = "", + val publicKey: String = "", /** * In case of a backup created from a password, the salt associated with the backup * private key. */ @Json(name = "private_key_salt") - var privateKeySalt: String? = null, + val privateKeySalt: String? = null, /** * In case of a backup created from a password, the number of key derivations. */ @Json(name = "private_key_iterations") - var privateKeyIterations: Int? = null, + val privateKeyIterations: Int? = null, /** * Signatures of the public key. * userId -> (deviceSignKeyId -> signature) */ - var signatures: Map>? = null + @Json(name = "signatures") + val signatures: Map>? = null ) { fun toJsonString(): String { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupCreationInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupCreationInfo.kt index a08ba9ba96..b329fa44c9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupCreationInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/MegolmBackupCreationInfo.kt @@ -19,20 +19,19 @@ package im.vector.matrix.android.internal.crypto.keysbackup.model /** * Data retrieved from Olm library. algorithm and authData will be send to the homeserver, and recoveryKey will be displayed to the user */ -class MegolmBackupCreationInfo { +data class MegolmBackupCreationInfo( + /** + * The algorithm used for storing backups [org.matrix.androidsdk.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP]. + */ + val algorithm: String = "", - /** - * The algorithm used for storing backups [org.matrix.androidsdk.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP]. - */ - var algorithm: String = "" + /** + * Authentication data. + */ + val authData: MegolmBackupAuthData? = null, - /** - * Authentication data. - */ - var authData: MegolmBackupAuthData? = null - - /** - * The Base58 recovery key. - */ - var recoveryKey: String = "" -} + /** + * The Base58 recovery key. + */ + val recoveryKey: String = "" +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/CreateKeysBackupVersionBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/CreateKeysBackupVersionBody.kt index 5efbc6d017..3b267280e5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/CreateKeysBackupVersionBody.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/CreateKeysBackupVersionBody.kt @@ -16,7 +16,21 @@ package im.vector.matrix.android.internal.crypto.keysbackup.model.rest +import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.util.JsonDict @JsonClass(generateAdapter = true) -class CreateKeysBackupVersionBody : KeysAlgorithmAndData() +data class CreateKeysBackupVersionBody( + /** + * The algorithm used for storing backups. Currently, only "m.megolm_backup.v1.curve25519-aes-sha2" is defined + */ + @Json(name = "algorithm") + override val algorithm: String? = null, + + /** + * algorithm-dependent data, for "m.megolm_backup.v1.curve25519-aes-sha2" see [im.vector.matrix.android.internal.crypto.keysbackup.MegolmBackupAuthData] + */ + @Json(name = "auth_data") + override val authData: JsonDict? = null +) : KeysAlgorithmAndData diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt index f172d45ffd..5f6e30b3b5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt @@ -29,25 +29,25 @@ data class KeyBackupData( * Required. The index of the first message in the session that the key can decrypt. */ @Json(name = "first_message_index") - var firstMessageIndex: Long = 0, + val firstMessageIndex: Long = 0, /** * Required. The number of times this key has been forwarded. */ @Json(name = "forwarded_count") - var forwardedCount: Int = 0, + val forwardedCount: Int = 0, /** * Whether the device backing up the key has verified the device that the key is from. */ @Json(name = "is_verified") - var isVerified: Boolean = false, + val isVerified: Boolean = false, /** * Algorithm-dependent data. */ @Json(name = "session_data") - var sessionData: Map? = null + val sessionData: Map? = null ) { fun toJsonString(): String { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysAlgorithmAndData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysAlgorithmAndData.kt index 6fba833589..81ca6586a3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysAlgorithmAndData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysAlgorithmAndData.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.keysbackup.model.rest -import com.squareup.moshi.Json import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupAuthData import im.vector.matrix.android.internal.di.MoshiProvider @@ -38,19 +37,17 @@ import im.vector.matrix.android.internal.di.MoshiProvider * } * */ -open class KeysAlgorithmAndData { +interface KeysAlgorithmAndData { /** * The algorithm used for storing backups. Currently, only "m.megolm_backup.v1.curve25519-aes-sha2" is defined */ - @Json(name = "algorithm") - var algorithm: String? = null + val algorithm: String? /** * algorithm-dependent data, for "m.megolm_backup.v1.curve25519-aes-sha2" see [im.vector.matrix.android.internal.crypto.keysbackup.MegolmBackupAuthData] */ - @Json(name = "auth_data") - var authData: JsonDict? = null + val authData: JsonDict? /** * Facility method to convert authData to a MegolmBackupAuthData object diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysBackupData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysBackupData.kt index 2f4165d8ab..240c79fd1e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysBackupData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysBackupData.kt @@ -24,9 +24,7 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) data class KeysBackupData( - // the keys are the room IDs, and the values are RoomKeysBackupData @Json(name = "rooms") - var roomIdToRoomKeysBackupData: MutableMap = HashMap() - + val roomIdToRoomKeysBackupData: MutableMap = HashMap() ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersionResult.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersionResult.kt index 4510cdd773..0addd1491e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersionResult.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeysVersionResult.kt @@ -16,16 +16,33 @@ package im.vector.matrix.android.internal.crypto.keysbackup.model.rest +import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.util.JsonDict @JsonClass(generateAdapter = true) data class KeysVersionResult( + /** + * The algorithm used for storing backups. Currently, only "m.megolm_backup.v1.curve25519-aes-sha2" is defined + */ + @Json(name = "algorithm") + override val algorithm: String? = null, + + /** + * algorithm-dependent data, for "m.megolm_backup.v1.curve25519-aes-sha2" see [im.vector.matrix.android.internal.crypto.keysbackup.MegolmBackupAuthData] + */ + @Json(name = "auth_data") + override val authData: JsonDict? = null, + // the backup version - var version: String? = null, + @Json(name = "version") + val version: String? = null, // The hash value which is an opaque string representing stored keys in the backup - var hash: String? = null, + @Json(name = "hash") + val hash: String? = null, // The number of keys stored in the backup. - var count: Int? = null -) : KeysAlgorithmAndData() + @Json(name = "count") + val count: Int? = null +) : KeysAlgorithmAndData diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/RoomKeysBackupData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/RoomKeysBackupData.kt index 5d69f63538..f3c218baca 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/RoomKeysBackupData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/RoomKeysBackupData.kt @@ -24,8 +24,7 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) data class RoomKeysBackupData( - // the keys are the session IDs, and the values are KeyBackupData @Json(name = "sessions") - var sessionIdToKeyBackupData: MutableMap = HashMap() + val sessionIdToKeyBackupData: MutableMap = HashMap() ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/UpdateKeysBackupVersionBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/UpdateKeysBackupVersionBody.kt index cb8ba5e26c..9d88af20ef 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/UpdateKeysBackupVersionBody.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/UpdateKeysBackupVersionBody.kt @@ -16,10 +16,25 @@ package im.vector.matrix.android.internal.crypto.keysbackup.model.rest +import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.util.JsonDict @JsonClass(generateAdapter = true) data class UpdateKeysBackupVersionBody( + /** + * The algorithm used for storing backups. Currently, only "m.megolm_backup.v1.curve25519-aes-sha2" is defined + */ + @Json(name = "algorithm") + override val algorithm: String? = null, + + /** + * algorithm-dependent data, for "m.megolm_backup.v1.curve25519-aes-sha2" see [im.vector.matrix.android.internal.crypto.keysbackup.MegolmBackupAuthData] + */ + @Json(name = "auth_data") + override val authData: JsonDict? = null, + // the backup version, mandatory + @Json(name = "version") val version: String -) : KeysAlgorithmAndData() +) : KeysAlgorithmAndData diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt index cc9b3bff74..ae53694a0f 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/MXDeviceInfo.kt @@ -26,48 +26,47 @@ import java.io.Serializable @JsonClass(generateAdapter = true) data class MXDeviceInfo( - /** * The id of this device. */ @Json(name = "device_id") - var deviceId: String, + val deviceId: String, /** * the user id */ @Json(name = "user_id") - var userId: String, + val userId: String, /** * The list of algorithms supported by this device. */ @Json(name = "algorithms") - var algorithms: List? = null, + val algorithms: List? = null, /** * A map from ":" to "". */ @Json(name = "keys") - var keys: Map? = null, + val keys: Map? = null, /** * The signature of this MXDeviceInfo. * A map from "" to a map from ":" to "" */ @Json(name = "signatures") - var signatures: Map>? = null, + val signatures: Map>? = null, /* * Additional data from the home server. */ @Json(name = "unsigned") - var unsigned: JsonDict? = null, + val unsigned: JsonDict? = null, /** * Verification state of this device. */ - var verified: Int = DEVICE_VERIFICATION_UNKNOWN + val verified: Int = DEVICE_VERIFICATION_UNKNOWN ) : Serializable { /** * Tells if the device is unknown @@ -137,11 +136,11 @@ data class MXDeviceInfo( map["user_id"] = userId if (null != algorithms) { - map["algorithms"] = algorithms!! + map["algorithms"] = algorithms } if (null != keys) { - map["keys"] = keys!! + map["keys"] = keys } return map diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/OlmInboundGroupSessionWrapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/OlmInboundGroupSessionWrapper.kt index 361b8bc205..cf1a3b237a 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/OlmInboundGroupSessionWrapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/OlmInboundGroupSessionWrapper.kt @@ -116,16 +116,16 @@ class OlmInboundGroupSessionWrapper : Serializable { return null } - MegolmSessionData().also { - it.senderClaimedEd25519Key = keysClaimed?.get("ed25519") - it.forwardingCurve25519KeyChain = ArrayList(forwardingCurve25519KeyChain!!) - it.senderKey = senderKey - it.senderClaimedKeys = keysClaimed - it.roomId = roomId - it.sessionId = olmInboundGroupSession!!.sessionIdentifier() - it.sessionKey = olmInboundGroupSession!!.export(olmInboundGroupSession!!.firstKnownIndex) - it.algorithm = MXCRYPTO_ALGORITHM_MEGOLM - } + MegolmSessionData( + senderClaimedEd25519Key = keysClaimed?.get("ed25519"), + forwardingCurve25519KeyChain = ArrayList(forwardingCurve25519KeyChain!!), + senderKey = senderKey, + senderClaimedKeys = keysClaimed, + roomId = roomId, + sessionId = olmInboundGroupSession!!.sessionIdentifier(), + sessionKey = olmInboundGroupSession!!.export(olmInboundGroupSession!!.firstKnownIndex), + algorithm = MXCRYPTO_ALGORITHM_MEGOLM + ) } catch (e: Exception) { Timber.e(e, "## export() : senderKey $senderKey failed") null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptionEventContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptionEventContent.kt index 6de50f84c2..05e97da68d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptionEventContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/EncryptionEventContent.kt @@ -23,22 +23,21 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) data class EncryptionEventContent( - /** * Required. The encryption algorithm to be used to encrypt messages sent in this room. Must be 'm.megolm.v1.aes-sha2'. */ @Json(name = "algorithm") - var algorithm: String, + val algorithm: String, /** * How long the session should be used before changing it. 604800000 (a week) is the recommended default. */ @Json(name = "rotation_period_ms") - var rotationPeriodMs: Long? = null, + val rotationPeriodMs: Long? = null, /** * How many messages should be sent before changing the session. 100 is the recommended default. */ @Json(name = "rotation_period_msgs") - var rotationPeriodMsgs: Long? = null + val rotationPeriodMsgs: Long? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/NewDeviceContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/NewDeviceContent.kt index a6777a4f12..62fe4293e7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/NewDeviceContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/NewDeviceContent.kt @@ -20,12 +20,11 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class NewDeviceContent( - // the device id @Json(name = "device_id") - var deviceId: String? = null, + val deviceId: String? = null, // the room ids list @Json(name = "rooms") - var rooms: List? = null + val rooms: List? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmEventContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmEventContent.kt index 7ac0b075be..2b2b49120a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmEventContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/event/OlmEventContent.kt @@ -27,11 +27,11 @@ data class OlmEventContent( * */ @Json(name = "ciphertext") - var ciphertext: Map? = null, + val ciphertext: Map? = null, /** * the sender key */ @Json(name = "sender_key") - var senderKey: String? = null + val senderKey: String? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceInfo.kt index 1289ef3d92..b058fac082 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DeviceInfo.kt @@ -30,31 +30,31 @@ data class DeviceInfo( * The owner user id (not documented and useless but the homeserver sent it. You should not need it) */ @Json(name = "user_id") - var user_id: String? = null, + val user_id: String? = null, /** * The device id */ @Json(name = "device_id") - var deviceId: String? = null, + val deviceId: String? = null, /** * The device display name */ @Json(name = "display_name") - var displayName: String? = null, + val displayName: String? = null, /** * The last time this device has been seen. */ @Json(name = "last_seen_ts") - var lastSeenTs: Long? = null, + val lastSeenTs: Long? = null, /** * The last ip address */ @Json(name = "last_seen_ip") - var lastSeenIp: String? = null + val lastSeenIp: String? = null ) : DatedObject { override val date: Long diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DevicesListResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DevicesListResponse.kt index 9b50b486dc..2bf3d06299 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DevicesListResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/DevicesListResponse.kt @@ -24,5 +24,5 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class DevicesListResponse( @Json(name = "devices") - var devices: List? = null + val devices: List? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileInfo.kt index 5e09b20c91..93e8b4d211 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileInfo.kt @@ -27,38 +27,38 @@ data class EncryptedFileInfo( * Required. The URL to the file. */ @Json(name = "url") - var url: String? = null, + val url: String? = null, /** * Not documented */ @Json(name = "mimetype") - var mimetype: String? = null, + val mimetype: String? = null, /** * Required. A JSON Web Key object. */ @Json(name = "key") - var key: EncryptedFileKey? = null, + val key: EncryptedFileKey? = null, /** * Required. The Initialisation Vector used by AES-CTR, encoded as unpadded base64. */ @Json(name = "iv") - var iv: String? = null, + val iv: String? = null, /** * Required. A map from an algorithm name to a hash of the ciphertext, encoded as unpadded base64. * Clients should support the SHA-256 hash, which uses the key "sha256". */ @Json(name = "hashes") - var hashes: Map? = null, + val hashes: Map? = null, /** * Required. Version of the encrypted attachments protocol. Must be "v2". */ @Json(name = "v") - var v: String? = null + val v: String? = null ) { /** * Check what the spec tells us diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileKey.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileKey.kt index 799819ceee..fa5885de49 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileKey.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedFileKey.kt @@ -24,31 +24,31 @@ data class EncryptedFileKey( * Required. Algorithm. Must be "A256CTR". */ @Json(name = "alg") - var alg: String? = null, + val alg: String? = null, /** * Required. Extractable. Must be true. This is a W3C extension. */ @Json(name = "ext") - var ext: Boolean? = null, + val ext: Boolean? = null, /** * Required. Key operations. Must at least contain "encrypt" and "decrypt". */ @Json(name = "key_ops") - var key_ops: List? = null, + val key_ops: List? = null, /** * Required. Key type. Must be "oct". */ @Json(name = "kty") - var kty: String? = null, + val kty: String? = null, /** * Required. The key, encoded as urlsafe unpadded base64. */ @Json(name = "k") - var k: String? = null + val k: String? = null ) { /** * Check what the spec tells us @@ -62,7 +62,7 @@ data class EncryptedFileKey( return false } - if (key_ops?.contains("encrypt") != true || key_ops?.contains("decrypt") != true) { + if (key_ops?.contains("encrypt") != true || !key_ops.contains("decrypt")) { return false } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedMessage.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedMessage.kt index c546cd04c4..e3ada0c0ab 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedMessage.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/EncryptedMessage.kt @@ -21,11 +21,12 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class EncryptedMessage( - var algorithm: String? = null, + @Json(name = "algorithm") + val algorithm: String? = null, @Json(name = "sender_key") - var senderKey: String? = null, + val senderKey: String? = null, @Json(name = "ciphertext") - var cipherText: Map? = null + val cipherText: Map? = null ) : SendToDeviceObject diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyChangesResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyChangesResponse.kt index 12d27a023f..3af7d7c8c5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyChangesResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyChangesResponse.kt @@ -25,9 +25,9 @@ import com.squareup.moshi.JsonClass internal data class KeyChangesResponse( // list of user ids which have new devices @Json(name = "changed") - var changed: List? = null, + val changed: List? = null, // List of user ids who are no more tracked. @Json(name = "left") - var left: List? = null + val left: List? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationDone.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationDone.kt index c0a72d29db..bdce77b31d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationDone.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationDone.kt @@ -24,7 +24,7 @@ import im.vector.matrix.android.internal.crypto.verification.VerificationInfoDon */ @JsonClass(generateAdapter = true) internal data class KeyVerificationDone( - @Json(name = "transaction_id") override var transactionID: String? = null + @Json(name = "transaction_id") override val transactionID: String? = null ) : SendToDeviceObject, VerificationInfoDone { override fun toSendToDeviceObject() = this diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationRequest.kt index 5bd09658b5..fcddb5c3d4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationRequest.kt @@ -27,7 +27,7 @@ internal data class KeyVerificationRequest( @Json(name = "from_device") override val fromDevice: String?, @Json(name = "methods") override val methods: List, @Json(name = "timestamp") override val timestamp: Long?, - @Json(name = "transaction_id") override var transactionID: String? = null + @Json(name = "transaction_id") override val transactionID: String? = null ) : SendToDeviceObject, VerificationInfoRequest { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimBody.kt index 38f6615dad..26ee1ebe38 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimBody.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimBody.kt @@ -24,16 +24,15 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) internal data class KeysClaimBody( - /** * The time (in milliseconds) to wait when downloading keys from remote servers. 10 seconds is the recommended default. */ @Json(name = "timeout") - var timeout: Int? = null, + val timeout: Int? = null, /** * Required. The keys to be claimed. A map from user ID, to a map from device ID to algorithm name. */ @Json(name = "one_time_keys") - var oneTimeKeys: Map> + val oneTimeKeys: Map> ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimResponse.kt index 59567ba77a..3483873fbb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysClaimResponse.kt @@ -24,11 +24,10 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) internal data class KeysClaimResponse( - /** * The requested keys ordered by device by user. * TODO Type does not match spec, should be Map */ @Json(name = "one_time_keys") - var oneTimeKeys: Map>>>? = null + val oneTimeKeys: Map>>>? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryBody.kt index 3dca696fcd..da2dd781dd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryBody.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysQueryBody.kt @@ -25,12 +25,11 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) internal data class KeysQueryBody( - /** * The time (in milliseconds) to wait when downloading keys from remote servers. 10 seconds is the recommended default. */ @Json(name = "timeout") - var timeout: Int? = null, + val timeout: Int? = null, /** * Required. The keys to be downloaded. @@ -45,6 +44,5 @@ internal data class KeysQueryBody( * by the notification in that sync. */ @Json(name = "token") - var token: String? = null - + val token: String? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadResponse.kt index 38360fa1cd..cd71749acf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeysUploadResponse.kt @@ -23,13 +23,11 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) internal data class KeysUploadResponse( - /** * The count per algorithm as returned by the home server: a map (algorithm to count). */ @Json(name = "one_time_key_counts") - var oneTimeKeyCounts: Map? = null - + val oneTimeKeyCounts: Map? = null ) { /** * Helper methods to extract information from 'oneTimeKeyCounts' diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt index 06f70ee25b..3eb6600e5e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt @@ -25,14 +25,14 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class RoomKeyRequestBody( @Json(name = "algorithm") - var algorithm: String? = null, + val algorithm: String? = null, @Json(name = "room_id") - var roomId: String? = null, + val roomId: String? = null, @Json(name = "sender_key") - var senderKey: String? = null, + val senderKey: String? = null, @Json(name = "session_id") - var sessionId: String? = null + val sessionId: String? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShare.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShare.kt index de2345e002..4ea95d84ae 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShare.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShare.kt @@ -15,21 +15,17 @@ */ package im.vector.matrix.android.internal.crypto.model.rest -import com.squareup.moshi.Json - /** - * Parent class representing an room key action request + * Interface representing an room key action request * Note: this class cannot be abstract because of [org.matrix.androidsdk.core.JsonUtils.toRoomKeyShare] */ -internal open class RoomKeyShare : SendToDeviceObject { +internal interface RoomKeyShare : SendToDeviceObject { - var action: String? = null + val action: String? - @Json(name = "requesting_device_id") - var requestingDeviceId: String? = null + val requestingDeviceId: String? - @Json(name = "request_id") - var requestId: String? = null + val requestId: String? companion object { const val ACTION_SHARE_REQUEST = "request" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareCancellation.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareCancellation.kt index fcfbfccbac..110eed04c1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareCancellation.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareCancellation.kt @@ -15,14 +15,21 @@ */ package im.vector.matrix.android.internal.crypto.model.rest +import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShare.Companion.ACTION_SHARE_CANCELLATION /** - * Class representing an room key request cancellation content + * Class representing a room key request cancellation content */ @JsonClass(generateAdapter = true) -internal class RoomKeyShareCancellation : RoomKeyShare() { - init { - action = ACTION_SHARE_CANCELLATION - } -} +internal data class RoomKeyShareCancellation( + @Json(name = "action") + override val action: String? = ACTION_SHARE_CANCELLATION, + + @Json(name = "requesting_device_id") + override val requestingDeviceId: String? = null, + + @Json(name = "request_id") + override val requestId: String? = null +) : RoomKeyShare diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt index 3b9d210812..d92bc03aab 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt @@ -16,16 +16,23 @@ */ package im.vector.matrix.android.internal.crypto.model.rest +import com.squareup.moshi.Json import com.squareup.moshi.JsonClass /** - * Class representing an room key request content + * Class representing a room key request content */ @JsonClass(generateAdapter = true) -internal class RoomKeyShareRequest : RoomKeyShare() { - var body: RoomKeyRequestBody? = null +internal data class RoomKeyShareRequest( + @Json(name = "action") + override val action: String? = RoomKeyShare.ACTION_SHARE_REQUEST, - init { - action = ACTION_SHARE_REQUEST - } -} + @Json(name = "requesting_device_id") + override val requestingDeviceId: String? = null, + + @Json(name = "request_id") + override val requestId: String? = null, + + @Json(name = "body") + val body: RoomKeyRequestBody? = null +) : RoomKeyShare diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UpdateDeviceInfoBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UpdateDeviceInfoBody.kt index f2ea24a960..8ae373ba8c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UpdateDeviceInfoBody.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/UpdateDeviceInfoBody.kt @@ -25,5 +25,5 @@ internal data class UpdateDeviceInfoBody( * The new display name for this device. If not given, the display name is unchanged. */ @Json(name = "display_name") - var displayName: String? = null + val displayName: String? = null ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt index 3a12df2cd7..7262eb5bb1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt @@ -20,6 +20,7 @@ package im.vector.matrix.android.internal.crypto.store import androidx.lifecycle.LiveData import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo import im.vector.matrix.android.api.util.Optional +import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequestCommon import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest import im.vector.matrix.android.internal.crypto.NewSessionListener import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest @@ -382,7 +383,7 @@ internal interface IMXCryptoStore { * * @param incomingRoomKeyRequest the incoming key request */ - fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest) + fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequestCommon) /** * Search an IncomingRoomKeyRequest diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt index 00a496cae4..8552868252 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt @@ -23,6 +23,7 @@ import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo import im.vector.matrix.android.api.util.Optional import im.vector.matrix.android.api.util.toOptional +import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequestCommon import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest import im.vector.matrix.android.internal.crypto.NewSessionListener import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest @@ -888,7 +889,7 @@ internal class RealmCryptoStore @Inject constructor( } } - override fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest) { + override fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequestCommon) { doRealmTransaction(realmConfiguration) { it.where() .equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, incomingRoomKeyRequest.userId) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt index 9b1d116aa9..38cece99ac 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt @@ -32,17 +32,17 @@ internal open class IncomingRoomKeyRequestEntity( ) : RealmObject() { fun toIncomingRoomKeyRequest(): IncomingRoomKeyRequest { - return IncomingRoomKeyRequest().also { - it.requestId = requestId - it.userId = userId - it.deviceId = deviceId - it.requestBody = RoomKeyRequestBody().apply { - algorithm = requestBodyAlgorithm - roomId = requestBodyRoomId - senderKey = requestBodySenderKey - sessionId = requestBodySessionId - } - } + return IncomingRoomKeyRequest( + requestId = requestId, + userId = userId, + deviceId = deviceId, + requestBody = RoomKeyRequestBody( + algorithm = requestBodyAlgorithm, + roomId = requestBodyRoomId, + senderKey = requestBodySenderKey, + sessionId = requestBodySessionId + ) + ) } fun putRequestBody(requestBody: RoomKeyRequestBody?) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt index 3130bd2f89..86fc177f2b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt @@ -43,12 +43,12 @@ internal open class OutgoingRoomKeyRequestEntity( fun toOutgoingRoomKeyRequest(): OutgoingRoomKeyRequest { val cancellationTxnId = this.cancellationTxnId return OutgoingRoomKeyRequest( - RoomKeyRequestBody().apply { - algorithm = requestBodyAlgorithm - roomId = requestBodyRoomId - senderKey = requestBodySenderKey - sessionId = requestBodySessionId - }, + RoomKeyRequestBody( + algorithm = requestBodyAlgorithm, + roomId = requestBodyRoomId, + senderKey = requestBodySenderKey, + sessionId = requestBodySessionId + ), getRecipients()!!, requestId!!, OutgoingRoomKeyRequest.RequestState.from(state) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt index 2e11bb1b3e..94fe3c1e8d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt @@ -29,7 +29,8 @@ internal interface DownloadKeysForUsersTask : Task?, // the up-to token - val token: String?) + val token: String? + ) } internal class DefaultDownloadKeysForUsers @Inject constructor( @@ -41,13 +42,10 @@ internal class DefaultDownloadKeysForUsers @Inject constructor( val downloadQuery = params.userIds?.associateWith { emptyMap() }.orEmpty() val body = KeysQueryBody( - deviceKeys = downloadQuery + deviceKeys = downloadQuery, + token = params.token?.takeIf { it.isNotEmpty() } ) - if (!params.token.isNullOrEmpty()) { - body.token = params.token - } - return executeRequest(eventBus) { apiCall = cryptoApi.downloadKeysForUsers(body) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/Filter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/Filter.kt index 9acdacd897..fc0472e32f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/Filter.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/Filter.kt @@ -24,13 +24,13 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) data class Filter( - @Json(name = "limit") var limit: Int? = null, - @Json(name = "senders") var senders: MutableList? = null, - @Json(name = "not_senders") var notSenders: MutableList? = null, - @Json(name = "types") var types: MutableList? = null, - @Json(name = "not_types") var notTypes: MutableList? = null, - @Json(name = "rooms") var rooms: MutableList? = null, - @Json(name = "not_rooms") var notRooms: MutableList? = null + @Json(name = "limit") val limit: Int? = null, + @Json(name = "senders") val senders: List? = null, + @Json(name = "not_senders") val notSenders: List? = null, + @Json(name = "types") val types: List? = null, + @Json(name = "not_types") val notTypes: List? = null, + @Json(name = "rooms") val rooms: List? = null, + @Json(name = "not_rooms") val notRooms: List? = null ) { fun hasData(): Boolean { return (limit != null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterBody.kt index fa66470c9b..535c66f637 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterBody.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterBody.kt @@ -26,11 +26,11 @@ import im.vector.matrix.android.internal.di.MoshiProvider */ @JsonClass(generateAdapter = true) internal data class FilterBody( - @Json(name = "event_fields") var eventFields: List? = null, - @Json(name = "event_format") var eventFormat: String? = null, - @Json(name = "presence") var presence: Filter? = null, - @Json(name = "account_data") var accountData: Filter? = null, - @Json(name = "room") var room: RoomFilter? = null + @Json(name = "event_fields") val eventFields: List? = null, + @Json(name = "event_format") val eventFormat: String? = null, + @Json(name = "presence") val presence: Filter? = null, + @Json(name = "account_data") val accountData: Filter? = null, + @Json(name = "room") val room: RoomFilter? = null ) { fun toJSONString(): String { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterFactory.kt index 86c94d3dfa..a070759de9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterFactory.kt @@ -21,32 +21,30 @@ import im.vector.matrix.android.api.session.events.model.EventType internal object FilterFactory { fun createDefaultFilterBody(): FilterBody { - val filterBody = FilterBody() - FilterUtil.enableLazyLoading(filterBody, true) - return filterBody + return FilterUtil.enableLazyLoading(FilterBody(), true) } fun createRiotFilterBody(): FilterBody { - val filterBody = FilterBody() - filterBody.room = RoomFilter().apply { - timeline = createRiotTimelineFilter() - state = createRiotStateFilter() - } - return filterBody + return FilterBody( + room = RoomFilter( + timeline = createRiotTimelineFilter(), + state = createRiotStateFilter() + ) + ) } fun createDefaultRoomFilter(): RoomEventFilter { - return RoomEventFilter().apply { - lazyLoadMembers = true - } + return RoomEventFilter( + lazyLoadMembers = true + ) } fun createRiotRoomFilter(): RoomEventFilter { - return RoomEventFilter().apply { - lazyLoadMembers = true - // TODO Enable this for optimization - // types = (listOfSupportedEventTypes + listOfSupportedStateEventTypes).toMutableList() - } + return RoomEventFilter( + lazyLoadMembers = true + // TODO Enable this for optimization + // types = (listOfSupportedEventTypes + listOfSupportedStateEventTypes).toMutableList() + ) } private fun createRiotTimelineFilter(): RoomEventFilter { @@ -57,9 +55,9 @@ internal object FilterFactory { } private fun createRiotStateFilter(): RoomEventFilter { - return RoomEventFilter().apply { - lazyLoadMembers = true - } + return RoomEventFilter( + lazyLoadMembers = true + ) } // Get only managed types by Riot diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterResponse.kt index b27ddae9d3..75e2c23da9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterResponse.kt @@ -24,5 +24,5 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) data class FilterResponse( - @Json(name = "filter_id") var filterId: String + @Json(name = "filter_id") val filterId: String ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterUtil.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterUtil.kt index d3020b3fa6..000df0d80e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterUtil.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterUtil.kt @@ -21,7 +21,6 @@ internal object FilterUtil { /** * Patch the filterBody to enable or disable the data save mode * - * * If data save mode is on, FilterBody will contains * FIXME New expected filter: * "{\"room\": {\"ephemeral\": {\"notTypes\": [\"m.typing\"]}}, \"presence\":{\"notTypes\": [\"*\"]}}" @@ -29,6 +28,7 @@ internal object FilterUtil { * @param filterBody filterBody to patch * @param useDataSaveMode true to enable data save mode */ + /* fun enableDataSaveMode(filterBody: FilterBody, useDataSaveMode: Boolean) { if (useDataSaveMode) { // Enable data save mode @@ -79,9 +79,10 @@ internal object FilterUtil { } } } + */ /** - * Patch the filterBody to enable or disable the lazy loading + * Compute a new filterBody to enable or disable the lazy loading * * * If lazy loading is on, the filterBody will looks like @@ -90,29 +91,23 @@ internal object FilterUtil { * @param filterBody filterBody to patch * @param useLazyLoading true to enable lazy loading */ - fun enableLazyLoading(filterBody: FilterBody, useLazyLoading: Boolean) { + fun enableLazyLoading(filterBody: FilterBody, useLazyLoading: Boolean): FilterBody { if (useLazyLoading) { // Enable lazy loading - if (filterBody.room == null) { - filterBody.room = RoomFilter() - } - if (filterBody.room!!.state == null) { - filterBody.room!!.state = RoomEventFilter() - } - - filterBody.room!!.state!!.lazyLoadMembers = true + return filterBody.copy( + room = filterBody.room?.copy( + state = filterBody.room.state?.copy(lazyLoadMembers = true) + ?: RoomEventFilter(lazyLoadMembers = true) + ) + ?: RoomFilter(state = RoomEventFilter(lazyLoadMembers = true)) + ) } else { - if (filterBody.room != null && filterBody.room!!.state != null) { - filterBody.room!!.state!!.lazyLoadMembers = null + val newRoomEventFilter = filterBody.room?.state?.copy(lazyLoadMembers = null)?.takeIf { it.hasData() } + val newRoomFilter = filterBody.room?.copy(state = newRoomEventFilter)?.takeIf { it.hasData() } - if (!filterBody.room!!.state!!.hasData()) { - filterBody.room!!.state = null - } - - if (!filterBody.room!!.hasData()) { - filterBody.room = null - } - } + return filterBody.copy( + room = newRoomFilter + ) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/RoomEventFilter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/RoomEventFilter.kt index ee81e399ee..9cdccc5c8b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/RoomEventFilter.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/RoomEventFilter.kt @@ -26,14 +26,14 @@ import im.vector.matrix.android.internal.di.MoshiProvider @JsonClass(generateAdapter = true) data class RoomEventFilter( @Json(name = "limit") var limit: Int? = null, - @Json(name = "not_senders") var notSenders: MutableList? = null, - @Json(name = "not_types") var notTypes: MutableList? = null, - @Json(name = "senders") var senders: MutableList? = null, - @Json(name = "types") var types: MutableList? = null, - @Json(name = "rooms") var rooms: MutableList? = null, - @Json(name = "not_rooms") var notRooms: List? = null, - @Json(name = "contains_url") var containsUrl: Boolean? = null, - @Json(name = "lazy_load_members") var lazyLoadMembers: Boolean? = null + @Json(name = "not_senders") val notSenders: List? = null, + @Json(name = "not_types") val notTypes: List? = null, + @Json(name = "senders") val senders: List? = null, + @Json(name = "types") val types: List? = null, + @Json(name = "rooms") val rooms: List? = null, + @Json(name = "not_rooms") val notRooms: List? = null, + @Json(name = "contains_url") val containsUrl: Boolean? = null, + @Json(name = "lazy_load_members") val lazyLoadMembers: Boolean? = null ) { fun toJSONString(): String { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/RoomFilter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/RoomFilter.kt index 4742bdb988..3109763570 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/RoomFilter.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/RoomFilter.kt @@ -24,13 +24,13 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) data class RoomFilter( - @Json(name = "not_rooms") var notRooms: List? = null, - @Json(name = "rooms") var rooms: List? = null, - @Json(name = "ephemeral") var ephemeral: RoomEventFilter? = null, - @Json(name = "include_leave") var includeLeave: Boolean? = null, - @Json(name = "state") var state: RoomEventFilter? = null, - @Json(name = "timeline") var timeline: RoomEventFilter? = null, - @Json(name = "account_data") var accountData: RoomEventFilter? = null + @Json(name = "not_rooms") val notRooms: List? = null, + @Json(name = "rooms") val rooms: List? = null, + @Json(name = "ephemeral") val ephemeral: RoomEventFilter? = null, + @Json(name = "include_leave") val includeLeave: Boolean? = null, + @Json(name = "state") val state: RoomEventFilter? = null, + @Json(name = "timeline") val timeline: RoomEventFilter? = null, + @Json(name = "account_data") val accountData: RoomEventFilter? = null ) { fun hasData(): Boolean { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/GroupSyncProfile.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/GroupSyncProfile.kt index 6d31e84d61..00e3377aa9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/GroupSyncProfile.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/GroupSyncProfile.kt @@ -24,10 +24,10 @@ internal data class GroupSyncProfile( /** * The name of the group, if any. May be nil. */ - @Json(name = "name") var name: String? = null, + @Json(name = "name") val name: String? = null, /** * The URL for the group's avatar. May be nil. */ - @Json(name = "avatar_url") var avatarUrl: String? = null + @Json(name = "avatar_url") val avatarUrl: String? = null ) From d167ff94969b4a7f6ca3eae7213e7e31d8a9996b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Feb 2020 17:58:04 +0100 Subject: [PATCH 094/187] Make KeysBackup test pass (still work to do) --- .../matrix/android/common/CryptoTestHelper.kt | 109 ++--- .../crypto/keysbackup/KeysBackupTest.kt | 463 ++++++------------ .../api/session/room/timeline/Timeline.kt | 4 +- .../session/room/timeline/TimelineService.kt | 3 + 4 files changed, 184 insertions(+), 395 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt index 765fd65db7..46aeb2a926 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt @@ -41,16 +41,15 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue -import java.util.Arrays import java.util.HashMap import java.util.concurrent.CountDownLatch -class CryptoTestHelper(val mTestHelper: CommonTestHelper) { +class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { - val messagesFromAlice: List = Arrays.asList("0 - Hello I'm Alice!", "4 - Go!") - val messagesFromBob: List = Arrays.asList("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.") + private val messagesFromAlice: List = listOf("0 - Hello I'm Alice!", "4 - Go!") + private val messagesFromBob: List = listOf("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.") - val defaultSessionParams = SessionTestParams(true) + private val defaultSessionParams = SessionTestParams(true) /** * @return alice session @@ -58,34 +57,23 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) { fun doE2ETestWithAliceInARoom(): CryptoTestData { val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) - var roomId: String? = null - val lock1 = CountDownLatch(1) + val roomId = mTestHelper.doSync { + aliceSession.createRoom(CreateRoomParams(name = "MyRoom"), it) + } - aliceSession.createRoom(CreateRoomParams(name = "MyRoom"), object : TestMatrixCallback(lock1) { - override fun onSuccess(data: String) { - roomId = data - super.onSuccess(data) - } - }) + val room = aliceSession.getRoom(roomId)!! - mTestHelper.await(lock1) - assertNotNull(roomId) + mTestHelper.doSync { + room.enableEncryption(callback = it) + } - val room = aliceSession.getRoom(roomId!!)!! - - val lock2 = CountDownLatch(1) - room.enableEncryption(callback = TestMatrixCallback(lock2)) - mTestHelper.await(lock2) - - return CryptoTestData(aliceSession, roomId!!) + return CryptoTestData(aliceSession, roomId) } /** * @return alice and bob sessions */ fun doE2ETestWithAliceAndBobInARoom(): CryptoTestData { - val statuses = HashMap() - val cryptoTestData = doE2ETestWithAliceInARoom() val aliceSession = cryptoTestData.firstSession val aliceRoomId = cryptoTestData.roomId @@ -94,7 +82,7 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) { val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams) - val lock1 = CountDownLatch(2) + val lock1 = CountDownLatch(1) val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) @@ -103,7 +91,6 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) { val newRoomObserver = object : Observer> { override fun onChanged(t: List?) { if (t?.isNotEmpty() == true) { - statuses["onNewRoom"] = "onNewRoom" lock1.countDown() bobRoomSummariesLive.removeObserver(this) } @@ -114,26 +101,20 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) { bobRoomSummariesLive.observeForever(newRoomObserver) } - aliceRoom.invite(bobSession.myUserId, callback = object : TestMatrixCallback(lock1) { - override fun onSuccess(data: Unit) { - statuses["invite"] = "invite" - super.onSuccess(data) - } - }) + mTestHelper.doSync { + aliceRoom.invite(bobSession.myUserId, callback = it) + } mTestHelper.await(lock1) - assertTrue(statuses.containsKey("invite") && statuses.containsKey("onNewRoom")) - - val lock2 = CountDownLatch(2) + val lock = CountDownLatch(1) val roomJoinedObserver = object : Observer> { override fun onChanged(t: List?) { if (bobSession.getRoom(aliceRoomId) ?.getRoomMember(aliceSession.myUserId) ?.membership == Membership.JOIN) { - statuses["AliceJoin"] = "AliceJoin" - lock2.countDown() + lock.countDown() bobRoomSummariesLive.removeObserver(this) } } @@ -143,19 +124,15 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) { bobRoomSummariesLive.observeForever(roomJoinedObserver) } - bobSession.joinRoom(aliceRoomId, callback = TestMatrixCallback(lock2)) + mTestHelper.doSync { bobSession.joinRoom(aliceRoomId, callback = it) } - mTestHelper.await(lock2) + mTestHelper.await(lock) // Ensure bob can send messages to the room // val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! // assertNotNull(roomFromBobPOV.powerLevels) // assertTrue(roomFromBobPOV.powerLevels.maySendMessage(bobSession.myUserId)) - assertTrue(statuses.toString() + "", statuses.containsKey("AliceJoin")) - -// bobSession.dataHandler.removeListener(bobEventListener) - return CryptoTestData(aliceSession, aliceRoomId, bobSession) } @@ -237,7 +214,7 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) { val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! - var lock = CountDownLatch(1) + val lock = CountDownLatch(1) val bobEventsListener = object : Timeline.Listener { override fun onTimelineFailure(throwable: Throwable) { @@ -249,63 +226,35 @@ class CryptoTestHelper(val mTestHelper: CommonTestHelper) { } override fun onTimelineUpdated(snapshot: List) { - val size = snapshot.filter { it.root.senderId != bobSession.myUserId && it.root.getClearType() == EventType.MESSAGE } - .size + val messages = snapshot.filter { it.root.getClearType() == EventType.MESSAGE } + .groupBy { it.root.senderId!! } - if (size == 3) { + // Alice has sent 2 messages and Bob has sent 3 messages + if (messages[aliceSession.myUserId]?.size == 2 && messages[bobSession.myUserId]?.size == 3) { lock.countDown() } } } val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(10)) + bobTimeline.start() bobTimeline.addListener(bobEventsListener) - val results = HashMap() - - // bobSession.dataHandler.addListener(object : MXEventListener() { - // override fun onToDeviceEvent(event: Event) { - // results["onToDeviceEvent"] = event - // lock.countDown() - // } - // }) - // Alice sends a message roomFromAlicePOV.sendTextMessage(messagesFromAlice[0]) - assertTrue(results.containsKey("onToDeviceEvent")) -// assertEquals(1, messagesReceivedByBobCount) - // Bob send a message - lock = CountDownLatch(1) + // Bob send 3 messages roomFromBobPOV.sendTextMessage(messagesFromBob[0]) - // android does not echo the messages sent from itself -// messagesReceivedByBobCount++ - mTestHelper.await(lock) -// assertEquals(2, messagesReceivedByBobCount) - - // Bob send a message - lock = CountDownLatch(1) roomFromBobPOV.sendTextMessage(messagesFromBob[1]) - // android does not echo the messages sent from itself -// messagesReceivedByBobCount++ - mTestHelper.await(lock) -// assertEquals(3, messagesReceivedByBobCount) - - // Bob send a message - lock = CountDownLatch(1) roomFromBobPOV.sendTextMessage(messagesFromBob[2]) - // android does not echo the messages sent from itself -// messagesReceivedByBobCount++ - mTestHelper.await(lock) -// assertEquals(4, messagesReceivedByBobCount) // Alice sends a message - lock = CountDownLatch(2) roomFromAlicePOV.sendTextMessage(messagesFromAlice[1]) + mTestHelper.await(lock) -// assertEquals(5, messagesReceivedByBobCount) bobTimeline.removeListener(bobEventsListener) + bobTimeline.dispose() return cryptoTestData } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt index 2edc0c3976..236fc24345 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.keysbackup import androidx.test.ext.junit.runners.AndroidJUnit4 import im.vector.matrix.android.InstrumentedTest -import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.listeners.ProgressListener import im.vector.matrix.android.api.listeners.StepProgressListener import im.vector.matrix.android.api.session.Session @@ -48,7 +47,6 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue -import org.junit.Assert.fail import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -58,7 +56,7 @@ import java.util.Collections import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FixMethodOrder(MethodSorters.JVM) class KeysBackupTest : InstrumentedTest { private val mTestHelper = CommonTestHelper(context()) @@ -120,28 +118,15 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) - val latch = CountDownLatch(1) + val megolmBackupCreationInfo = mTestHelper.doSync { + keysBackup.prepareKeysBackupVersion(null, null, it) + } - keysBackup.prepareKeysBackupVersion(null, null, object : MatrixCallback { - override fun onSuccess(data: MegolmBackupCreationInfo) { - assertNotNull(data) - - assertEquals(MXCRYPTO_ALGORITHM_MEGOLM_BACKUP, data.algorithm) - assertNotNull(data.authData) - assertNotNull(data.authData!!.publicKey) - assertNotNull(data.authData!!.signatures) - assertNotNull(data.recoveryKey) - - latch.countDown() - } - - override fun onFailure(failure: Throwable) { - fail(failure.localizedMessage) - - latch.countDown() - } - }) - mTestHelper.await(latch) + assertEquals(MXCRYPTO_ALGORITHM_MEGOLM_BACKUP, megolmBackupCreationInfo.algorithm) + assertNotNull(megolmBackupCreationInfo.authData) + assertNotNull(megolmBackupCreationInfo.authData!!.publicKey) + assertNotNull(megolmBackupCreationInfo.authData!!.signatures) + assertNotNull(megolmBackupCreationInfo.recoveryKey) stateObserver.stopAndCheckStates(null) bobSession.close() @@ -160,39 +145,16 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) - var megolmBackupCreationInfo: MegolmBackupCreationInfo? = null - val latch = CountDownLatch(1) - keysBackup.prepareKeysBackupVersion(null, null, object : MatrixCallback { - override fun onSuccess(data: MegolmBackupCreationInfo) { - megolmBackupCreationInfo = data - - latch.countDown() - } - - override fun onFailure(failure: Throwable) { - fail(failure.localizedMessage) - - latch.countDown() - } - }) - mTestHelper.await(latch) - - assertNotNull(megolmBackupCreationInfo) + val megolmBackupCreationInfo = mTestHelper.doSync { + keysBackup.prepareKeysBackupVersion(null, null, it) + } assertFalse(keysBackup.isEnabled) - val latch2 = CountDownLatch(1) - // Create the version - keysBackup.createKeysBackupVersion(megolmBackupCreationInfo!!, object : TestMatrixCallback(latch2) { - override fun onSuccess(data: KeysVersion) { - assertNotNull(data) - assertNotNull(data.version) - - super.onSuccess(data) - } - }) - mTestHelper.await(latch2) + mTestHelper.doSync { + keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it) + } // Backup must be enable now assertTrue(keysBackup.isEnabled) @@ -259,18 +221,17 @@ class KeysBackupTest : InstrumentedTest { assertEquals(2, nbOfKeys) - val latch = CountDownLatch(1) - var lastBackedUpKeysProgress = 0 - keysBackup.backupAllGroupSessions(object : ProgressListener { - override fun onProgress(progress: Int, total: Int) { - assertEquals(nbOfKeys, total) - lastBackedUpKeysProgress = progress - } - }, TestMatrixCallback(latch)) + mTestHelper.doSync { + keysBackup.backupAllGroupSessions(object : ProgressListener { + override fun onProgress(progress: Int, total: Int) { + assertEquals(nbOfKeys, total) + lastBackedUpKeysProgress = progress + } + }, it) + } - mTestHelper.await(latch) assertEquals(nbOfKeys, lastBackedUpKeysProgress) val backedUpKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true) @@ -335,23 +296,17 @@ class KeysBackupTest : InstrumentedTest { val testData = createKeysBackupScenarioWithPassword(null) // - Restore the e2e backup from the homeserver - val latch2 = CountDownLatch(1) - var importRoomKeysResult: ImportRoomKeysResult? = null - testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, - testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, - null, - null, - null, - object : TestMatrixCallback(latch2) { - override fun onSuccess(data: ImportRoomKeysResult) { - importRoomKeysResult = data - super.onSuccess(data) - } - } - ) - mTestHelper.await(latch2) + val importRoomKeysResult = mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, + testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, + null, + null, + null, + it + ) + } - checkRestoreSuccess(testData, importRoomKeysResult!!.totalNumberOfKeys, importRoomKeysResult!!.successfullyNumberOfImportedKeys) + checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) testData.cryptoTestData.close() } @@ -383,23 +338,17 @@ class KeysBackupTest : InstrumentedTest { assertTrue(unsentRequest != null || sentRequest != null) // - Restore the e2e backup from the homeserver - val latch2 = CountDownLatch(1) - var importRoomKeysResult: ImportRoomKeysResult? = null - testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, - testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, - null, - null, - null, - object : TestMatrixCallback(latch2) { - override fun onSuccess(data: ImportRoomKeysResult) { - importRoomKeysResult = data - super.onSuccess(data) - } - } - ) - mTestHelper.await(latch2) + val importRoomKeysResult = mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, + testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, + null, + null, + null, + it + ) + } - checkRestoreSuccess(testData, importRoomKeysResult!!.totalNumberOfKeys, importRoomKeysResult!!.successfullyNumberOfImportedKeys) + checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) // - There must be no more pending key share requests val unsentRequestAfterRestoration = cryptoStore2 @@ -437,13 +386,13 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) // - Trust the backup from the new device - val latch = CountDownLatch(1) - testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersion( - testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, - true, - TestMatrixCallback(latch) - ) - mTestHelper.await(latch) + mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersion( + testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, + true, + it + ) + } // Wait for backup state to be ReadyToBackUp waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) @@ -453,35 +402,20 @@ class KeysBackupTest : InstrumentedTest { assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val latch2 = CountDownLatch(1) - var keysVersionResult: KeysVersionResult? = null - testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion( - object : TestMatrixCallback(latch2) { - override fun onSuccess(data: KeysVersionResult?) { - keysVersionResult = data - super.onSuccess(data) - } - } - ) - mTestHelper.await(latch2) + val keysVersionResult = mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) + } // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) - val latch3 = CountDownLatch(1) - var keysBackupVersionTrust: KeysBackupVersionTrust? = null - testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult!!, - object : TestMatrixCallback(latch3) { - override fun onSuccess(data: KeysBackupVersionTrust) { - keysBackupVersionTrust = data - super.onSuccess(data) - } - }) - mTestHelper.await(latch3) + val keysBackupVersionTrust = mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) + } // - It must be trusted and must have 2 signatures now - assertTrue(keysBackupVersionTrust!!.usable) - assertEquals(2, keysBackupVersionTrust!!.signatures.size) + assertTrue(keysBackupVersionTrust.usable) + assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) testData.cryptoTestData.close() @@ -511,13 +445,13 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) // - Trust the backup from the new device with the recovery key - val latch = CountDownLatch(1) - testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey( - testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, - testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, - TestMatrixCallback(latch) - ) - mTestHelper.await(latch) + mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey( + testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, + testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, + it + ) + } // Wait for backup state to be ReadyToBackUp waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) @@ -527,35 +461,20 @@ class KeysBackupTest : InstrumentedTest { assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val latch2 = CountDownLatch(1) - var keysVersionResult: KeysVersionResult? = null - testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion( - object : TestMatrixCallback(latch2) { - override fun onSuccess(data: KeysVersionResult?) { - keysVersionResult = data - super.onSuccess(data) - } - } - ) - mTestHelper.await(latch2) + val keysVersionResult = mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) + } // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) - val latch3 = CountDownLatch(1) - var keysBackupVersionTrust: KeysBackupVersionTrust? = null - testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult!!, - object : TestMatrixCallback(latch3) { - override fun onSuccess(data: KeysBackupVersionTrust) { - keysBackupVersionTrust = data - super.onSuccess(data) - } - }) - mTestHelper.await(latch3) + val keysBackupVersionTrust = mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) + } // - It must be trusted and must have 2 signatures now - assertTrue(keysBackupVersionTrust!!.usable) - assertEquals(2, keysBackupVersionTrust!!.signatures.size) + assertTrue(keysBackupVersionTrust.usable) + assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) testData.cryptoTestData.close() @@ -626,13 +545,13 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) // - Trust the backup from the new device with the password - val latch = CountDownLatch(1) - testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithPassphrase( - testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, - password, - TestMatrixCallback(latch) - ) - mTestHelper.await(latch) + mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithPassphrase( + testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, + password, + it + ) + } // Wait for backup state to be ReadyToBackUp waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) @@ -642,35 +561,20 @@ class KeysBackupTest : InstrumentedTest { assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val latch2 = CountDownLatch(1) - var keysVersionResult: KeysVersionResult? = null - testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion( - object : TestMatrixCallback(latch2) { - override fun onSuccess(data: KeysVersionResult?) { - keysVersionResult = data - super.onSuccess(data) - } - } - ) - mTestHelper.await(latch2) + val keysVersionResult = mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) + } // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) - val latch3 = CountDownLatch(1) - var keysBackupVersionTrust: KeysBackupVersionTrust? = null - testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult!!, - object : TestMatrixCallback(latch3) { - override fun onSuccess(data: KeysBackupVersionTrust) { - keysBackupVersionTrust = data - super.onSuccess(data) - } - }) - mTestHelper.await(latch3) + val keysBackupVersionTrust = mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) + } // - It must be trusted and must have 2 signatures now - assertTrue(keysBackupVersionTrust!!.usable) - assertEquals(2, keysBackupVersionTrust!!.signatures.size) + assertTrue(keysBackupVersionTrust.usable) + assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) testData.cryptoTestData.close() @@ -764,27 +668,21 @@ class KeysBackupTest : InstrumentedTest { val testData = createKeysBackupScenarioWithPassword(password) // - Restore the e2e backup with the password - val latch2 = CountDownLatch(1) - var importRoomKeysResult: ImportRoomKeysResult? = null val steps = ArrayList() - testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, - password, - null, - null, - object : StepProgressListener { - override fun onStepProgress(step: StepProgressListener.Step) { - steps.add(step) - } - }, - object : TestMatrixCallback(latch2) { - override fun onSuccess(data: ImportRoomKeysResult) { - importRoomKeysResult = data - super.onSuccess(data) - } - } - ) - mTestHelper.await(latch2) + val importRoomKeysResult = mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, + password, + null, + null, + object : StepProgressListener { + override fun onStepProgress(step: StepProgressListener.Step) { + steps.add(step) + } + }, + it + ) + } // Check steps assertEquals(105, steps.size) @@ -807,7 +705,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(50, (steps[103] as StepProgressListener.Step.ImportingKey).progress) assertEquals(100, (steps[104] as StepProgressListener.Step.ImportingKey).progress) - checkRestoreSuccess(testData, importRoomKeysResult!!.totalNumberOfKeys, importRoomKeysResult!!.successfullyNumberOfImportedKeys) + checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) testData.cryptoTestData.close() } @@ -861,23 +759,17 @@ class KeysBackupTest : InstrumentedTest { val testData = createKeysBackupScenarioWithPassword(password) // - Restore the e2e backup with the recovery key. - val latch2 = CountDownLatch(1) - var importRoomKeysResult: ImportRoomKeysResult? = null - testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, - testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, - null, - null, - null, - object : TestMatrixCallback(latch2) { - override fun onSuccess(data: ImportRoomKeysResult) { - importRoomKeysResult = data - super.onSuccess(data) - } - } - ) - mTestHelper.await(latch2) + val importRoomKeysResult = mTestHelper.doSync { + testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, + testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, + null, + null, + null, + it + ) + } - checkRestoreSuccess(testData, importRoomKeysResult!!.totalNumberOfKeys, importRoomKeysResult!!.successfullyNumberOfImportedKeys) + checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) testData.cryptoTestData.close() } @@ -932,39 +824,20 @@ class KeysBackupTest : InstrumentedTest { prepareAndCreateKeysBackupData(keysBackup) // Get key backup version from the home server - var keysVersionResult: KeysVersionResult? = null - val lock = CountDownLatch(1) - keysBackup.getCurrentVersion(object : TestMatrixCallback(lock) { - override fun onSuccess(data: KeysVersionResult?) { - keysVersionResult = data - super.onSuccess(data) - } - }) - mTestHelper.await(lock) - - assertNotNull(keysVersionResult) + val keysVersionResult = mTestHelper.doSync { + keysBackup.getCurrentVersion(it) + } // - Check the returned KeyBackupVersion is trusted - val latch = CountDownLatch(1) - var keysBackupVersionTrust: KeysBackupVersionTrust? = null - keysBackup.getKeysBackupTrust(keysVersionResult!!, object : MatrixCallback { - override fun onSuccess(data: KeysBackupVersionTrust) { - keysBackupVersionTrust = data - latch.countDown() - } - - override fun onFailure(failure: Throwable) { - super.onFailure(failure) - latch.countDown() - } - }) - mTestHelper.await(latch) + val keysBackupVersionTrust = mTestHelper.doSync { + keysBackup.getKeysBackupTrust(keysVersionResult!!, it) + } assertNotNull(keysBackupVersionTrust) - assertTrue(keysBackupVersionTrust!!.usable) - assertEquals(1, keysBackupVersionTrust!!.signatures.size) + assertTrue(keysBackupVersionTrust.usable) + assertEquals(1, keysBackupVersionTrust.signatures.size) - val signature = keysBackupVersionTrust!!.signatures[0] + val signature = keysBackupVersionTrust.signatures[0] assertTrue(signature.valid) assertNotNull(signature.device) assertEquals(cryptoTestData.firstSession.cryptoService().getMyDevice().deviceId, signature.deviceId) @@ -1079,21 +952,17 @@ class KeysBackupTest : InstrumentedTest { mTestHelper.await(latch0) // - Create a new backup with fake data on the homeserver, directly using the rest client - val latch = CountDownLatch(1) - val megolmBackupCreationInfo = mCryptoTestHelper.createFakeMegolmBackupCreationInfo() - (keysBackup as DefaultKeysBackupService).createFakeKeysBackupVersion(megolmBackupCreationInfo, TestMatrixCallback(latch)) - mTestHelper.await(latch) + mTestHelper.doSync { + (keysBackup as DefaultKeysBackupService).createFakeKeysBackupVersion(megolmBackupCreationInfo, it) + } // Reset the store backup status for keys (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store.resetBackupMarkers() // - Make alice back up all her keys again val latch2 = CountDownLatch(1) - keysBackup.backupAllGroupSessions(object : ProgressListener { - override fun onProgress(progress: Int, total: Int) { - } - }, TestMatrixCallback(latch2, false)) + keysBackup.backupAllGroupSessions(null, TestMatrixCallback(latch2, false)) mTestHelper.await(latch2) // -> That must fail and her backup state must be WrongBackUpVersion @@ -1129,12 +998,9 @@ class KeysBackupTest : InstrumentedTest { prepareAndCreateKeysBackupData(keysBackup) // Wait for keys backup to finish by asking again to backup keys. - val latch = CountDownLatch(1) - keysBackup.backupAllGroupSessions(object : ProgressListener { - override fun onProgress(progress: Int, total: Int) { - } - }, TestMatrixCallback(latch)) - mTestHelper.await(latch) + mTestHelper.doSync { + keysBackup.backupAllGroupSessions(null, it) + } val oldDeviceId = cryptoTestData.firstSession.sessionParams.credentials.deviceId!! val oldKeyBackupVersion = keysBackup.currentBackupVersion @@ -1178,7 +1044,7 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup2.isEnabled) // - Validate the old device from the new one - aliceSession2.cryptoService().setDeviceVerification(DeviceTrustLevel(false, true), aliceSession2.myUserId, oldDeviceId) + aliceSession2.cryptoService().setDeviceVerification(DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true), aliceSession2.myUserId, oldDeviceId) // -> Backup should automatically enable on the new device val latch4 = CountDownLatch(1) @@ -1198,9 +1064,9 @@ class KeysBackupTest : InstrumentedTest { // -> It must use the same backup version assertEquals(oldKeyBackupVersion, aliceSession2.cryptoService().keysBackupService().currentBackupVersion) - val latch5 = CountDownLatch(1) - aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, TestMatrixCallback(latch5)) - mTestHelper.await(latch5) + mTestHelper.doSync { + aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, it) + } // -> It must success assertTrue(aliceSession2.cryptoService().keysBackupService().isEnabled) @@ -1230,12 +1096,8 @@ class KeysBackupTest : InstrumentedTest { assertTrue(keysBackup.isEnabled) - val latch = CountDownLatch(1) - // Delete the backup - keysBackup.deleteBackup(keyBackupCreationInfo.version, TestMatrixCallback(latch)) - - mTestHelper.await(latch) + mTestHelper.doSync { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) } // Backup is now disabled assertFalse(keysBackup.isEnabled) @@ -1280,49 +1142,26 @@ class KeysBackupTest : InstrumentedTest { password: String? = null): PrepareKeysBackupDataResult { val stateObserver = StateObserver(keysBackup) - var megolmBackupCreationInfo: MegolmBackupCreationInfo? = null - val latch = CountDownLatch(1) - keysBackup.prepareKeysBackupVersion(password, null, object : MatrixCallback { - override fun onSuccess(data: MegolmBackupCreationInfo) { - megolmBackupCreationInfo = data - - latch.countDown() - } - - override fun onFailure(failure: Throwable) { - fail(failure.localizedMessage) - - latch.countDown() - } - }) - mTestHelper.await(latch) + val megolmBackupCreationInfo = mTestHelper.doSync { + keysBackup.prepareKeysBackupVersion(password, null, it) + } assertNotNull(megolmBackupCreationInfo) assertFalse(keysBackup.isEnabled) - val latch2 = CountDownLatch(1) - // Create the version - var version: String? = null - keysBackup.createKeysBackupVersion(megolmBackupCreationInfo!!, object : TestMatrixCallback(latch2) { - override fun onSuccess(data: KeysVersion) { - assertNotNull(data) - assertNotNull(data.version) + val keysVersion = mTestHelper.doSync { + keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it) + } - version = data.version - - super.onSuccess(data) - } - }) - mTestHelper.await(latch2) + assertNotNull(keysVersion.version) // Backup must be enable now assertTrue(keysBackup.isEnabled) - assertNotNull(version) stateObserver.stopAndCheckStates(null) - return PrepareKeysBackupDataResult(megolmBackupCreationInfo!!, version!!) + return PrepareKeysBackupDataResult(megolmBackupCreationInfo, keysVersion.version!!) } private fun assertKeysEquals(keys1: MegolmSessionData?, keys2: MegolmSessionData?) { @@ -1369,16 +1208,16 @@ class KeysBackupTest : InstrumentedTest { // - Do an e2e backup to the homeserver val prepareKeysBackupDataResult = prepareAndCreateKeysBackupData(keysBackup, password) - val latch = CountDownLatch(1) var lastProgress = 0 var lastTotal = 0 - keysBackup.backupAllGroupSessions(object : ProgressListener { - override fun onProgress(progress: Int, total: Int) { - lastProgress = progress - lastTotal = total - } - }, TestMatrixCallback(latch)) - mTestHelper.await(latch) + mTestHelper.doSync { + keysBackup.backupAllGroupSessions(object : ProgressListener { + override fun onProgress(progress: Int, total: Int) { + lastProgress = progress + lastTotal = total + } + }, it) + } assertEquals(2, lastProgress) assertEquals(2, lastTotal) @@ -1386,9 +1225,7 @@ class KeysBackupTest : InstrumentedTest { val aliceUserId = cryptoTestData.firstSession.myUserId // Logout first Alice session, else they will share the same Crypto store and some tests may fail. - val latch2 = CountDownLatch(1) - cryptoTestData.firstSession.signOut(true, TestMatrixCallback(latch2)) - mTestHelper.await(latch2) + mTestHelper.doSync { cryptoTestData.firstSession.signOut(true, it) } // - Log Alice on a new device val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, defaultSessionParamsWithInitialSync) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/Timeline.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/Timeline.kt index 164afb3a60..eb4a9b59e4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/Timeline.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/Timeline.kt @@ -41,12 +41,12 @@ interface Timeline { fun removeAllListeners() /** - * This should be called before any other method after creating the timeline. It ensures the underlying database is open + * This must be called before any other method after creating the timeline. It ensures the underlying database is open */ fun start() /** - * This should be called when you don't need the timeline. It ensures the underlying database get closed. + * This must be called when you don't need the timeline. It ensures the underlying database get closed. */ fun dispose() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt index 2b23ee40ca..a69127532e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt @@ -27,6 +27,9 @@ interface TimelineService { /** * Instantiate a [Timeline] with an optional initial eventId, to be used with permalink. * You can also configure some settings with the [settings] param. + * + * Important: the returned Timeline has to be started + * * @param eventId the optional initial eventId. * @param settings settings to configure the timeline. * @return the instantiated timeline From 9ef267cb07155606bbb8c0b061ca091101c50970 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Feb 2020 18:36:13 +0100 Subject: [PATCH 095/187] Cleanup after each test --- .../android/account/AccountCreationTest.kt | 10 ++-- .../matrix/android/common/CommonTestHelper.kt | 9 ++- .../matrix/android/common/CryptoTestData.kt | 8 +-- .../crypto/crosssigning/XSigningTest.kt | 20 +++---- .../crypto/keysbackup/KeysBackupTest.kt | 55 ++++++++++--------- .../internal/crypto/ssss/QuadSTests.kt | 10 ++-- .../internal/crypto/verification/SASTest.kt | 16 +++--- .../verification/qrcode/VerificationTest.kt | 2 +- 8 files changed, 64 insertions(+), 66 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/account/AccountCreationTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/account/AccountCreationTest.kt index c44ac9c47b..679ea8f3fe 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/account/AccountCreationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/account/AccountCreationTest.kt @@ -38,9 +38,7 @@ class AccountCreationTest : InstrumentedTest { fun createAccountTest() { val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true)) - commonTestHelper.signout(session) - - session.close() + commonTestHelper.signOutAndClose(session) } @Test @@ -50,14 +48,14 @@ class AccountCreationTest : InstrumentedTest { // Log again to the same account val session2 = commonTestHelper.logIntoAccount(session.myUserId, SessionTestParams(withInitialSync = true)) - session.close() - session2.close() + commonTestHelper.signOutAndClose(session) + commonTestHelper.signOutAndClose(session2) } @Test fun simpleE2eTest() { val res = cryptoTestHelper.doE2ETestWithAliceInARoom() - res.close() + res.cleanUp(commonTestHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt index 2e18133071..44770f3378 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt @@ -283,11 +283,10 @@ class CommonTestHelper(context: Context) { /** * Clear all provided sessions */ - fun Iterable.close() = forEach { it.close() } + fun Iterable.signOutAndClose() = forEach { signOutAndClose(it) } - fun signout(session: Session) { - val lock = CountDownLatch(1) - session.signOut(true, TestMatrixCallback(lock)) - await(lock) + fun signOutAndClose(session: Session) { + doSync { session.signOut(true, it) } + session.close() } } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestData.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestData.kt index 8ad9f1ec6f..7eea832160 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestData.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestData.kt @@ -23,9 +23,9 @@ data class CryptoTestData(val firstSession: Session, val secondSession: Session? = null, val thirdSession: Session? = null) { - fun close() { - firstSession.close() - secondSession?.close() - secondSession?.close() + fun cleanUp(testHelper: CommonTestHelper) { + testHelper.signOutAndClose(firstSession) + secondSession?.let { testHelper.signOutAndClose(it) } + thirdSession?.let { testHelper.signOutAndClose(it) } } } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt index 069d32acaf..5681d3e8ec 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt @@ -55,7 +55,7 @@ class XSigningTest : InstrumentedTest { assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified()) - mTestHelper.signout(aliceSession) + mTestHelper.signOutAndClose(aliceSession) } @Test @@ -74,12 +74,8 @@ class XSigningTest : InstrumentedTest { password = TestConstants.PASSWORD ) - val latch = CountDownLatch(2) - - aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, TestMatrixCallback(latch)) - bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, TestMatrixCallback(latch)) - - mTestHelper.await(latch) + mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, it) } + mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, it) } // Check that alice can see bob keys val downloadLatch = CountDownLatch(1) @@ -96,8 +92,8 @@ class XSigningTest : InstrumentedTest { assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted()) - mTestHelper.signout(aliceSession) - mTestHelper.signout(bobSession) + mTestHelper.signOutAndClose(aliceSession) + mTestHelper.signOutAndClose(bobSession) } @Test @@ -202,8 +198,8 @@ class XSigningTest : InstrumentedTest { val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null) assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified()) - mTestHelper.signout(aliceSession) - mTestHelper.signout(bobSession) - mTestHelper.signout(bobSession2) + mTestHelper.signOutAndClose(aliceSession) + mTestHelper.signOutAndClose(bobSession) + mTestHelper.signOutAndClose(bobSession2) } } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt index 236fc24345..b039522e0c 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt @@ -129,7 +129,7 @@ class KeysBackupTest : InstrumentedTest { assertNotNull(megolmBackupCreationInfo.recoveryKey) stateObserver.stopAndCheckStates(null) - bobSession.close() + mTestHelper.signOutAndClose(bobSession) } /** @@ -160,7 +160,7 @@ class KeysBackupTest : InstrumentedTest { assertTrue(keysBackup.isEnabled) stateObserver.stopAndCheckStates(null) - bobSession.close() + mTestHelper.signOutAndClose(bobSession) } /** @@ -200,7 +200,7 @@ class KeysBackupTest : InstrumentedTest { KeysBackupState.ReadyToBackUp ) ) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } /** @@ -239,7 +239,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys) stateObserver.stopAndCheckStates(null) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } /** @@ -282,7 +282,7 @@ class KeysBackupTest : InstrumentedTest { assertKeysEquals(session.exportKeys(), sessionData) stateObserver.stopAndCheckStates(null) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } /** @@ -308,7 +308,7 @@ class KeysBackupTest : InstrumentedTest { checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -359,7 +359,7 @@ class KeysBackupTest : InstrumentedTest { // Request is either sent or unsent assertTrue(unsentRequestAfterRestoration == null && sentRequestAfterRestoration == null) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -418,7 +418,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -477,7 +477,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -516,7 +516,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) stateObserver.stopAndCheckStates(null) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -577,7 +577,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -619,7 +619,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) stateObserver.stopAndCheckStates(null) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -652,7 +652,7 @@ class KeysBackupTest : InstrumentedTest { // onSuccess may not have been called assertNull(importRoomKeysResult) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -707,7 +707,7 @@ class KeysBackupTest : InstrumentedTest { checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -743,7 +743,7 @@ class KeysBackupTest : InstrumentedTest { // onSuccess may not have been called assertNull(importRoomKeysResult) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -771,7 +771,7 @@ class KeysBackupTest : InstrumentedTest { checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -804,7 +804,7 @@ class KeysBackupTest : InstrumentedTest { // onSuccess may not have been called assertNull(importRoomKeysResult) - testData.cryptoTestData.close() + testData.cleanUp(mTestHelper) } /** @@ -844,7 +844,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.credentials.deviceId) stateObserver.stopAndCheckStates(null) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } /** @@ -873,7 +873,7 @@ class KeysBackupTest : InstrumentedTest { // - Log Alice on a new device val aliceSession2 = mTestHelper.logIntoAccount(cryptoTestData.firstSession.myUserId, defaultSessionParamsWithInitialSync) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) val keysBackup2 = aliceSession2.cryptoService().keysBackupService() @@ -903,7 +903,7 @@ class KeysBackupTest : InstrumentedTest { stateObserver.stopAndCheckStates(null) stateObserver2.stopAndCheckStates(null) - aliceSession2.close() + mTestHelper.signOutAndClose(aliceSession2) } /** @@ -970,7 +970,7 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) stateObserver.stopAndCheckStates(null) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } /** @@ -1073,8 +1073,8 @@ class KeysBackupTest : InstrumentedTest { stateObserver.stopAndCheckStates(null) stateObserver2.stopAndCheckStates(null) - aliceSession2.close() - cryptoTestData.close() + mTestHelper.signOutAndClose(aliceSession2) + cryptoTestData.cleanUp(mTestHelper) } /** @@ -1103,7 +1103,7 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) stateObserver.stopAndCheckStates(null) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } /* ========================================================================================== @@ -1186,7 +1186,12 @@ class KeysBackupTest : InstrumentedTest { private data class KeysBackupScenarioData(val cryptoTestData: CryptoTestData, val aliceKeys: List, val prepareKeysBackupDataResult: PrepareKeysBackupDataResult, - val aliceSession2: Session) + val aliceSession2: Session) { + fun cleanUp(testHelper: CommonTestHelper) { + cryptoTestData.cleanUp(testHelper) + testHelper.signOutAndClose(aliceSession2) + } + } /** * Common initial condition diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt index 7802096338..8dd2109df3 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/ssss/QuadSTests.kt @@ -128,7 +128,7 @@ class QuadSTests : InstrumentedTest { assertNotNull(defaultKeyAccountData?.content) assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key")) - mTestHelper.signout(aliceSession) + mTestHelper.signOutAndClose(aliceSession) } @Test @@ -172,7 +172,7 @@ class QuadSTests : InstrumentedTest { } assertEquals("Secret mismatch", clearSecret, decryptedSecret) - mTestHelper.signout(aliceSession) + mTestHelper.signOutAndClose(aliceSession) } @Test @@ -192,7 +192,7 @@ class QuadSTests : InstrumentedTest { quadS.setDefaultKey(TEST_KEY_ID, it) } - mTestHelper.signout(aliceSession) + mTestHelper.signOutAndClose(aliceSession) } @Test @@ -239,7 +239,7 @@ class QuadSTests : InstrumentedTest { ) } - mTestHelper.signout(aliceSession) + mTestHelper.signOutAndClose(aliceSession) } @Test @@ -298,7 +298,7 @@ class QuadSTests : InstrumentedTest { ) } - mTestHelper.signout(aliceSession) + mTestHelper.signOutAndClose(aliceSession) } private fun assertAccountData(session: Session, type: String): UserAccountDataEvent { diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt index 7fad87b82d..85fc7e1b36 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/SASTest.kt @@ -132,7 +132,7 @@ class SASTest : InstrumentedTest { assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID)) assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID)) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } @Test @@ -189,7 +189,7 @@ class SASTest : InstrumentedTest { assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } @Test @@ -227,7 +227,7 @@ class SASTest : InstrumentedTest { val cancelReq = canceledToDeviceEvent!!.content.toModel()!! assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } @Test @@ -265,7 +265,7 @@ class SASTest : InstrumentedTest { val cancelReq = canceledToDeviceEvent!!.content.toModel()!! assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } private fun fakeBobStart(bobSession: Session, @@ -334,7 +334,7 @@ class SASTest : InstrumentedTest { mTestHelper.await(aliceCreatedLatch) mTestHelper.await(aliceCancelledLatch) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } /** @@ -393,7 +393,7 @@ class SASTest : InstrumentedTest { assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings!!.contains(it)) } - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } @Test @@ -449,7 +449,7 @@ class SASTest : InstrumentedTest { assertEquals("Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL), bobTx.getShortCodeRepresentation(SasMode.DECIMAL)) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } @Test @@ -514,6 +514,6 @@ class SASTest : InstrumentedTest { assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified) assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified) - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/VerificationTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/VerificationTest.kt index d62aef077e..ae8a1ad718 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/VerificationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/VerificationTest.kt @@ -227,6 +227,6 @@ class VerificationTest : InstrumentedTest { pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode } - cryptoTestData.close() + cryptoTestData.cleanUp(mTestHelper) } } From 1b763fb4c896081ee256708fe1e598d00a110a9f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Feb 2020 18:52:36 +0100 Subject: [PATCH 096/187] Cleanup XSigningTest --- .../crypto/crosssigning/XSigningTest.kt | 103 +++++------------- 1 file changed, 30 insertions(+), 73 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt index 5681d3e8ec..57027878f9 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt @@ -2,12 +2,10 @@ package im.vector.matrix.android.internal.crypto.crosssigning import androidx.test.ext.junit.runners.AndroidJUnit4 import im.vector.matrix.android.InstrumentedTest -import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.common.CommonTestHelper import im.vector.matrix.android.common.CryptoTestHelper import im.vector.matrix.android.common.SessionTestParams import im.vector.matrix.android.common.TestConstants -import im.vector.matrix.android.common.TestMatrixCallback import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth @@ -21,7 +19,6 @@ import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters -import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -34,14 +31,13 @@ class XSigningTest : InstrumentedTest { fun test_InitializeAndStoreKeys() { val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - val aliceLatch = CountDownLatch(1) - aliceSession.cryptoService().crossSigningService() - .initializeCrossSigning(UserPasswordAuth( - user = aliceSession.myUserId, - password = TestConstants.PASSWORD - ), TestMatrixCallback(aliceLatch)) - - mTestHelper.await(aliceLatch) + mTestHelper.doSync { + aliceSession.cryptoService().crossSigningService() + .initializeCrossSigning(UserPasswordAuth( + user = aliceSession.myUserId, + password = TestConstants.PASSWORD + ), it) + } val myCrossSigningKeys = aliceSession.cryptoService().crossSigningService().getMyCrossSigningKeys() val masterPubKey = myCrossSigningKeys?.masterKey() @@ -78,9 +74,7 @@ class XSigningTest : InstrumentedTest { mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, it) } // Check that alice can see bob keys - val downloadLatch = CountDownLatch(1) - aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, TestMatrixCallback(downloadLatch)) - mTestHelper.await(downloadLatch) + mTestHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) } val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId) assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV!!.masterKey()) @@ -112,88 +106,51 @@ class XSigningTest : InstrumentedTest { password = TestConstants.PASSWORD ) - val latch = CountDownLatch(2) - - aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, TestMatrixCallback(latch)) - bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, TestMatrixCallback(latch)) - - mTestHelper.await(latch) + mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(aliceAuthParams, it) } + mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(bobAuthParams, it) } // Check that alice can see bob keys - val downloadLatch = CountDownLatch(1) val bobUserId = bobSession.myUserId - aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, TestMatrixCallback(downloadLatch)) - mTestHelper.await(downloadLatch) + mTestHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) } val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId) assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false) - val trustLatch = CountDownLatch(1) - aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, object : MatrixCallback { - override fun onSuccess(data: Unit) { - trustLatch.countDown() - } - - override fun onFailure(failure: Throwable) { - fail("Failed to trust bob") - } - }) - mTestHelper.await(trustLatch) + mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) } // Now bobs logs in on a new device and verifies it // We will want to test that in alice POV, this new device would be trusted by cross signing val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true)) - val bobSecondDeviceId = bobSession2.sessionParams.credentials.deviceId + val bobSecondDeviceId = bobSession2.sessionParams.credentials.deviceId!! // Check that bob first session sees the new login - val bobKeysLatch = CountDownLatch(1) - bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, object : MatrixCallback> { - override fun onFailure(failure: Throwable) { - fail("Failed to get device") - } + val data = mTestHelper.doSync> { + bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) + } - override fun onSuccess(data: MXUsersDevicesMap) { - if (data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId!!) == false) { - fail("Bob should see the new device") - } - bobKeysLatch.countDown() - } - }) - mTestHelper.await(bobKeysLatch) + if (data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId) == false) { + fail("Bob should see the new device") + } val bobSecondDevicePOVFirstDevice = bobSession.cryptoService().getDeviceInfo(bobUserId, bobSecondDeviceId) assertNotNull("Bob Second device should be known and persisted from first", bobSecondDevicePOVFirstDevice) // Manually mark it as trusted from first session - val bobSignLatch = CountDownLatch(1) - bobSession.cryptoService().crossSigningService().signDevice(bobSecondDeviceId!!, object : MatrixCallback { - override fun onSuccess(data: Unit) { - bobSignLatch.countDown() - } - - override fun onFailure(failure: Throwable) { - fail("Failed to trust bob ${failure.localizedMessage}") - } - }) - mTestHelper.await(bobSignLatch) + mTestHelper.doSync { + bobSession.cryptoService().crossSigningService().signDevice(bobSecondDeviceId, it) + } // Now alice should cross trust bob's second device - val aliceKeysLatch = CountDownLatch(1) - aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, object : MatrixCallback> { - override fun onFailure(failure: Throwable) { - fail("Failed to get device") - } + val data2 = mTestHelper.doSync> { + aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) - override fun onSuccess(data: MXUsersDevicesMap) { - // check that the device is seen - if (data.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId) == false) { - fail("Alice should see the new device") - } - aliceKeysLatch.countDown() - } - }) - mTestHelper.await(aliceKeysLatch) + } + + // check that the device is seen + if (data2.getUserDeviceIds(bobUserId)?.contains(bobSecondDeviceId) == false) { + fail("Alice should see the new device") + } val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null) assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified()) From c41661ece84a0c99827fc0e9dc316e9c7252f1a0 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 21 Feb 2020 19:03:16 +0100 Subject: [PATCH 097/187] Fixes #712 --- .../notifications/NotifiableEventResolver.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt index 17339956e5..b810e13ea1 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt @@ -31,6 +31,7 @@ import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResu import im.vector.riotx.BuildConfig import im.vector.riotx.R import im.vector.riotx.core.resources.StringProvider +import im.vector.riotx.features.home.room.detail.timeline.format.DisplayableEventFormatter import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventFormatter import timber.log.Timber import java.util.UUID @@ -43,7 +44,8 @@ import javax.inject.Inject * this pattern allow decoupling between the object responsible of displaying notifications and the matrix sdk. */ class NotifiableEventResolver @Inject constructor(private val stringProvider: StringProvider, - private val noticeEventFormatter: NoticeEventFormatter) { + private val noticeEventFormatter: NoticeEventFormatter, + private val displayableEventFormatter: DisplayableEventFormatter) { // private val eventDisplay = RiotEventDisplay(context) @@ -90,9 +92,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St if (room == null) { Timber.e("## Unable to resolve room for eventId [$event]") // Ok room is not known in store, but we can still display something - val body = - event.getLastMessageBody() - ?: stringProvider.getString(R.string.notification_unknown_new_event) + val body = displayableEventFormatter.format(event, false) val roomName = stringProvider.getString(R.string.notification_unknown_room_name) val senderDisplayName = event.getDisambiguatedDisplayName() @@ -103,7 +103,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St noisy = false, // will be updated senderName = senderDisplayName, senderId = event.root.senderId, - body = body, + body = body.toString(), roomId = event.root.roomId!!, roomName = roomName) @@ -125,8 +125,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St } } - val body = event.getLastMessageBody() - ?: stringProvider.getString(R.string.notification_unknown_new_event) + val body = displayableEventFormatter.format(event, false).toString() val roomName = room.roomSummary()?.displayName ?: "" val senderDisplayName = event.getDisambiguatedDisplayName() From 1a6d4d0b0371917496e1b21748028679000f8775 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Feb 2020 19:08:30 +0100 Subject: [PATCH 098/187] Try to fix XSigningTest --- .../crypto/crosssigning/ShieldTrustUpdater.kt | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/ShieldTrustUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/ShieldTrustUpdater.kt index 5bc6e2df0f..8fb33731a7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/ShieldTrustUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/ShieldTrustUpdater.kt @@ -28,6 +28,7 @@ import io.realm.RealmConfiguration import kotlinx.coroutines.launch import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe +import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReference import javax.inject.Inject @@ -47,6 +48,8 @@ internal class ShieldTrustUpdater @Inject constructor( private val backgroundCryptoRealm = AtomicReference() private val backgroundSessionRealm = AtomicReference() + private val isStarted = AtomicBoolean() + // private var cryptoDevicesResult: RealmResults? = null // private val cryptoDeviceChangeListener = object : OrderedRealmCollectionChangeListener> { @@ -57,38 +60,46 @@ internal class ShieldTrustUpdater @Inject constructor( // } fun start() { - eventBus.register(this) - BACKGROUND_HANDLER.post { - val cryptoRealm = Realm.getInstance(cryptoRealmConfiguration) - backgroundCryptoRealm.set(cryptoRealm) + if (isStarted.compareAndSet(false, true)) { + eventBus.register(this) + BACKGROUND_HANDLER.post { + val cryptoRealm = Realm.getInstance(cryptoRealmConfiguration) + backgroundCryptoRealm.set(cryptoRealm) // cryptoDevicesResult = cryptoRealm.where().findAll() // cryptoDevicesResult?.addChangeListener(cryptoDeviceChangeListener) - backgroundSessionRealm.set(Realm.getInstance(sessionRealmConfiguration)) + backgroundSessionRealm.set(Realm.getInstance(sessionRealmConfiguration)) + } } } fun stop() { - eventBus.unregister(this) - BACKGROUND_HANDLER.post { - // cryptoDevicesResult?.removeAllChangeListeners() - backgroundCryptoRealm.getAndSet(null).also { - it?.close() - } - backgroundSessionRealm.getAndSet(null).also { - it?.close() + if (isStarted.compareAndSet(true, false)) { + eventBus.unregister(this) + BACKGROUND_HANDLER.post { + // cryptoDevicesResult?.removeAllChangeListeners() + backgroundCryptoRealm.getAndSet(null).also { + it?.close() + } + backgroundSessionRealm.getAndSet(null).also { + it?.close() + } } } } @Subscribe fun onRoomMemberChange(update: SessionToCryptoRoomMembersUpdate) { + if (!isStarted.get()) { + return + } + taskExecutor.executorScope.launch { val updatedTrust = computeTrustTask.execute(ComputeTrustTask.Params(update.userIds)) // We need to send that back to session base BACKGROUND_HANDLER.post { - backgroundSessionRealm.get().executeTransaction { realm -> + backgroundSessionRealm.get()?.executeTransaction { realm -> roomSummaryUpdater.updateShieldTrust(realm, update.roomId, updatedTrust) } } @@ -97,6 +108,10 @@ internal class ShieldTrustUpdater @Inject constructor( @Subscribe fun onTrustUpdate(update: CryptoToSessionUserTrustChange) { + if (!isStarted.get()) { + return + } + onCryptoDevicesChange(update.userIds) } @@ -123,7 +138,7 @@ internal class ShieldTrustUpdater @Inject constructor( taskExecutor.executorScope.launch { val updatedTrust = computeTrustTask.execute(ComputeTrustTask.Params(userList)) BACKGROUND_HANDLER.post { - backgroundSessionRealm.get().executeTransaction { realm -> + backgroundSessionRealm.get()?.executeTransaction { realm -> roomSummaryUpdater.updateShieldTrust(realm, roomId, updatedTrust) } } From b03b6bfc370fe9eb2d5d0e8ad627f0c5bac8d72b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Feb 2020 12:53:18 +0100 Subject: [PATCH 099/187] Fix crash discovered thanks to integration tests --- .../internal/crypto/keysbackup/DefaultKeysBackupService.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt index 5206aeedc2..3ec5a2f979 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -80,6 +80,7 @@ import im.vector.matrix.android.internal.util.JsonCanonicalizer import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.awaitCallback import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.olm.OlmException @@ -809,7 +810,10 @@ internal class DefaultKeysBackupService @Inject constructor( // new key is sent val delayInMs = Random.nextLong(KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS) - uiHandler.postDelayed({ backupKeys() }, delayInMs) + cryptoCoroutineScope.launch { + delay(delayInMs) + uiHandler.post { backupKeys() } + } } else -> { Timber.v("maybeBackupKeys: Skip it because state: $state") From 3e587af163e486306d491dd6026ce94b1ee89bc6 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 24 Feb 2020 14:07:15 +0100 Subject: [PATCH 100/187] klint --- .../riotx/features/notifications/NotifiableEventResolver.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt index b810e13ea1..6f881f2b8e 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt @@ -26,7 +26,6 @@ import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.RoomMemberContent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.getEditedEventId -import im.vector.matrix.android.api.session.room.timeline.getLastMessageBody import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult import im.vector.riotx.BuildConfig import im.vector.riotx.R From 1e44e775036f1237b7ca9a84128faf41faa5e8f4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Feb 2020 14:42:47 +0100 Subject: [PATCH 101/187] Make code robust to int received instead of boolean https://github.com/matrix-org/synapse/issues/6977 --- .../keysbackup/model/rest/KeyBackupData.kt | 3 ++ .../android/internal/di/MoshiProvider.kt | 17 ++++++- .../network/parsing/ForceToBoolean.kt | 49 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/ForceToBoolean.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt index 5f6e30b3b5..b2d10687aa 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/model/rest/KeyBackupData.kt @@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.crypto.keysbackup.model.rest import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.network.parsing.ForceToBoolean /** * Backup data for one key. @@ -39,7 +40,9 @@ data class KeyBackupData( /** * Whether the device backing up the key has verified the device that the key is from. + * Force to boolean because of https://github.com/matrix-org/synapse/issues/6977 */ + @ForceToBoolean @Json(name = "is_verified") val isVerified: Boolean = false, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt index cd4e9abbc1..3164bf75c0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt @@ -17,7 +17,21 @@ package im.vector.matrix.android.internal.di import com.squareup.moshi.Moshi -import im.vector.matrix.android.api.session.room.model.message.* +import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent +import im.vector.matrix.android.api.session.room.model.message.MessageContent +import im.vector.matrix.android.api.session.room.model.message.MessageDefaultContent +import im.vector.matrix.android.api.session.room.model.message.MessageEmoteContent +import im.vector.matrix.android.api.session.room.model.message.MessageFileContent +import im.vector.matrix.android.api.session.room.model.message.MessageImageContent +import im.vector.matrix.android.api.session.room.model.message.MessageLocationContent +import im.vector.matrix.android.api.session.room.model.message.MessageNoticeContent +import im.vector.matrix.android.api.session.room.model.message.MessageOptionsContent +import im.vector.matrix.android.api.session.room.model.message.MessagePollResponseContent +import im.vector.matrix.android.api.session.room.model.message.MessageTextContent +import im.vector.matrix.android.api.session.room.model.message.MessageType +import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent +import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent +import im.vector.matrix.android.internal.network.parsing.ForceToBooleanJsonAdapter import im.vector.matrix.android.internal.network.parsing.RuntimeJsonAdapterFactory import im.vector.matrix.android.internal.network.parsing.UriMoshiAdapter import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData @@ -31,6 +45,7 @@ object MoshiProvider { private val moshi: Moshi = Moshi.Builder() .add(UriMoshiAdapter()) + .add(ForceToBooleanJsonAdapter()) .add(RuntimeJsonAdapterFactory.of(UserAccountData::class.java, "type", UserAccountDataEvent::class.java) .registerSubtype(UserAccountDataDirectMessages::class.java, UserAccountData.TYPE_DIRECT_MESSAGES) .registerSubtype(UserAccountDataIgnoredUsers::class.java, UserAccountData.TYPE_IGNORED_USER_LIST) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/ForceToBoolean.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/ForceToBoolean.kt new file mode 100644 index 0000000000..b823104b4d --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/parsing/ForceToBoolean.kt @@ -0,0 +1,49 @@ +/* + * 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 im.vector.matrix.android.internal.network.parsing + +import com.squareup.moshi.FromJson +import com.squareup.moshi.JsonQualifier +import com.squareup.moshi.JsonReader +import com.squareup.moshi.ToJson +import timber.log.Timber + +@JsonQualifier +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FUNCTION) +annotation class ForceToBoolean + +internal class ForceToBooleanJsonAdapter { + @ToJson + fun toJson(@ForceToBoolean b: Boolean): Boolean { + return b + } + + @FromJson + @ForceToBoolean + fun fromJson(reader: JsonReader): Boolean { + return when (val token = reader.peek()) { + JsonReader.Token.NUMBER -> reader.nextInt() != 0 + JsonReader.Token.BOOLEAN -> reader.nextBoolean() + else -> { + Timber.e("Expecting a boolean or a int but get: $token") + reader.skipValue() + false + } + } + } +} From b3c8b5526dc96504a1d511a228053912043db38a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Feb 2020 15:03:12 +0100 Subject: [PATCH 102/187] Each session now have it's proper crypto store --- .../android/internal/crypto/keysbackup/KeysBackupTest.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt index b039522e0c..89864e20b9 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt @@ -1229,9 +1229,6 @@ class KeysBackupTest : InstrumentedTest { val aliceUserId = cryptoTestData.firstSession.myUserId - // Logout first Alice session, else they will share the same Crypto store and some tests may fail. - mTestHelper.doSync { cryptoTestData.firstSession.signOut(true, it) } - // - Log Alice on a new device val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, defaultSessionParamsWithInitialSync) From 1124aa25fd6fcab79bf4b54331a089801c05bfc6 Mon Sep 17 00:00:00 2001 From: onurays Date: Mon, 24 Feb 2020 17:22:07 +0300 Subject: [PATCH 103/187] Code review fixes. --- .../RoomMemberProfileFragment.kt | 9 +++----- .../RoomMemberProfileViewEvents.kt | 2 +- .../RoomMemberProfileViewModel.kt | 7 +++++-- .../roomprofile/RoomProfileFragment.kt | 9 +++----- .../roomprofile/RoomProfileViewEvents.kt | 2 +- .../roomprofile/RoomProfileViewModel.kt | 7 +++++-- .../main/res/drawable/ic_material_share.xml | 21 ++++++++++++++++++- .../res/layout/fragment_matrix_profile.xml | 2 +- .../res/menu/vector_room_member_profile.xml | 1 + .../src/main/res/menu/vector_room_profile.xml | 2 +- vector/src/main/res/values/styles_riot.xml | 4 ++++ 11 files changed, 45 insertions(+), 21 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt index 6b51f78881..f261da2da9 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -29,7 +29,6 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import im.vector.matrix.android.api.permalinks.PermalinkFactory import im.vector.matrix.android.api.util.MatrixItem import im.vector.riotx.R import im.vector.riotx.core.animations.AppBarStateChangeListener @@ -99,7 +98,7 @@ class RoomMemberProfileFragment @Inject constructor( is RoomMemberProfileViewEvents.Failure -> showFailure(it.throwable) is RoomMemberProfileViewEvents.OnIgnoreActionSuccess -> Unit is RoomMemberProfileViewEvents.StartVerification -> handleStartVerification(it) - is RoomMemberProfileViewEvents.ShareRoomMemberProfile -> handleShareRoomMemberProfile() + is RoomMemberProfileViewEvents.ShareRoomMemberProfile -> handleShareRoomMemberProfile(it.permalink) }.exhaustive } } @@ -225,9 +224,7 @@ class RoomMemberProfileFragment @Inject constructor( vectorBaseActivity.notImplemented("Mention") } - private fun handleShareRoomMemberProfile() = withState(viewModel) { - PermalinkFactory.createPermalink(it.userId)?.let { permalink -> - startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) - } + private fun handleShareRoomMemberProfile(permalink: String) = withState(viewModel) { + startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) } } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt index d590040180..5d8757a337 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewEvents.kt @@ -32,5 +32,5 @@ sealed class RoomMemberProfileViewEvents : VectorViewEvents { val canCrossSign: Boolean ) : RoomMemberProfileViewEvents() - object ShareRoomMemberProfile : RoomMemberProfileViewEvents() + data class ShareRoomMemberProfile(val permalink: String) : RoomMemberProfileViewEvents() } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index eaad2c52a2..e4fbe85806 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -26,6 +26,7 @@ import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.permalinks.PermalinkFactory import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.events.model.EventType @@ -236,7 +237,9 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } } - private fun handleShareRoomMemberProfile() { - _viewEvents.post(RoomMemberProfileViewEvents.ShareRoomMemberProfile) + private fun handleShareRoomMemberProfile() = withState { state -> + PermalinkFactory.createPermalink(state.userId)?.let { permalink -> + _viewEvents.post(RoomMemberProfileViewEvents.ShareRoomMemberProfile(permalink)) + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index ec0a8e51e1..2b2a879e7e 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -26,7 +26,6 @@ import androidx.core.view.isVisible import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import im.vector.matrix.android.api.permalinks.PermalinkFactory import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.R @@ -94,7 +93,7 @@ class RoomProfileFragment @Inject constructor( is RoomProfileViewEvents.Loading -> showLoading(it.message) is RoomProfileViewEvents.Failure -> showFailure(it.throwable) is RoomProfileViewEvents.OnLeaveRoomSuccess -> onLeaveRoom() - is RoomProfileViewEvents.ShareRoomProfile -> onShareRoomProfile() + is RoomProfileViewEvents.ShareRoomProfile -> onShareRoomProfile(it.permalink) }.exhaustive } roomListQuickActionsSharedActionViewModel @@ -204,9 +203,7 @@ class RoomProfileFragment @Inject constructor( .show() } - private fun onShareRoomProfile() { - PermalinkFactory.createPermalink(roomProfileArgs.roomId)?.let { permalink -> - startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) - } + private fun onShareRoomProfile(permalink: String) { + startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt index 50e4775252..7a08a08126 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewEvents.kt @@ -26,5 +26,5 @@ sealed class RoomProfileViewEvents : VectorViewEvents { data class Failure(val throwable: Throwable) : RoomProfileViewEvents() object OnLeaveRoomSuccess : RoomProfileViewEvents() - object ShareRoomProfile : RoomProfileViewEvents() + data class ShareRoomProfile(val permalink: String) : RoomProfileViewEvents() } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt index e291556bd4..261e5f04d0 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt @@ -23,6 +23,7 @@ import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.permalinks.PermalinkFactory import im.vector.matrix.android.api.session.Session import im.vector.matrix.rx.rx import im.vector.matrix.rx.unwrap @@ -90,7 +91,9 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R }) } - private fun handleShareRoomProfile() { - _viewEvents.post(RoomProfileViewEvents.ShareRoomProfile) + private fun handleShareRoomProfile() = withState { state -> + PermalinkFactory.createPermalink(state.roomId)?.let { permalink -> + _viewEvents.post(RoomProfileViewEvents.ShareRoomProfile(permalink)) + } } } diff --git a/vector/src/main/res/drawable/ic_material_share.xml b/vector/src/main/res/drawable/ic_material_share.xml index fd72fa1c6d..5ecbfe3388 100644 --- a/vector/src/main/res/drawable/ic_material_share.xml +++ b/vector/src/main/res/drawable/ic_material_share.xml @@ -1,5 +1,24 @@ - + + + + + diff --git a/vector/src/main/res/layout/fragment_matrix_profile.xml b/vector/src/main/res/layout/fragment_matrix_profile.xml index cf31a6fe60..6e3eca06bf 100644 --- a/vector/src/main/res/layout/fragment_matrix_profile.xml +++ b/vector/src/main/res/layout/fragment_matrix_profile.xml @@ -15,7 +15,7 @@ \ No newline at end of file diff --git a/vector/src/main/res/menu/vector_room_profile.xml b/vector/src/main/res/menu/vector_room_profile.xml index 637cb9ef11..f6331018f0 100644 --- a/vector/src/main/res/menu/vector_room_profile.xml +++ b/vector/src/main/res/menu/vector_room_profile.xml @@ -5,6 +5,6 @@ android:id="@+id/roomProfileShareAction" android:icon="@drawable/ic_material_share" android:title="@string/share" - app:iconTint="@color/riotx_accent" + app:iconTint="?attr/colorAccent" app:showAsAction="ifRoom" /> \ No newline at end of file diff --git a/vector/src/main/res/values/styles_riot.xml b/vector/src/main/res/values/styles_riot.xml index 76d1988f61..c4b42fe4fe 100644 --- a/vector/src/main/res/values/styles_riot.xml +++ b/vector/src/main/res/values/styles_riot.xml @@ -30,6 +30,10 @@ "sans-serif" + + From b3d4747d97ba85d08339567ac16b1f76c8ee2bec Mon Sep 17 00:00:00 2001 From: onurays Date: Mon, 24 Feb 2020 17:35:57 +0300 Subject: [PATCH 104/187] Code review fixes. --- .../java/im/vector/riotx/features/media/ImageContentRenderer.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt index cbdb19d132..d11af4f350 100644 --- a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt +++ b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt @@ -146,7 +146,6 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: imageView.setImageLoaderCallback(object: DefaultImageLoaderCallback { override fun onSuccess(image: File?) { - super.onSuccess(image) imageView.ssiv.orientation = ORIENTATION_USE_EXIF } }) From dec62a893ea76870a30fc1209a712528d49f9ebd Mon Sep 17 00:00:00 2001 From: onurays Date: Mon, 24 Feb 2020 18:06:59 +0300 Subject: [PATCH 105/187] Code review fixes. --- .../roommemberprofile/RoomMemberProfileFragment.kt | 2 +- .../roommemberprofile/RoomMemberProfileViewModel.kt | 4 ++-- .../riotx/features/roomprofile/RoomProfileViewModel.kt | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt index f261da2da9..c62e8ce226 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -224,7 +224,7 @@ class RoomMemberProfileFragment @Inject constructor( vectorBaseActivity.notImplemented("Mention") } - private fun handleShareRoomMemberProfile(permalink: String) = withState(viewModel) { + private fun handleShareRoomMemberProfile(permalink: String) { startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) } } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index e4fbe85806..44c214bc99 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -237,8 +237,8 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } } - private fun handleShareRoomMemberProfile() = withState { state -> - PermalinkFactory.createPermalink(state.userId)?.let { permalink -> + private fun handleShareRoomMemberProfile() { + PermalinkFactory.createPermalink(initialState.userId)?.let { permalink -> _viewEvents.post(RoomMemberProfileViewEvents.ShareRoomMemberProfile(permalink)) } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt index 261e5f04d0..d49727d12d 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt @@ -31,7 +31,7 @@ import im.vector.riotx.R import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.resources.StringProvider -class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: RoomProfileViewState, +class RoomProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomProfileViewState, private val stringProvider: StringProvider, private val session: Session) : VectorViewModel(initialState) { @@ -67,7 +67,7 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R override fun handle(action: RoomProfileAction) = when (action) { RoomProfileAction.LeaveRoom -> handleLeaveRoom() is RoomProfileAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action) - is RoomProfileAction.ShareRoomProfile -> handleShareRoomProfile() + is RoomProfileAction.ShareRoomProfile -> handleShareRoomProfile() } private fun handleChangeNotificationMode(action: RoomProfileAction.ChangeRoomNotificationState) { @@ -91,8 +91,8 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted initialState: R }) } - private fun handleShareRoomProfile() = withState { state -> - PermalinkFactory.createPermalink(state.roomId)?.let { permalink -> + private fun handleShareRoomProfile() { + PermalinkFactory.createPermalink(initialState.roomId)?.let { permalink -> _viewEvents.post(RoomProfileViewEvents.ShareRoomProfile(permalink)) } } From b680e24938472dea0bc1bf837aeb05d0bfa5575b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Feb 2020 16:00:28 +0100 Subject: [PATCH 106/187] Fix some tests --- .../matrix/android/common/CommonTestHelper.kt | 2 ++ .../matrix/android/common/CryptoTestHelper.kt | 2 +- .../matrix/android/common/TestConstants.kt | 6 ++--- .../crypto/keysbackup/KeysBackupTest.kt | 26 +++++++++++-------- .../internal/session/DefaultSession.kt | 5 ++++ 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt index 44770f3378..da14b21f3c 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt @@ -130,12 +130,14 @@ class CommonTestHelper(context: Context) { } } val timeline = room.createTimeline(null, TimelineSettings(10)) + timeline.start() timeline.addListener(onEventSentListener) for (i in 0 until nbOfMessages) { room.sendTextMessage(message + " #" + (i + 1)) } await(latch) timeline.removeListener(onEventSentListener) + timeline.dispose() // Check that all events has been created assertEquals(nbOfMessages.toLong(), sentEvents.size.toLong()) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt index 46aeb2a926..826c70a63f 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt @@ -236,7 +236,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { } } - val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(10)) + val bobTimeline = roomFromBobPOV.createTimeline(null, TimelineSettings(20)) bobTimeline.start() bobTimeline.addListener(bobEventsListener) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestConstants.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestConstants.kt index 60cc87d330..2346898ca7 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestConstants.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestConstants.kt @@ -22,11 +22,11 @@ object TestConstants { const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080" - // Time out to use when waiting for server response. 60s - private const val AWAIT_TIME_OUT_MILLIS = 60000 + // Time out to use when waiting for server response. 10s + private const val AWAIT_TIME_OUT_MILLIS = 10_000 // Time out to use when waiting for server response, when the debugger is connected. 10 minutes - private const val AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS = 10 * 60000 + private const val AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS = 10 * 60_000 const val USER_ALICE = "Alice" const val USER_BOB = "Bob" diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt index 89864e20b9..e0c7e8273a 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt @@ -47,6 +47,7 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue +import org.junit.Assert.fail import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -101,6 +102,8 @@ class KeysBackupTest : InstrumentedTest { assertEquals(sessionsCount, sessions3.size) assertEquals(sessionsCount, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)) assertEquals(0, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true)) + + cryptoTestData.cleanUp(mTestHelper) } /** @@ -325,6 +328,8 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun restoreKeysBackupAndKeyShareRequestTest() { + fail("Check with Valere for this test. I think we do not send key share request") + val testData = createKeysBackupScenarioWithPassword(null) // - Check the SDK sent key share requests @@ -885,12 +890,12 @@ class KeysBackupTest : InstrumentedTest { keysBackup2.addListener(object : KeysBackupStateListener { override fun onStateChange(newState: KeysBackupState) { // Check the backup completes - if (keysBackup.state == KeysBackupState.ReadyToBackUp) { + if (newState == KeysBackupState.ReadyToBackUp) { count++ if (count == 2) { // Remove itself from the list of listeners - keysBackup.removeListener(this) + keysBackup2.removeListener(this) latch.countDown() } @@ -1026,15 +1031,14 @@ class KeysBackupTest : InstrumentedTest { var isSuccessful = false val latch2 = CountDownLatch(1) - keysBackup2.backupAllGroupSessions(object : ProgressListener { - override fun onProgress(progress: Int, total: Int) { - } - }, object : TestMatrixCallback(latch2, false) { - override fun onSuccess(data: Unit) { - isSuccessful = true - super.onSuccess(data) - } - }) + keysBackup2.backupAllGroupSessions( + null, + object : TestMatrixCallback(latch2, false) { + override fun onSuccess(data: Unit) { + isSuccessful = true + super.onSuccess(data) + } + }) mTestHelper.await(latch2) assertFalse(isSuccessful) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index e7cb963c5f..c4ddaec98e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -217,4 +217,9 @@ internal class DefaultSession @Inject constructor( override fun removeListener(listener: Session.Listener) { sessionListeners.removeListener(listener) } + + // For easy debugging + override fun toString(): String { + return "$myUserId - ${sessionParams.credentials.deviceId}" + } } From 96af1957f95815dc4bf32950768ef88241ca86b5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Feb 2020 16:27:58 +0100 Subject: [PATCH 107/187] Fix bug on progress step, found by integration test testBackupWithPassword() --- .../internal/crypto/actions/MegolmSessionDataImporter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt index e0d6d72c0d..6f41116b90 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt @@ -83,7 +83,7 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi } if (progressListener != null) { - val progress = 100 * cpt / totalNumbersOfKeys + val progress = 100 * (cpt + 1) / totalNumbersOfKeys if (lastProgress != progress) { lastProgress = progress From c6b3b1e6488cdc3dabe8c3d22953f99d5c17680e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Feb 2020 17:27:04 +0100 Subject: [PATCH 108/187] Fix bug on test --- .../matrix/android/common/CommonTestHelper.kt | 19 +++++++++++++------ .../crypto/keysbackup/KeysBackupTest.kt | 4 +--- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt index da14b21f3c..386787b882 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt @@ -28,7 +28,10 @@ import im.vector.matrix.android.api.auth.data.LoginFlowResult import im.vector.matrix.android.api.auth.registration.RegistrationResult import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.LocalEcho +import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.Room +import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineSettings @@ -113,7 +116,7 @@ class CommonTestHelper(context: Context) { fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List { val sentEvents = ArrayList(nbOfMessages) val latch = CountDownLatch(nbOfMessages) - val onEventSentListener = object : Timeline.Listener { + val timelineListener = object : Timeline.Listener { override fun onTimelineFailure(throwable: Throwable) { } @@ -122,21 +125,25 @@ class CommonTestHelper(context: Context) { } override fun onTimelineUpdated(snapshot: List) { - // TODO Count only new messages? - if (snapshot.count { it.root.type == EventType.MESSAGE } == nbOfMessages) { - sentEvents.addAll(snapshot.filter { it.root.type == EventType.MESSAGE }) + val newMessages = snapshot + .filter { LocalEcho.isLocalEchoId(it.eventId).not() } + .filter { it.root.getClearType() == EventType.MESSAGE } + .filter { it.root.getClearContent().toModel()?.body?.startsWith(message) == true } + + if (newMessages.size == nbOfMessages) { + sentEvents.addAll(newMessages) latch.countDown() } } } val timeline = room.createTimeline(null, TimelineSettings(10)) timeline.start() - timeline.addListener(onEventSentListener) + timeline.addListener(timelineListener) for (i in 0 until nbOfMessages) { room.sendTextMessage(message + " #" + (i + 1)) } await(latch) - timeline.removeListener(onEventSentListener) + timeline.removeListener(timelineListener) timeline.dispose() // Check that all events has been created diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt index e0c7e8273a..77ba66d341 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt @@ -861,6 +861,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun testCheckAndStartKeysBackupWhenRestartingAMatrixSession() { + fail("This test still fail. To investigate") // - Create a backup version val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() @@ -1011,9 +1012,6 @@ class KeysBackupTest : InstrumentedTest { val oldKeyBackupVersion = keysBackup.currentBackupVersion val aliceUserId = cryptoTestData.firstSession.myUserId - // Close first Alice session, else they will share the same Crypto store and the test fails. - cryptoTestData.firstSession.close() - // - Log Alice on a new device val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, defaultSessionParamsWithInitialSync) From 981c9ac4ac59a6e252d6ed4c4642b68f5510c645 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 21 Feb 2020 13:56:15 +0100 Subject: [PATCH 109/187] Invite sync: assign eventId and remove the primaryKey constraint --- .../api/session/room/model/RoomSummary.kt | 5 ++- .../internal/database/mapper/EventMapper.kt | 3 +- .../database/mapper/RoomSummaryMapper.kt | 3 +- .../internal/database/model/EventEntity.kt | 2 +- .../database/model/RoomSummaryEntity.kt | 3 +- .../database/query/EventEntityQueries.kt | 7 ++++ .../session/room/RoomSummaryUpdater.kt | 4 ++- .../room/membership/LoadRoomMembersTask.kt | 5 ++- .../room/timeline/TokenChunkEventPersistor.kt | 9 ++--- .../internal/session/sync/RoomSyncHandler.kt | 33 ++++++++++--------- .../home/room/detail/RoomDetailViewModel.kt | 4 +-- .../home/room/list/RoomSummaryItemFactory.kt | 4 +-- 12 files changed, 48 insertions(+), 34 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt index 28c56125f1..3cba06fcac 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt @@ -45,7 +45,8 @@ data class RoomSummary constructor( val versioningState: VersioningState = VersioningState.NONE, val readMarkerId: String? = null, val userDrafts: List = emptyList(), - var isEncrypted: Boolean, + val isEncrypted: Boolean, + val inviterId: String? = null, val typingRoomMemberIds: List = emptyList(), val breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS, // TODO Plug it @@ -62,3 +63,5 @@ data class RoomSummary constructor( const val NOT_IN_BREADCRUMBS = -1 } } + + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt index 75eb61274d..3793b3c33f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt @@ -32,7 +32,8 @@ internal object EventMapper { val uds = if (event.unsignedData == null) null else MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).toJson(event.unsignedData) val eventEntity = EventEntity() - eventEntity.eventId = event.eventId ?: "" + //TODO change this as we shouldn't use event everywhere + eventEntity.eventId = event.eventId ?: "$roomId-${System.currentTimeMillis()}-${event.hashCode()}" eventEntity.roomId = event.roomId ?: roomId eventEntity.content = ContentMapper.map(event.content) val resolvedPrevContent = event.prevContent ?: event.unsignedData?.prevContent diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt index 617dc6acfa..de01aab4e8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt @@ -77,7 +77,8 @@ internal class RoomSummaryMapper @Inject constructor( isEncrypted = roomSummaryEntity.isEncrypted, typingRoomMemberIds = roomSummaryEntity.typingUserIds.toList(), breadcrumbsIndex = roomSummaryEntity.breadcrumbsIndex, - roomEncryptionTrustLevel = roomSummaryEntity.roomEncryptionTrustLevel + roomEncryptionTrustLevel = roomSummaryEntity.roomEncryptionTrustLevel, + inviterId = roomSummaryEntity.inviterId ) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt index d7bb49b1fe..b8e2954e3f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt @@ -24,7 +24,7 @@ import io.realm.RealmObject import io.realm.annotations.Index import io.realm.annotations.PrimaryKey -internal open class EventEntity(@PrimaryKey var eventId: String = "", +internal open class EventEntity(@Index var eventId: String = "", @Index var roomId: String = "", @Index var type: String = "", var content: String? = null, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt index c4ebe3cbaa..7009e762fb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt @@ -49,7 +49,8 @@ internal open class RoomSummaryEntity( var flatAliases: String = "", var isEncrypted: Boolean = false, var typingUserIds: RealmList = RealmList(), - var roomEncryptionTrustLevelStr: String? = null + var roomEncryptionTrustLevelStr: String? = null, + var inviterId: String? = null ) : RealmObject() { private var membershipStr: String = Membership.NONE.name diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt index 59908aa990..d998c41ccb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt @@ -23,6 +23,13 @@ import io.realm.RealmList import io.realm.RealmQuery import io.realm.kotlin.where +internal fun EventEntity.copyToRealmOrIgnore(realm: Realm): EventEntity { + return realm.where() + .equalTo(EventEntityFields.EVENT_ID, eventId) + .equalTo(EventEntityFields.ROOM_ID, roomId) + .findFirst() ?: realm.copyToRealm(this) +} + internal fun EventEntity.Companion.where(realm: Realm, eventId: String): RealmQuery { return realm.where() .equalTo(EventEntityFields.EVENT_ID, eventId) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index a441d17196..864f25cf12 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -81,7 +81,8 @@ internal class RoomSummaryUpdater @Inject constructor( roomSummary: RoomSyncSummary? = null, unreadNotifications: RoomSyncUnreadNotifications? = null, updateMembers: Boolean = false, - ephemeralResult: RoomSyncHandler.EphemeralResult? = null) { + ephemeralResult: RoomSyncHandler.EphemeralResult? = null, + inviterId: String? = null) { val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId) if (roomSummary != null) { if (roomSummary.heroes.isNotEmpty()) { @@ -133,6 +134,7 @@ internal class RoomSummaryUpdater @Inject constructor( roomSummaryEntity.isEncrypted = encryptionEvent != null roomSummaryEntity.typingUserIds.clear() roomSummaryEntity.typingUserIds.addAll(ephemeralResult?.typingUserIds.orEmpty()) + roomSummaryEntity.inviterId = inviterId if (updateMembers) { val otherRoomMembers = RoomMemberHelper(realm, roomId) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt index 5dfe54a7bb..41993c323a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt @@ -22,6 +22,7 @@ import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.internal.database.mapper.toEntity import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity import im.vector.matrix.android.internal.database.model.RoomEntity +import im.vector.matrix.android.internal.database.query.copyToRealmOrIgnore import im.vector.matrix.android.internal.database.query.getOrCreate import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.network.executeRequest @@ -73,9 +74,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor( if (roomMemberEvent.eventId == null || roomMemberEvent.stateKey == null) { continue } - val eventEntity = roomMemberEvent.toEntity(roomId, SendState.SYNCED).let { - realm.copyToRealmOrUpdate(it) - } + val eventEntity = roomMemberEvent.toEntity(roomId, SendState.SYNCED).copyToRealmOrIgnore(realm) CurrentStateEventEntity.getOrCreate(realm, roomId, roomMemberEvent.stateKey, roomMemberEvent.type).apply { eventId = roomMemberEvent.eventId root = eventEntity diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt index 41920158c4..164626224b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -31,6 +31,7 @@ import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.TimelineEventEntity +import im.vector.matrix.android.internal.database.query.copyToRealmOrIgnore import im.vector.matrix.android.internal.database.query.create import im.vector.matrix.android.internal.database.query.find import im.vector.matrix.android.internal.database.query.findAllIncludingEvents @@ -199,9 +200,7 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy val stateEvents = receivedChunk.stateEvents for (stateEvent in stateEvents) { - val stateEventEntity = stateEvent.toEntity(roomId, SendState.SYNCED).let { - realm.copyToRealmOrUpdate(it) - } + val stateEventEntity = stateEvent.toEntity(roomId, SendState.SYNCED).copyToRealmOrIgnore(realm) currentChunk.addStateEvent(roomId, stateEventEntity, direction) if (stateEvent.type == EventType.STATE_ROOM_MEMBER && stateEvent.stateKey != null) { roomMemberContentsByUser[stateEvent.stateKey] = stateEvent.content.toModel() @@ -213,9 +212,7 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy continue } eventIds.add(event.eventId) - val eventEntity = event.toEntity(roomId, SendState.SYNCED).let { - realm.copyToRealmOrUpdate(it) - } + val eventEntity = event.toEntity(roomId, SendState.SYNCED).copyToRealmOrIgnore(realm) if (event.type == EventType.STATE_ROOM_MEMBER && event.stateKey != null) { val contentToUse = if (direction == PaginationDirection.BACKWARDS) { event.prevContent diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt index ab2c9a48a6..2ab9c07ec7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt @@ -32,6 +32,7 @@ import im.vector.matrix.android.internal.database.mapper.toEntity import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity import im.vector.matrix.android.internal.database.model.RoomEntity +import im.vector.matrix.android.internal.database.query.copyToRealmOrIgnore import im.vector.matrix.android.internal.database.query.find import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom import im.vector.matrix.android.internal.database.query.getOrCreate @@ -93,7 +94,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle } is HandlingStrategy.INVITED -> handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_invited_rooms, 0.1f) { - handleInvitedRoom(realm, it.key, it.value, syncLocalTimeStampMillis) + handleInvitedRoom(realm, it.key, it.value) } is HandlingStrategy.LEFT -> { @@ -134,9 +135,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle if (event.eventId == null || event.stateKey == null) { continue } - val eventEntity = event.toEntity(roomId, SendState.SYNCED).let { - realm.copyToRealmOrUpdate(it) - } + val eventEntity = event.toEntity(roomId, SendState.SYNCED).copyToRealmOrIgnore(realm) CurrentStateEventEntity.getOrCreate(realm, roomId, event.stateKey, event.type).apply { eventId = event.eventId root = eventEntity @@ -177,19 +176,26 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle private fun handleInvitedRoom(realm: Realm, roomId: String, - roomSync: InvitedRoomSync, - syncLocalTimestampMillis: Long): RoomEntity { + roomSync: InvitedRoomSync): RoomEntity { Timber.v("Handle invited sync for room $roomId") val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId) roomEntity.membership = Membership.INVITE if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) { - val chunkEntity = handleTimelineEvents(realm, roomId, roomEntity, roomSync.inviteState.events, syncLocalTimestampMillis = syncLocalTimestampMillis) - roomEntity.addOrUpdate(chunkEntity) + roomSync.inviteState.events.forEach { + if (it.stateKey == null) { + return@forEach + } + val eventEntity = it.toEntity(roomId, SendState.SYNCED).copyToRealmOrIgnore(realm) + CurrentStateEventEntity.getOrCreate(realm, roomId, it.stateKey, it.type).apply { + eventId = eventEntity.eventId + root = eventEntity + } + } } - val hasRoomMember = roomSync.inviteState?.events?.firstOrNull { + val inviterEvent = roomSync.inviteState?.events?.lastOrNull { it.type == EventType.STATE_ROOM_MEMBER - } != null - roomSummaryUpdater.update(realm, roomId, Membership.INVITE, updateMembers = hasRoomMember) + } + roomSummaryUpdater.update(realm, roomId, Membership.INVITE, updateMembers = true, inviterId = inviterEvent?.senderId) return roomEntity } @@ -197,7 +203,6 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle roomId: String, roomSync: RoomSync): RoomEntity { val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId) - roomEntity.membership = Membership.LEAVE roomEntity.chunks.deleteAllFromRealm() roomSummaryUpdater.update(realm, roomId, Membership.LEAVE, roomSync.summary, roomSync.unreadNotifications) @@ -229,9 +234,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle } eventIds.add(event.eventId) val ageLocalTs = event.unsignedData?.age?.let { syncLocalTimestampMillis - it } - val eventEntity = event.toEntity(roomId, SendState.SYNCED, ageLocalTs).let { - realm.copyToRealmOrUpdate(it) - } + val eventEntity = event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm) if (event.isStateEvent() && event.stateKey != null) { CurrentStateEventEntity.getOrCreate(realm, roomId, event.stateKey, event.type).apply { eventId = event.eventId diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 4e112ab6dc..8a231fb25d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -950,8 +950,8 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro private fun observeSummaryState() { asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary -> if (summary.membership == Membership.INVITE) { - summary.latestPreviewableEvent?.root?.senderId?.let { senderId -> - session.getUser(senderId) + summary.inviterId?.let { inviterId -> + session.getUser(inviterId) }?.also { setState { copy(asyncInviter = Success(it)) } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt index baa93f96e3..7d8cf0b6c1 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomSummaryItemFactory.kt @@ -60,9 +60,9 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor rejectingErrorRoomsIds: Set, listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> { val secondLine = if (roomSummary.isDirect) { - roomSummary.latestPreviewableEvent?.root?.senderId + roomSummary.inviterId } else { - roomSummary.latestPreviewableEvent?.root?.senderId?.let { + roomSummary.inviterId?.let { stringProvider.getString(R.string.invited_by, it) } } From 9cdb1da614e777136c95df6ed4f2a1de381037b3 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 21 Feb 2020 13:56:39 +0100 Subject: [PATCH 110/187] Start fixing notif for invites --- .../notifications/NotifiableEventResolver.kt | 12 ++++++------ .../notifications/NotificationDrawerManager.kt | 7 +++---- .../features/notifications/NotificationUtils.kt | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt index 6f881f2b8e..1f9f54127b 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt @@ -51,20 +51,20 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St fun resolveEvent(event: Event/*, roomState: RoomState?, bingRule: PushRule?*/, session: Session): NotifiableEvent? { val roomID = event.roomId ?: return null val eventId = event.eventId ?: return null + if (event.getClearType() == EventType.STATE_ROOM_MEMBER) { + return resolveStateRoomEvent(event, session) + } val timelineEvent = session.getRoom(roomID)?.getTimeLineEvent(eventId) ?: return null when (event.getClearType()) { - EventType.MESSAGE -> { + EventType.MESSAGE -> { return resolveMessageEvent(timelineEvent, session) } - EventType.ENCRYPTED -> { + EventType.ENCRYPTED -> { val messageEvent = resolveMessageEvent(timelineEvent, session) messageEvent?.lockScreenVisibility = NotificationCompat.VISIBILITY_PRIVATE return messageEvent } - EventType.STATE_ROOM_MEMBER -> { - return resolveStateRoomEvent(event, session) - } - else -> { + else -> { // If the event can be displayed, display it as is Timber.w("NotifiableEventResolver Received an unsupported event matching a bing rule") // TODO Better event text display diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt index 24ae0e722a..8173d24550 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt @@ -130,9 +130,9 @@ class NotificationDrawerManager @Inject constructor(private val context: Context fun onEventRedacted(eventId: String) { synchronized(eventList) { - eventList.filter { it.eventId == eventId }.map { notifiableEvent -> - notifiableEvent.isRedacted = true - notifiableEvent.hasBeenDisplayed = false + eventList.find { it.eventId == eventId }?.apply { + isRedacted = true + hasBeenDisplayed = false } } } @@ -182,7 +182,6 @@ class NotificationDrawerManager @Inject constructor(private val context: Context e is InviteNotifiableEvent && e.roomId == roomId } } - notificationUtils.cancelNotificationMessage(roomId, ROOM_INVITATION_NOTIFICATION_ID) } diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt index 7d8e43d0be..ff0947598f 100755 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt @@ -515,7 +515,7 @@ class NotificationUtils @Inject constructor(private val context: Context, val joinIntent = Intent(context, NotificationBroadcastReceiver::class.java) joinIntent.action = JOIN_ACTION joinIntent.data = Uri.parse("foobar://$roomId&$matrixId") - rejectIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) + joinIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) val joinIntentPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(), joinIntent, PendingIntent.FLAG_UPDATE_CURRENT) addAction( From 133d8d7f147643ed7db20d745f029026f4e3a044 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 21 Feb 2020 14:03:11 +0100 Subject: [PATCH 111/187] RoomList: fix joining/rejecting state --- .../features/home/room/list/RoomListViewModel.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt index 7886bf844b..cc1970dadc 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt @@ -101,7 +101,18 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, .observeOn(Schedulers.computation()) .map { buildRoomSummaries(it) } .execute { async -> - copy(asyncFilteredRooms = async) + val invitedRooms = async()?.get(RoomCategory.INVITE)?.map { it.roomId } ?: emptyList() + val remainingJoining = joiningRoomsIds.intersect(invitedRooms) + val remainingRejecting = rejectingRoomsIds.intersect(invitedRooms) + val remainingRejectErrors = rejectingErrorRoomsIds.intersect(invitedRooms) + val remainingJoinErrors = joiningErrorRoomsIds.intersect(invitedRooms) + copy( + asyncFilteredRooms = async, + joiningRoomsIds = remainingJoining, + rejectingRoomsIds = remainingRejecting, + rejectingErrorRoomsIds = remainingRejectErrors, + joiningErrorRoomsIds = remainingJoinErrors + ) } } @@ -229,7 +240,6 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, else -> groupRooms.add(room) } } - return RoomSummaries().apply { put(RoomCategory.INVITE, invites) put(RoomCategory.FAVOURITE, favourites) From 8b0305c91d8eb6f7512ff92689a0387842e36d03 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 21 Feb 2020 14:04:33 +0100 Subject: [PATCH 112/187] Clean code --- .../vector/matrix/android/api/session/room/model/RoomSummary.kt | 2 -- .../matrix/android/internal/database/mapper/EventMapper.kt | 2 +- .../matrix/android/internal/database/model/EventEntity.kt | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt index 3cba06fcac..6171b2633b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt @@ -63,5 +63,3 @@ data class RoomSummary constructor( const val NOT_IN_BREADCRUMBS = -1 } } - - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt index 3793b3c33f..575f78edaf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt @@ -32,7 +32,7 @@ internal object EventMapper { val uds = if (event.unsignedData == null) null else MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).toJson(event.unsignedData) val eventEntity = EventEntity() - //TODO change this as we shouldn't use event everywhere + // TODO change this as we shouldn't use event everywhere eventEntity.eventId = event.eventId ?: "$roomId-${System.currentTimeMillis()}-${event.hashCode()}" eventEntity.roomId = event.roomId ?: roomId eventEntity.content = ContentMapper.map(event.content) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt index b8e2954e3f..72015afc43 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt @@ -22,7 +22,6 @@ import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResu import im.vector.matrix.android.internal.di.MoshiProvider import io.realm.RealmObject import io.realm.annotations.Index -import io.realm.annotations.PrimaryKey internal open class EventEntity(@Index var eventId: String = "", @Index var roomId: String = "", From b694721728ec287a76992df0c66ce83e10f753c8 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 21 Feb 2020 14:06:05 +0100 Subject: [PATCH 113/187] Update CHANGES --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 709e347bf0..21c5dba8bb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -19,6 +19,7 @@ Bugfix 🐛: - Fix rotation of full-size image (#647) - Fix joining rooms from directory via federation isn't working. (#808) - Leaving a room creates a stuck "leaving room" loading screen. (#1041) + - Fix some invitation handling issues Translations 🗣: - From 9c566b19f7808865e2f89d7360c9ffa49626a5e7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Feb 2020 17:35:43 +0100 Subject: [PATCH 114/187] Ensure all eventId start with `$`, even ids for local echo --- .../vector/matrix/android/api/session/events/model/LocalEcho.kt | 2 +- .../matrix/android/internal/database/mapper/EventMapper.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/LocalEcho.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/LocalEcho.kt index 1dbee475e0..67ae3b2d73 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/LocalEcho.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/LocalEcho.kt @@ -20,7 +20,7 @@ import java.util.UUID object LocalEcho { - private const val PREFIX = "local." + private const val PREFIX = "\$local." fun isLocalEchoId(eventId: String) = eventId.startsWith(PREFIX) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt index 575f78edaf..e6a082c720 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt @@ -33,7 +33,7 @@ internal object EventMapper { else MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).toJson(event.unsignedData) val eventEntity = EventEntity() // TODO change this as we shouldn't use event everywhere - eventEntity.eventId = event.eventId ?: "$roomId-${System.currentTimeMillis()}-${event.hashCode()}" + eventEntity.eventId = event.eventId ?: "$$roomId-${System.currentTimeMillis()}-${event.hashCode()}" eventEntity.roomId = event.roomId ?: roomId eventEntity.content = ContentMapper.map(event.content) val resolvedPrevContent = event.prevContent ?: event.unsignedData?.prevContent From 10239828589c1251a66c62f81b9b327a62066452 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Feb 2020 17:40:28 +0100 Subject: [PATCH 115/187] Code readability --- .../riotx/features/home/room/list/RoomListViewModel.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt index cc1970dadc..81c75ed1d7 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt @@ -101,17 +101,17 @@ class RoomListViewModel @Inject constructor(initialState: RoomListViewState, .observeOn(Schedulers.computation()) .map { buildRoomSummaries(it) } .execute { async -> - val invitedRooms = async()?.get(RoomCategory.INVITE)?.map { it.roomId } ?: emptyList() + val invitedRooms = async()?.get(RoomCategory.INVITE)?.map { it.roomId }.orEmpty() val remainingJoining = joiningRoomsIds.intersect(invitedRooms) + val remainingJoinErrors = joiningErrorRoomsIds.intersect(invitedRooms) val remainingRejecting = rejectingRoomsIds.intersect(invitedRooms) val remainingRejectErrors = rejectingErrorRoomsIds.intersect(invitedRooms) - val remainingJoinErrors = joiningErrorRoomsIds.intersect(invitedRooms) copy( asyncFilteredRooms = async, joiningRoomsIds = remainingJoining, + joiningErrorRoomsIds = remainingJoinErrors, rejectingRoomsIds = remainingRejecting, - rejectingErrorRoomsIds = remainingRejectErrors, - joiningErrorRoomsIds = remainingJoinErrors + rejectingErrorRoomsIds = remainingRejectErrors ) } } From d710a5955485035087dff32ecd2e183468af75a3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Feb 2020 17:44:51 +0100 Subject: [PATCH 116/187] Update change log --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 21c5dba8bb..90246416af 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -19,7 +19,7 @@ Bugfix 🐛: - Fix rotation of full-size image (#647) - Fix joining rooms from directory via federation isn't working. (#808) - Leaving a room creates a stuck "leaving room" loading screen. (#1041) - - Fix some invitation handling issues + - Fix some invitation handling issues (#1013) Translations 🗣: - From 483256093aadf319ff0e8e284280dd6484e71756 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 24 Feb 2020 18:27:18 +0100 Subject: [PATCH 117/187] Invite: fix room member not saved --- .../matrix/android/internal/session/sync/RoomSyncHandler.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt index 2ab9c07ec7..5b7c39a3d9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt @@ -190,6 +190,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle eventId = eventEntity.eventId root = eventEntity } + roomMemberEventHandler.handle(realm, roomId, it) } } val inviterEvent = roomSync.inviteState?.events?.lastOrNull { From b9b921a4dfa5bbff094c201f3a83e3def5934874 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Feb 2020 09:53:17 +0100 Subject: [PATCH 118/187] Code quality --- .../android/internal/crypto/crosssigning/XSigningTest.kt | 1 - .../matrix/android/internal/session/filter/FilterUtil.kt | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt index 57027878f9..b56ee4a489 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/crosssigning/XSigningTest.kt @@ -144,7 +144,6 @@ class XSigningTest : InstrumentedTest { // Now alice should cross trust bob's second device val data2 = mTestHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) - } // check that the device is seen diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterUtil.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterUtil.kt index 000df0d80e..3f4e61e6b5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterUtil.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/FilterUtil.kt @@ -78,8 +78,7 @@ internal object FilterUtil { filterBody.presence = null } } - } - */ + } */ /** * Compute a new filterBody to enable or disable the lazy loading From 08e4b4473c6bfd81a4c6d04b06b60ddfd42ac658 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Feb 2020 09:57:12 +0100 Subject: [PATCH 119/187] Disable manual rules now checked by ktlint --- tools/check/forbidden_strings_in_code.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt index 5c5b600828..a0bd725118 100644 --- a/tools/check/forbidden_strings_in_code.txt +++ b/tools/check/forbidden_strings_in_code.txt @@ -67,9 +67,10 @@ DO NOT COMMIT ### invalid formatting \s{8}/\*\n \* -[^\w]if\( -while\( -for\( +# Now checked by ktlint +# [^\w]if\( +# while\( +# for\( # Add space after // # DISABLED To re-enable when code will be formatted globally From 19b9617a09e604aa2ddb4e3c11252e81afa365fd Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 25 Feb 2020 10:53:47 +0100 Subject: [PATCH 120/187] Invite: fix inviterId being overrided --- .../android/internal/session/room/RoomSummaryUpdater.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index 864f25cf12..e6e2b16477 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -134,7 +134,12 @@ internal class RoomSummaryUpdater @Inject constructor( roomSummaryEntity.isEncrypted = encryptionEvent != null roomSummaryEntity.typingUserIds.clear() roomSummaryEntity.typingUserIds.addAll(ephemeralResult?.typingUserIds.orEmpty()) - roomSummaryEntity.inviterId = inviterId + + if (roomSummaryEntity.membership == Membership.INVITE && inviterId != null) { + roomSummaryEntity.inviterId = inviterId + } else if (roomSummaryEntity.membership != Membership.INVITE) { + roomSummaryEntity.inviterId = null + } if (updateMembers) { val otherRoomMembers = RoomMemberHelper(realm, roomId) From 17e028178ece73502c70ded16a94ff19474392da Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Feb 2020 11:20:11 +0100 Subject: [PATCH 121/187] Code robustness (avoid using !!) --- .../internal/crypto/IncomingRoomKeyRequest.kt | 20 ++++++++++--------- .../IncomingRoomKeyRequestCancellation.kt | 19 +++++++++--------- .../crypto/IncomingRoomKeyRequestManager.kt | 4 ++-- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt index 93ba5aafe5..39b4678a27 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt @@ -64,15 +64,17 @@ data class IncomingRoomKeyRequest( * * @param event the event */ - fun fromEvent(event: Event): IncomingRoomKeyRequest { - val roomKeyShareRequest = event.getClearContent().toModel()!! - - return IncomingRoomKeyRequest( - userId = event.senderId, - deviceId = roomKeyShareRequest.requestingDeviceId, - requestId = roomKeyShareRequest.requestId, - requestBody = roomKeyShareRequest.body ?: RoomKeyRequestBody() - ) + fun fromEvent(event: Event): IncomingRoomKeyRequest? { + return event.getClearContent() + .toModel() + ?.let { + IncomingRoomKeyRequest( + userId = event.senderId, + deviceId = it.requestingDeviceId, + requestId = it.requestId, + requestBody = it.body ?: RoomKeyRequestBody() + ) + } } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt index a33828505e..6779936f3a 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestCancellation.kt @@ -40,20 +40,21 @@ data class IncomingRoomKeyRequestCancellation( override val requestId: String? = null ) : IncomingRoomKeyRequestCommon { companion object { - /** * Factory * * @param event the event */ - fun fromEvent(event: Event): IncomingRoomKeyRequestCancellation { - val roomKeyShareRequestCancellation = event.getClearContent().toModel()!! - - return IncomingRoomKeyRequestCancellation( - userId = event.senderId, - deviceId = roomKeyShareRequestCancellation.requestingDeviceId, - requestId = roomKeyShareRequestCancellation.requestId - ) + fun fromEvent(event: Event): IncomingRoomKeyRequestCancellation? { + return event.getClearContent() + .toModel() + ?.let { + IncomingRoomKeyRequestCancellation( + userId = event.senderId, + deviceId = it.requestingDeviceId, + requestId = it.requestId + ) + } } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt index 9a6458cf37..8bad39bd25 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt @@ -53,8 +53,8 @@ internal class IncomingRoomKeyRequestManager @Inject constructor( fun onRoomKeyRequestEvent(event: Event) { val roomKeyShare = event.getClearContent().toModel() when (roomKeyShare?.action) { - RoomKeyShare.ACTION_SHARE_REQUEST -> receivedRoomKeyRequests.add(IncomingRoomKeyRequest.fromEvent(event)) - RoomKeyShare.ACTION_SHARE_CANCELLATION -> receivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation.fromEvent(event)) + RoomKeyShare.ACTION_SHARE_REQUEST -> IncomingRoomKeyRequest.fromEvent(event)?.let { receivedRoomKeyRequests.add(it) } + RoomKeyShare.ACTION_SHARE_CANCELLATION -> IncomingRoomKeyRequestCancellation.fromEvent(event)?.let { receivedRoomKeyRequestCancellations.add(it) } else -> Timber.e("## onRoomKeyRequestEvent() : unsupported action ${roomKeyShare?.action}") } } From cfe9c4bb41c712d327305a0f3fcea935c6899ad9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Feb 2020 12:03:14 +0100 Subject: [PATCH 122/187] Display internal id of a room section in room profile, with copy to clipboard action, only in developer mode --- .../roomprofile/RoomProfileController.kt | 17 +++++++++++++++++ .../features/roomprofile/RoomProfileFragment.kt | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt index ab1774233a..5f8c000497 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt @@ -25,10 +25,12 @@ import im.vector.riotx.core.epoxy.profiles.buildProfileSection import im.vector.riotx.core.resources.ColorProvider import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.ui.list.genericFooterItem +import im.vector.riotx.features.settings.VectorPreferences import javax.inject.Inject class RoomProfileController @Inject constructor( private val stringProvider: StringProvider, + private val vectorPreferences: VectorPreferences, colorProvider: ColorProvider ) : TypedEpoxyController() { @@ -43,6 +45,7 @@ class RoomProfileController @Inject constructor( fun onUploadsClicked() fun onSettingsClicked() fun onLeaveRoomClicked() + fun onRoomIdClicked() } override fun buildModels(data: RoomProfileViewState?) { @@ -105,5 +108,19 @@ class RoomProfileController @Inject constructor( editable = false, action = { callback?.onLeaveRoomClicked() } ) + + // Advanced + if (vectorPreferences.developerMode()) { + buildProfileSection(stringProvider.getString(R.string.room_settings_category_advanced_title)) + buildProfileAction( + id = "roomId", + title = stringProvider.getString(R.string.room_settings_room_internal_id), + subtitle = roomSummary.roomId, + dividerColor = dividerColor, + divider = false, + editable = false, + action = { callback?.onRoomIdClicked() } + ) + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index 2b2a879e7e..869810b08b 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -36,6 +36,7 @@ import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.platform.VectorBaseFragment +import im.vector.riotx.core.utils.copyToClipboard import im.vector.riotx.core.utils.startSharePlainTextIntent import im.vector.riotx.features.crypto.util.toImageRes import im.vector.riotx.features.home.AvatarRenderer @@ -203,6 +204,10 @@ class RoomProfileFragment @Inject constructor( .show() } + override fun onRoomIdClicked() { + copyToClipboard(requireContext(), roomProfileArgs.roomId) + } + private fun onShareRoomProfile(permalink: String) { startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) } From b95dfa4473f098947efd207dcf0045d5bc7dcd15 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Feb 2020 18:14:02 +0100 Subject: [PATCH 123/187] Create getBestName method for User --- .../vector/matrix/android/api/session/user/model/User.kt | 7 ++++++- .../createdirect/CreateDirectRoomKnownUsersFragment.kt | 2 +- .../features/notifications/NotificationDrawerManager.kt | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/model/User.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/model/User.kt index f569f5e47e..753c9b609c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/model/User.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/model/User.kt @@ -24,4 +24,9 @@ data class User( val userId: String, val displayName: String? = null, val avatarUrl: String? = null -) +) { + /** + * Return the display name or the user id + */ + fun getBestName() = displayName?.takeIf { it.isNotEmpty() } ?: userId +} diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt index fc1a4b9e65..57727dcfaf 100644 --- a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt @@ -159,7 +159,7 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor( val chip = Chip(requireContext()) chip.setChipBackgroundColorResource(android.R.color.transparent) chip.chipStrokeWidth = dimensionConverter.dpToPx(1).toFloat() - chip.text = if (user.displayName.isNullOrBlank()) user.userId else user.displayName + chip.text = user.getBestName() chip.isClickable = true chip.isCheckable = false chip.isCloseIconVisible = true diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt index 8173d24550..0f1d5f466e 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt @@ -203,7 +203,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context val user = session.getUser(session.myUserId) // myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash - val myUserDisplayName = user?.displayName?.takeIf { it.isNotBlank() } ?: session.myUserId + val myUserDisplayName = user?.getBestName() ?: session.myUserId val myUserAvatarUrl = session.contentUrlResolver().resolveThumbnail(user?.avatarUrl, avatarSize, avatarSize, ContentUrlResolver.ThumbnailMethod.SCALE) synchronized(eventList) { Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER ") From a55e0f1af4d4151da8ea14ca188c476e6041f9d7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Feb 2020 18:17:40 +0100 Subject: [PATCH 124/187] Fix crash - workaround --- .../features/createdirect/CreateDirectRoomKnownUsersFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt index 57727dcfaf..41855d824c 100644 --- a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt @@ -142,7 +142,7 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor( if (data.isAdded) { addChipToGroup(data.user, chipGroup) } else { - if (chipGroup.size > data.index) { + if (data.index in 0..chipGroup.size) { chipGroup.removeViewAt(data.index) } } From 1072060cbbccc261e18e31d629a4c2dd3e58ff86 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Feb 2020 19:00:38 +0100 Subject: [PATCH 125/187] New direct chat: selecting a participant sometimes results in two breadcrumbs (#1022) --- CHANGES.md | 1 + .../CreateDirectRoomKnownUsersFragment.kt | 39 +++++++------------ .../CreateDirectRoomViewEvents.kt | 9 +---- .../createdirect/CreateDirectRoomViewModel.kt | 19 +-------- 4 files changed, 18 insertions(+), 50 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 90246416af..ffbaa42b3b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,6 +20,7 @@ Bugfix 🐛: - Fix joining rooms from directory via federation isn't working. (#808) - Leaving a room creates a stuck "leaving room" loading screen. (#1041) - Fix some invitation handling issues (#1013) + - New direct chat: selecting a participant sometimes results in two breadcrumbs (#1022) Translations 🗣: - diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt index 41855d824c..24b5394e5c 100644 --- a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomKnownUsersFragment.kt @@ -23,17 +23,14 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.widget.ScrollView -import androidx.core.view.size import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import com.google.android.material.chip.Chip -import com.google.android.material.chip.ChipGroup import com.jakewharton.rxbinding3.widget.textChanges import im.vector.matrix.android.api.session.user.model.User import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.configureWith -import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.extensions.hideKeyboard import im.vector.riotx.core.extensions.setupAsSearch import im.vector.riotx.core.platform.VectorBaseFragment @@ -61,11 +58,6 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor( setupFilterView() setupAddByMatrixIdView() setupCloseView() - viewModel.observeViewEvents { - when (it) { - is CreateDirectRoomViewEvents.SelectUserAction -> updateChipsView(it) - }.exhaustive - } viewModel.selectSubscribe(this, CreateDirectRoomViewState::selectedUsers) { renderSelectedUsers(it) } @@ -138,24 +130,24 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor( knownUsersController.setData(it) } - private fun updateChipsView(data: CreateDirectRoomViewEvents.SelectUserAction) { - if (data.isAdded) { - addChipToGroup(data.user, chipGroup) - } else { - if (data.index in 0..chipGroup.size) { - chipGroup.removeViewAt(data.index) + private fun renderSelectedUsers(selectedUsers: Set) { + invalidateOptionsMenu() + + val currentNumberOfChips = chipGroup.childCount + val newNumberOfChips = selectedUsers.size + + chipGroup.removeAllViews() + selectedUsers.forEach { addChipToGroup(it) } + + // Scroll to the bottom when adding chips. When removing chips, do not scroll + if (newNumberOfChips >= currentNumberOfChips) { + chipGroupScrollView.post { + chipGroupScrollView.fullScroll(ScrollView.FOCUS_DOWN) } } } - private fun renderSelectedUsers(selectedUsers: Set) { - invalidateOptionsMenu() - if (selectedUsers.isNotEmpty() && chipGroup.size == 0) { - selectedUsers.forEach { addChipToGroup(it, chipGroup) } - } - } - - private fun addChipToGroup(user: User, chipGroup: ChipGroup) { + private fun addChipToGroup(user: User) { val chip = Chip(requireContext()) chip.setChipBackgroundColorResource(android.R.color.transparent) chip.chipStrokeWidth = dimensionConverter.dpToPx(1).toFloat() @@ -167,9 +159,6 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor( chip.setOnCloseIconClickListener { viewModel.handle(CreateDirectRoomAction.RemoveSelectedUser(user)) } - chipGroupScrollView.post { - chipGroupScrollView.fullScroll(ScrollView.FOCUS_DOWN) - } } override fun onItemClick(user: User) { diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewEvents.kt index 168f23c9f7..0ed584ac6b 100644 --- a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewEvents.kt @@ -16,16 +16,9 @@ package im.vector.riotx.features.createdirect -import im.vector.matrix.android.api.session.user.model.User import im.vector.riotx.core.platform.VectorViewEvents /** * Transient events for create direct room screen */ -sealed class CreateDirectRoomViewEvents : VectorViewEvents { - data class SelectUserAction( - val user: User, - val isAdded: Boolean, - val index: Int - ) : CreateDirectRoomViewEvents() -} +sealed class CreateDirectRoomViewEvents : VectorViewEvents diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt index b115a623a8..71fae11486 100644 --- a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt @@ -27,9 +27,9 @@ import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams -import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.util.toMatrixItem import im.vector.matrix.rx.rx +import im.vector.riotx.core.extensions.toggle import im.vector.riotx.core.platform.VectorViewModel import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers @@ -91,30 +91,15 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted } private fun handleRemoveSelectedUser(action: CreateDirectRoomAction.RemoveSelectedUser) = withState { state -> - val index = state.selectedUsers.indexOfFirst { it.userId == action.user.userId } val selectedUsers = state.selectedUsers.minus(action.user) setState { copy(selectedUsers = selectedUsers) } - _viewEvents.post(CreateDirectRoomViewEvents.SelectUserAction(action.user, false, index)) } private fun handleSelectUser(action: CreateDirectRoomAction.SelectUser) = withState { state -> // Reset the filter asap directoryUsersSearch.accept("") - val isAddOperation: Boolean - val selectedUsers: Set - val indexOfUser = state.selectedUsers.indexOfFirst { it.userId == action.user.userId } - val changeIndex: Int - if (indexOfUser == -1) { - changeIndex = state.selectedUsers.size - selectedUsers = state.selectedUsers.plus(action.user) - isAddOperation = true - } else { - changeIndex = indexOfUser - selectedUsers = state.selectedUsers.minus(action.user) - isAddOperation = false - } + val selectedUsers = state.selectedUsers.toggle(action.user) setState { copy(selectedUsers = selectedUsers) } - _viewEvents.post(CreateDirectRoomViewEvents.SelectUserAction(action.user, isAddOperation, changeIndex)) } private fun observeDirectoryUsers() { From e73f138151965f357157406f3a5175758c83c6be Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Feb 2020 07:17:25 +0100 Subject: [PATCH 126/187] Improve CreateRoomParams API: update Javadoc and ensure the return values will not be discarded (#1070) --- CHANGES.md | 1 + .../room/model/create/CreateRoomParams.kt | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ffbaa42b3b..7d62e54caa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -28,6 +28,7 @@ Translations 🗣: SDK API changes ⚠️: - Get crypto methods through Session.cryptoService() - ProgressListener.onProgress() function will be invoked on the background thread instead of UI thread + - Improve CreateRoomParams API (#1070) Build 🧱: - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt index b69c189f89..536bf16dbc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.api.session.room.model.create import android.util.Patterns +import androidx.annotation.CheckResult import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import im.vector.matrix.android.api.MatrixPatterns.isUserId @@ -120,14 +121,15 @@ data class CreateRoomParams( @Json(name = "power_level_content_override") val powerLevelContentOverride: PowerLevelsContent? = null ) { - /** - * Set to true means that if cross-signing is enabled and we can get keys for every invited users, - * the encryption will be enabled on the created room - */ @Transient internal var enableEncryptionIfInvitedUsersSupportIt: Boolean = false private set + /** + * After calling this method, when the room will be created, if cross-signing is enabled and we can get keys for every invited users, + * the encryption will be enabled on the created room + * @return this, to allow chaining methods + */ fun enableEncryptionIfInvitedUsersSupportIt(): CreateRoomParams { enableEncryptionIfInvitedUsersSupportIt = true return this @@ -137,7 +139,9 @@ data class CreateRoomParams( * Add the crypto algorithm to the room creation parameters. * * @param algorithm the algorithm + * @return a modified copy of the CreateRoomParams object, or this if there is no modification */ + @CheckResult fun enableEncryptionWithAlgorithm(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM): CreateRoomParams { return if (algorithm == MXCRYPTO_ALGORITHM_MEGOLM) { val contentMap = mapOf("algorithm" to algorithm) @@ -161,7 +165,9 @@ data class CreateRoomParams( * Force the history visibility in the room creation parameters. * * @param historyVisibility the expected history visibility, set null to remove any existing value. + * @return a modified copy of the CreateRoomParams object */ + @CheckResult fun setHistoryVisibility(historyVisibility: RoomHistoryVisibility?): CreateRoomParams { // Remove the existing value if any. val newInitialStates = initialStates @@ -187,7 +193,9 @@ data class CreateRoomParams( /** * Mark as a direct message room. + * @return a modified copy of the CreateRoomParams object */ + @CheckResult fun setDirectMessage(): CreateRoomParams { return copy( preset = CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT, @@ -217,6 +225,7 @@ data class CreateRoomParams( fun isDirect(): Boolean { return preset == CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT && isDirect == true + // TODO This test is not ok && (1 == getInviteCount() || 1 == getInvite3PidCount()) } @@ -232,7 +241,9 @@ data class CreateRoomParams( * ids might be a matrix id or an email address. * * @param ids the participant ids to add. + * @return a modified copy of the CreateRoomParams object */ + @CheckResult fun addParticipantIds(hsConfig: HomeServerConnectionConfig, userId: String, ids: List): CreateRoomParams { From fc95bf492624d2f2c39702767c6f4f9898e5e8e8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Feb 2020 07:32:22 +0100 Subject: [PATCH 127/187] Improve CreateRoomParams API: update some API for better chaining of builder like methods (#1070) --- .../room/model/create/CreateRoomParams.kt | 39 ++++++++++++------- .../createroom/CreateRoomViewModel.kt | 9 ++--- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt index 536bf16dbc..af2a5bf1e0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt @@ -128,33 +128,46 @@ data class CreateRoomParams( /** * After calling this method, when the room will be created, if cross-signing is enabled and we can get keys for every invited users, * the encryption will be enabled on the created room + * @param value true to activate this behavior. * @return this, to allow chaining methods */ - fun enableEncryptionIfInvitedUsersSupportIt(): CreateRoomParams { - enableEncryptionIfInvitedUsersSupportIt = true + fun enableEncryptionIfInvitedUsersSupportIt(value: Boolean = true): CreateRoomParams { + enableEncryptionIfInvitedUsersSupportIt = value return this } /** * Add the crypto algorithm to the room creation parameters. * - * @param algorithm the algorithm + * @param enable true to enable encryption. + * @param algorithm the algorithm, default to [MXCRYPTO_ALGORITHM_MEGOLM], which is actually the only supported algorithm for the moment * @return a modified copy of the CreateRoomParams object, or this if there is no modification */ @CheckResult - fun enableEncryptionWithAlgorithm(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM): CreateRoomParams { + fun enableEncryptionWithAlgorithm(enable: Boolean = true, + algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM): CreateRoomParams { + // Remove the existing value if any. + val newInitialStates = initialStates + ?.filter { it.type != EventType.STATE_ROOM_ENCRYPTION } + return if (algorithm == MXCRYPTO_ALGORITHM_MEGOLM) { - val contentMap = mapOf("algorithm" to algorithm) + if (enable) { + val contentMap = mapOf("algorithm" to algorithm) - val algoEvent = Event( - type = EventType.STATE_ROOM_ENCRYPTION, - stateKey = "", - content = contentMap.toContent() - ) + val algoEvent = Event( + type = EventType.STATE_ROOM_ENCRYPTION, + stateKey = "", + content = contentMap.toContent() + ) - copy( - initialStates = initialStates.orEmpty().filter { it.type != EventType.STATE_ROOM_ENCRYPTION } + algoEvent - ) + copy( + initialStates = newInitialStates.orEmpty() + algoEvent + ) + } else { + return copy( + initialStates = newInitialStates + ) + } } else { Timber.e("Unsupported algorithm: $algorithm") this diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewModel.kt index 6c750af5ac..d5a290c1f7 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomViewModel.kt @@ -30,7 +30,6 @@ import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.room.model.create.CreateRoomPreset -import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import im.vector.riotx.core.platform.EmptyViewEvents import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity @@ -91,10 +90,10 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: Cr visibility = if (state.isInRoomDirectory) RoomDirectoryVisibility.PUBLIC else RoomDirectoryVisibility.PRIVATE, // Public room preset = if (state.isPublic) CreateRoomPreset.PRESET_PUBLIC_CHAT else CreateRoomPreset.PRESET_PRIVATE_CHAT - ).let { - // Encryption - if (state.isEncrypted) it.enableEncryptionWithAlgorithm(MXCRYPTO_ALGORITHM_MEGOLM) else it - } + ) + // Encryption + .enableEncryptionWithAlgorithm(state.isEncrypted) + session.createRoom(createRoomParams, object : MatrixCallback { override fun onSuccess(data: String) { From 67180fd8db3eafd3c0b3bd0786f05a4b54f9878b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Feb 2020 07:38:10 +0100 Subject: [PATCH 128/187] New direct chat: selecting several participants was not adding the room to the direct chats list --- CHANGES.md | 1 + .../room/model/create/CreateRoomParams.kt | 16 ---------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7d62e54caa..37b6c63393 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ Bugfix 🐛: - Leaving a room creates a stuck "leaving room" loading screen. (#1041) - Fix some invitation handling issues (#1013) - New direct chat: selecting a participant sometimes results in two breadcrumbs (#1022) + - New direct chat: selecting several participants was not adding the room to the direct chats list Translations 🗣: - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt index af2a5bf1e0..1abbe9ef3a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt @@ -216,20 +216,6 @@ data class CreateRoomParams( ) } - /** - * @return the invite count - */ - private fun getInviteCount(): Int { - return invitedUserIds?.size ?: 0 - } - - /** - * @return the pid invite count - */ - private fun getInvite3PidCount(): Int { - return invite3pids?.size ?: 0 - } - /** * Tells if the created room can be a direct chat one. * @@ -238,8 +224,6 @@ data class CreateRoomParams( fun isDirect(): Boolean { return preset == CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT && isDirect == true - // TODO This test is not ok - && (1 == getInviteCount() || 1 == getInvite3PidCount()) } /** From 076ac23b38372b94fdbb0ba466b1ee91e7d8346c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Feb 2020 07:59:54 +0100 Subject: [PATCH 129/187] Modification done by AndroidStudio 3.6 --- .idea/codeStyles/Project.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 40ee4ee5cf..b3719669fd 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,9 +1,6 @@
    - @@ -137,9 +134,9 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".features.home.HomeActivity" /> - + @@ -149,25 +146,17 @@ android:theme="@style/AppTheme.AttachmentsPreview" /> - - - - - + android:exported="false" /> - - - + android:exported="false" /> , + val resultKeyStoreAlias: String + ) : Parcelable + + private val uiDisposables = CompositeDisposable() + + private fun Disposable.disposeOnDestroyView(): Disposable { + uiDisposables.add(this) + return this + } + + private val viewModel: SharedSecureStorageViewModel by viewModel() + @Inject lateinit var viewModelFactory: SharedSecureStorageViewModel.Factory + @Inject lateinit var errorFormatter: ErrorFormatter + + override fun injectWith(injector: ScreenComponent) { + super.injectWith(injector) + injector.inject(this) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + toolbar.visibility = View.GONE + if (isFirstCreation()) { + addFragment(R.id.container, SharedSecuredStoragePassphraseFragment::class.java) + } + + + viewModel.viewEvents + .observe() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + observeViewEvents(it) + } + .disposeOnDestroyView() + + viewModel.subscribe(this) { +// renderState(it) + } + } + + private fun observeViewEvents(it: SharedSecureStorageViewEvent?) { + when (it) { + is SharedSecureStorageViewEvent.Dismiss -> { + setResult(Activity.RESULT_CANCELED) + finish() + } + is SharedSecureStorageViewEvent.ShowModalLoading -> { + showWaitingView() + } + is SharedSecureStorageViewEvent.HideModalLoading -> { + hideWaitingView() + } + is SharedSecureStorageViewEvent.UpdateLoadingState -> { + updateWaitingView(it.waitingData) + } + } + } + +// fun renderState(state: SharedSecureStorageViewState) { +// } + + companion object { + + const val RESULT_KEYSTORE_ALIAS = "SharedSecureStorageActivity" + fun newIntent(context: Context, keyId: String? = null, requestedSecrets: List, resultKeyStoreAlias: String = RESULT_KEYSTORE_ALIAS): Intent { + require(requestedSecrets.isNotEmpty()) + return Intent(context, SharedSecureStorageActivity::class.java).also { + it.putExtra(MvRx.KEY_ARG, Args( + keyId, + requestedSecrets, + resultKeyStoreAlias + )) + } + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecureStorageViewModel.kt new file mode 100644 index 0000000000..44ba52a8d3 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -0,0 +1,134 @@ +/* + * 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 im.vector.riotx.features.crypto.quads + +import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.listeners.ProgressListener +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.securestorage.Curve25519AesSha2KeySpec +import im.vector.matrix.android.api.session.securestorage.KeyInfoResult +import im.vector.riotx.R +import im.vector.riotx.core.platform.VectorViewModel +import im.vector.riotx.core.platform.WaitingViewData +import im.vector.riotx.core.resources.StringProvider + +data class SharedSecureStorageViewState( + val requestedSecrets: List = emptyList(), + val passphraseVisible: Boolean = false +) : MvRxState + +class SharedSecureStorageViewModel @AssistedInject constructor( + @Assisted initialState: SharedSecureStorageViewState, + private val stringProvider: StringProvider, + private val session: Session) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: SharedSecureStorageViewState): SharedSecureStorageViewModel + } + + override fun handle(action: SharedSecureStorageAction) = withState { state -> + when (action) { + is SharedSecureStorageAction.TogglePasswordVisibility -> { + setState { + copy( + passphraseVisible = !passphraseVisible + ) + } + } + is SharedSecureStorageAction.Cancel -> { + _viewEvents.post(SharedSecureStorageViewEvent.Dismiss) + } + is SharedSecureStorageAction.SubmitPassphrase -> { + _viewEvents.post(SharedSecureStorageViewEvent.ShowModalLoading) + val passphrase = action.passphrase + val keyInfoResult = session.sharedSecretStorageService.getDefaultKey() + if (!keyInfoResult.isSuccess()) { + _viewEvents.post(SharedSecureStorageViewEvent.HideModalLoading) + _viewEvents.post(SharedSecureStorageViewEvent.Error("Cannot find ssss key")) + return@withState + } + val keyInfo = (keyInfoResult as KeyInfoResult.Success).keyInfo + + // TODO +// val decryptedSecretMap = HashMap() +// val errors = ArrayList() + _viewEvents.post(SharedSecureStorageViewEvent.UpdateLoadingState( + WaitingViewData( + message = stringProvider.getString(R.string.keys_backup_restoring_computing_key_waiting_message), + isIndeterminate = true + ) + )) + state.requestedSecrets.forEach { + val keySpec = Curve25519AesSha2KeySpec.fromPassphrase( + passphrase, + keyInfo.content.passphrase?.salt ?: "", + keyInfo.content.passphrase?.iterations ?: 0, + // TODO + object : ProgressListener { + override fun onProgress(progress: Int, total: Int) { + _viewEvents.post(SharedSecureStorageViewEvent.UpdateLoadingState( + WaitingViewData( + message = stringProvider.getString(R.string.keys_backup_restoring_computing_key_waiting_message), + isIndeterminate = false, + progress = progress, + progressTotal = total + ) + )) + } + } + ) + session.sharedSecretStorageService.getSecret( + name = it, + keyId = keyInfo.id, + secretKey = keySpec, + callback = object : MatrixCallback { + override fun onSuccess(data: String) { + _viewEvents.post(SharedSecureStorageViewEvent.HideModalLoading) + } + + override fun onFailure(failure: Throwable) { + _viewEvents.post(SharedSecureStorageViewEvent.HideModalLoading) + _viewEvents.post(SharedSecureStorageViewEvent.InlineError(failure.localizedMessage)) + } + }) + } + } + } + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: SharedSecureStorageViewState): SharedSecureStorageViewModel? { + val activity: SharedSecureStorageActivity = viewModelContext.activity() + val args: SharedSecureStorageActivity.Args = activity.intent.getParcelableExtra(MvRx.KEY_ARG) + return activity.viewModelFactory.create( + SharedSecureStorageViewState( + requestedSecrets = args.requestedSecrets + ) + ) + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecuredStoragePassphraseFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecuredStoragePassphraseFragment.kt new file mode 100644 index 0000000000..58c24cf2e9 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecuredStoragePassphraseFragment.kt @@ -0,0 +1,142 @@ +/* + * 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 im.vector.riotx.features.crypto.quads + +import android.os.Bundle +import android.view.View +import android.view.inputmethod.EditorInfo +import android.widget.Button +import android.widget.EditText +import android.widget.ImageView +import android.widget.TextView +import butterknife.BindView +import butterknife.OnClick +import com.airbnb.mvrx.activityViewModel +import com.airbnb.mvrx.withState +import com.google.android.material.textfield.TextInputLayout +import com.jakewharton.rxbinding3.widget.editorActionEvents +import com.jakewharton.rxbinding3.widget.textChanges +import im.vector.riotx.R +import im.vector.riotx.core.extensions.showPassword +import im.vector.riotx.core.platform.VectorBaseFragment +import im.vector.riotx.core.utils.DebouncedClickListener +import me.gujun.android.span.span +import java.util.concurrent.TimeUnit + +class SharedSecuredStoragePassphraseFragment : VectorBaseFragment() { + + override fun getLayoutResId() = R.layout.fragment_ssss_access_from_passphrase + + val sharedViewModel: SharedSecureStorageViewModel by activityViewModel() + + @BindView(R.id.ssss_restore_with_passphrase_warning_text) + lateinit var warningText: TextView + + @BindView(R.id.ssss_restore_with_passphrase_warning_reason) + lateinit var reasonText: TextView + + @BindView(R.id.ssss_passphrase_enter_til) + lateinit var mPassphraseInputLayout: TextInputLayout + + @BindView(R.id.ssss_passphrase_enter_edittext) + lateinit var mPassphraseTextEdit: EditText + + @BindView(R.id.ssss_view_show_password) + lateinit var mPassphraseReveal: ImageView + + @BindView(R.id.ssss_passphrase_submit) + lateinit var submitButton: Button + + @BindView(R.id.ssss_passphrase_cancel) + lateinit var cancelButton: Button + + @OnClick(R.id.ssss_view_show_password) + fun toggleVisibilityMode() { + sharedViewModel.handle(SharedSecureStorageAction.TogglePasswordVisibility) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + warningText.text = span { + span(getString(R.string.enter_secret_storage_passphrase_warning)) { + textStyle = "bold" + } + +" " + +getString(R.string.enter_secret_storage_passphrase_warning_text) + } + + reasonText.text = getString(R.string.enter_secret_storage_passphrase_reason_verify) + + mPassphraseTextEdit.editorActionEvents() + .throttleFirst(300, TimeUnit.MILLISECONDS) + .subscribe { + if (it.actionId == EditorInfo.IME_ACTION_DONE) { + submit() + } + } + .disposeOnDestroyView() + + mPassphraseTextEdit.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_NULL) { + submit() + return@setOnEditorActionListener true + } + return@setOnEditorActionListener false + } + + mPassphraseTextEdit.textChanges() + .subscribe { + mPassphraseInputLayout.error = null + submitButton.isEnabled = it.isNotBlank() + } + .disposeOnDestroyView() + + sharedViewModel.observeViewEvents { + when (it) { + is SharedSecureStorageViewEvent.InlineError -> { + mPassphraseInputLayout.error = it.message + } + } + } + + submitButton.setOnClickListener(DebouncedClickListener( + View.OnClickListener { + submit() + } + )) + + cancelButton.setOnClickListener(DebouncedClickListener( + View.OnClickListener { + sharedViewModel.handle(SharedSecureStorageAction.Cancel) + } + )) + } + + fun submit() { + val text = mPassphraseTextEdit.text.toString() + if (text.isBlank()) return // Should not reach this point as button disabled + submitButton.isEnabled = false + sharedViewModel.handle(SharedSecureStorageAction.SubmitPassphrase(text)) + } + + override fun invalidate() = withState(sharedViewModel) { state -> + val shouldBeVisible = state.passphraseVisible + mPassphraseTextEdit.showPassword(shouldBeVisible) + mPassphraseReveal.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black) + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationAction.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationAction.kt index 111328b0b1..15eba2d138 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationAction.kt @@ -29,4 +29,5 @@ sealed class VerificationAction : VectorViewModelAction { data class SASDoNotMatchAction(val otherUserId: String, val sasTransactionId: String) : VerificationAction() object GotItConclusion : VerificationAction() object SkipVerification : VerificationAction() + object VerifyFromPassphrase : VerificationAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt index 554344b06f..0efa2a8480 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt @@ -36,6 +36,7 @@ import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.extensions.commitTransactionNow import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment +import im.vector.riotx.features.crypto.quads.SharedSecureStorageActivity import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment import im.vector.riotx.features.crypto.verification.conclusion.VerificationConclusionFragment import im.vector.riotx.features.crypto.verification.emoji.VerificationEmojiCodeFragment @@ -87,6 +88,9 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { viewModel.observeViewEvents { when (it) { is VerificationBottomSheetViewEvents.Dismiss -> dismiss() + is VerificationBottomSheetViewEvents.AccessSecretStore -> { + startActivity(SharedSecureStorageActivity.newIntent(requireContext(),null, listOf("m.cross_signing.user_signing"))) + } }.exhaustive } } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewEvents.kt index 5509ecbe16..3a3bf54883 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewEvents.kt @@ -23,4 +23,5 @@ import im.vector.riotx.core.platform.VectorViewEvents */ sealed class VerificationBottomSheetViewEvents : VectorViewEvents { object Dismiss : VerificationBottomSheetViewEvents() + object AccessSecretStore : VerificationBottomSheetViewEvents() } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt index 7ae3cf29eb..981387abd7 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -253,6 +253,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini is VerificationAction.SkipVerification -> { _viewEvents.post(VerificationBottomSheetViewEvents.Dismiss) } + is VerificationAction.VerifyFromPassphrase -> { + _viewEvents.post(VerificationBottomSheetViewEvents.AccessSecretStore) + } }.exhaustive } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestFragment.kt index 0cc7a2beab..64000d07a1 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestFragment.kt @@ -63,6 +63,7 @@ class VerificationRequestFragment @Inject constructor( } override fun onClickRecoverFromPassphrase() { + viewModel.handle(VerificationAction.VerifyFromPassphrase) } override fun onClickDismiss() { diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt index 55e217670e..95cea89702 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt @@ -148,9 +148,22 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { // We need to ask sharedActionViewModel.hasDisplayedCompleteSecurityPrompt = true - navigator.waitSessionVerification(this) - } else { - // TODO upgrade security -> bootstrap cross signing + PopupAlertManager.postVectorAlert( + PopupAlertManager.VectorAlert( + uid = "completeSecurity", + title = getString(R.string.new_signin), + description = getString(R.string.complete_security), + iconId = R.drawable.ic_shield_warning + ).apply { + colorInt = ContextCompat.getColor(this@HomeActivity, R.color.riotx_destructive_accent) + contentAction = Runnable { + (weakCurrentActivity?.get() as? VectorBaseActivity)?.let { + it.navigator.waitSessionVerification(it) + } + } + dismissedAction = Runnable {} + } + ) } } diff --git a/vector/src/main/res/layout/fragment_ssss_access_from_passphrase.xml b/vector/src/main/res/layout/fragment_ssss_access_from_passphrase.xml new file mode 100644 index 0000000000..db50cfd34d --- /dev/null +++ b/vector/src/main/res/layout/fragment_ssss_access_from_passphrase.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/dimens.xml b/vector/src/main/res/values/dimens.xml index 665d1819f7..411945c8ed 100644 --- a/vector/src/main/res/values/dimens.xml +++ b/vector/src/main/res/values/dimens.xml @@ -32,5 +32,6 @@ 280dp + 16dp \ 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 6c2caedd01..31cd015051 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2151,4 +2151,5 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming No Connectivity to the server has been lost + ShareSecureStorageActivity diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 082ce235bb..e6a2701e33 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -21,6 +21,15 @@ Can‘t access an existing session? Use your recovery key or passphrase + + + New Sign In + + Enter secret storage passphrase + Warning: + You should only access secret storage from a trusted device + Access your secure message history and your cross-signing identity for verifying other sessions by entering your passphrase + diff --git a/vector/src/main/res/values/styles.xml b/vector/src/main/res/values/styles.xml new file mode 100644 index 0000000000..0bb90ea4d0 --- /dev/null +++ b/vector/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + - -