From 83d83e081265c3ea15475299c40a782a95982c7e Mon Sep 17 00:00:00 2001 From: onurays Date: Mon, 10 Feb 2020 17:58:48 +0300 Subject: [PATCH 01/50] Show confirmation dialog before deleting a message Fixes #967 --- CHANGES.md | 1 + .../home/room/detail/RoomDetailFragment.kt | 9 +++++++- .../main/res/layout/dialog_delete_event.xml | 21 +++++++++++++++++++ vector/src/main/res/values/strings_riotX.xml | 4 ++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/res/layout/dialog_delete_event.xml diff --git a/CHANGES.md b/CHANGES.md index 1a373da04c..7c56f9b6f9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ Features ✨: - Improvements πŸ™Œ: + - Show confirmation dialog before deleting a message (#967) - Improve navigation to the timeline (#789, #862) - Improve network detection. It is now based on the sync request status (#873, #882) 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 0d6dd292e9..733d474067 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 @@ -1108,7 +1108,14 @@ class RoomDetailFragment @Inject constructor( showSnackWithMessage(getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT) } is EventSharedAction.Delete -> { - roomDetailViewModel.handle(RoomDetailAction.RedactAction(action.eventId, context?.getString(R.string.event_redacted_by_user_reason))) + val layout = requireActivity().layoutInflater.inflate(R.layout.dialog_delete_event, null) + AlertDialog.Builder(requireActivity()) + .setView(layout) + .setPositiveButton(R.string.delete) { _, _ -> + roomDetailViewModel.handle(RoomDetailAction.RedactAction(action.eventId, context?.getString(R.string.event_redacted_by_user_reason))) + } + .setNegativeButton(R.string.cancel, null) + .show() } is EventSharedAction.Share -> { // TODO current data communication is too limited diff --git a/vector/src/main/res/layout/dialog_delete_event.xml b/vector/src/main/res/layout/dialog_delete_event.xml new file mode 100644 index 0000000000..9a508c055e --- /dev/null +++ b/vector/src/main/res/layout/dialog_delete_event.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 95e66e68be..28cf0dc948 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -19,6 +19,10 @@ + + Delete message? + + From 1bc1c8ec4af7b4aea1b41951b71cf7db434b8870 Mon Sep 17 00:00:00 2001 From: onurays Date: Mon, 10 Feb 2020 18:17:22 +0300 Subject: [PATCH 02/50] Reorder improvements. --- CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7c56f9b6f9..468ea40a2b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,9 +5,9 @@ Features ✨: - Improvements πŸ™Œ: - - Show confirmation dialog before deleting a message (#967) - Improve navigation to the timeline (#789, #862) - - Improve network detection. It is now based on the sync request status (#873, #882) + - Improve network detection. It is now based on the sync request status (#873, #882) + - Show confirmation dialog before deleting a message (#967) Other changes: - Support SSO login with Firefox account (#606) From ba0133a047deb61cffb68527ad12f716137ce604 Mon Sep 17 00:00:00 2001 From: onurays Date: Mon, 10 Feb 2020 23:03:37 +0300 Subject: [PATCH 03/50] Reason input for redacting event is added. --- .../home/room/detail/RoomDetailFragment.kt | 28 +++++++++++++----- .../main/res/layout/dialog_delete_event.xml | 29 +++++++++++++++++++ vector/src/main/res/values/strings_riotX.xml | 5 +++- 3 files changed, 53 insertions(+), 9 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 733d474067..2f33a0e5ba 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 @@ -61,6 +61,7 @@ import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.github.piasy.biv.BigImageViewer import com.github.piasy.biv.loader.ImageLoader +import com.google.android.material.checkbox.MaterialCheckBox import com.google.android.material.snackbar.Snackbar import com.google.android.material.textfield.TextInputEditText import com.jakewharton.rxbinding3.widget.textChanges @@ -793,6 +794,24 @@ class RoomDetailFragment @Inject constructor( .show() } + private fun promptReasonToDeleteEvent(eventId: String) { + val layout = requireActivity().layoutInflater.inflate(R.layout.dialog_delete_event, null) + val reasonCheckBox = layout.findViewById(R.id.deleteEventReasonCheck) + val reasonInput = layout.findViewById(R.id.deleteEventReasonInput) + + reasonCheckBox.setOnCheckedChangeListener { _, isChecked -> reasonInput.isEnabled = isChecked } + + AlertDialog.Builder(requireActivity()) + .setView(layout) + .setTitle(R.string.delete_event_dialog_title) + .setPositiveButton(R.string.remove) { _, _ -> + val reason = if (reasonCheckBox.isChecked) reasonInput.text.toString().takeIf { it.isNotBlank() } else null + roomDetailViewModel.handle(RoomDetailAction.RedactAction(eventId, reason)) + } + .setNegativeButton(R.string.cancel, null) + .show() + } + private fun displayRoomDetailActionResult(result: Async) { when (result) { is Fail -> { @@ -1108,14 +1127,7 @@ class RoomDetailFragment @Inject constructor( showSnackWithMessage(getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT) } is EventSharedAction.Delete -> { - val layout = requireActivity().layoutInflater.inflate(R.layout.dialog_delete_event, null) - AlertDialog.Builder(requireActivity()) - .setView(layout) - .setPositiveButton(R.string.delete) { _, _ -> - roomDetailViewModel.handle(RoomDetailAction.RedactAction(action.eventId, context?.getString(R.string.event_redacted_by_user_reason))) - } - .setNegativeButton(R.string.cancel, null) - .show() + promptReasonToDeleteEvent(action.eventId) } is EventSharedAction.Share -> { // TODO current data communication is too limited diff --git a/vector/src/main/res/layout/dialog_delete_event.xml b/vector/src/main/res/layout/dialog_delete_event.xml index 9a508c055e..eee3cc9df5 100644 --- a/vector/src/main/res/layout/dialog_delete_event.xml +++ b/vector/src/main/res/layout/dialog_delete_event.xml @@ -18,4 +18,33 @@ android:text="@string/delete_event_dialog_content" app:layout_constraintTop_toTopOf="parent" /> + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 28cf0dc948..914d3a1ddf 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -20,7 +20,10 @@ - Delete message? + Confirm Removal + Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change. + Include a reason + Reason for redacting From a7274b9df00ee6a4b27fcff62b3107c73436c76d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 10 Feb 2020 21:41:59 +0100 Subject: [PATCH 04/50] Version++ --- CHANGES.md | 21 +++++++++++++++++++++ vector/build.gradle | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index d083a5f2f5..ce10d8cc9a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,24 @@ +Changes in RiotX 0.16.0 (2020-XX-XX) +=================================================== + +Features ✨: + - + +Improvements πŸ™Œ: + - + +Other changes: + - + +Bugfix πŸ›: + - + +Translations πŸ—£: + - + +Build 🧱: + - + Changes in RiotX 0.15.0 (2020-02-10) =================================================== diff --git a/vector/build.gradle b/vector/build.gradle index fca82f935e..0517482904 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -15,7 +15,7 @@ androidExtensions { } ext.versionMajor = 0 -ext.versionMinor = 15 +ext.versionMinor = 16 ext.versionPatch = 0 static def getGitTimestamp() { From ce028f8bd202a73354899bc5b5bbc56a90997533 Mon Sep 17 00:00:00 2001 From: onurays Date: Tue, 11 Feb 2020 00:10:24 +0300 Subject: [PATCH 05/50] Gathering reason input is refactored. --- .../riotx/features/home/room/detail/RoomDetailFragment.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 2f33a0e5ba..351293833b 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 @@ -805,7 +805,9 @@ class RoomDetailFragment @Inject constructor( .setView(layout) .setTitle(R.string.delete_event_dialog_title) .setPositiveButton(R.string.remove) { _, _ -> - val reason = if (reasonCheckBox.isChecked) reasonInput.text.toString().takeIf { it.isNotBlank() } else null + val reason = reasonInput.text.toString() + .takeIf { reasonCheckBox.isChecked } + ?.takeIf { it.isNotBlank() } roomDetailViewModel.handle(RoomDetailAction.RedactAction(eventId, reason)) } .setNegativeButton(R.string.cancel, null) From 35ed22ab2b1db6cbdb7860805f3aa9a3fa38e59c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 10 Feb 2020 22:19:43 +0100 Subject: [PATCH 06/50] improve script --- tools/release/download_buildkite_artifacts.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/release/download_buildkite_artifacts.py b/tools/release/download_buildkite_artifacts.py index 849b98582e..ef4251a14f 100755 --- a/tools/release/download_buildkite_artifacts.py +++ b/tools/release/download_buildkite_artifacts.py @@ -44,7 +44,6 @@ parser.add_argument('-b', parser.add_argument('-e', '--expecting', type=int, - default=-1, help='the expected number of artifacts. If omitted, no check will be done.') parser.add_argument('-d', '--directory', @@ -93,8 +92,8 @@ print(" git commit message : \"%s\"" % data0.get('message')) print(" build state : %s" % data0.get('state')) if data0.get('state') != 'passed': - print("❌ Error, the build failed (state: %s)" % data0.get('state')) - exit(0) + print("❌ Error, the build is in state '%s', and not 'passed'" % data0.get('state')) + exit(1) ### Fetch artifacts list @@ -110,7 +109,7 @@ data = json.loads(r.content.decode()) print(" %d artifact(s) found." % len(data)) -if args.expecting != -1 and args.expecting != len(data): +if args.expecting is not None and args.expecting != len(data): print("Error, expecting %d artifacts and found %d." % (args.expecting, len(data))) exit(1) From 96341df5e7fdc9ff6855b2646ecec68962c19c51 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 11 Feb 2020 11:00:01 +0100 Subject: [PATCH 07/50] Format layout --- vector/src/main/res/layout/dialog_delete_event.xml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/vector/src/main/res/layout/dialog_delete_event.xml b/vector/src/main/res/layout/dialog_delete_event.xml index eee3cc9df5..8ca7a25113 100644 --- a/vector/src/main/res/layout/dialog_delete_event.xml +++ b/vector/src/main/res/layout/dialog_delete_event.xml @@ -22,28 +22,29 @@ android:id="@+id/deleteEventReasonCheck" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginTop="8dp" android:checked="true" android:text="@string/delete_event_dialog_reason_checkbox" - android:layout_marginTop="8dp" - app:layout_constraintTop_toBottomOf="@+id/deleteEventConfirmationText" - app:layout_constraintStart_toStartOf="parent"/> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/deleteEventConfirmationText" /> + app:layout_constraintTop_toBottomOf="@+id/deleteEventReasonCheck"> + android:text="@string/event_redacted_by_user_reason" /> From b40da4ef0ff50aee9b832cbc72ddc12aa1cfdf62 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 11 Feb 2020 11:00:33 +0100 Subject: [PATCH 08/50] Disable TextInputLayout instead of TextInputEditText --- .../riotx/features/home/room/detail/RoomDetailFragment.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 5a905a2e93..831d66b7f4 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 @@ -64,6 +64,7 @@ import com.github.piasy.biv.loader.ImageLoader import com.google.android.material.checkbox.MaterialCheckBox import com.google.android.material.snackbar.Snackbar import com.google.android.material.textfield.TextInputEditText +import com.google.android.material.textfield.TextInputLayout import com.jakewharton.rxbinding3.widget.textChanges import im.vector.matrix.android.api.permalinks.PermalinkFactory import im.vector.matrix.android.api.session.Session @@ -790,9 +791,10 @@ class RoomDetailFragment @Inject constructor( private fun promptReasonToDeleteEvent(eventId: String) { val layout = requireActivity().layoutInflater.inflate(R.layout.dialog_delete_event, null) val reasonCheckBox = layout.findViewById(R.id.deleteEventReasonCheck) + val reasonTextInputLayout = layout.findViewById(R.id.deleteEventReasonTextInputLayout) val reasonInput = layout.findViewById(R.id.deleteEventReasonInput) - reasonCheckBox.setOnCheckedChangeListener { _, isChecked -> reasonInput.isEnabled = isChecked } + reasonCheckBox.setOnCheckedChangeListener { _, isChecked -> reasonTextInputLayout.isEnabled = isChecked } AlertDialog.Builder(requireActivity()) .setView(layout) From 6fce2a306641c4765abeb71d9d2164e5b8d415a0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 11 Feb 2020 11:04:21 +0100 Subject: [PATCH 09/50] Change wording adding "..." because there is now confirmation dialog And make the item red because it is destructive. Also use "redact" terminology --- .../features/home/room/detail/RoomDetailFragment.kt | 8 ++++---- .../room/detail/timeline/action/EventSharedAction.kt | 12 +++++++----- .../timeline/action/MessageActionsEpoxyController.kt | 2 +- .../timeline/action/MessageActionsViewModel.kt | 2 +- vector/src/main/res/values/strings_riotX.xml | 2 +- 5 files changed, 14 insertions(+), 12 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 831d66b7f4..7303b7ce7f 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 @@ -788,7 +788,7 @@ class RoomDetailFragment @Inject constructor( .show() } - private fun promptReasonToDeleteEvent(eventId: String) { + private fun promptReasonToRedactEvent(eventId: String) { val layout = requireActivity().layoutInflater.inflate(R.layout.dialog_delete_event, null) val reasonCheckBox = layout.findViewById(R.id.deleteEventReasonCheck) val reasonTextInputLayout = layout.findViewById(R.id.deleteEventReasonTextInputLayout) @@ -797,8 +797,8 @@ class RoomDetailFragment @Inject constructor( reasonCheckBox.setOnCheckedChangeListener { _, isChecked -> reasonTextInputLayout.isEnabled = isChecked } AlertDialog.Builder(requireActivity()) - .setView(layout) .setTitle(R.string.delete_event_dialog_title) + .setView(layout) .setPositiveButton(R.string.remove) { _, _ -> val reason = reasonInput.text.toString() .takeIf { reasonCheckBox.isChecked } @@ -1120,8 +1120,8 @@ class RoomDetailFragment @Inject constructor( copyToClipboard(requireContext(), action.content, false) showSnackWithMessage(getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT) } - is EventSharedAction.Delete -> { - promptReasonToDeleteEvent(action.eventId) + is EventSharedAction.Redact -> { + promptReasonToRedactEvent(action.eventId) } is EventSharedAction.Share -> { // TODO current data communication is too limited diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt index 91922611dd..033199c88b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/EventSharedAction.kt @@ -22,7 +22,9 @@ import im.vector.riotx.R import im.vector.riotx.core.platform.VectorSharedAction import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData -sealed class EventSharedAction(@StringRes val titleRes: Int, @DrawableRes val iconResId: Int) : VectorSharedAction { +sealed class EventSharedAction(@StringRes val titleRes: Int, + @DrawableRes val iconResId: Int, + val destructive: Boolean = false) : VectorSharedAction { object Separator : EventSharedAction(0, 0) @@ -51,10 +53,10 @@ sealed class EventSharedAction(@StringRes val titleRes: Int, @DrawableRes val ic EventSharedAction(R.string.global_retry, R.drawable.ic_refresh_cw) data class Remove(val eventId: String) : - EventSharedAction(R.string.remove, R.drawable.ic_trash) + EventSharedAction(R.string.remove, R.drawable.ic_trash, true) - data class Delete(val eventId: String) : - EventSharedAction(R.string.delete, R.drawable.ic_delete) + data class Redact(val eventId: String) : + EventSharedAction(R.string.message_action_item_redact, R.drawable.ic_delete, true) data class Cancel(val eventId: String) : EventSharedAction(R.string.cancel, R.drawable.ic_close_round) @@ -81,7 +83,7 @@ sealed class EventSharedAction(@StringRes val titleRes: Int, @DrawableRes val ic EventSharedAction(R.string.report_content_custom, R.drawable.ic_report_custom) data class IgnoreUser(val senderId: String?) : - EventSharedAction(R.string.message_ignore_user, R.drawable.ic_alert_triangle) + EventSharedAction(R.string.message_ignore_user, R.drawable.ic_alert_triangle, true) data class QuickReact(val eventId: String, val clickedOn: String, val add: Boolean) : EventSharedAction(0, 0) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index bdfdb02be1..d2cdf37eab 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -111,7 +111,7 @@ class MessageActionsEpoxyController @Inject constructor( showExpand(action is EventSharedAction.ReportContent) expanded(state.expendedReportContentMenu) listener(View.OnClickListener { listener?.didSelectMenuAction(action) }) - destructive(action is EventSharedAction.IgnoreUser) + destructive(action.destructive) } if (action is EventSharedAction.ReportContent && state.expendedReportContentMenu) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 114d80d9af..4b130e2103 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -227,7 +227,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } if (canRedact(timelineEvent, session.myUserId)) { - add(EventSharedAction.Delete(eventId)) + add(EventSharedAction.Redact(eventId)) } if (canCopy(msgType)) { diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 914d3a1ddf..e43eb70a88 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -11,7 +11,7 @@ - + Remove… From 3573aea6002adc2692677c81a47e07f8803d761f Mon Sep 17 00:00:00 2001 From: onurays Date: Tue, 11 Feb 2020 16:48:28 +0300 Subject: [PATCH 10/50] Fix crash by removing all notifications after clearing cache. Fixes #878 --- CHANGES.md | 2 +- vector/src/main/java/im/vector/riotx/features/MainActivity.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0af92bf211..1ffa3c7ebe 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,7 +11,7 @@ Other changes: - Bugfix πŸ›: - - + - Fix crash by removing all notifications after clearing cache (#878) Translations πŸ—£: - diff --git a/vector/src/main/java/im/vector/riotx/features/MainActivity.kt b/vector/src/main/java/im/vector/riotx/features/MainActivity.kt index c714be7650..bc5a1aff95 100644 --- a/vector/src/main/java/im/vector/riotx/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/MainActivity.kt @@ -85,7 +85,7 @@ class MainActivity : VectorBaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) args = parseArgs() - if (args.clearCredentials || args.isUserLoggedOut) { + if (args.clearCredentials || args.isUserLoggedOut || args.clearCache) { clearNotifications() } // Handle some wanted cleanup From 6ff974b3eab925024ae9897ee9955eb6e4316b47 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Feb 2020 11:16:21 +0100 Subject: [PATCH 11/50] Fix issue with verification when other client declares it can only show QR code (#988) --- CHANGES.md | 1 + .../verification/qrcode/VerificationTest.kt | 232 ++++++++++++++++++ .../PendingVerificationRequest.kt | 38 ++- .../VerificationChooseMethodViewModel.kt | 14 +- 4 files changed, 272 insertions(+), 13 deletions(-) create mode 100644 matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/VerificationTest.kt diff --git a/CHANGES.md b/CHANGES.md index 1ffa3c7ebe..0920127803 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ Other changes: Bugfix πŸ›: - Fix crash by removing all notifications after clearing cache (#878) + - Fix issue with verification when other client declares it can only show QR code (#988) Translations πŸ—£: - 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 new file mode 100644 index 0000000000..adde452619 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/VerificationTest.kt @@ -0,0 +1,232 @@ +/* + * 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 im.vector.matrix.android.api.session.crypto.sas.VerificationMethod +import im.vector.matrix.android.api.session.crypto.sas.VerificationService +import im.vector.matrix.android.common.CommonTestHelper +import im.vector.matrix.android.common.CryptoTestHelper +import im.vector.matrix.android.common.TestConstants +import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth +import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest +import org.amshove.kluent.shouldBe +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.JVM) +class VerificationTest : InstrumentedTest { + private val mTestHelper = CommonTestHelper(context()) + private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + + data class ExpectedResult( + val sasIsSupported: Boolean = false, + val otherCanScanQrCode: Boolean = false, + val otherCanShowQrCode: Boolean = false + ) + + private val sas = listOf( + VerificationMethod.SAS + ) + + private val sasShow = listOf( + VerificationMethod.SAS, + VerificationMethod.QR_CODE_SHOW + ) + + private val sasScan = listOf( + VerificationMethod.SAS, + VerificationMethod.QR_CODE_SCAN + ) + + private val sasShowScan = listOf( + VerificationMethod.SAS, + VerificationMethod.QR_CODE_SHOW, + VerificationMethod.QR_CODE_SCAN + ) + + @Test + fun test_aliceAndBob_sas_sas() = doTest( + sas, + sas, + ExpectedResult(sasIsSupported = true), + ExpectedResult(sasIsSupported = true) + ) + + @Test + fun test_aliceAndBob_sas_show() = doTest( + sas, + sasShow, + ExpectedResult(sasIsSupported = true), + ExpectedResult(sasIsSupported = true) + ) + + @Test + fun test_aliceAndBob_show_sas() = doTest( + sasShow, + sas, + ExpectedResult(sasIsSupported = true), + ExpectedResult(sasIsSupported = true) + ) + + @Test + fun test_aliceAndBob_sas_scan() = doTest( + sas, + sasScan, + ExpectedResult(sasIsSupported = true), + ExpectedResult(sasIsSupported = true) + ) + + @Test + fun test_aliceAndBob_scan_sas() = doTest( + sasScan, + sas, + ExpectedResult(sasIsSupported = true), + ExpectedResult(sasIsSupported = true) + ) + + @Test + fun test_aliceAndBob_scan_scan() = doTest( + sasScan, + sasScan, + ExpectedResult(sasIsSupported = true), + ExpectedResult(sasIsSupported = true) + ) + + @Test + fun test_aliceAndBob_show_show() = doTest( + sasShow, + sasShow, + ExpectedResult(sasIsSupported = true), + ExpectedResult(sasIsSupported = true) + ) + + @Test + fun test_aliceAndBob_show_scan() = doTest( + sasShow, + sasScan, + ExpectedResult(sasIsSupported = true, otherCanScanQrCode = true), + ExpectedResult(sasIsSupported = true, otherCanShowQrCode = true) + ) + + @Test + fun test_aliceAndBob_scan_show() = doTest( + sasScan, + sasShow, + ExpectedResult(sasIsSupported = true, otherCanShowQrCode = true), + ExpectedResult(sasIsSupported = true, otherCanScanQrCode = true) + ) + + @Test + fun test_aliceAndBob_all_all() = doTest( + sasShowScan, + sasShowScan, + ExpectedResult(sasIsSupported = true, otherCanShowQrCode = true, otherCanScanQrCode = true), + ExpectedResult(sasIsSupported = true, otherCanShowQrCode = true, otherCanScanQrCode = true) + ) + + // TODO Add tests without SAS + + private fun doTest(aliceSupportedMethods: List, + bobSupportedMethods: List, + expectedResultForAlice: ExpectedResult, + expectedResultForBob: ExpectedResult) { + val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + + val aliceSession = cryptoTestData.firstSession + val bobSession = cryptoTestData.secondSession!! + + mTestHelper.doSync { callback -> + aliceSession.getCrossSigningService() + .initializeCrossSigning(UserPasswordAuth( + user = aliceSession.myUserId, + password = TestConstants.PASSWORD + ), callback) + } + + mTestHelper.doSync { callback -> + bobSession.getCrossSigningService() + .initializeCrossSigning(UserPasswordAuth( + user = bobSession.myUserId, + password = TestConstants.PASSWORD + ), callback) + } + + val aliceVerificationService = aliceSession.getVerificationService() + val bobVerificationService = bobSession.getVerificationService() + + var aliceReadyPendingVerificationRequest: PendingVerificationRequest? = null + var bobReadyPendingVerificationRequest: PendingVerificationRequest? = null + + val latch = CountDownLatch(2) + val aliceListener = object : VerificationService.VerificationListener { + override fun verificationRequestUpdated(pr: PendingVerificationRequest) { + // Step 4: Alice receive the ready request + if (pr.isReady) { + aliceReadyPendingVerificationRequest = pr + latch.countDown() + } + } + } + aliceVerificationService.addListener(aliceListener) + + val bobListener = object : VerificationService.VerificationListener { + override fun verificationRequestCreated(pr: PendingVerificationRequest) { + // Step 2: Bob accepts the verification request + bobVerificationService.readyPendingVerificationInDMs( + bobSupportedMethods, + aliceSession.myUserId, + cryptoTestData.roomId, + pr.transactionId!! + ) + } + + override fun verificationRequestUpdated(pr: PendingVerificationRequest) { + // Step 3: Bob is ready + if (pr.isReady) { + bobReadyPendingVerificationRequest = pr + latch.countDown() + } + } + } + bobVerificationService.addListener(bobListener) + + val bobUserId = bobSession.myUserId + // Step 1: Alice starts a verification request + aliceVerificationService.requestKeyVerificationInDMs(aliceSupportedMethods, bobUserId, cryptoTestData.roomId) + mTestHelper.await(latch) + + aliceReadyPendingVerificationRequest!!.let { pr -> + pr.isSasSupported() shouldBe expectedResultForAlice.sasIsSupported + pr.otherCanShowQrCode() shouldBe expectedResultForAlice.otherCanShowQrCode + pr.otherCanScanQrCode() shouldBe expectedResultForAlice.otherCanScanQrCode + } + + bobReadyPendingVerificationRequest!!.let { pr -> + pr.isSasSupported() shouldBe expectedResultForBob.sasIsSupported + pr.otherCanShowQrCode() shouldBe expectedResultForBob.otherCanShowQrCode + pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode + } + + cryptoTestData.close() + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/PendingVerificationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/PendingVerificationRequest.kt index 3379ddd2ed..fe5f9dadb9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/PendingVerificationRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/PendingVerificationRequest.kt @@ -15,8 +15,8 @@ */ package im.vector.matrix.android.internal.crypto.verification +import im.vector.matrix.android.api.extensions.orFalse import im.vector.matrix.android.api.session.crypto.sas.CancelCode -import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SAS @@ -46,11 +46,37 @@ data class PendingVerificationRequest( val isFinished: Boolean = isSuccessful || cancelConclusion != null - fun hasMethod(method: VerificationMethod): Boolean? { - return when (method) { - VerificationMethod.SAS -> readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS) - VerificationMethod.QR_CODE_SHOW -> readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW) - VerificationMethod.QR_CODE_SCAN -> readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN) + /** + * SAS is supported if I support it and the other party support it + */ + fun isSasSupported(): Boolean { + return requestInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse() + && readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse() + } + + /** + * Other can show QR code if I can scan QR code and other can show QR code + */ + fun otherCanShowQrCode(): Boolean { + return if (isIncoming) { + requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() + && readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() + } else { + requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() + && readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() + } + } + + /** + * Other can scan QR code if I can show QR code and other can scan QR code + */ + fun otherCanScanQrCode(): Boolean { + return if (isIncoming) { + requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() + && readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() + } else { + requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() + && readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() } } } 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 75c1b69058..17d8c1578d 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 @@ -21,9 +21,9 @@ 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.extensions.orFalse import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.crypto.sas.QrCodeVerificationTransaction -import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod import im.vector.matrix.android.api.session.crypto.sas.VerificationService import im.vector.matrix.android.api.session.crypto.sas.VerificationTransaction import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest @@ -66,9 +66,9 @@ class VerificationChooseMethodViewModel @AssistedInject constructor( setState { copy( - otherCanShowQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SHOW) ?: false, - otherCanScanQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SCAN) ?: false, - SASModeAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false + otherCanShowQrCode = pvr?.otherCanShowQrCode().orFalse(), + otherCanScanQrCode = pvr?.otherCanScanQrCode().orFalse(), + SASModeAvailable = pvr?.isSasSupported().orFalse() ) } } @@ -103,10 +103,10 @@ class VerificationChooseMethodViewModel @AssistedInject constructor( return VerificationChooseMethodViewState(otherUserId = args.otherUserId, transactionId = args.verificationId ?: "", - otherCanShowQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SHOW) ?: false, - otherCanScanQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SCAN) ?: false, + otherCanShowQrCode = pvr?.otherCanShowQrCode().orFalse(), + otherCanScanQrCode = pvr?.otherCanScanQrCode().orFalse(), qrCodeText = (qrCodeVerificationTransaction as? QrCodeVerificationTransaction)?.qrCodeText, - SASModeAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false + SASModeAvailable = pvr?.isSasSupported().orFalse() ) } } From 377d944228394ededdb848eb0d6abd4d319a2172 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 11 Feb 2020 18:08:12 +0100 Subject: [PATCH 12/50] Cleanup API --- .../android/api/session/crypto/sas/VerificationService.kt | 2 +- .../crypto/verification/DefaultVerificationService.kt | 4 ++-- .../internal/crypto/verification/VerificationTransport.kt | 8 +++++--- .../verification/VerificationTransportRoomMessage.kt | 4 ++-- .../crypto/verification/VerificationTransportToDevice.kt | 4 ++-- .../verification/IncomingVerificationRequestHandler.kt | 1 - .../riotx/features/home/room/detail/RoomDetailAction.kt | 6 +++--- .../riotx/features/home/room/detail/RoomDetailFragment.kt | 2 +- .../features/home/room/detail/RoomDetailViewModel.kt | 1 - .../room/detail/timeline/factory/MessageItemFactory.kt | 1 - .../room/detail/timeline/item/VerificationRequestItem.kt | 8 ++------ 11 files changed, 18 insertions(+), 23 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt index 0dd143f792..3572905019 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt @@ -68,11 +68,11 @@ interface VerificationService { otherDevices: List?): PendingVerificationRequest fun declineVerificationRequestInDMs(otherUserId: String, - otherDeviceId: String, transactionId: String, roomId: String) // Only SAS method is supported for the moment + // TODO Parameter otherDeviceId should be removed in this case fun beginKeyVerificationInDMs(method: VerificationMethod, transactionId: String, roomId: String, 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 fc14b0a23a..d7589ede5c 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 @@ -1151,9 +1151,9 @@ internal class DefaultVerificationService @Inject constructor( return verificationRequest } - override fun declineVerificationRequestInDMs(otherUserId: String, otherDeviceId: String, transactionId: String, roomId: String) { + override fun declineVerificationRequestInDMs(otherUserId: String, transactionId: String, roomId: String) { verificationTransportRoomMessageFactory.createTransport(roomId, null) - .cancelTransaction(transactionId, otherUserId, otherDeviceId, CancelCode.User) + .cancelTransaction(transactionId, otherUserId, null, CancelCode.User) getExistingVerificationRequest(otherUserId, transactionId)?.let { updatePendingRequest(it.copy( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransport.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransport.kt index bc85cddf26..ee0e66959d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransport.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransport.kt @@ -42,7 +42,7 @@ internal interface VerificationTransport { fun cancelTransaction(transactionId: String, otherUserId: String, - otherUserDeviceId: String, + otherUserDeviceId: String?, code: CancelCode) fun done(transactionId: String) @@ -79,11 +79,13 @@ internal interface VerificationTransport { fun createMac(tid: String, mac: Map, keys: String): VerificationInfoMac - fun createReady(tid: String, fromDevice: String, methods: List): VerificationInfoReady + fun createReady(tid: String, + fromDevice: String, + methods: List): VerificationInfoReady // TODO Refactor fun sendVerificationReady(keyReq: VerificationInfoReady, otherUserId: String, - otherDeviceId: String, + otherDeviceId: String?, callback: (() -> Unit)?) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransportRoomMessage.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransportRoomMessage.kt index 30d94c52d9..11093ca3ba 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransportRoomMessage.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransportRoomMessage.kt @@ -208,7 +208,7 @@ internal class VerificationTransportRoomMessage( } } - override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceId: String, code: CancelCode) { + override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceId: String?, code: CancelCode) { Timber.d("## SAS canceling transaction $transactionId for reason $code") val event = createEventAndLocalEcho( type = EventType.KEY_VERIFICATION_CANCEL, @@ -337,7 +337,7 @@ internal class VerificationTransportRoomMessage( override fun sendVerificationReady(keyReq: VerificationInfoReady, otherUserId: String, - otherDeviceId: String, + otherDeviceId: String?, callback: (() -> Unit)?) { // Not applicable (send event is called directly) Timber.w("## SAS ignored verification ready with methods: ${keyReq.methods}") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransportToDevice.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransportToDevice.kt index f76f8331bc..1dae8fba68 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransportToDevice.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationTransportToDevice.kt @@ -80,7 +80,7 @@ internal class VerificationTransportToDevice( override fun sendVerificationReady(keyReq: VerificationInfoReady, otherUserId: String, - otherDeviceId: String, + otherDeviceId: String?, callback: (() -> Unit)?) { Timber.d("## SAS sending verification ready with methods: ${keyReq.methods}") val contentMap = MXUsersDevicesMap() @@ -159,7 +159,7 @@ internal class VerificationTransportToDevice( .executeBy(taskExecutor) } - override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceId: String, code: CancelCode) { + override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceId: String?, code: CancelCode) { Timber.d("## SAS canceling transaction $transactionId for reason $code") val cancelMessage = KeyVerificationCancel.create(transactionId, code) val contentMap = MXUsersDevicesMap() 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 0fbbccee8a..0ed29a9058 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 @@ -145,7 +145,6 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context } dismissedAction = Runnable { session?.getVerificationService()?.declineVerificationRequestInDMs(pr.otherUserId, - pr.requestInfo?.fromDevice ?: "", pr.transactionId ?: "", pr.roomId ?: "" ) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt index d0716bd047..053a0a75c6 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt @@ -65,8 +65,8 @@ sealed class RoomDetailAction : VectorViewModelAction { object ClearSendQueue : RoomDetailAction() object ResendAll : RoomDetailAction() - data class AcceptVerificationRequest(val transactionId: String, val otherUserId: String, val otherdDeviceId: String) : RoomDetailAction() - data class DeclineVerificationRequest(val transactionId: String, val otherUserId: String, val otherdDeviceId: String) : RoomDetailAction() + data class AcceptVerificationRequest(val transactionId: String, val otherUserId: String) : RoomDetailAction() + data class DeclineVerificationRequest(val transactionId: String, val otherUserId: String) : RoomDetailAction() data class RequestVerification(val userId: String) : RoomDetailAction() - data class ResumeVerification(val transactionId: String, val otherUserId: String? = null, val otherdDeviceId: String? = null) : RoomDetailAction() + data class ResumeVerification(val transactionId: String, val otherUserId: String?) : RoomDetailAction() } 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 7303b7ce7f..42ce21de08 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 @@ -1028,7 +1028,7 @@ class RoomDetailFragment @Inject constructor( override fun onEventCellClicked(informationData: MessageInformationData, messageContent: MessageContent?, view: View) { if (messageContent is MessageVerificationRequestContent) { - roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId)) + roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId, 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 710c70a948..0a343595ea 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 @@ -833,7 +833,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro private fun handleDeclineVerification(action: RoomDetailAction.DeclineVerificationRequest) { session.getVerificationService().declineVerificationRequestInDMs( action.otherUserId, - action.otherdDeviceId, action.transactionId, room.roomId) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 65a6f5f244..aa85950e01 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -181,7 +181,6 @@ class MessageItemFactory @Inject constructor( VerificationRequestItem.Attributes( otherUserId = otherUserId, otherUserName = otherUserName.toString(), - fromDevide = messageContent.fromDevice ?: "", referenceId = informationData.eventId, informationData = informationData, avatarRenderer = attributes.avatarRenderer, 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 24f992a001..853e40c516 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 @@ -134,12 +134,9 @@ abstract class VerificationRequestItem : AbsBaseMessageItem Date: Tue, 11 Feb 2020 18:25:01 +0100 Subject: [PATCH 13/50] Clenaup VerificationService.VerificationListener --- .../internal/crypto/verification/SASTest.kt | 42 ------------------- .../session/crypto/sas/VerificationService.kt | 10 ++--- .../crypto/keysrequest/KeyRequestHandler.kt | 3 -- .../IncomingVerificationRequestHandler.kt | 6 --- .../settings/devices/DevicesViewModel.kt | 1 - 5 files changed, 4 insertions(+), 58 deletions(-) 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 6ae2489993..6b254efd5d 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 @@ -67,13 +67,9 @@ class SASTest : InstrumentedTest { val bobTxCreatedLatch = CountDownLatch(1) val bobListener = object : VerificationService.VerificationListener { - override fun transactionCreated(tx: VerificationTransaction) {} - override fun transactionUpdated(tx: VerificationTransaction) { bobTxCreatedLatch.countDown() } - - override fun markedAsManuallyVerified(userId: String, deviceId: String) {} } bobVerificationService.addListener(bobListener) @@ -107,8 +103,6 @@ class SASTest : InstrumentedTest { val cancelLatch = CountDownLatch(1) val bobListener2 = object : VerificationService.VerificationListener { - override fun transactionCreated(tx: VerificationTransaction) {} - override fun transactionUpdated(tx: VerificationTransaction) { if (tx.transactionId == txID) { val immutableState = (tx as SASDefaultVerificationTransaction).state @@ -117,8 +111,6 @@ class SASTest : InstrumentedTest { } } } - - override fun markedAsManuallyVerified(userId: String, deviceId: String) {} } bobVerificationService.addListener(bobListener2) @@ -158,16 +150,12 @@ class SASTest : InstrumentedTest { val cancelLatch = CountDownLatch(1) val bobListener = object : VerificationService.VerificationListener { - override fun transactionCreated(tx: VerificationTransaction) {} - override fun transactionUpdated(tx: VerificationTransaction) { if (tx.transactionId == tid && tx.state is VerificationTxState.Cancelled) { cancelReason = (tx.state as VerificationTxState.Cancelled).cancelCode cancelLatch.countDown() } } - - override fun markedAsManuallyVerified(userId: String, deviceId: String) {} } bobSession.getVerificationService().addListener(bobListener) @@ -187,15 +175,11 @@ class SASTest : InstrumentedTest { val aliceDevice = aliceSession.getMyDevice().deviceId val aliceListener = object : VerificationService.VerificationListener { - override fun transactionCreated(tx: VerificationTransaction) {} - override fun transactionUpdated(tx: VerificationTransaction) { if ((tx as IncomingSasVerificationTransaction).uxState === IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) { (tx as IncomingSasVerificationTransaction).performAccept() } } - - override fun markedAsManuallyVerified(userId: String, deviceId: String) {} } aliceSession.getVerificationService().addListener(aliceListener) @@ -339,8 +323,6 @@ class SASTest : InstrumentedTest { aliceCancelledLatch.countDown() } } - - override fun markedAsManuallyVerified(userId: String, deviceId: String) {} } aliceVerificationService.addListener(aliceListener) @@ -373,10 +355,6 @@ class SASTest : InstrumentedTest { val aliceAcceptedLatch = CountDownLatch(1) val aliceListener = object : VerificationService.VerificationListener { - override fun markedAsManuallyVerified(userId: String, deviceId: String) {} - - override fun transactionCreated(tx: VerificationTransaction) {} - override fun transactionUpdated(tx: VerificationTransaction) { if ((tx as SASDefaultVerificationTransaction).state === VerificationTxState.OnAccepted) { val at = tx as SASDefaultVerificationTransaction @@ -389,16 +367,12 @@ class SASTest : InstrumentedTest { aliceVerificationService.addListener(aliceListener) val bobListener = object : VerificationService.VerificationListener { - override fun transactionCreated(tx: VerificationTransaction) {} - override fun transactionUpdated(tx: VerificationTransaction) { if ((tx as IncomingSasVerificationTransaction).uxState === IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) { val at = tx as IncomingSasVerificationTransaction at.performAccept() } } - - override fun markedAsManuallyVerified(userId: String, deviceId: String) {} } bobVerificationService.addListener(bobListener) @@ -434,8 +408,6 @@ class SASTest : InstrumentedTest { val aliceSASLatch = CountDownLatch(1) val aliceListener = object : VerificationService.VerificationListener { - override fun transactionCreated(tx: VerificationTransaction) {} - override fun transactionUpdated(tx: VerificationTransaction) { val uxState = (tx as OutgoingSasVerificationTransaction).uxState when (uxState) { @@ -445,15 +417,11 @@ class SASTest : InstrumentedTest { else -> Unit } } - - override fun markedAsManuallyVerified(userId: String, deviceId: String) {} } aliceVerificationService.addListener(aliceListener) val bobSASLatch = CountDownLatch(1) val bobListener = object : VerificationService.VerificationListener { - override fun transactionCreated(tx: VerificationTransaction) {} - override fun transactionUpdated(tx: VerificationTransaction) { val uxState = (tx as IncomingSasVerificationTransaction).uxState when (uxState) { @@ -466,8 +434,6 @@ class SASTest : InstrumentedTest { bobSASLatch.countDown() } } - - override fun markedAsManuallyVerified(userId: String, deviceId: String) {} } bobVerificationService.addListener(bobListener) @@ -498,8 +464,6 @@ class SASTest : InstrumentedTest { val aliceSASLatch = CountDownLatch(1) val aliceListener = object : VerificationService.VerificationListener { - override fun transactionCreated(tx: VerificationTransaction) {} - override fun transactionUpdated(tx: VerificationTransaction) { val uxState = (tx as OutgoingSasVerificationTransaction).uxState when (uxState) { @@ -512,15 +476,11 @@ class SASTest : InstrumentedTest { else -> Unit } } - - override fun markedAsManuallyVerified(userId: String, deviceId: String) {} } aliceVerificationService.addListener(aliceListener) val bobSASLatch = CountDownLatch(1) val bobListener = object : VerificationService.VerificationListener { - override fun transactionCreated(tx: VerificationTransaction) {} - override fun transactionUpdated(tx: VerificationTransaction) { val uxState = (tx as IncomingSasVerificationTransaction).uxState when (uxState) { @@ -536,8 +496,6 @@ class SASTest : InstrumentedTest { else -> Unit } } - - override fun markedAsManuallyVerified(userId: String, deviceId: String) {} } bobVerificationService.addListener(bobListener) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt index 3572905019..9df2dc9a89 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt @@ -95,15 +95,13 @@ interface VerificationService { otherUserId: String, transactionId: String): Boolean - // fun transactionUpdated(tx: SasVerificationTransaction) - interface VerificationListener { - fun transactionCreated(tx: VerificationTransaction) - fun transactionUpdated(tx: VerificationTransaction) - fun markedAsManuallyVerified(userId: String, deviceId: String) {} - fun verificationRequestCreated(pr: PendingVerificationRequest) {} fun verificationRequestUpdated(pr: PendingVerificationRequest) {} + + fun transactionCreated(tx: VerificationTransaction) {} + fun transactionUpdated(tx: VerificationTransaction) {} + fun markedAsManuallyVerified(userId: String, deviceId: String) {} } companion object { 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 856c71f888..bc97ac5ee5 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 @@ -262,9 +262,6 @@ class KeyRequestHandler @Inject constructor(private val context: Context) } } - override fun transactionCreated(tx: VerificationTransaction) { - } - override fun transactionUpdated(tx: VerificationTransaction) { if (tx is SasVerificationTransaction) { val state = tx.state 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 0ed29a9058..99c28b52dc 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 @@ -48,8 +48,6 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context this.session = null } - override fun transactionCreated(tx: VerificationTransaction) {} - override fun transactionUpdated(tx: VerificationTransaction) { if (!tx.isToDeviceTransport()) return // TODO maybe check also if @@ -111,9 +109,6 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context } } - override fun markedAsManuallyVerified(userId: String, deviceId: String) { - } - override fun verificationRequestCreated(pr: PendingVerificationRequest) { // For incoming request we should prompt (if not in activity where this request apply) if (pr.isIncoming) { @@ -162,7 +157,6 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context if (pr.isIncoming && (pr.isReady || pr.handledByOtherSession)) { PopupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr)) } - super.verificationRequestUpdated(pr) } private fun uniqueIdForVerificationRequest(pr: PendingVerificationRequest) = 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 333249f3de..1862412cb5 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 @@ -89,7 +89,6 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic super.onCleared() } - override fun transactionCreated(tx: VerificationTransaction) {} override fun transactionUpdated(tx: VerificationTransaction) { if (tx.state == VerificationTxState.Verified) { refreshDevicesList() From 6acfab324201271bb1d8fb5b647d2a81e8fdeaa3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Feb 2020 11:19:04 +0100 Subject: [PATCH 14/50] Rename VerificationListener to Listener --- .../internal/crypto/verification/SASTest.kt | 22 +++++++++---------- .../verification/qrcode/VerificationTest.kt | 4 ++-- .../session/crypto/sas/VerificationService.kt | 7 +++--- .../DefaultVerificationService.kt | 6 ++--- .../crypto/keysrequest/KeyRequestHandler.kt | 2 +- .../IncomingVerificationRequestHandler.kt | 2 +- .../VerificationBottomSheetViewModel.kt | 2 +- .../VerificationChooseMethodViewModel.kt | 2 +- .../emoji/VerificationEmojiCodeViewModel.kt | 2 +- .../settings/devices/DevicesViewModel.kt | 2 +- 10 files changed, 26 insertions(+), 25 deletions(-) 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 6b254efd5d..79670bb21e 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 @@ -66,7 +66,7 @@ class SASTest : InstrumentedTest { val bobVerificationService = bobSession!!.getVerificationService() val bobTxCreatedLatch = CountDownLatch(1) - val bobListener = object : VerificationService.VerificationListener { + val bobListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { bobTxCreatedLatch.countDown() } @@ -102,7 +102,7 @@ class SASTest : InstrumentedTest { // Let's cancel from alice side val cancelLatch = CountDownLatch(1) - val bobListener2 = object : VerificationService.VerificationListener { + val bobListener2 = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { if (tx.transactionId == txID) { val immutableState = (tx as SASDefaultVerificationTransaction).state @@ -149,7 +149,7 @@ class SASTest : InstrumentedTest { var cancelReason: CancelCode? = null val cancelLatch = CountDownLatch(1) - val bobListener = object : VerificationService.VerificationListener { + val bobListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { if (tx.transactionId == tid && tx.state is VerificationTxState.Cancelled) { cancelReason = (tx.state as VerificationTxState.Cancelled).cancelCode @@ -174,7 +174,7 @@ class SASTest : InstrumentedTest { val aliceUserID = aliceSession.myUserId val aliceDevice = aliceSession.getMyDevice().deviceId - val aliceListener = object : VerificationService.VerificationListener { + val aliceListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { if ((tx as IncomingSasVerificationTransaction).uxState === IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) { (tx as IncomingSasVerificationTransaction).performAccept() @@ -312,7 +312,7 @@ class SASTest : InstrumentedTest { val aliceCreatedLatch = CountDownLatch(2) val aliceCancelledLatch = CountDownLatch(2) val createdTx = mutableListOf() - val aliceListener = object : VerificationService.VerificationListener { + val aliceListener = object : VerificationService.Listener { override fun transactionCreated(tx: VerificationTransaction) { createdTx.add(tx as SASDefaultVerificationTransaction) aliceCreatedLatch.countDown() @@ -354,7 +354,7 @@ class SASTest : InstrumentedTest { var startReq: KeyVerificationStart? = null val aliceAcceptedLatch = CountDownLatch(1) - val aliceListener = object : VerificationService.VerificationListener { + val aliceListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { if ((tx as SASDefaultVerificationTransaction).state === VerificationTxState.OnAccepted) { val at = tx as SASDefaultVerificationTransaction @@ -366,7 +366,7 @@ class SASTest : InstrumentedTest { } aliceVerificationService.addListener(aliceListener) - val bobListener = object : VerificationService.VerificationListener { + val bobListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { if ((tx as IncomingSasVerificationTransaction).uxState === IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) { val at = tx as IncomingSasVerificationTransaction @@ -407,7 +407,7 @@ class SASTest : InstrumentedTest { val bobVerificationService = bobSession!!.getVerificationService() val aliceSASLatch = CountDownLatch(1) - val aliceListener = object : VerificationService.VerificationListener { + val aliceListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { val uxState = (tx as OutgoingSasVerificationTransaction).uxState when (uxState) { @@ -421,7 +421,7 @@ class SASTest : InstrumentedTest { aliceVerificationService.addListener(aliceListener) val bobSASLatch = CountDownLatch(1) - val bobListener = object : VerificationService.VerificationListener { + val bobListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { val uxState = (tx as IncomingSasVerificationTransaction).uxState when (uxState) { @@ -463,7 +463,7 @@ class SASTest : InstrumentedTest { val bobVerificationService = bobSession!!.getVerificationService() val aliceSASLatch = CountDownLatch(1) - val aliceListener = object : VerificationService.VerificationListener { + val aliceListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { val uxState = (tx as OutgoingSasVerificationTransaction).uxState when (uxState) { @@ -480,7 +480,7 @@ class SASTest : InstrumentedTest { aliceVerificationService.addListener(aliceListener) val bobSASLatch = CountDownLatch(1) - val bobListener = object : VerificationService.VerificationListener { + val bobListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { val uxState = (tx as IncomingSasVerificationTransaction).uxState when (uxState) { 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 adde452619..61ea0f35b4 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 @@ -178,7 +178,7 @@ class VerificationTest : InstrumentedTest { var bobReadyPendingVerificationRequest: PendingVerificationRequest? = null val latch = CountDownLatch(2) - val aliceListener = object : VerificationService.VerificationListener { + val aliceListener = object : VerificationService.Listener { override fun verificationRequestUpdated(pr: PendingVerificationRequest) { // Step 4: Alice receive the ready request if (pr.isReady) { @@ -189,7 +189,7 @@ class VerificationTest : InstrumentedTest { } aliceVerificationService.addListener(aliceListener) - val bobListener = object : VerificationService.VerificationListener { + val bobListener = object : VerificationService.Listener { override fun verificationRequestCreated(pr: PendingVerificationRequest) { // Step 2: Bob accepts the verification request bobVerificationService.readyPendingVerificationInDMs( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt index 9df2dc9a89..d3b33cb6f5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt @@ -30,9 +30,9 @@ import im.vector.matrix.android.internal.crypto.verification.PendingVerification */ interface VerificationService { - fun addListener(listener: VerificationListener) + fun addListener(listener: Listener) - fun removeListener(listener: VerificationListener) + fun removeListener(listener: Listener) /** * Mark this device as verified manually @@ -95,7 +95,8 @@ interface VerificationService { otherUserId: String, transactionId: String): Boolean - interface VerificationListener { + interface Listener { + // TODO javadoc fun verificationRequestCreated(pr: PendingVerificationRequest) {} fun verificationRequestUpdated(pr: PendingVerificationRequest) {} 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 d7589ede5c..00ac4a6986 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 @@ -176,9 +176,9 @@ internal class DefaultVerificationService @Inject constructor( } } - private var listeners = ArrayList() + private var listeners = ArrayList() - override fun addListener(listener: VerificationService.VerificationListener) { + override fun addListener(listener: VerificationService.Listener) { uiHandler.post { if (!listeners.contains(listener)) { listeners.add(listener) @@ -186,7 +186,7 @@ internal class DefaultVerificationService @Inject constructor( } } - override fun removeListener(listener: VerificationService.VerificationListener) { + override fun removeListener(listener: VerificationService.Listener) { uiHandler.post { listeners.remove(listener) } 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 bc97ac5ee5..f890aef91b 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 @@ -56,7 +56,7 @@ import kotlin.collections.HashMap @Singleton class KeyRequestHandler @Inject constructor(private val context: Context) : RoomKeysRequestListener, - VerificationService.VerificationListener { + VerificationService.Listener { private val alertsToRequests = HashMap>() 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 99c28b52dc..8765dbc0d9 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 @@ -34,7 +34,7 @@ import javax.inject.Singleton * Listens to the VerificationManager and add a new notification when an incoming request is detected. */ @Singleton -class IncomingVerificationRequestHandler @Inject constructor(private val context: Context) : VerificationService.VerificationListener { +class IncomingVerificationRequestHandler @Inject constructor(private val context: Context) : VerificationService.Listener { private var session: Session? = null 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 a9f9987c7f..85b878fe16 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 @@ -60,7 +60,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini @Assisted args: VerificationBottomSheet.VerificationArgs, private val session: Session) : VectorViewModel(initialState), - VerificationService.VerificationListener { + VerificationService.Listener { init { session.getVerificationService().addListener(this) 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 17d8c1578d..bdb07ed0dc 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 @@ -45,7 +45,7 @@ data class VerificationChooseMethodViewState( class VerificationChooseMethodViewModel @AssistedInject constructor( @Assisted initialState: VerificationChooseMethodViewState, private val session: Session -) : VectorViewModel(initialState), VerificationService.VerificationListener { +) : VectorViewModel(initialState), VerificationService.Listener { override fun transactionCreated(tx: VerificationTransaction) { transactionUpdated(tx) 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 69d106a4b4..637b7d7cc9 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 @@ -52,7 +52,7 @@ data class VerificationEmojiCodeViewState( class VerificationEmojiCodeViewModel @AssistedInject constructor( @Assisted initialState: VerificationEmojiCodeViewState, private val session: Session -) : VectorViewModel(initialState), VerificationService.VerificationListener { +) : VectorViewModel(initialState), VerificationService.Listener { init { withState { state -> 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 1862412cb5..b931f5d66f 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 @@ -52,7 +52,7 @@ data class DevicesViewState( class DevicesViewModel @AssistedInject constructor(@Assisted initialState: DevicesViewState, private val session: Session) - : VectorViewModel(initialState), VerificationService.VerificationListener { + : VectorViewModel(initialState), VerificationService.Listener { @AssistedInject.Factory interface Factory { From 3a044bd6552348e2e05b5f7a8f1f763f81d1c75f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Feb 2020 11:33:36 +0100 Subject: [PATCH 15/50] Add Javadoc --- .../session/crypto/sas/VerificationService.kt | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt index d3b33cb6f5..1b5f5d3dd6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt @@ -96,12 +96,31 @@ interface VerificationService { transactionId: String): Boolean interface Listener { - // TODO javadoc + /** + * Called when a verification request is created either by the user, or by the other user. + */ fun verificationRequestCreated(pr: PendingVerificationRequest) {} + + /** + * Called when a verification request is updated. + */ fun verificationRequestUpdated(pr: PendingVerificationRequest) {} + /** + * Called when a transaction is created, either by the user or initiated by the other user. + */ fun transactionCreated(tx: VerificationTransaction) {} + + /** + * Called when a transaction is updated. You may be interested to track the state of the VerificationTransaction. + */ fun transactionUpdated(tx: VerificationTransaction) {} + + /** + * Inform the the deviceId of the userId has been marked as manually verified by the SDK. + * It will be called after VerificationService.markedLocallyAsManuallyVerified() is called. + * + */ fun markedAsManuallyVerified(userId: String, deviceId: String) {} } From a0aebed3f7eb3042f2c9963e1418e8edc631a0a5 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 3 Jan 2020 01:22:05 +0100 Subject: [PATCH 16/50] Message Poll UX, and model --- .../api/session/events/model/RelationType.kt | 2 + .../model/message/MessageOptionsContent.kt | 54 ++++++++ .../session/room/model/message/MessageType.kt | 3 + .../room/model/relation/ReactionInfo.kt | 3 +- .../room/model/relation/RelationContent.kt | 1 + .../model/relation/RelationDefaultContent.kt | 3 +- .../api/session/room/send/SendService.kt | 7 + .../android/internal/di/MoshiProvider.kt | 1 + .../session/room/send/DefaultSendService.kt | 7 + .../room/send/LocalEchoEventFactory.kt | 16 +++ .../home/room/detail/RoomDetailAction.kt | 2 + .../home/room/detail/RoomDetailViewModel.kt | 5 + .../timeline/factory/MessageItemFactory.kt | 20 ++- .../detail/timeline/item/MessagePollItem.kt | 131 ++++++++++++++++++ .../timeline/item/PollResultLineView.kt | 73 ++++++++++ .../res/layout/item_timeline_event_base.xml | 7 + .../item_timeline_event_poll_result_item.xml | 41 ++++++ .../layout/item_timeline_event_poll_stub.xml | 108 +++++++++++++++ vector/src/main/res/values/attrs.xml | 7 + vector/src/main/res/values/strings_riotX.xml | 10 +- vector/src/main/res/values/styles_riot.xml | 8 ++ 21 files changed, 502 insertions(+), 7 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageOptionsContent.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessagePollItem.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/PollResultLineView.kt create mode 100644 vector/src/main/res/layout/item_timeline_event_poll_result_item.xml create mode 100644 vector/src/main/res/layout/item_timeline_event_poll_stub.xml diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/RelationType.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/RelationType.kt index 06b3e9bf2e..11e0f882c7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/RelationType.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/RelationType.kt @@ -26,4 +26,6 @@ object RelationType { const val REPLACE = "m.replace" /** Lets you define an event which references an existing event.*/ const val REFERENCE = "m.reference" + /** Lets you define an event which references an existing event.*/ + const val RESPONSE = "m.response" } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageOptionsContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageOptionsContent.kt new file mode 100644 index 0000000000..4841f134ae --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageOptionsContent.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.matrix.android.api.session.room.model.message + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.session.events.model.Content +import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent + +enum class OptionsType(val value: String) { + POLL("m.pool"), + BUTTONS("m.buttons"), +} + +/** + * Polls and bot buttons are m.room.message events with a msgtype of m.options, + */ +@JsonClass(generateAdapter = true) +data class MessageOptionsContent( + @Json(name = "msgtype") override val type: String, + @Json(name = "type") val optionType: String? = null, + @Json(name = "body") override val body: String, + @Json(name = "label") val label: String?, + @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, + @Json(name = "options") val options: List? = null, + @Json(name = "m.new_content") override val newContent: Content? = null +) : MessageContent + +@JsonClass(generateAdapter = true) +data class OptionItems( + @Json(name = "label") val label: String?, + @Json(name = "value") val value: String? +) + +@JsonClass(generateAdapter = true) +data class MessagePollResponseContent( + @Json(name = "msgtype") override val type: String = MessageType.MSGTYPE_RESPONSE, + @Json(name = "body") override val body: String, + @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, + @Json(name = "m.new_content") override val newContent: Content? = null +) : MessageContent diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageType.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageType.kt index 2707283325..d75cd7f8dc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageType.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageType.kt @@ -25,6 +25,9 @@ object MessageType { const val MSGTYPE_VIDEO = "m.video" const val MSGTYPE_LOCATION = "m.location" const val MSGTYPE_FILE = "m.file" + const val MSGTYPE_OPTIONS = "m.options" + const val MSGTYPE_RESPONSE = "m.response" + const val MSGTYPE_POLL_CLOSED = "m.poll_closed" const val MSGTYPE_VERIFICATION_REQUEST = "m.key.verification.request" // Add, in local, a fake message type in order to StickerMessage can inherit Message class // Because sticker isn't a message type but a event type without msgtype field diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/ReactionInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/ReactionInfo.kt index c4cbde98eb..622250da4e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/ReactionInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/ReactionInfo.kt @@ -25,5 +25,6 @@ data class ReactionInfo( @Json(name = "event_id") override val eventId: String, val key: String, // always null for reaction - @Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null + @Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null, + @Json(name = "option") override val option: Int? = null ) : RelationContent diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationContent.kt index c66d1b9770..d43c9f6a0c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationContent.kt @@ -23,4 +23,5 @@ interface RelationContent { val type: String? val eventId: String? val inReplyTo: ReplyToContent? + val option: Int? } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationDefaultContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationDefaultContent.kt index 853a381740..892fc61dee 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationDefaultContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationDefaultContent.kt @@ -22,5 +22,6 @@ import com.squareup.moshi.JsonClass data class RelationDefaultContent( @Json(name = "rel_type") override val type: String?, @Json(name = "event_id") override val eventId: String?, - @Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null + @Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null, + @Json(name = "option") override val option: Int? = null ) : RelationContent diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt index ac1b50bbcb..d3d4b13cc0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt @@ -61,6 +61,13 @@ interface SendService { */ fun sendMedias(attachments: List): Cancelable + /** + * Method to send a list of media asynchronously. + * @param attachments the list of media to send + * @return a [Cancelable] + */ + fun sendPollReply(pollEventId: String, optionIndex: Int, optionValue: String): Cancelable + /** * Redacts (delete) the given event. * @param event The event to redact 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 98cf9e234e..03b2c9d41d 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 @@ -46,6 +46,7 @@ object MoshiProvider { .registerSubtype(MessageVideoContent::class.java, MessageType.MSGTYPE_VIDEO) .registerSubtype(MessageLocationContent::class.java, MessageType.MSGTYPE_LOCATION) .registerSubtype(MessageFileContent::class.java, MessageType.MSGTYPE_FILE) + .registerSubtype(MessageOptionsContent::class.java, MessageType.MSGTYPE_OPTIONS) .registerSubtype(MessageVerificationRequestContent::class.java, MessageType.MSGTYPE_VERIFICATION_REQUEST) ) .add(SerializeNulls.JSON_ADAPTER_FACTORY) 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 30247ade12..6e303db93b 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 @@ -84,6 +84,13 @@ internal class DefaultSendService @AssistedInject constructor( return sendEvent(event) } + override fun sendPollReply(pollEventId: String, optionIndex: Int, optionValue: String): Cancelable { + val event = localEchoEventFactory.createPollReplyEvent(roomId, pollEventId, optionIndex, optionValue).also { + saveLocalEcho(it) + } + return sendEvent(event) + } + private fun sendEvent(event: Event): Cancelable { // Encrypted room handling return if (cryptoService.isRoomEncrypted(roomId)) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index bfa1d380ae..0cfd36b8e9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -36,6 +36,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageFileContent import im.vector.matrix.android.api.session.room.model.message.MessageFormat import im.vector.matrix.android.api.session.room.model.message.MessageImageContent +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 @@ -132,6 +133,21 @@ internal class LocalEchoEventFactory @Inject constructor( )) } + fun createPollReplyEvent(roomId: String, + pollEventId: String, + optionIndex: Int, + optionLabel: String): Event { + return createEvent(roomId, + MessagePollResponseContent( + body = optionLabel, + relatesTo = RelationDefaultContent( + type = RelationType.RESPONSE, + option = optionIndex, + eventId = pollEventId) + + )) + } + fun createReplaceTextOfReply(roomId: String, eventReplaced: TimelineEvent, originalEvent: TimelineEvent, diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt index d0716bd047..ba0c187856 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt @@ -53,6 +53,8 @@ sealed class RoomDetailAction : VectorViewModelAction { data class ResendMessage(val eventId: String) : RoomDetailAction() data class RemoveFailedEcho(val eventId: String) : RoomDetailAction() + data class ReplyToPoll(val eventId: String, val optionIndex: Int, val optionValue: String) : RoomDetailAction() + data class ReportContent( val eventId: String, val senderId: String?, 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 710c70a948..f39ac66941 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 @@ -199,6 +199,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action) is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages() is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages() + is RoomDetailAction.ReplyToPoll -> replyToPoll(action) is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action) is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action) is RoomDetailAction.RequestVerification -> handleRequestVerification(action) @@ -855,6 +856,10 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } } + private fun replyToPoll(action: RoomDetailAction.ReplyToPoll) { + room.sendPollReply(action.eventId, action.optionIndex, action.optionValue) + } + private fun observeSyncState() { session.rx() .liveSyncState() diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 65a6f5f244..d24cb8a19b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -33,6 +33,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageEmoteConte import im.vector.matrix.android.api.session.room.model.message.MessageFileContent import im.vector.matrix.android.api.session.room.model.message.MessageImageInfoContent 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.MessageTextContent import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent @@ -57,7 +58,6 @@ import im.vector.riotx.features.home.room.detail.timeline.helper.MessageInformat import im.vector.riotx.features.home.room.detail.timeline.helper.MessageItemAttributesFactory import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.riotx.features.home.room.detail.timeline.item.AbsMessageItem -import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem import im.vector.riotx.features.home.room.detail.timeline.item.MessageBlockCodeItem import im.vector.riotx.features.home.room.detail.timeline.item.MessageBlockCodeItem_ import im.vector.riotx.features.home.room.detail.timeline.item.MessageFileItem @@ -137,10 +137,21 @@ class MessageItemFactory @Inject constructor( is MessageFileContent -> buildFileMessageItem(messageContent, informationData, highlight, callback, attributes) is MessageAudioContent -> buildAudioMessageItem(messageContent, informationData, highlight, callback, attributes) is MessageVerificationRequestContent -> buildVerificationRequestMessageItem(messageContent, informationData, highlight, callback, attributes) + is MessageOptionsContent -> buildPollMessageItem(messageContent, informationData, highlight, callback, attributes) else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback) } } + private fun buildPollMessageItem(messageContent: MessageOptionsContent, informationData: MessageInformationData, highlight: Boolean, callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes): VectorEpoxyModel<*>? { + return MessagePollItem_() + .attributes(attributes) + .callback(callback) + .informationData(informationData) + .leftGuideline(avatarSizeProvider.leftGuideline) + .optionsContent(messageContent) + .highlighted(highlight) + } + private fun buildAudioMessageItem(messageContent: MessageAudioContent, @Suppress("UNUSED_PARAMETER") informationData: MessageInformationData, @@ -228,9 +239,10 @@ class MessageItemFactory @Inject constructor( private fun buildNotHandledMessageItem(messageContent: MessageContent, informationData: MessageInformationData, highlight: Boolean, - callback: TimelineEventController.Callback?): DefaultItem? { - val text = stringProvider.getString(R.string.rendering_event_error_type_of_message_not_handled, messageContent.msgType) - return defaultItemFactory.create(text, informationData, highlight, callback) + callback: TimelineEventController.Callback?, + attributes: AbsMessageItem.Attributes): MessageTextItem? { + // For compatibility reason we should display the body + return buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes) } private fun buildImageMessageItem(messageContent: MessageImageInfoContent, diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessagePollItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessagePollItem.kt new file mode 100644 index 0000000000..fcabb44ba6 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessagePollItem.kt @@ -0,0 +1,131 @@ +/* + * 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.home.room.detail.timeline.item + +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.matrix.android.api.session.room.model.message.MessageOptionsContent +import im.vector.riotx.R +import im.vector.riotx.core.extensions.setTextOrHide +import im.vector.riotx.core.utils.DebouncedClickListener +import im.vector.riotx.features.home.room.detail.RoomDetailAction +import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController + +@EpoxyModelClass(layout = R.layout.item_timeline_event_base) +abstract class MessagePollItem : AbsMessageItem() { + + @EpoxyAttribute + var optionsContent: MessageOptionsContent? = null + + @EpoxyAttribute + var callback: TimelineEventController.Callback? = null + + @EpoxyAttribute + var informationData: MessageInformationData? = null + + override fun getViewType() = STUB_ID + + override fun bind(holder: Holder) { + super.bind(holder) + + holder.pollId = informationData?.eventId + holder.callback = callback + holder.optionValues = optionsContent?.options?.map { it.value ?: it.label } + + renderSendState(holder.view, holder.labelText) + + holder.labelText.setTextOrHide(optionsContent?.label) + + val buttons = listOf(holder.button1, holder.button2, holder.button3, holder.button4, holder.button5) + + buttons.forEach { it.isVisible = false } + + optionsContent?.options?.forEachIndexed { index, item -> + if (index < buttons.size) { + buttons[index].let { + it.text = item.label + it.isVisible = true + } + } + } + + val resultLines = listOf(holder.result1, holder.result2, holder.result3, holder.result4, holder.result5) + + resultLines.forEach { it.isVisible = false } + optionsContent?.options?.forEachIndexed { index, item -> + if (index < resultLines.size) { + resultLines[index].let { + it.label = item.label + it.optionSelected = index == 0 + it.percent = "20%" + it.isVisible = true + } + } + } + holder.infoText.text = holder.view.context.resources.getQuantityString(R.plurals.poll_info, 0, 0) + } + + override fun unbind(holder: Holder) { + holder.pollId = null + holder.callback = null + holder.optionValues = null + super.unbind(holder) + } + + class Holder : AbsMessageItem.Holder(STUB_ID) { + + var pollId: String? = null + var optionValues : List? = null + var callback: TimelineEventController.Callback? = null + + val button1 by bind