diff --git a/CHANGES.rst b/CHANGES.rst index 27cd14680..b7d87db0c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,13 @@ +Changes to Matrix Android SDK in 0.9.36 (2020-09-14) +======================================================= + +Improvements: + - Crypto: Only create one olm session at a time per device (vector-im/riot-android#3056). + +Bugfix: + - Killed Application misses some notifications + - Fix a crash on the crypto code (race condition?) + Changes to Matrix Android SDK in 0.9.35 (2020-05-20) ======================================================= diff --git a/README.md b/README.md index e68f9b502..796a7add6 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,15 @@ [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=matrix.android.sdk&metric=vulnerabilities)](https://sonarcloud.io/project/issues?id=matrix.android.sdk&resolved=false&types=VULNERABILITY) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=matrix.android.sdk&metric=bugs)](https://sonarcloud.io/project/issues?id=matrix.android.sdk&resolved=false&types=BUG) +Important Announcement +====================== + +This SDK is deprecated and the core team does not work anymore on it. + +We strongly recommends that new projects use [the new Android Matrix SDK](https://github.com/matrix-org/matrix-android-sdk2). + +We can provide best effort support for existing projects that are still using this SDK though. + matrix-android-sdk ================== The [Matrix] SDK for Android wraps the Matrix REST API calls in asynchronous Java methods and provides basic structures for storing and handling data. diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSessionResult.java b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSessionResult.java index 77dbfd892..a390b6fd6 100755 --- a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSessionResult.java +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/data/MXOlmSessionResult.java @@ -16,9 +16,7 @@ package org.matrix.androidsdk.crypto.data; -import java.io.Serializable; - -public class MXOlmSessionResult implements Serializable { +public class MXOlmSessionResult { /** * the device */ @@ -30,6 +28,9 @@ public class MXOlmSessionResult implements Serializable { */ public String mSessionId; + // True if mSessionId has been retrieved, (even if mSessionId is null) + public Boolean hasResult; + /** * Constructor * @@ -39,5 +40,6 @@ public class MXOlmSessionResult implements Serializable { public MXOlmSessionResult(MXDeviceInfo device, String sessionId) { mDevice = device; mSessionId = sessionId; + hasResult = sessionId != null && !sessionId.isEmpty(); } } \ No newline at end of file diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java index 989accf00..1bb091a58 100755 --- a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/MXCryptoImpl.java @@ -73,6 +73,7 @@ import org.matrix.androidsdk.crypto.interfaces.CryptoRoomState; import org.matrix.androidsdk.crypto.interfaces.CryptoSession; import org.matrix.androidsdk.crypto.interfaces.CryptoSyncResponse; +import org.matrix.androidsdk.crypto.internal.otk.OneTimeKeysResponseHandler; import org.matrix.androidsdk.crypto.keysbackup.KeysBackup; import org.matrix.androidsdk.crypto.model.crypto.KeysUploadResponse; import org.matrix.androidsdk.crypto.model.crypto.RoomKeyContent; @@ -138,6 +139,8 @@ public class MXCryptoImpl implements MXCrypto { private Map> mLastPublishedOneTimeKeys; + private OneTimeKeysResponseHandler oneTimeKeysResponseHandler = new OneTimeKeysResponseHandler(this); + // the encryption is starting private boolean mIsStarting; @@ -300,7 +303,7 @@ public MXCryptoImpl(@NonNull CryptoSession matrixSession, mCryptoStore.storeUserDevices(mSession.getMyUserId(), myDevices); // Create the VerificationManager before setting the CryptoEventsListener, to avoid crash (vector-im/riot-android#3396) - mShortCodeVerificationManager = new VerificationManager(mSession); + mShortCodeVerificationManager = new VerificationManager(mSession, this); mSession.getDataHandler().setCryptoEventsListener(mEventListener); @@ -1107,6 +1110,7 @@ public void ensureOlmSessionsForDevices(final Map> de } if (devicesWithoutSession.size() == 0) { + Log.d(LOG_TAG, "[MXCrypto] ensureOlmSessionsForDevices: Have already sessions for all"); if (null != callback) { getUIHandler().post(new Runnable() { @Override @@ -1118,84 +1122,44 @@ public void run() { return; } - // Prepare the request for claiming one-time keys + // Devices for which we will make a /claim request MXUsersDevicesMap usersDevicesToClaim = new MXUsersDevicesMap<>(); - final String oneTimeKeyAlgorithm = MXKey.KEY_SIGNED_CURVE_25519_TYPE; + Set deviceIdentityKeysWithOlmSessionsInProgress = oneTimeKeysResponseHandler.getDeviceIdentityKeysWithOlmSessionsInProgress(); + // Prepare the request for claiming one-time keys for (MXDeviceInfo device : devicesWithoutSession) { - usersDevicesToClaim.setObject(oneTimeKeyAlgorithm, device.userId, device.deviceId); + String deviceIdentityKey = device.identityKey(); + + // Claim only if a request is not yet pending + if (!deviceIdentityKeysWithOlmSessionsInProgress.contains(deviceIdentityKey)) { + usersDevicesToClaim.setObject(MXKey.KEY_SIGNED_CURVE_25519_TYPE, device.userId, device.deviceId); + } } - // TODO: this has a race condition - if we try to send another message - // while we are claiming a key, we will end up claiming two and setting up - // two sessions. - // - // That should eventually resolve itself, but it's poor form. + Log.d(LOG_TAG, "[MXCrypto] ensureOlmSessionsForDevices: " + usersDevicesToClaim.getMap().size() + + " out of " + devicesWithoutSession.size() + " sessions to claim one time keys"); Log.d(LOG_TAG, "## claimOneTimeKeysForUsersDevices() : " + usersDevicesToClaim); + OneTimeKeysResponseHandler.PendingRequest pendingRequest = new OneTimeKeysResponseHandler.PendingRequest( + devicesByUser, + callback, + results); + + oneTimeKeysResponseHandler.addPendingRequest(pendingRequest); + + if (usersDevicesToClaim.getMap().isEmpty()) { + return; + } + mCryptoRestClient.claimOneTimeKeysForUsersDevices(usersDevicesToClaim, new ApiCallback>() { @Override public void onSuccess(final MXUsersDevicesMap oneTimeKeys) { getEncryptingThreadHandler().post(new Runnable() { @Override public void run() { - try { - Log.d(LOG_TAG, "## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: " + oneTimeKeys); - - Set userIds = devicesByUser.keySet(); - - for (String userId : userIds) { - List deviceInfos = devicesByUser.get(userId); - - for (MXDeviceInfo deviceInfo : deviceInfos) { - - MXKey oneTimeKey = null; - - List deviceIds = oneTimeKeys.getUserDeviceIds(userId); - - if (null != deviceIds) { - for (String deviceId : deviceIds) { - MXOlmSessionResult olmSessionResult = results.getObject(deviceId, userId); - - if (null != olmSessionResult.mSessionId) { - // We already have a result for this device - continue; - } - - MXKey key = oneTimeKeys.getObject(deviceId, userId); - - if (TextUtils.equals(key.type, oneTimeKeyAlgorithm)) { - oneTimeKey = key; - } - - if (null == oneTimeKey) { - Log.d(LOG_TAG, "## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm - + " for device " + userId + " : " + deviceId); - continue; - } - - // Update the result for this device in results - olmSessionResult.mSessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo); - } - } - } - } - } catch (Exception e) { - Log.e(LOG_TAG, "## ensureOlmSessionsForDevices() " + e.getMessage(), e); - } - - if (!hasBeenReleased()) { - if (null != callback) { - getUIHandler().post(new Runnable() { - @Override - public void run() { - callback.onSuccess(results); - } - }); - } - } + oneTimeKeysResponseHandler.onOtkRetrieved(oneTimeKeys); } }); } @@ -1204,32 +1168,41 @@ public void run() { public void onNetworkError(Exception e) { Log.e(LOG_TAG, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed" + e.getMessage(), e); - if (null != callback) { - callback.onNetworkError(e); - } + getEncryptingThreadHandler().post(new Runnable() { + @Override + public void run() { + oneTimeKeysResponseHandler.onNetworkError(e, usersDevicesToClaim); + } + }); } @Override public void onMatrixError(MatrixError e) { Log.e(LOG_TAG, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed" + e.getMessage()); - if (null != callback) { - callback.onMatrixError(e); - } + getEncryptingThreadHandler().post(new Runnable() { + @Override + public void run() { + oneTimeKeysResponseHandler.onMatrixError(e, usersDevicesToClaim); + } + }); } @Override public void onUnexpectedError(Exception e) { Log.e(LOG_TAG, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed" + e.getMessage(), e); - if (null != callback) { - callback.onUnexpectedError(e); - } + getEncryptingThreadHandler().post(new Runnable() { + @Override + public void run() { + oneTimeKeysResponseHandler.onUnexpectedError(e, usersDevicesToClaim); + } + }); } }); } - private String verifyKeyAndStartSession(MXKey oneTimeKey, String userId, MXDeviceInfo deviceInfo) { + public String verifyKeyAndStartSession(MXKey oneTimeKey, String userId, MXDeviceInfo deviceInfo) { String sessionId = null; String deviceId = deviceInfo.deviceId; diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt new file mode 100644 index 000000000..f383b2a33 --- /dev/null +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/internal/otk/OneTimeKeysResponseHandler.kt @@ -0,0 +1,166 @@ +/* + * 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 org.matrix.androidsdk.crypto.internal.otk + +import org.matrix.androidsdk.core.Log +import org.matrix.androidsdk.core.callback.ApiCallback +import org.matrix.androidsdk.core.model.MatrixError +import org.matrix.androidsdk.crypto.data.MXDeviceInfo +import org.matrix.androidsdk.crypto.data.MXKey +import org.matrix.androidsdk.crypto.data.MXOlmSessionResult +import org.matrix.androidsdk.crypto.data.MXUsersDevicesMap +import org.matrix.androidsdk.crypto.internal.MXCryptoImpl + +/** + * This class keep the several requests to get OneTimeKeys. + * Several requests may ask for the same deviceId, so this class spread the answer to all the caller + */ +class OneTimeKeysResponseHandler( + private val mxCryptoImpl: MXCryptoImpl +) { + companion object { + private const val LOG_TAG = "OneTimeKeysResponseHandler" + } + + data class PendingRequest( + val devicesByUser: Map>, + /** + * The callback which will be called when the result are full + */ + val callback: ApiCallback>, + val aggregatedResult: MXUsersDevicesMap + ) + + private val pendingRequests = mutableListOf() + + fun addPendingRequest(pendingRequest: PendingRequest) { + pendingRequests.add(pendingRequest) + } + + fun removePendingRequest(pendingRequest: PendingRequest) { + pendingRequests.remove(pendingRequest) + } + + /** + * Return the list of device identity keys we are establishing an olm session with. + */ + fun getDeviceIdentityKeysWithOlmSessionsInProgress(): Set { + return pendingRequests.toList() + .map { it.devicesByUser } + .flatMap { it.values } + .flatten() + .map { it.identityKey() } + .toSet() + } + + fun onOtkRetrieved(oneTimeKeys: MXUsersDevicesMap) { + Log.d(LOG_TAG, "## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys") + + val cacheOfSessionId = mutableMapOf() + + // Spread the result to all pending requests + pendingRequests.toList().forEach { pendingRequest -> + pendingRequest.aggregatedResult.map.keys.forEach { userId -> + pendingRequest.aggregatedResult.map[userId]!!.keys.forEach { deviceId -> + val result = pendingRequest.aggregatedResult.getObject(deviceId, userId)!! + + if (result.mSessionId != null) { + // We already have a result for this device + Unit + } else { + // Check in cache + val sessionId = cacheOfSessionId.getOrPut(result.mDevice) { + val key = oneTimeKeys.getObject(deviceId, userId) + ?.takeIf { it.type == MXKey.KEY_SIGNED_CURVE_25519_TYPE } + + if (key == null) { + Log.d(LOG_TAG, "## ensureOlmSessionsForDevices() : No one-time keys " + MXKey.KEY_SIGNED_CURVE_25519_TYPE + + " for device " + userId + " : " + deviceId) + null + } else { + mxCryptoImpl.verifyKeyAndStartSession(key, userId, result.mDevice) + } + } + + result.mSessionId = sessionId + result.hasResult = true + } + } + } + } + + // Ok, call the callback if all sessions are established + if (!mxCryptoImpl.hasBeenReleased()) { + pendingRequests.toList().forEach { pendingRequest -> + if (pendingRequest.aggregatedResult.map.all { a -> a.value.all { b -> b.value.hasResult } }) { + mxCryptoImpl.getUIHandler().post { + pendingRequest.callback.onSuccess(pendingRequest.aggregatedResult) + + // Also remove the pending request + pendingRequests.remove(pendingRequest) + } + } + } + } + } + + fun onNetworkError(e: Exception, usersDevicesToClaim: MXUsersDevicesMap) { + onError(usersDevicesToClaim) { + it.callback.onNetworkError(e) + } + } + + fun onMatrixError(e: MatrixError, usersDevicesToClaim: MXUsersDevicesMap) { + onError(usersDevicesToClaim) { + it.callback.onMatrixError(e) + } + } + + fun onUnexpectedError(e: Exception, usersDevicesToClaim: MXUsersDevicesMap) { + onError(usersDevicesToClaim) { + it.callback.onUnexpectedError(e) + } + } + + private fun onError(usersDevicesToClaim: MXUsersDevicesMap, callCallback: ((PendingRequest) -> Unit)) { + // Spread the failure to all the pending requests which are waiting for this session + val concernedPendingRequests = mutableSetOf() + + pendingRequests.toList().forEach mainForEach@{ pendingRequest -> + pendingRequest.aggregatedResult.map.keys.forEach { userId -> + pendingRequest.aggregatedResult.map[userId]!!.keys.forEach { deviceId -> + if (usersDevicesToClaim.getObject(deviceId, userId) == MXKey.KEY_SIGNED_CURVE_25519_TYPE) { + // This pending request was waiting for a key + concernedPendingRequests.add(pendingRequest) + return@mainForEach + } + } + } + } + + if (!mxCryptoImpl.hasBeenReleased()) { + concernedPendingRequests.forEach { + mxCryptoImpl.getUIHandler().post { + callCallback.invoke(it) + } + } + } + + // Also remove the pending requests + pendingRequests.removeAll(concernedPendingRequests) + } +} \ No newline at end of file diff --git a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/verification/VerificationManager.kt b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/verification/VerificationManager.kt index d02093753..deb1a1849 100644 --- a/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/verification/VerificationManager.kt +++ b/matrix-sdk-crypto/src/main/java/org/matrix/androidsdk/crypto/verification/VerificationManager.kt @@ -22,6 +22,7 @@ import org.matrix.androidsdk.core.JsonUtility import org.matrix.androidsdk.core.Log import org.matrix.androidsdk.core.callback.ApiCallback import org.matrix.androidsdk.core.model.MatrixError +import org.matrix.androidsdk.crypto.MXCrypto import org.matrix.androidsdk.crypto.data.MXDeviceInfo import org.matrix.androidsdk.crypto.data.MXUsersDevicesMap import org.matrix.androidsdk.crypto.interfaces.CryptoEvent @@ -35,7 +36,10 @@ import kotlin.collections.HashMap * Short codes interactive verification is a more user friendly way of verifying devices * that is still maintaining a good level of security (alternative to the 43-character strings compare method). */ -class VerificationManager(val session: CryptoSession) : VerificationTransaction.Listener { +class VerificationManager ( + private val session: CryptoSession, + private val mxCrypto: MXCrypto +) : VerificationTransaction.Listener { interface VerificationManagerListener { fun transactionCreated(tx: VerificationTransaction) @@ -50,7 +54,7 @@ class VerificationManager(val session: CryptoSession) : VerificationTransaction. // Event received from the sync fun onToDeviceEvent(event: CryptoEvent) { - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { when (event.getType()) { CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_START -> { onStartRequestReceived(event) @@ -116,7 +120,7 @@ class VerificationManager(val session: CryptoSession) : VerificationTransaction. } fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) { - session.requireCrypto().setDeviceVerification(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, + mxCrypto.setDeviceVerification(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, deviceID, userId, object : ApiCallback { @@ -210,27 +214,27 @@ class VerificationManager(val session: CryptoSession) : VerificationTransaction. startReq: KeyVerificationStart, success: (MXUsersDevicesMap) -> Unit, error: () -> Unit) { - session.requireCrypto().getDeviceList().downloadKeys(listOf(otherUserId), true, object : ApiCallback> { + mxCrypto.getDeviceList().downloadKeys(listOf(otherUserId), true, object : ApiCallback> { override fun onUnexpectedError(e: Exception) { - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { error() } } override fun onNetworkError(e: Exception) { - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { error() } } override fun onMatrixError(e: MatrixError) { - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { error() } } override fun onSuccess(info: MXUsersDevicesMap) { - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { if (info.getUserDeviceIds(otherUserId).contains(startReq.fromDevice)) { success(info) } else { @@ -371,7 +375,7 @@ class VerificationManager(val session: CryptoSession) : VerificationTransaction. if (KeyVerificationStart.VERIF_METHOD_SAS == method) { val tx = OutgoingSASVerificationRequest(txID, userId, deviceID) addTransaction(tx) - session.requireCrypto().getDecryptingThreadHandler().post { + mxCrypto.getDecryptingThreadHandler().post { tx.start(session) } return txID @@ -387,7 +391,7 @@ class VerificationManager(val session: CryptoSession) : VerificationTransaction. val buff = StringBuffer() buff .append(session.myUserId).append("|") - .append(session.requireCrypto().myDevice.deviceId).append("|") + .append(mxCrypto.myDevice.deviceId).append("|") .append(userId).append("|") .append(deviceID).append("|") .append(UUID.randomUUID().toString()) diff --git a/matrix-sdk/build.gradle b/matrix-sdk/build.gradle index 2bd507e1e..1ac938971 100644 --- a/matrix-sdk/build.gradle +++ b/matrix-sdk/build.gradle @@ -27,8 +27,8 @@ android { defaultConfig { minSdkVersion 16 targetSdkVersion 28 - versionCode 935 - versionName "0.9.35" + versionCode 936 + versionName "0.9.36" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // Enable multi dex for test diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoTest.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoTest.java index 96afdeb5d..75ec1c8a1 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoTest.java +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/crypto/CryptoTest.java @@ -21,9 +21,10 @@ import android.content.Context; import android.os.SystemClock; +import android.text.TextUtils; + import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import android.text.TextUtils; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; @@ -471,6 +472,7 @@ public void onSuccess(MXUsersDevicesMap info) { Assert.assertNotNull(sessionWithAliceDevice); Assert.assertNotNull(sessionWithAliceDevice.mSessionId); + Assert.assertTrue(sessionWithAliceDevice.hasResult); Assert.assertEquals("AliceDevice", sessionWithAliceDevice.mDevice.deviceId); MXSession bobSession2 = mTestHelper.createNewSession(bobSession, mCryptoTestHelper.getDefaultSessionParams()); @@ -549,6 +551,7 @@ public void onSuccess(MXUsersDevicesMap info) { MXOlmSessionResult sessionWithAliceDevice2 = result2.getObject("AliceDevice", aliceSession.getMyUserId()); Assert.assertNotNull(sessionWithAliceDevice2); Assert.assertNotNull(sessionWithAliceDevice2.mSessionId); + Assert.assertTrue(sessionWithAliceDevice2.hasResult); Assert.assertEquals("AliceDevice", sessionWithAliceDevice2.mDevice.deviceId); bobSession.clear(context); diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java index e9409ea66..6dcfba5af 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomAccountData.java @@ -54,8 +54,8 @@ public class RoomAccountData implements java.io.Serializable { // The events the user wants to hide in this room. private Map hiddenEvents = null; - // Store the content of all the provided events by using their event type. - private Map eventContentsMap = new HashMap<>(); + // Store all the provided events by using their event type. + private Map eventsMap = new HashMap<>(); /** * Process an event that modifies room account data (like m.tag event). @@ -64,27 +64,24 @@ public class RoomAccountData implements java.io.Serializable { */ public void handleEvent(Event event) { final String eventType = event.getType(); - final JsonObject jsonObject = event.getContentAsJsonObject(); - - if (eventType.equals(Event.EVENT_TYPE_TAGS)) { - roomTags = RoomTag.roomTagsWithTagEvent(event); - } else if (eventType.equals(Event.EVENT_TYPE_URL_PREVIEW)) { - if (jsonObject != null && jsonObject.has(AccountDataElement.ACCOUNT_DATA_KEY_URL_PREVIEW_DISABLE)) { - final boolean disabled = jsonObject.get(AccountDataElement.ACCOUNT_DATA_KEY_URL_PREVIEW_DISABLE).getAsBoolean(); - isURLPreviewAllowedByUser = !disabled; + if (eventType != null) { + final JsonObject jsonObject = event.getContentAsJsonObject(); + + if (eventType.equals(Event.EVENT_TYPE_TAGS)) { + roomTags = RoomTag.roomTagsWithTagEvent(event); + } else if (eventType.equals(Event.EVENT_TYPE_URL_PREVIEW)) { + if (jsonObject != null && jsonObject.has(AccountDataElement.ACCOUNT_DATA_KEY_URL_PREVIEW_DISABLE)) { + final boolean disabled = jsonObject.get(AccountDataElement.ACCOUNT_DATA_KEY_URL_PREVIEW_DISABLE).getAsBoolean(); + isURLPreviewAllowedByUser = !disabled; + } + } else if (eventType.equals(Event.EVENT_TYPE_TAGGED_EVENTS)) { + final TaggedEventsContent taggedEventContent = JsonUtils.toTaggedEventsContent(jsonObject); + favouriteEvents = taggedEventContent.getFavouriteEvents(); + hiddenEvents = taggedEventContent.getHiddenEvents(); } - } else if (eventType.equals(Event.EVENT_TYPE_TAGGED_EVENTS)) { - final TaggedEventsContent taggedEventContent = JsonUtils.toTaggedEventsContent(jsonObject); - favouriteEvents = taggedEventContent.getFavouriteEvents(); - hiddenEvents = taggedEventContent.getHiddenEvents(); - } - // Store by default the content of all the provided events. - if (jsonObject != null) { - eventContentsMap.put(eventType, jsonObject); - } else { - // Store an empty JsonObject - eventContentsMap.put(eventType, new JsonObject()); + // Store by default all the provided events. + eventsMap.put(eventType, event); } } @@ -189,7 +186,11 @@ public TaggedEventInfo hiddenEventInfo(String eventId) { */ @Nullable public JsonObject eventContent(String eventType) { - return eventContentsMap.get(eventType); + Event event = eventsMap.get(eventType); + if (event != null) { + return event.getContentAsJsonObject(); + } + return null; } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/TaggedEvents.kt b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/TaggedEvents.kt index 6fa81b623..6404f79c9 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/TaggedEvents.kt +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/TaggedEvents.kt @@ -17,6 +17,7 @@ package org.matrix.androidsdk.rest.model import com.google.gson.annotations.SerializedName +import java.io.Serializable /** * Class used to parse the content of a m.tagged_events type event. @@ -30,7 +31,7 @@ data class TaggedEventsContent ( @JvmField @SerializedName("tags") var tags: Map> = emptyMap() -) { +) : Serializable { fun getFavouriteEvents(): Map { return tags[TAG_FAVOURITE] ?: emptyMap() } @@ -75,7 +76,7 @@ data class TaggedEventInfo(@JvmField @JvmField @SerializedName("tagged_at") var taggedAt: Long? = null -) { +) : Serializable { companion object { fun with(keywords: List?, originServerTs: Long?, taggedAt: Long?): TaggedEventInfo { return TaggedEventInfo().apply {