diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt
index 99479d87ec..9b6e364e38 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt
@@ -25,6 +25,7 @@ import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
 import im.vector.matrix.android.internal.di.MoshiProvider
 import timber.log.Timber
 import java.util.*
+import kotlin.collections.HashMap
 
 typealias Content = JsonDict
 
@@ -146,21 +147,25 @@ data class Event(
                 val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
                 mClearEvent = adapter.fromJsonValue(decryptionResult.clearEvent)
 
-            }
-            mClearEvent?.apply {
-                mSenderCurve25519Key = decryptionResult.senderCurve25519Key
-                mClaimedEd25519Key = decryptionResult.claimedEd25519Key
-                mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain
-                try {
-                    // Add "m.relates_to" data from e2e event to the unencrypted event
-                    // TODO
-                    //if (getWireContent().getAsJsonObject().has("m.relates_to")) {
-                    //    clearEvent!!.getContentAsJsonObject()
-                    //            .add("m.relates_to", getWireContent().getAsJsonObject().get("m.relates_to"))
-                    //}
-                } catch (e: Exception) {
-                    Timber.e(e, "Unable to restore 'm.relates_to' the clear event")
+                if (mClearEvent != null) {
+                    mSenderCurve25519Key = decryptionResult.senderCurve25519Key
+                    mClaimedEd25519Key = decryptionResult.claimedEd25519Key
+                    mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain
+
+                    try {
+                        content?.get("m.relates_to")?.let { clearRelates ->
+                            mClearEvent = mClearEvent?.copy(
+                                    content = HashMap(mClearEvent!!.content).apply {
+                                        this["m.relates_to"] = clearRelates
+                                    }
+                            )
+                        }
+                    } catch (e: Exception) {
+                        Timber.e(e, "Unable to restore 'm.relates_to' the clear event")
+                    }
                 }
+
+
             }
         }
         mCryptoError = null
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt
index cd7be990f7..38935f1950 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt
@@ -69,7 +69,7 @@ internal class RoomFactory @Inject constructor(private val context: Context,
         val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask)
         val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask)
         val readService = DefaultReadService(roomId, monarchy, taskExecutor, setReadMarkersTask, credentials)
-        val relationService = DefaultRelationService(context, credentials, roomId, eventFactory, findReactionEventForUndoTask, monarchy, taskExecutor)
+        val relationService = DefaultRelationService(context, credentials, roomId, eventFactory, cryptoService, findReactionEventForUndoTask, monarchy, taskExecutor)
 
         return DefaultRoom(
                 roomId,
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt
index 9b07bb2851..cf0b3f1cc1 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt
@@ -21,6 +21,7 @@ import androidx.work.OneTimeWorkRequest
 import com.zhuinden.monarchy.Monarchy
 import im.vector.matrix.android.api.MatrixCallback
 import im.vector.matrix.android.api.auth.data.Credentials
+import im.vector.matrix.android.api.session.crypto.CryptoService
 import im.vector.matrix.android.api.session.events.model.Event
 import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
 import im.vector.matrix.android.api.session.room.model.message.MessageType
@@ -33,6 +34,7 @@ import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryE
 import im.vector.matrix.android.internal.database.model.RoomEntity
 import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
 import im.vector.matrix.android.internal.database.query.where
+import im.vector.matrix.android.internal.session.room.send.EncryptEventWorker
 import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
 import im.vector.matrix.android.internal.session.room.send.RedactEventWorker
 import im.vector.matrix.android.internal.session.room.send.SendEventWorker
@@ -49,12 +51,12 @@ internal class DefaultRelationService @Inject constructor(private val context: C
                                                           private val credentials: Credentials,
                                                           private val roomId: String,
                                                           private val eventFactory: LocalEchoEventFactory,
+                                                          private val cryptoService: CryptoService,
                                                           private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
                                                           private val monarchy: Monarchy,
                                                           private val taskExecutor: TaskExecutor)
     : RelationService {
 
-
     override fun sendReaction(reaction: String, targetEventId: String): Cancelable {
         val event = eventFactory.createReactionEvent(roomId, targetEventId, reaction)
                 .also {
@@ -65,13 +67,8 @@ internal class DefaultRelationService @Inject constructor(private val context: C
         return CancelableWork(context, sendRelationWork.id)
     }
 
-
     private fun createSendRelationWork(event: Event): OneTimeWorkRequest {
-        val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
-        val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
-
-        return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData)
-
+        return createSendEventWork(event)
     }
 
     override fun undoReaction(reaction: String, targetEventId: String, myUserId: String)/*: Cancelable*/ {
@@ -119,31 +116,44 @@ internal class DefaultRelationService @Inject constructor(private val context: C
         val event = eventFactory.createReplaceTextEvent(roomId, targetEventId, newBodyText, newBodyAutoMarkdown, MessageType.MSGTYPE_TEXT, compatibilityBodyText).also {
             saveLocalEcho(it)
         }
-        val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
-        val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
-
-        //TODO use relation API?
-
-        val workRequest = TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData)
+        val workRequest = createSendEventWork(event)
         TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
         return CancelableWork(context, workRequest.id)
 
     }
 
-
     override fun replyToMessage(eventReplied: Event, replyText: String): Cancelable? {
         val event = eventFactory.createReplyTextEvent(roomId, eventReplied, replyText)?.also {
             saveLocalEcho(it)
         } ?: return null
-        val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
-        val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
 
+        if (cryptoService.isRoomEncrypted(roomId)) {
+            val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
+            val workRequest = createSendEventWork(event)
+            TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
+            return CancelableWork(context, encryptWork.id)
+
+        } else {
+            val workRequest = createSendEventWork(event)
+            TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
+            return CancelableWork(context, workRequest.id)
+        }
 
-        val workRequest = TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData)
-        TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
-        return CancelableWork(context, workRequest.id)
     }
 
+    private fun createEncryptEventWork(event: Event, keepKeys: List<String>?): OneTimeWorkRequest {
+        // Same parameter
+        val params = EncryptEventWorker.Params(credentials.userId, roomId, event, keepKeys)
+        val sendWorkData = WorkerParamsFactory.toData(params)
+        return TimelineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData)
+    }
+
+    private fun createSendEventWork(event: Event): OneTimeWorkRequest {
+        val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
+        val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
+        val workRequest = TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData)
+        return workRequest
+    }
 
     override fun getEventSummaryLive(eventId: String): LiveData<List<EventAnnotationsSummary>> {
         return monarchy.findAllMappedWithChanges(
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt
index 359eb18148..f7503672e5 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt
@@ -39,11 +39,15 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
     internal data class Params(
             override val userId: String,
             val roomId: String,
-            val event: Event
+            val event: Event,
+            /**Do not encrypt these keys, keep them as is in encrypted content (e.g. m.relates_to)*/
+            val keepKeys: List<String>? = null
     ) : SessionWorkerParams
 
-    @Inject lateinit var crypto: CryptoService
-    @Inject lateinit var localEchoUpdater: LocalEchoUpdater
+    @Inject
+    lateinit var crypto: CryptoService
+    @Inject
+    lateinit var localEchoUpdater: LocalEchoUpdater
 
     override fun doWork(): Result {
 
@@ -65,8 +69,13 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
         var result: MXEncryptEventContentResult? = null
         var error: Throwable? = null
 
+        val localMutableContent = HashMap(localEvent.content)
+        params.keepKeys?.forEach {
+            localMutableContent.remove(it)
+        }
+
         try {
-            crypto.encryptEventContent(localEvent.content!!, localEvent.type, params.roomId, object : MatrixCallback<MXEncryptEventContentResult> {
+            crypto.encryptEventContent(localMutableContent, localEvent.type, params.roomId, object : MatrixCallback<MXEncryptEventContentResult> {
                 override fun onSuccess(data: MXEncryptEventContentResult) {
                     result = data
                     latch.countDown()
@@ -83,15 +92,24 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
         }
         latch.await()
 
-        val safeResult = result
-        if (safeResult != null) {
+        if (result != null) {
+            var modifiedContent = HashMap(result?.eventContent)
+            params.keepKeys?.forEach { toKeep ->
+                localEvent.content?.get(toKeep)?.let {
+                    //put it back in the encrypted thing
+                    modifiedContent[toKeep] = it
+                }
+            }
+            val safeResult = result!!.copy(eventContent = modifiedContent)
             val encryptedEvent = localEvent.copy(
                     type = safeResult.eventType,
                     content = safeResult.eventContent
             )
             val nextWorkerParams = SendEventWorker.Params(params.userId, params.roomId, encryptedEvent)
             return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
+
         }
+
         val safeError = error
         val sendState = when (safeError) {
             is Failure.CryptoError -> SendState.FAILED_UNKNOWN_DEVICES
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/JsonCanonicalizer.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/JsonCanonicalizer.kt
index 9c8c78a379..41dd0f7d1b 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/JsonCanonicalizer.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/JsonCanonicalizer.kt
@@ -100,7 +100,7 @@ object JsonCanonicalizer {
 
                 return result.toString()
             }
-            is String     -> return "\"" + src.toString() + "\""
+            is String     -> return  JSONObject.quote(src.toString())
             else          -> return src.toString()
         }
     }
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt
index f7a3b964db..081c83a615 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt
@@ -247,7 +247,7 @@ class RoomDetailFragment :
                     //TODO this is used at several places, find way to refactor?
                     val messageContent: MessageContent? =
                             event.annotations?.editSummary?.aggregatedContent?.toModel()
-                                    ?: event.root.content.toModel()
+                                    ?: event.root.getClearContent().toModel()
                     val nonFormattedBody = messageContent?.body ?: ""
                     var formattedBody: CharSequence? = null
                     if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt
index 30cc1d1e1a..e01203711d 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt
@@ -15,10 +15,8 @@
  */
 package im.vector.riotredesign.features.home.room.detail.timeline.action
 
-import com.airbnb.mvrx.FragmentViewModelContext
 import com.airbnb.mvrx.MvRxState
 import com.airbnb.mvrx.MvRxViewModelFactory
-import com.airbnb.mvrx.ViewModelContext
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
 import im.vector.matrix.android.api.session.Session
@@ -33,11 +31,10 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
 import im.vector.riotredesign.R
 import im.vector.riotredesign.core.platform.VectorViewModel
 import im.vector.riotredesign.core.resources.StringProvider
+import im.vector.riotredesign.core.utils.isSingleEmoji
 import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
 import org.json.JSONObject
 
-import im.vector.riotredesign.core.utils.isSingleEmoji
-
 
 data class SimpleAction(val uid: String, val titleRes: Int, val iconResId: Int?, val data: Any? = null)
 
@@ -80,11 +77,6 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
         const val ACTION_FLAG = "ACTION_FLAG"
         const val ACTION_QUICK_REACT = "ACTION_QUICK_REACT"
         const val ACTION_VIEW_REACTIONS = "ACTION_VIEW_REACTIONS"
-
-        override fun create(viewModelContext: ViewModelContext, state: MessageMenuState): MessageMenuViewModel? {
-            val fragment: MessageMenuFragment = (viewModelContext as FragmentViewModelContext).fragment()
-            return fragment.messageMenuViewModelFactory.create(state)
-        }
     }
 
     init {
@@ -95,7 +87,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
         val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state
 
         val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
-                ?: event.root.content.toModel()
+                ?: event.root.getClearContent().toModel()
         val type = messageContent?.type
 
         val actions = if (!event.sendState.isSent()) {