diff --git a/.travis.yml b/.travis.yml index d892221f1..8684fcdac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,3 +43,5 @@ script: - ./gradlew clean lintRelease test assembleRelease assembleAndroidTest --stacktrace - ./tools/check/check_code_quality.sh - ./tools/travis/check_pr.sh + # Check that indonesian files are identical. Due to Android issue, the resource folder must be value-in/, and Weblate export data into value-id/. + - diff ./matrix-sdk/src/main/res/values-id/strings.xml ./matrix-sdk/src/main/res/values-in/strings.xml diff --git a/CHANGES.rst b/CHANGES.rst index 34a03ccfd..c0d78adcf 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,15 @@ +Changes to Matrix Android SDK in 0.9.12 (2018-10-18) +======================================================= + +Improvements: + - Improve certificate pinning management for HomeServerConnectionConfig. + - Room display name is now computed by the Matrix SDK + +Bugfix: + - Fix strip previous reply when they contain new line (vector-im/riot-android#2612) + - Enable CLEARTEXT communication for http endpoints (vector-im/riot-android#2495) + - Back paginating in a room with LL makes some avatars to vanish (vector-im/riot-android#2639) + Changes to Matrix Android SDK in 0.9.11 (2018-10-10) ======================================================= diff --git a/matrix-sdk/build.gradle b/matrix-sdk/build.gradle index 2d8bb2e11..ce7936a1c 100644 --- a/matrix-sdk/build.gradle +++ b/matrix-sdk/build.gradle @@ -14,8 +14,8 @@ android { defaultConfig { minSdkVersion 16 targetSdkVersion 26 - versionCode 911 - versionName "0.9.11" + versionCode 912 + versionName "0.9.12" resValue "string", "flavor_description", "SDKApp" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/common/CommonTestHelper.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/common/CommonTestHelper.java index bcbef2901..4bae8ad38 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/common/CommonTestHelper.java +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/common/CommonTestHelper.java @@ -76,7 +76,6 @@ public HomeServerConnectionConfig createHomeServerConfig(@Nullable Credentials c final HomeServerConnectionConfig hs = new HomeServerConnectionConfig.Builder() .withHomeServerUri(Uri.parse(TestConstants.TESTS_HOME_SERVER_URL)) .withCredentials(credentials) - .withAllowHttpConnection() .build(); return hs; } @@ -355,4 +354,17 @@ public void onSuccess(Credentials credentials) { public void await(CountDownLatch latch) throws InterruptedException { Assert.assertTrue(latch.await(TestConstants.AWAIT_TIME_OUT_MILLIS, TimeUnit.MILLISECONDS)); } + + /** + * Clear all provided sessions + * + * @param sessions the sessions to clear + */ + public void clearAllSessions(List sessions) { + final Context context = InstrumentationRegistry.getContext(); + + for (MXSession session : sessions) { + session.clear(context); + } + } } diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/data/room/RoomAvatarResolverTest.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/data/room/RoomAvatarResolverTest.java new file mode 100644 index 000000000..bb46034cf --- /dev/null +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/data/room/RoomAvatarResolverTest.java @@ -0,0 +1,147 @@ +/* + * Copyright 2018 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.data.room; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; + +import junit.framework.Assert; + +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.matrix.androidsdk.data.Room; + +@FixMethodOrder(MethodSorters.JVM) +public class RoomAvatarResolverTest { + + private RoomTestHelper mRoomTestHelper = new RoomTestHelper(); + + @Test + public void RoomAvatar_getAvatar_noLL_avatar() { + RoomAvatar_getAvatar_avatar(false); + } + + @Test + public void RoomAvatar_getAvatar_LL_avatar() { + RoomAvatar_getAvatar_avatar(true); + } + + private void RoomAvatar_getAvatar_avatar(boolean withLazyLoading) { + Context context = InstrumentationRegistry.getContext(); + + // It does not depend on the number of users + for (int i = 0; i < 10; i++) { + Room room = mRoomTestHelper.createRoom(context, withLazyLoading, i, false); + + room.getState().avatar_url = "mxc://avatar_url"; + Assert.assertEquals("mxc://avatar_url", room.getAvatarUrl()); + } + } + + @Test + public void RoomAvatar_getAvatar_noLL_noAvatar() { + RoomAvatar_getAvatar_noAvatar(false); + } + + @Test + public void RoomAvatar_getAvatar_LL_noAvatar() { + RoomAvatar_getAvatar_noAvatar(true); + } + + private void RoomAvatar_getAvatar_noAvatar(boolean withLazyLoading) { + Context context = InstrumentationRegistry.getContext(); + + Room room; + + // Only me in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 1, false); + Assert.assertNull(room.getAvatarUrl()); + + // I have an avatar + room.getMember(mRoomTestHelper.getMyUserId()).avatarUrl = "mxc://my_avatar_url"; + Assert.assertEquals("mxc://my_avatar_url", room.getAvatarUrl()); + + // One other user in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 2, false); + Assert.assertNull(room.getAvatarUrl()); + + // I have an avatar + room.getMember(mRoomTestHelper.getMyUserId()).avatarUrl = "mxc://my_avatar_url"; + Assert.assertNull(room.getAvatarUrl()); + + // Other user has an avatar + room.getMember(mRoomTestHelper.getUserId(2)).avatarUrl = "mxc://other_user_avatar_url"; + Assert.assertEquals("mxc://other_user_avatar_url", room.getAvatarUrl()); + + // 2 other users in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 3, false); + + // I have an avatar + room.getMember(mRoomTestHelper.getMyUserId()).avatarUrl = "mxc://my_avatar_url"; + // Other user has an avatar + room.getMember(mRoomTestHelper.getUserId(2)).avatarUrl = "mxc://other_user_avatar_url"; + + Assert.assertNull(room.getAvatarUrl()); + } + + @Test + public void RoomAvatar_getAvatar_noLL_invitation() { + RoomAvatar_getAvatar_invitation(false); + } + + @Test + public void RoomAvatar_getAvatar_LL_invitation() { + RoomAvatar_getAvatar_invitation(true); + } + + private void RoomAvatar_getAvatar_invitation(boolean withLazyLoading) { + Context context = InstrumentationRegistry.getContext(); + + Room room; + + // Only me in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 1, true); + Assert.assertNull(room.getAvatarUrl()); + + // I have an avatar + room.getMember(mRoomTestHelper.getMyUserId()).avatarUrl = "mxc://my_avatar_url"; + Assert.assertEquals("mxc://my_avatar_url", room.getAvatarUrl()); + + // One other user in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 2, true); + Assert.assertNull(room.getAvatarUrl()); + + // I have an avatar + room.getMember(mRoomTestHelper.getMyUserId()).avatarUrl = "mxc://my_avatar_url"; + Assert.assertNull(room.getAvatarUrl()); + + // Inviter has an avatar + room.getMember(mRoomTestHelper.getUserId(2)).avatarUrl = "mxc://other_user_avatar_url"; + Assert.assertEquals("mxc://other_user_avatar_url", room.getAvatarUrl()); + + // 2 other users in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 3, true); + + // I have an avatar + room.getMember(mRoomTestHelper.getMyUserId()).avatarUrl = "mxc://my_avatar_url"; + // Other user has an avatar + room.getMember(mRoomTestHelper.getUserId(2)).avatarUrl = "mxc://other_user_avatar_url"; + + Assert.assertEquals("mxc://other_user_avatar_url", room.getAvatarUrl()); + } +} diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/data/room/RoomDisplayNameResolverTest.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/data/room/RoomDisplayNameResolverTest.java new file mode 100644 index 000000000..a9b9cf078 --- /dev/null +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/data/room/RoomDisplayNameResolverTest.java @@ -0,0 +1,157 @@ +/* + * Copyright 2018 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.data.room; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; + +import junit.framework.Assert; + +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.matrix.androidsdk.data.Room; + +import java.util.ArrayList; + +@FixMethodOrder(MethodSorters.JVM) +public class RoomDisplayNameResolverTest { + + private RoomTestHelper mRoomTestHelper = new RoomTestHelper(); + + @Test + public void RoomName_getRoomDisplayName_noLL_emptyRoom() { + RoomName_getRoomDisplayName_emptyRoom(false); + } + + @Test + public void RoomName_getRoomDisplayName_LL_emptyRoom() { + RoomName_getRoomDisplayName_emptyRoom(true); + } + + private void RoomName_getRoomDisplayName_emptyRoom(boolean withLazyLoading) { + Context context = InstrumentationRegistry.getContext(); + Room room = mRoomTestHelper.createRoom(context, withLazyLoading, 0, false); + + Assert.assertEquals("Empty room", room.getRoomDisplayName(context)); + } + + @Test + public void RoomName_getRoomDisplayName_noLL_roomName() { + RoomName_getRoomDisplayName_roomName(false); + } + + @Test + public void RoomName_getRoomDisplayName_LL_roomName() { + RoomName_getRoomDisplayName_roomName(true); + } + + private void RoomName_getRoomDisplayName_roomName(boolean withLazyLoading) { + Context context = InstrumentationRegistry.getContext(); + + // It does not depend on the number of users + for (int i = 0; i < 10; i++) { + Room room = mRoomTestHelper.createRoom(context, withLazyLoading, i, false); + + room.getState().aliases = new ArrayList<>(); + room.getState().aliases.add("Alias"); + Assert.assertEquals("Alias", room.getRoomDisplayName(context)); + + // Canonical alias get priority over alias + room.getState().setCanonicalAlias("Canonical"); + Assert.assertEquals("Canonical", room.getRoomDisplayName(context)); + + // Room name get priority over alias and canonical alias + room.getState().name = "Room Name"; + Assert.assertEquals("Room Name", room.getRoomDisplayName(context)); + } + } + + @Test + public void RoomName_getRoomDisplayName_noLL_user() { + RoomName_getRoomDisplayName_user(false); + } + + @Test + public void RoomName_getRoomDisplayName_LL_user() { + RoomName_getRoomDisplayName_user(true); + } + + private void RoomName_getRoomDisplayName_user(boolean withLazyLoading) { + Context context = InstrumentationRegistry.getContext(); + + Room room; + + // Only me in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 1, false); + Assert.assertEquals("Empty room", room.getRoomDisplayName(context)); + + // One other user in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 2, false); + Assert.assertEquals("UserName_2", room.getRoomDisplayName(context)); + + // 2 other users in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 3, false); + Assert.assertEquals("UserName_2 and UserName_3", room.getRoomDisplayName(context)); + + room = mRoomTestHelper.createRoom(context, withLazyLoading, 4, false); + Assert.assertEquals("UserName_2 and 3 others", room.getRoomDisplayName(context)); + + room = mRoomTestHelper.createRoom(context, withLazyLoading, 5, false); + Assert.assertEquals("UserName_2 and 4 others", room.getRoomDisplayName(context)); + + room = mRoomTestHelper.createRoom(context, withLazyLoading, 10, false); + Assert.assertEquals("UserName_2 and 9 others", room.getRoomDisplayName(context)); + } + + @Test + public void RoomName_getRoomDisplayName_noLL_invitation() { + RoomName_getRoomDisplayName_invitation(false); + } + + @Test + public void RoomName_getRoomDisplayName_LL_invitation() { + RoomName_getRoomDisplayName_invitation(true); + } + + private void RoomName_getRoomDisplayName_invitation(boolean withLazyLoading) { + Context context = InstrumentationRegistry.getContext(); + + Room room; + + // Only me in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 1, true); + Assert.assertEquals("Room Invite", room.getRoomDisplayName(context)); + + // One other user in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 2, true); + Assert.assertEquals("Invite from UserName_2", room.getRoomDisplayName(context)); + + // 2 other users in the room + room = mRoomTestHelper.createRoom(context, withLazyLoading, 3, true); + Assert.assertEquals("Invite from UserName_2", room.getRoomDisplayName(context)); + + room = mRoomTestHelper.createRoom(context, withLazyLoading, 4, true); + Assert.assertEquals("Invite from UserName_2", room.getRoomDisplayName(context)); + + room = mRoomTestHelper.createRoom(context, withLazyLoading, 5, true); + Assert.assertEquals("Invite from UserName_2", room.getRoomDisplayName(context)); + + room = mRoomTestHelper.createRoom(context, withLazyLoading, 10, true); + Assert.assertEquals("Invite from UserName_2", room.getRoomDisplayName(context)); + } +} diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/data/room/RoomTestHelper.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/data/room/RoomTestHelper.java new file mode 100644 index 000000000..6cfc75252 --- /dev/null +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/data/room/RoomTestHelper.java @@ -0,0 +1,149 @@ +/* + * Copyright 2018 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.data.room; + +import android.content.Context; + +import org.matrix.androidsdk.MXDataHandler; +import org.matrix.androidsdk.data.Room; +import org.matrix.androidsdk.data.RoomState; +import org.matrix.androidsdk.data.RoomSummary; +import org.matrix.androidsdk.data.store.IMXStore; +import org.matrix.androidsdk.data.store.MXMemoryStore; +import org.matrix.androidsdk.rest.model.RoomMember; +import org.matrix.androidsdk.rest.model.login.Credentials; +import org.matrix.androidsdk.rest.model.sync.RoomSyncSummary; + +import java.util.ArrayList; + +public class RoomTestHelper { + /** + * Create a room, with or without lazy loading and with x number of room members + * First room member will always be the current user + * + * @param context + * @param withLazyLoading + * @param nbOfMembers + * @param amIInvited + * @return + */ + public Room createRoom(Context context, boolean withLazyLoading, int nbOfMembers, boolean amIInvited) { + Credentials credentials = new Credentials(); + credentials.userId = getMyUserId(); + IMXStore store = new MXMemoryStore(credentials, context); + + MXDataHandler mxDataHandler = new MXDataHandler(store, credentials); + mxDataHandler.setLazyLoadingEnabled(withLazyLoading); + + Room room = new Room(mxDataHandler, store, getRoomId()); + + store.storeRoom(room); + + RoomSummary roomSummary = new RoomSummary(); + roomSummary.setRoomId(getRoomId()); + store.storeSummary(roomSummary); + + if (amIInvited) { + roomSummary.setIsInvited(); + } else { + roomSummary.setIsJoined(); + } + + if (withLazyLoading && !amIInvited) { + // Populate room summary + RoomSyncSummary roomSyncSummary = new RoomSyncSummary(); + + roomSyncSummary.joinedMembersCount = nbOfMembers; + roomSyncSummary.invitedMembersCount = 0; + + // heroes + // Heroes does not include current user + if (nbOfMembers >= 2) { + roomSyncSummary.heroes = new ArrayList<>(); + for (int i = 2; i <= Math.min(6, nbOfMembers); i++) { + roomSyncSummary.heroes.add(getUserId(i)); + } + } + + roomSummary.setRoomSyncSummary(roomSyncSummary); + } + + if (amIInvited) { + // Maximum 2 members will be sent by the sync + initMembers(room.getState(), Math.min(2, nbOfMembers), true); + + // Pass the sender name (the inviter id) + if (nbOfMembers >= 2) { + room.getMember(getMyUserId()).mSender = getUserId(2); + } + } else { + initMembers(room.getState(), nbOfMembers, false); + } + + return room; + } + + private void initMembers(RoomState roomState, int nbOfMembers, boolean amIInvited) { + for (int i = 1; i <= nbOfMembers; i++) { + roomState.setMember(getUserId(i), createRoomMember(i, amIInvited)); + } + } + + private RoomMember createRoomMember(int i, boolean amIInvited) { + RoomMember roomMember = new RoomMember(); + + roomMember.setUserId(getUserId(i)); + roomMember.displayname = getUserName(i); + if (i == 1 && amIInvited) { + roomMember.membership = RoomMember.MEMBERSHIP_INVITE; + } else { + roomMember.membership = RoomMember.MEMBERSHIP_JOIN; + } + // Add a TS because they will be ordered + roomMember.setOriginServerTs(i); + + return roomMember; + } + + public String getMyUserId() { + return "@MyUserId"; + } + + private String getMyUserName() { + return "MyUserName"; + } + + private String getRoomId() { + return "!RoomId"; + } + + public String getUserId(int i) { + if (i == 1) { + return getMyUserId(); + } + + return "UserId_" + i; + } + + private String getUserName(int i) { + if (i == 1) { + return getMyUserName(); + } + + return "UserName_" + i; + } +} diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/LazyLoadingScenarioData.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/LazyLoadingScenarioData.java index c7f3798be..8a71ef9ed 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/LazyLoadingScenarioData.java +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/LazyLoadingScenarioData.java @@ -22,9 +22,9 @@ * The sessions are not synced by default as you want to perform some custom tests */ public class LazyLoadingScenarioData { - final MXSession aliceSession; - final MXSession bobSession; - final MXSession samSession; + final public MXSession aliceSession; + final public MXSession bobSession; + final public MXSession samSession; final String roomId; final String bobMessageId; diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/LazyLoadingTestHelper.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/LazyLoadingTestHelper.java index 5b36e7b66..77a0ac6ff 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/LazyLoadingTestHelper.java +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/LazyLoadingTestHelper.java @@ -29,6 +29,7 @@ import org.matrix.androidsdk.data.RoomState; import org.matrix.androidsdk.rest.model.Event; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -56,7 +57,7 @@ public LazyLoadingTestHelper(CommonTestHelper mCommonTestHelper) { * - Alice sends 50 messages * - Alice makes an initial /sync with lazy-loading enabled or not * - * @param withLazyLoading true to enable lazy loading for alice account + * @param withLazyLoading true to enable lazy loading for Alice, Bob and Sam accounts * @return initialized data */ public LazyLoadingScenarioData createScenario(boolean withLazyLoading) throws Exception { @@ -138,4 +139,24 @@ public void onSuccess(String info) { samSession = mTestHelper.logIntoAccount(samId, logSessionParams); return new LazyLoadingScenarioData(aliceSession, bobSession, samSession, roomId, bobMessageId); } + + /** + * Clear all non null sessions in lazy loading scenario data + * + * @param data + */ + public void clearAllSessions(LazyLoadingScenarioData data) { + List sessionsToClear = new ArrayList<>(); + if (data.aliceSession != null) { + sessionsToClear.add(data.aliceSession); + } + if (data.bobSession != null) { + sessionsToClear.add(data.bobSession); + } + if (data.samSession != null) { + sessionsToClear.add(data.samSession); + } + + mTestHelper.clearAllSessions(sessionsToClear); + } } diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomMembersTest.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomMembersTest.java index 82c3aee1a..dd0d9c4af 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomMembersTest.java +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomMembersTest.java @@ -65,6 +65,7 @@ public void onSuccess(List roomMembers) { } }); mTestHelper.await(lock); + mLazyLoadingTestHelper.clearAllSessions(data); } @Test @@ -90,6 +91,7 @@ public void onSuccess(List roomMembers) { } }); mTestHelper.await(lock); + mLazyLoadingTestHelper.clearAllSessions(data); } @Test @@ -115,6 +117,7 @@ public void onSuccess(List roomMembers) { } }); mTestHelper.await(lock); + mLazyLoadingTestHelper.clearAllSessions(data); } @Test @@ -137,6 +140,7 @@ private void RoomMembers_CheckAlreadyLoadedCount(final boolean withLazyLoading) } else { Assert.assertEquals(4, members.size()); } + mLazyLoadingTestHelper.clearAllSessions(data); } } diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomNameScenarioData.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomNameScenarioData.java new file mode 100644 index 000000000..5f6b5455e --- /dev/null +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomNameScenarioData.java @@ -0,0 +1,35 @@ +/* + * Copyright 2018 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.lazyloading; + +import org.matrix.androidsdk.MXSession; + +import java.util.List; + +/** + * Data holder for lazy loading tests + * The sessions are not synced by default as you want to perform some custom tests + */ +public class RoomNameScenarioData { + final public List userSessions; + + final String roomId; + + public RoomNameScenarioData(List userSessions, String roomId) { + this.userSessions = userSessions; + this.roomId = roomId; + } +} \ No newline at end of file diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomNameTest.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomNameTest.java new file mode 100644 index 000000000..2b4be86da --- /dev/null +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomNameTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2018 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.lazyloading; + +import android.support.test.InstrumentationRegistry; + +import junit.framework.Assert; + +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.matrix.androidsdk.MXSession; +import org.matrix.androidsdk.RestClient; +import org.matrix.androidsdk.common.CommonTestHelper; + +import java.util.Arrays; +import java.util.List; + +@FixMethodOrder(MethodSorters.JVM) +public class RoomNameTest { + + private CommonTestHelper mTestHelper = new CommonTestHelper(); + private RoomNameTestHelper mRoomNameTestHelper = new RoomNameTestHelper(mTestHelper); + + private List userQuantities = Arrays.asList(1, 2, 3, 10); + + @BeforeClass + public static void init() { + RestClient.initUserAgent(InstrumentationRegistry.getContext()); + } + + @Test + public void RoomName_noName_ShouldLoadAllMembers() throws Exception { + RoomState_noName(false); + } + + @Test + public void RoomName_noName_LazyLoading() throws Exception { + RoomState_noName(true); + } + + private void RoomState_noName(final boolean withLazyLoading) throws Exception { + for (int qty : userQuantities) { + RoomNameScenarioData data = mRoomNameTestHelper.createScenario(qty, null, withLazyLoading); + checkAllName(data, null); + mRoomNameTestHelper.clearAllSessions(data); + } + } + + @Test + public void RoomName_name_ShouldLoadAllMembers() throws Exception { + RoomState_name(false); + } + + @Test + public void RoomName_name_LazyLoading() throws Exception { + RoomState_name(true); + } + + private void RoomState_name(final boolean withLazyLoading) throws Exception { + for (int qty : userQuantities) { + RoomNameScenarioData data = mRoomNameTestHelper.createScenario(qty, "Room name " + qty, withLazyLoading); + checkAllName(data, "Room name " + qty); + mRoomNameTestHelper.clearAllSessions(data); + } + } + + /* ========================================================================================== + * PRIVATE + * ========================================================================================== */ + + private void checkAllName(RoomNameScenarioData roomNameScenarioData, String expectedName) { + for (MXSession session : roomNameScenarioData.userSessions) { + Assert.assertEquals(expectedName, session.getDataHandler().getRoom(roomNameScenarioData.roomId).getState().name); + } + } +} diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomNameTestHelper.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomNameTestHelper.java new file mode 100644 index 000000000..65e5af31f --- /dev/null +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomNameTestHelper.java @@ -0,0 +1,144 @@ +/* + * Copyright 2018 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.lazyloading; + +import junit.framework.Assert; + +import org.matrix.androidsdk.MXSession; +import org.matrix.androidsdk.common.CommonTestHelper; +import org.matrix.androidsdk.common.SessionTestParams; +import org.matrix.androidsdk.common.TestApiCallback; +import org.matrix.androidsdk.data.Room; +import org.matrix.androidsdk.data.RoomState; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import javax.annotation.Nullable; + +/** + * + */ +public class RoomNameTestHelper { + + private final CommonTestHelper mTestHelper; + + public RoomNameTestHelper(CommonTestHelper mCommonTestHelper) { + mTestHelper = mCommonTestHelper; + } + + /** + * Create a base scenario for all room name tests + * + * @param nbOfUsers nb of user to create and to invite in the room (min to 1) + * @param roomName initial room name, or null for no room name + * @param withLazyLoading true to enable lazy loading for alice account + * @return initialized data + */ + public RoomNameScenarioData createScenario(final int nbOfUsers, + @Nullable final String roomName, + final boolean withLazyLoading) throws Exception { + + final SessionTestParams createSessionParams = SessionTestParams.newBuilder() + .withInitialSync(true) + .build(); + + List createdSessions = new ArrayList<>(nbOfUsers); + + // Create all the accounts + for (int i = 0; i < nbOfUsers; i++) { + MXSession session = mTestHelper.createAccount("User_" + i, createSessionParams); + + createdSessions.add(session); + } + + Assert.assertEquals(nbOfUsers, createdSessions.size()); + + // First user create a Room + + final MXSession firstSession = createdSessions.get(0); + + final Map results = new HashMap<>(); + CountDownLatch latch = new CountDownLatch(1); + firstSession.createRoom(new TestApiCallback(latch) { + @Override + public void onSuccess(String info) { + results.put("roomId", info); + super.onSuccess(info); + } + }); + mTestHelper.await(latch); + + final String roomId = results.get("roomId"); + final Room room = firstSession.getDataHandler().getRoom(roomId); + + // Update room name + if (roomName != null) { + latch = new CountDownLatch(1); + room.updateName(roomName, new TestApiCallback(latch)); + mTestHelper.await(latch); + } + + //update join rules + latch = new CountDownLatch(1); + room.updateJoinRules(RoomState.JOIN_RULE_PUBLIC, new TestApiCallback(latch)); + mTestHelper.await(latch); + + // TODO Test with invitations + // all other users join the room + for (int i = 1; i < nbOfUsers; i++) { + latch = new CountDownLatch(1); + createdSessions.get(i).joinRoom(roomId, new TestApiCallback(latch)); + mTestHelper.await(latch); + } + + //invite dave + latch = new CountDownLatch(1); + room.invite("@dave:localhost:8480", new TestApiCallback(latch)); + mTestHelper.await(latch); + + final SessionTestParams logSessionParams = SessionTestParams.newBuilder() + .withLazyLoading(withLazyLoading) + .withInitialSync(true) + .build(); + + List loggedSessions = new ArrayList<>(nbOfUsers); + + // open new sessions, using the same user ids + for (MXSession session : createdSessions) { + MXSession loggedSession = mTestHelper.logIntoAccount(session.getMyUserId(), logSessionParams); + + loggedSessions.add(loggedSession); + } + + // Clear created sessions (must be done after getting the user ids) + mTestHelper.clearAllSessions(createdSessions); + + return new RoomNameScenarioData(loggedSessions, roomId); + } + + /** + * Clear all sessions in room name scenario data + * + * @param data + */ + public void clearAllSessions(RoomNameScenarioData data) { + mTestHelper.clearAllSessions(data.userSessions); + } +} diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomStateTest.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomStateTest.java index 3d613bc30..7f4cbcce8 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomStateTest.java +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomStateTest.java @@ -70,6 +70,7 @@ private void RoomState_InitialSync(final boolean withLazyLoading) throws Excepti Assert.assertEquals(1, aliceRoom.getNumberOfInvitedMembers()); Assert.assertEquals(3, aliceRoom.getNumberOfJoinedMembers()); + mLazyLoadingTestHelper.clearAllSessions(data); } @Test @@ -107,6 +108,7 @@ public void onEvent(Event event, EventTimeline.Direction direction, RoomState ro Assert.assertEquals(1, aliceRoom.getNumberOfInvitedMembers()); Assert.assertEquals(3, aliceRoom.getNumberOfJoinedMembers()); + mLazyLoadingTestHelper.clearAllSessions(data); } @Test @@ -165,6 +167,7 @@ public void onEvent(Event event, EventTimeline.Direction direction, RoomState ro }); recursiveBackPaginate(liveTimeline, 0, 30, 120); mTestHelper.await(lock); + mLazyLoadingTestHelper.clearAllSessions(data); } @Test @@ -201,6 +204,7 @@ public void onSuccess(Void info) { Assert.assertEquals(1, aliceRoom.getNumberOfInvitedMembers()); Assert.assertEquals(3, aliceRoom.getNumberOfJoinedMembers()); + mLazyLoadingTestHelper.clearAllSessions(data); } @Test @@ -266,6 +270,7 @@ public void onSuccess(Integer info) { } }); mTestHelper.await(lock); + mLazyLoadingTestHelper.clearAllSessions(data); } @Test @@ -338,6 +343,7 @@ public void onSuccess(Integer info) { } }); mTestHelper.await(lock); + mLazyLoadingTestHelper.clearAllSessions(data); } /** diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomSummaryTest.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomSummaryTest.java index 03ecb85b5..5d7742a68 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomSummaryTest.java +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/RoomSummaryTest.java @@ -66,6 +66,7 @@ private void RoomSummary_CheckMembership(boolean withLazyLoading) throws Excepti final RoomSummary samRoomSummary = samRoom.getRoomSummary(); Assert.assertNotNull(samRoomSummary); Assert.assertTrue(samRoomSummary.isJoined()); + mLazyLoadingTestHelper.clearAllSessions(data); } @Test @@ -92,6 +93,7 @@ private void RoomSummary_MemberCount(boolean withLazyLoading) throws Exception { Assert.assertEquals(0, roomSummary.getNumberOfJoinedMembers()); Assert.assertEquals(0, roomSummary.getNumberOfInvitedMembers()); } + mLazyLoadingTestHelper.clearAllSessions(data); } @Test @@ -115,6 +117,7 @@ private void RoomSummary_CheckRoomSummaryIsNullAfterLeavingFromAnotherDevice(boo mTestHelper.syncSession(data.aliceSession, false); final RoomSummary roomSummary = data.aliceSession.getDataHandler().getStore().getSummary(data.roomId); Assert.assertNull(roomSummary); + mLazyLoadingTestHelper.clearAllSessions(data); } } diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/SearchTest.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/SearchTest.java index ae279e62e..7d19e8b8c 100644 --- a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/SearchTest.java +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/lazyloading/SearchTest.java @@ -51,6 +51,7 @@ public void onSuccess(SearchResponse info) { } }); mTestHelper.await(lock); + mLazyLoadingTestHelper.clearAllSessions(data); } } diff --git a/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/util/EventDisplayTest.java b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/util/EventDisplayTest.java new file mode 100644 index 000000000..c402d9f23 --- /dev/null +++ b/matrix-sdk/src/androidTest/java/org/matrix/androidsdk/util/EventDisplayTest.java @@ -0,0 +1,158 @@ +/* + * Copyright 2018 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.util; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.text.SpannableStringBuilder; + +import com.google.gson.JsonObject; + +import org.junit.Assert; +import org.junit.Test; +import org.matrix.androidsdk.interfaces.HtmlToolbox; +import org.matrix.androidsdk.rest.model.Event; +import org.matrix.androidsdk.rest.model.message.Message; + +public class EventDisplayTest { + + @Test + public void EventDisplay_message_Simple_text() { + Context context = InstrumentationRegistry.getContext(); + + Event event = new Event(); + event.type = Event.EVENT_TYPE_MESSAGE; + + event.content = new JsonObject(); + ((JsonObject) event.content).addProperty("body", "message"); + + EventDisplay eventDisplay = new EventDisplay(context); + + CharSequence textualDisplay = eventDisplay.getTextualDisplay(event, null); + + Assert.assertTrue(textualDisplay instanceof String); + Assert.assertEquals("message", textualDisplay); + } + + @Test + public void EventDisplay_formattedMessage_Simple_text() { + EventDisplay eventDisplay = new EventDisplay(InstrumentationRegistry.getContext()); + Event event = createEventWithFormattedBody("message"); + + CharSequence textualDisplay = eventDisplay.getTextualDisplay(event, null); + + Assert.assertTrue(textualDisplay instanceof SpannableStringBuilder); + Assert.assertEquals("message", textualDisplay.toString()); + } + + @Test + public void EventDisplay_formattedMessage_italic_text() { + EventDisplay eventDisplay = new EventDisplay(InstrumentationRegistry.getContext()); + Event event = createEventWithFormattedBody("italic"); + + CharSequence textualDisplay = eventDisplay.getTextualDisplay(event, null); + + Assert.assertTrue(textualDisplay instanceof SpannableStringBuilder); + Assert.assertEquals("italic", textualDisplay.toString()); + } + + @Test + public void EventDisplay_formattedMessage_bold_text() { + EventDisplay eventDisplay = new EventDisplay(InstrumentationRegistry.getContext()); + Event event = createEventWithFormattedBody("bold"); + + CharSequence textualDisplay = eventDisplay.getTextualDisplay(event, null); + + Assert.assertTrue(textualDisplay instanceof SpannableStringBuilder); + Assert.assertEquals("bold", textualDisplay.toString()); + } + + /** + * This test check list item. It also check the trimming which has been added at the end of + * {@link EventDisplay#getFormattedMessage(Context, JsonObject, HtmlToolbox)} + */ + @Test + public void EventDisplay_formattedMessage_li_text() { + EventDisplay eventDisplay = new EventDisplay(InstrumentationRegistry.getContext()); + Event event = createEventWithFormattedBody("
  1. list
"); + + CharSequence textualDisplay = eventDisplay.getTextualDisplay(event, null); + + Assert.assertTrue(textualDisplay instanceof SpannableStringBuilder); + Assert.assertEquals("list", textualDisplay.toString()); + } + + /** + * This test check list with several items + */ + @Test + public void EventDisplay_formattedMessage_lili_text() { + EventDisplay eventDisplay = new EventDisplay(InstrumentationRegistry.getContext()); + Event event = createEventWithFormattedBody("
  1. list
  2. item
"); + + CharSequence textualDisplay = eventDisplay.getTextualDisplay(event, null); + + Assert.assertTrue(textualDisplay instanceof SpannableStringBuilder); + Assert.assertEquals("list\nitem", textualDisplay.toString()); + } + + /** + * Test blockquote. It also check the trimming which has been added at the end of + * {@link EventDisplay#getFormattedMessage(Context, JsonObject, HtmlToolbox)} + */ + @Test + public void EventDisplay_formattedMessage_blockquote_text() { + EventDisplay eventDisplay = new EventDisplay(InstrumentationRegistry.getContext()); + Event event = createEventWithFormattedBody("
blockquote
"); + + CharSequence textualDisplay = eventDisplay.getTextualDisplay(event, null); + + Assert.assertTrue(textualDisplay instanceof SpannableStringBuilder); + Assert.assertEquals("blockquote", textualDisplay.toString()); + } + + /** + * Test blockquote with text + */ + @Test + public void EventDisplay_formattedMessage_blockquoteWithText_text() { + EventDisplay eventDisplay = new EventDisplay(InstrumentationRegistry.getContext()); + Event event = createEventWithFormattedBody("
blockquote
message"); + + CharSequence textualDisplay = eventDisplay.getTextualDisplay(event, null); + + Assert.assertTrue(textualDisplay instanceof SpannableStringBuilder); + Assert.assertEquals("blockquote\n\nmessage", textualDisplay.toString()); + } + + // TODO Test other message type + + /* ========================================================================================== + * Private + * ========================================================================================== */ + + private Event createEventWithFormattedBody(String formattedBody) { + Event event = new Event(); + event.type = Event.EVENT_TYPE_MESSAGE; + + event.content = new JsonObject(); + ((JsonObject) event.content).addProperty("format", Message.FORMAT_MATRIX_HTML); + ((JsonObject) event.content).addProperty("formatted_body", formattedBody); + + return event; + } +} diff --git a/matrix-sdk/src/main/AndroidManifest.xml b/matrix-sdk/src/main/AndroidManifest.xml index b57fd6a07..4bfd8ee9d 100644 --- a/matrix-sdk/src/main/AndroidManifest.xml +++ b/matrix-sdk/src/main/AndroidManifest.xml @@ -1,13 +1,16 @@ - - + - + @@ -17,10 +20,7 @@ - - + diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/HomeServerConnectionConfig.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/HomeServerConnectionConfig.java index fc1396116..e7272f675 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/HomeServerConnectionConfig.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/HomeServerConnectionConfig.java @@ -20,7 +20,6 @@ import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; import org.json.JSONArray; import org.json.JSONException; @@ -40,7 +39,7 @@ public class HomeServerConnectionConfig { // the home server URI - private Uri mHsUri; + private Uri mHomeServerUri; // the identity server URI private Uri mIdentityServerUri; // the anti-virus server URI @@ -57,8 +56,6 @@ public class HomeServerConnectionConfig { private List mTlsCipherSuites; // should accept TLS extensions private boolean mShouldAcceptTlsExtensions = true; - // allow Http connection - private boolean mAllowHttpExtension; // Force usage of TLS versions private boolean mForceUsageTlsVersions; @@ -75,14 +72,14 @@ private HomeServerConnectionConfig() { * @param uri the new HS uri */ public void setHomeserverUri(Uri uri) { - mHsUri = uri; + mHomeServerUri = uri; } /** * @return the home server uri */ public Uri getHomeserverUri() { - return mHsUri; + return mHomeServerUri; } /** @@ -93,7 +90,7 @@ public Uri getIdentityServerUri() { return mIdentityServerUri; } // Else consider the HS uri by default. - return mHsUri; + return mHomeServerUri; } /** @@ -104,7 +101,7 @@ public Uri getAntiVirusServerUri() { return mAntiVirusServerUri; } // Else consider the HS uri by default. - return mHsUri; + return mHomeServerUri; } /** @@ -161,13 +158,6 @@ public boolean shouldAcceptTlsExtensions() { return mShouldAcceptTlsExtensions; } - /** - * @return true if Http connection is allowed (false by default). - */ - public boolean isHttpConnectionAllowed() { - return mAllowHttpExtension; - } - /** * @return true if the usage of TlsVersions has to be forced */ @@ -178,7 +168,7 @@ public boolean forceUsageOfTlsVersions() { @Override public String toString() { return "HomeserverConnectionConfig{" + - "mHsUri=" + mHsUri + + "mHomeServerUri=" + mHomeServerUri + ", mIdentityServerUri=" + mIdentityServerUri + ", mAntiVirusServerUri=" + mAntiVirusServerUri + ", mAllowedFingerprints size=" + mAllowedFingerprints.size() + @@ -199,7 +189,7 @@ public String toString() { public JSONObject toJson() throws JSONException { JSONObject json = new JSONObject(); - json.put("home_server_url", mHsUri.toString()); + json.put("home_server_url", mHomeServerUri.toString()); json.put("identity_server_url", getIdentityServerUri().toString()); if (mAntiVirusServerUri != null) { json.put("antivirus_server_url", mAntiVirusServerUri.toString()); @@ -253,14 +243,6 @@ public JSONObject toJson() throws JSONException { * @throws JSONException the conversion failure reason */ public static HomeServerConnectionConfig fromJson(JSONObject jsonObject) throws JSONException { - JSONArray fingerprintArray = jsonObject.optJSONArray("fingerprints"); - List fingerprints = new ArrayList<>(); - if (fingerprintArray != null) { - for (int i = 0; i < fingerprintArray.length(); i++) { - fingerprints.add(Fingerprint.fromJson(fingerprintArray.getJSONObject(i))); - } - } - JSONObject credentialsObj = jsonObject.optJSONObject("credentials"); Credentials creds = credentialsObj != null ? Credentials.fromJson(credentialsObj) : null; @@ -268,9 +250,15 @@ public static HomeServerConnectionConfig fromJson(JSONObject jsonObject) throws .withHomeServerUri(Uri.parse(jsonObject.getString("home_server_url"))) .withIdentityServerUri(jsonObject.has("identity_server_url") ? Uri.parse(jsonObject.getString("identity_server_url")) : null) .withCredentials(creds) - .withAllowedFingerPrints(fingerprints) .withPin(jsonObject.optBoolean("pin", false)); + JSONArray fingerprintArray = jsonObject.optJSONArray("fingerprints"); + if (fingerprintArray != null) { + for (int i = 0; i < fingerprintArray.length(); i++) { + builder.addAllowedFingerPrint(Fingerprint.fromJson(fingerprintArray.getJSONObject(i))); + } + } + // Set the anti-virus server uri if any if (jsonObject.has("antivirus_server_url")) { builder.withAntiVirusServerUri(Uri.parse(jsonObject.getString("antivirus_server_url"))); @@ -317,24 +305,36 @@ public Builder() { } /** - * @param hsUri The URI to use to connect to the homeserver. Cannot be null + * create a Builder from an existing HomeServerConnectionConfig + */ + public Builder(HomeServerConnectionConfig from) { + try { + mHomeServerConnectionConfig = HomeServerConnectionConfig.fromJson(from.toJson()); + } catch (JSONException e) { + // Should not happen + throw new RuntimeException("Unable to create a HomeServerConnectionConfig", e); + } + } + + /** + * @param homeServerUri The URI to use to connect to the homeserver. Cannot be null * @return this builder */ - public Builder withHomeServerUri(final Uri hsUri) { - if (hsUri == null || (!"http".equals(hsUri.getScheme()) && !"https".equals(hsUri.getScheme()))) { - throw new RuntimeException("Invalid home server URI: " + hsUri); + public Builder withHomeServerUri(final Uri homeServerUri) { + if (homeServerUri == null || (!"http".equals(homeServerUri.getScheme()) && !"https".equals(homeServerUri.getScheme()))) { + throw new RuntimeException("Invalid home server URI: " + homeServerUri); } // remove trailing / - if (hsUri.toString().endsWith("/")) { + if (homeServerUri.toString().endsWith("/")) { try { - String url = hsUri.toString(); - mHomeServerConnectionConfig.mHsUri = Uri.parse(url.substring(0, url.length() - 1)); + String url = homeServerUri.toString(); + mHomeServerConnectionConfig.mHomeServerUri = Uri.parse(url.substring(0, url.length() - 1)); } catch (Exception e) { - throw new RuntimeException("Invalid home server URI: " + hsUri); + throw new RuntimeException("Invalid home server URI: " + homeServerUri); } } else { - mHomeServerConnectionConfig.mHsUri = hsUri; + mHomeServerConnectionConfig.mHomeServerUri = homeServerUri; } return this; @@ -374,12 +374,12 @@ public Builder withCredentials(@Nullable Credentials credentials) { } /** - * @param allowedFingerprints If using SSL, allow server certs that match these fingerprints. + * @param allowedFingerprint If using SSL, allow server certs that match this fingerprint. * @return this builder */ - public Builder withAllowedFingerPrints(@Nullable List allowedFingerprints) { - if (allowedFingerprints != null) { - mHomeServerConnectionConfig.mAllowedFingerprints.addAll(allowedFingerprints); + public Builder addAllowedFingerPrint(@Nullable Fingerprint allowedFingerprint) { + if (allowedFingerprint != null) { + mHomeServerConnectionConfig.mAllowedFingerprints.add(allowedFingerprint); } return this; @@ -466,22 +466,13 @@ public Builder withAntiVirusServerUri(@Nullable Uri antivirusServerUri) { return this; } - /** - * For test only: allow Http connection - */ - @VisibleForTesting - public Builder withAllowHttpConnection() { - mHomeServerConnectionConfig.mAllowHttpExtension = true; - return this; - } - /** * Convenient method to limit the TLS versions and cipher suites for this Builder * Ref: * - https://www.ssi.gouv.fr/uploads/2017/02/security-recommendations-for-tls_v1.1.pdf * - https://developer.android.com/reference/javax/net/ssl/SSLEngine * - * @param tlsLimitations true to use Tls limitations + * @param tlsLimitations true to use Tls limitations * @param enableCompatibilityMode set to true for Android < 20 * @return this builder */ @@ -521,7 +512,7 @@ public Builder withTlsLimitations(boolean tlsLimitations, boolean enableCompatib */ public HomeServerConnectionConfig build() { // Check mandatory parameters - if (mHomeServerConnectionConfig.mHsUri == null) { + if (mHomeServerConnectionConfig.mHomeServerUri == null) { throw new RuntimeException("Home server URI not set"); } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/MXDataHandler.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/MXDataHandler.java index 77d829a62..bf31aa261 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/MXDataHandler.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/MXDataHandler.java @@ -24,7 +24,6 @@ import android.text.TextUtils; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import org.matrix.androidsdk.call.MXCallsManager; @@ -40,7 +39,6 @@ import org.matrix.androidsdk.data.metrics.MetricsListener; import org.matrix.androidsdk.data.store.IMXStore; import org.matrix.androidsdk.data.store.MXMemoryStore; -import org.matrix.androidsdk.data.timeline.EventTimeline; import org.matrix.androidsdk.db.MXMediasCache; import org.matrix.androidsdk.groups.GroupsManager; import org.matrix.androidsdk.listeners.IMXEventListener; @@ -53,7 +51,6 @@ import org.matrix.androidsdk.rest.client.ProfileRestClient; import org.matrix.androidsdk.rest.client.RoomsRestClient; import org.matrix.androidsdk.rest.client.ThirdPidRestClient; -import org.matrix.androidsdk.rest.json.ConditionDeserializer; import org.matrix.androidsdk.rest.model.ChunkEvents; import org.matrix.androidsdk.rest.model.Event; import org.matrix.androidsdk.rest.model.MatrixError; @@ -62,7 +59,6 @@ import org.matrix.androidsdk.rest.model.RoomMember; import org.matrix.androidsdk.rest.model.User; import org.matrix.androidsdk.rest.model.bingrules.BingRule; -import org.matrix.androidsdk.rest.model.bingrules.Condition; import org.matrix.androidsdk.rest.model.bingrules.PushRuleSet; import org.matrix.androidsdk.rest.model.bingrules.PushRulesResponse; import org.matrix.androidsdk.rest.model.group.InvitedGroupSync; @@ -75,7 +71,6 @@ import org.matrix.androidsdk.util.Log; import org.matrix.androidsdk.util.MXOsHandler; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -928,7 +923,7 @@ public void onSuccess(ChunkEvents info) { if (info.chunk != null) { for (Event event : info.chunk) { - room.getState().applyState(getStore(), event, EventTimeline.Direction.FORWARDS); + room.getState().applyState(event, true, getStore()); } } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/MXSession.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/MXSession.java index 49286e9cd..531b1a4c2 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/MXSession.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/MXSession.java @@ -2558,9 +2558,8 @@ public Builder withPushServerUrl(@Nullable String pushServerUrl) { if (!TextUtils.isEmpty(pushServerUrl)) { // pusher uses a custom server try { - HomeServerConnectionConfig alteredHsConfig = new HomeServerConnectionConfig.Builder() + HomeServerConnectionConfig alteredHsConfig = new HomeServerConnectionConfig.Builder(mxSession.mPushersRestClient.mHsConfig) .withHomeServerUri(Uri.parse(pushServerUrl)) - .withCredentials(mxSession.mHsConfig.getCredentials()) .build(); pushersRestClient = new PushersRestClient(alteredHsConfig); } catch (Exception e) { diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/RestClient.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/RestClient.java index fc02ec8a5..6c959e347 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/RestClient.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/RestClient.java @@ -87,7 +87,7 @@ public enum EndPointServer { private static final int READ_TIMEOUT_MS = 60000; private static final int WRITE_TIMEOUT_MS = 60000; - protected Credentials mCredentials; + private Credentials mCredentials; protected T mApi; @@ -196,17 +196,18 @@ public Response intercept(Chain chain) throws IOException { okHttpClientBuilder.dispatcher(new Dispatcher(new MXRestExecutorService())); } + final String endPoint = makeEndpoint(hsConfig, uriPrefix, endPointServer); + try { Pair pair = CertUtil.newPinnedSSLSocketFactory(hsConfig); okHttpClientBuilder.sslSocketFactory(pair.first, pair.second); okHttpClientBuilder.hostnameVerifier(CertUtil.newHostnameVerifier(hsConfig)); - okHttpClientBuilder.connectionSpecs(CertUtil.newConnectionSpecs(hsConfig)); + okHttpClientBuilder.connectionSpecs(CertUtil.newConnectionSpecs(hsConfig, endPoint)); } catch (Exception e) { - Log.e(LOG_TAG, "## RestClient() setSslSocketFactory failed" + e.getMessage(), e); + Log.e(LOG_TAG, "## RestClient() setSslSocketFactory failed: " + e.getMessage(), e); } mOkHttpClient = okHttpClientBuilder.build(); - final String endPoint = makeEndpoint(hsConfig, uriPrefix, endPointServer); // Rest adapter for turning API interfaces into actual REST-calling objects Retrofit.Builder builder = new Retrofit.Builder() @@ -404,20 +405,4 @@ public Credentials getCredentials() { public void setCredentials(Credentials credentials) { mCredentials = credentials; } - - /** - * Default protected constructor for unit tests. - */ - protected RestClient() { - } - - /** - * Protected setter for injection by unit tests. - * - * @param api the api object - */ - @VisibleForTesting() - protected void setApi(T api) { - mApi = api; - } } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/adapters/MessageRow.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/adapters/MessageRow.java index 8985a7580..06225476f 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/adapters/MessageRow.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/adapters/MessageRow.java @@ -1,12 +1,13 @@ -/* +/* * Copyright 2014 OpenMarket Ltd - * + * Copyright 2018 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. @@ -15,32 +16,72 @@ */ package org.matrix.androidsdk.adapters; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ParagraphStyle; +import android.text.style.QuoteSpan; + import org.matrix.androidsdk.data.RoomState; import org.matrix.androidsdk.rest.model.Event; +import org.matrix.androidsdk.rest.model.RoomCreateContent; +import org.matrix.androidsdk.rest.model.RoomMember; +import org.matrix.androidsdk.util.EventDisplay; -// this class defines a MessagesAdapter Item. +/** + * this class defines a MessagesAdapter Item. + */ public class MessageRow { // the linked event + @NonNull private Event mEvent; + // the room state + @Nullable private final RoomState mRoomState; + // Cache of the sender display name + private final String mSenderDisplayName; + + // Cache of the computed text + private SpannableString mText; + + @Nullable + private RoomCreateContent.Predecessor mRoomCreateContentPredecessor; + + @Nullable + private final RoomMember mSender; + /** * Constructor * * @param event the event. * @param roomState the room state */ - public MessageRow(Event event, RoomState roomState) { + public MessageRow(@NonNull Event event, @Nullable RoomState roomState) { mEvent = event; mRoomState = roomState; - } + if (roomState == null) { + // Use the id of the sender as display name + mSenderDisplayName = event.getSender(); + mRoomCreateContentPredecessor = null; + mSender = null; + } else { + mSenderDisplayName = roomState.getMemberName(event.getSender()); + if (roomState.getRoomCreateContent() != null) { + mRoomCreateContentPredecessor = roomState.getRoomCreateContent().predecessor; + } + mSender = roomState.getMember(event.getSender()); + } + } /** * @return the event. */ + @NonNull public Event getEvent() { return mEvent; } @@ -50,14 +91,64 @@ public Event getEvent() { * * @param event the event. */ - public void updateEvent(Event event) { + public void updateEvent(@NonNull Event event) { mEvent = event; + + // invalidate our cache + mText = null; + } + + /** + * Get the text of the event + * + * @param style + * @param display + * @return + */ + public Spannable getText(ParagraphStyle style, EventDisplay display) { + if (mText == null) { + CharSequence textualDisplay = display.getTextualDisplay(mEvent, mRoomState); + + mText = new SpannableString((null == textualDisplay) ? "" : textualDisplay); + + // Change to BlockQuote Spannable to customize it + replaceQuoteSpans(mText, style); + } + + return mText; + } + + public String getSenderDisplayName() { + return mSenderDisplayName; } + @Nullable + public RoomCreateContent.Predecessor getRoomCreateContentPredecessor() { + return mRoomCreateContentPredecessor; + } + + @Nullable + public RoomMember getSender() { + return mSender; + } + + /* ========================================================================================== + * Private + * ========================================================================================== */ + /** - * @return the room state. + * Replace all QuoteSpan instances by instances of VectorQuoteSpan + * + * @param spannable */ - public RoomState getRoomState() { - return mRoomState; + private void replaceQuoteSpans(Spannable spannable, ParagraphStyle style) { + QuoteSpan[] quoteSpans = spannable.getSpans(0, spannable.length(), QuoteSpan.class); + for (QuoteSpan quoteSpan : quoteSpans) { + int start = spannable.getSpanStart(quoteSpan); + int end = spannable.getSpanEnd(quoteSpan); + int flags = spannable.getSpanFlags(quoteSpan); + spannable.removeSpan(quoteSpan); + spannable.setSpan(style, start, end, flags); + } } } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/call/IMXCall.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/call/IMXCall.java index 944580f91..ab7dfb3b2 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/call/IMXCall.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/call/IMXCall.java @@ -24,6 +24,8 @@ import org.matrix.androidsdk.data.Room; import org.matrix.androidsdk.rest.model.Event; +import javax.annotation.Nullable; + /** * Audio/video call interface. * See {@link MXWebRtcCall} and {@link MXChromeCall}. @@ -170,9 +172,9 @@ public interface IMXCall { /** * The call is hung up. * - * @param reason the reason + * @param reason the reason, or null for no reason. Reasons are used to indicate errors in the current VoIP implementation. */ - void hangup(String reason); + void hangup(@Nullable String reason); /** * Add a listener to the call manager. diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXCall.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXCall.java index 51bde742f..0f2f04ebf 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXCall.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXCall.java @@ -40,6 +40,8 @@ import java.util.Set; import java.util.Timer; +import javax.annotation.Nullable; + /** * This class is the default implementation */ @@ -217,8 +219,10 @@ public void onAnsweredElsewhere() { /** * The call is hung up. + * + * @param reason the reason, or null for no reason. Reasons are used to indicate errors in the current VoIP implementation. */ - public void hangup(String reason) { + public void hangup(@Nullable String reason) { } // getters / setters diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXChromeCall.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXChromeCall.java index 6ece1d208..257868ad8 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXChromeCall.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXChromeCall.java @@ -44,6 +44,8 @@ import java.util.Timer; import java.util.TimerTask; +import javax.annotation.Nullable; + public class MXChromeCall extends MXCall { private static final String LOG_TAG = MXChromeCall.class.getSimpleName(); @@ -379,9 +381,11 @@ public void run() { /** * The call is hung up. + * + * @param reason the reason, or null for no reason. Reasons are used to indicate errors in the current VoIP implementation. */ @Override - public void hangup(String reason) { + public void hangup(@Nullable String reason) { super.hangup(reason); if (!CALL_STATE_CREATED.equals(getCallState()) && (null != mWebView)) { diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXWebRtcCall.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXWebRtcCall.java index dc689ddbb..6bd2ab630 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXWebRtcCall.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/call/MXWebRtcCall.java @@ -58,6 +58,8 @@ import java.util.Timer; import java.util.TimerTask; +import javax.annotation.Nullable; + public class MXWebRtcCall extends MXCall { private static final String LOG_TAG = MXWebRtcCall.class.getSimpleName(); @@ -1638,9 +1640,11 @@ public void onSetFailure(String s) { /** * The call is hung up. + * + * @param reason the reason, or null for no reason. Reasons are used to indicate errors in the current VoIP implementation. */ @Override - public void hangup(String reason) { + public void hangup(@Nullable String reason) { super.hangup(reason); Log.d(LOG_TAG, "## hangup(): reason=" + reason); diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/Room.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/Room.java index 0706ed978..70688ddab 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/Room.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/Room.java @@ -33,7 +33,6 @@ import android.text.TextUtils; import android.util.Pair; -import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; @@ -43,6 +42,8 @@ import org.matrix.androidsdk.call.MXCallsManager; import org.matrix.androidsdk.crypto.MXCryptoError; import org.matrix.androidsdk.crypto.data.MXEncryptEventContentResult; +import org.matrix.androidsdk.data.room.RoomAvatarResolver; +import org.matrix.androidsdk.data.room.RoomDisplayNameResolver; import org.matrix.androidsdk.data.store.IMXStore; import org.matrix.androidsdk.data.timeline.EventTimeline; import org.matrix.androidsdk.data.timeline.EventTimelineFactory; @@ -136,6 +137,12 @@ public class Room { // true when the current room is a left one private boolean mIsLeft; + // Class to compute room display name + private final RoomDisplayNameResolver mRoomDisplayNameResolver; + + // Class to compute room avatar + private final RoomAvatarResolver mRoomAvatarResolver; + /** * Constructor * FIXME All this @NonNull annotation must be also added to the class members and getters @@ -149,6 +156,8 @@ public Room(@NonNull final MXDataHandler dataHandler, @NonNull final IMXStore st mStore = store; mMyUserId = mDataHandler.getUserId(); mTimeline = EventTimelineFactory.liveTimeline(mDataHandler, this, roomId); + mRoomDisplayNameResolver = new RoomDisplayNameResolver(this); + mRoomAvatarResolver = new RoomAvatarResolver(this); } /** @@ -268,7 +277,7 @@ private void handleEphemeralEvents(List events) { List typingUsers = null; try { - typingUsers = (new Gson()).fromJson(eventContent.get("user_ids"), new TypeToken>() { + typingUsers = JsonUtils.getBasicGson().fromJson(eventContent.get("user_ids"), new TypeToken>() { }.getType()); } catch (Exception e) { Log.e(LOG_TAG, "## handleEphemeralEvents() : exception " + e.getMessage(), e); @@ -542,6 +551,14 @@ public void run() { }); } + /** + * @param context the application context. + * @return the computed room display name + */ + public String getRoomDisplayName(Context context) { + return mRoomDisplayNameResolver.resolve(context); + } + public String getTopic() { return getState().topic; } @@ -630,7 +647,7 @@ public void onSucceed(JsonObject object) { Map map = null; try { - map = new Gson().fromJson(object, new TypeToken>() { + map = JsonUtils.getBasicGson().fromJson(object, new TypeToken>() { }.getType()); } catch (Exception e) { Log.e(LOG_TAG, "joinWithThirdPartySigned : Gson().fromJson failed" + e.getMessage(), e); @@ -737,6 +754,7 @@ public void onMatrixError(MatrixError e) { Log.e(LOG_TAG, "join onMatrixError " + e.getMessage()); if (MatrixError.UNKNOWN.equals(e.errcode) && TextUtils.equals("No known servers", e.error)) { + // It can happen when user wants to join a room he was invited to, but the inviter has left // minging kludge until https://matrix.org/jira/browse/SYN-678 is fixed // 'Error when trying to join an empty room should be more explicit e.error = getStore().getContext().getString(org.matrix.androidsdk.R.string.room_error_join_failed_empty_room); @@ -974,26 +992,12 @@ public void onSuccess(Void info) { */ @Nullable public String getAvatarUrl() { - String res = getState().getAvatarUrl(); - - // detect if it is a room with no more than 2 members (i.e. an alone or a 1:1 chat) - if (null == res) { - if (getNumberOfMembers() == 1 && !getState().getLoadedMembers().isEmpty()) { - res = getState().getLoadedMembers().get(0).getAvatarUrl(); - } else if (getNumberOfMembers() == 2 && getState().getLoadedMembers().size() > 1) { - RoomMember m1 = getState().getLoadedMembers().get(0); - RoomMember m2 = getState().getLoadedMembers().get(1); - - res = TextUtils.equals(m1.getUserId(), mMyUserId) ? m2.getAvatarUrl() : m1.getAvatarUrl(); - } - } - - return res; + return mRoomAvatarResolver.resolve(); } /** - * The call avatar is the same as the room avatar except there are only 2 JOINED members. - * In this case, it returns the avtar of the other joined member. + * The call avatar is the same as the room avatar except when there are only 2 JOINED members. + * In this case, it returns the avatar of the other joined member. * * @return the call avatar URL. */ diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomMediaMessagesSender.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomMediaMessagesSender.java index 2ff1435ff..7c5b943f6 100755 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomMediaMessagesSender.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/RoomMediaMessagesSender.java @@ -56,6 +56,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.regex.Pattern; /** * Room helper to send media messages in the right order. @@ -87,6 +88,9 @@ class RoomMediaMessagesSender { // encoding creation threads private static android.os.Handler mEncodingHandler = null; + // Pattern to strip previous reply when replying to a message. It also matches multi lines previous reply, when for instance containing blockquote. + private static Pattern sPreviousReplyPattern = Pattern.compile("^.*", Pattern.DOTALL); + /** * Constructor * @@ -514,7 +518,7 @@ private String includeReplyToToFormattedBody(Event replyToEvent, boolean isEmote) { if (stripPreviousReplyTo) { // Strip replyToFormattedBody from previous reply to - replyToFormattedBody = replyToFormattedBody.replaceAll("^.*", ""); + replyToFormattedBody = sPreviousReplyPattern.matcher(replyToFormattedBody).replaceAll(""); } StringBuilder ret = new StringBuilder("
mMergedAliasesList; - // + // Map of list of state event, the key are the event type private Map> mStateEvents = new HashMap<>(); // The canonical alias of the room. @@ -457,7 +458,15 @@ public void onSuccess(List members) { * @return true if it is a call conference room. */ public boolean isConferenceUserRoom() { - return getDataHandler().getStore().getSummary(roomId).isConferenceUserRoom(); + if (getDataHandler() != null + && getDataHandler().getStore() != null + && getDataHandler().getStore().getSummary(roomId) != null) { + return getDataHandler().getStore().getSummary(roomId).isConferenceUserRoom(); + } else { + Log.w(LOG_TAG, "## isConferenceUserRoom(): something is null"); + } + + return false; } /** @@ -466,7 +475,13 @@ public boolean isConferenceUserRoom() { * @param isConferenceUserRoom true when it is an user conference room. */ public void setIsConferenceUserRoom(boolean isConferenceUserRoom) { - getDataHandler().getStore().getSummary(roomId).setIsConferenceUserRoom(isConferenceUserRoom); + if (getDataHandler() != null + && getDataHandler().getStore() != null + && getDataHandler().getStore().getSummary(roomId) != null) { + getDataHandler().getStore().getSummary(roomId).setIsConferenceUserRoom(isConferenceUserRoom); + } else { + Log.w(LOG_TAG, "## setIsConferenceUserRoom(): something is null"); + } } /** @@ -475,7 +490,8 @@ public void setIsConferenceUserRoom(boolean isConferenceUserRoom) { * @param userId the user id. * @param member the new member value. */ - private void setMember(String userId, RoomMember member) { + @VisibleForTesting + public void setMember(String userId, RoomMember member) { // Populate a basic user object if there is none if (member.getUserId() == null) { member.setUserId(userId); @@ -826,13 +842,6 @@ public RoomTombstoneContent getRoomTombstoneContent() { return mRoomTombstoneContent; } - /** - * @return true if the room has a predecessor - */ - public boolean hasPredecessor() { - return mRoomCreateContent != null && mRoomCreateContent.hasPredecessor(); - } - /** * @return the room create content */ @@ -858,17 +867,26 @@ public String encryptionAlgorithm() { /** * Apply the given event (relevant for state changes) to our state. * - * @param store the store to use - * @param event the event - * @param direction how the event should affect the state: Forwards for applying, backwards for un-applying (applying the previous state) + * @param event the state event + * @param considerNewContent how the event should affect the state: true for applying (consider the content of the event), + * false for un-applying (consider the previous content of the event) + * @param store the store to use to store the event and the User. Can be null * @return true if the event is managed */ - public boolean applyState(IMXStore store, Event event, EventTimeline.Direction direction) { + public boolean applyState(@NonNull Event event, + boolean considerNewContent, + @Nullable IMXStore store) { if (event.stateKey == null) { return false; } - JsonObject contentToConsider = (direction == EventTimeline.Direction.FORWARDS) ? event.getContentAsJsonObject() : event.getPrevContentAsJsonObject(); + JsonObject contentToConsider; + if (considerNewContent) { + contentToConsider = event.getContentAsJsonObject(); + } else { + contentToConsider = event.getPrevContentAsJsonObject(); + } + String eventType = event.getType(); try { @@ -936,7 +954,7 @@ public boolean applyState(IMXStore store, Event event, EventTimeline.Direction d member.setOriginalEventId(event.eventId); member.mSender = event.getSender(); - if ((null != store) && (direction == EventTimeline.Direction.FORWARDS)) { + if (store != null) { store.storeRoomStateEvent(roomId, event); } @@ -975,7 +993,7 @@ public boolean applyState(IMXStore store, Event event, EventTimeline.Direction d } } - if ((direction == EventTimeline.Direction.FORWARDS) && (null != store)) { + if (store != null) { store.updateUserWithRoomMemberEvent(member); } @@ -997,7 +1015,7 @@ public boolean applyState(IMXStore store, Event event, EventTimeline.Direction d thirdPartyInvite.token = event.stateKey; - if ((direction == EventTimeline.Direction.FORWARDS) && (null != store)) { + if (store != null) { store.storeRoomStateEvent(roomId, event); } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/comparator/Comparators.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/comparator/Comparators.java index 19c306c14..cf61e943d 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/comparator/Comparators.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/comparator/Comparators.java @@ -22,7 +22,9 @@ public class Comparators { - // comparator to sort from the oldest to the latest. + /** + * Comparator to sort DatedObjects from the oldest to the latest. + */ public static final Comparator ascComparator = new Comparator() { @Override public int compare(DatedObject datedObject1, DatedObject datedObject2) { @@ -30,7 +32,9 @@ public int compare(DatedObject datedObject1, DatedObject datedObject2) { } }; - // comparator to sort from the latest to the oldest. + /** + * Comparator to sort DatedObjects from the latest to the oldest. + */ public static final Comparator descComparator = new Comparator() { @Override public int compare(DatedObject datedObject1, DatedObject datedObject2) { diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/room/RoomAvatarResolver.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/room/RoomAvatarResolver.java new file mode 100644 index 000000000..2e2816a6f --- /dev/null +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/room/RoomAvatarResolver.java @@ -0,0 +1,73 @@ +/* + * Copyright 2018 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.data.room; + +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import org.matrix.androidsdk.data.Room; +import org.matrix.androidsdk.rest.model.RoomMember; + +/** + * This class computes room avatar + */ +public class RoomAvatarResolver { + + private static final String LOG_TAG = RoomAvatarResolver.class.getSimpleName(); + + private final Room mRoom; + + public RoomAvatarResolver(Room room) { + mRoom = room; + } + + /** + * Compute the room avatar url + * + * @return the room avatar url, can be a fallback to a room member avatar or null + */ + @Nullable + public String resolve() { + String res = mRoom.getState().getAvatarUrl(); + + if (res == null) { + if (mRoom.isInvited()) { + // In this case, if LazyLoading is ON, we cannot rely of mRoom.getNumberOfMembers() (it will return 0) + if (mRoom.getState().getLoadedMembers().size() == 1) { + res = mRoom.getState().getLoadedMembers().get(0).getAvatarUrl(); + } else if (mRoom.getState().getLoadedMembers().size() > 1) { + RoomMember m1 = mRoom.getState().getLoadedMembers().get(0); + RoomMember m2 = mRoom.getState().getLoadedMembers().get(1); + + res = TextUtils.equals(m1.getUserId(), mRoom.getDataHandler().getUserId()) ? m2.getAvatarUrl() : m1.getAvatarUrl(); + } + } else { + // detect if it is a room with no more than 2 members (i.e. an alone or a 1:1 chat) + if (mRoom.getNumberOfMembers() == 1 && !mRoom.getState().getLoadedMembers().isEmpty()) { + res = mRoom.getState().getLoadedMembers().get(0).getAvatarUrl(); + } else if (mRoom.getNumberOfMembers() == 2 && mRoom.getState().getLoadedMembers().size() > 1) { + RoomMember m1 = mRoom.getState().getLoadedMembers().get(0); + RoomMember m2 = mRoom.getState().getLoadedMembers().get(1); + + res = TextUtils.equals(m1.getUserId(), mRoom.getDataHandler().getUserId()) ? m2.getAvatarUrl() : m1.getAvatarUrl(); + } + } + } + + return res; + } +} diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/room/RoomDisplayNameResolver.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/room/RoomDisplayNameResolver.java new file mode 100644 index 000000000..394614beb --- /dev/null +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/room/RoomDisplayNameResolver.java @@ -0,0 +1,152 @@ +/* + * Copyright 2018 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.data.room; + +import android.content.Context; +import android.text.TextUtils; + +import org.matrix.androidsdk.R; +import org.matrix.androidsdk.data.Room; +import org.matrix.androidsdk.data.RoomState; +import org.matrix.androidsdk.data.comparator.Comparators; +import org.matrix.androidsdk.rest.model.RoomMember; +import org.matrix.androidsdk.util.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * This class computes room display name + */ +public class RoomDisplayNameResolver { + + private static final String LOG_TAG = RoomDisplayNameResolver.class.getSimpleName(); + + private final Room mRoom; + + public RoomDisplayNameResolver(Room room) { + mRoom = room; + } + + /** + * Compute the room display name + * + * @param context + * @return the room display name + */ + public String resolve(Context context) { + try { + // this algorithm is the one defined in + // https://github.com/matrix-org/matrix-js-sdk/blob/develop/lib/models/room.js#L617 + // calculateRoomName(room, userId) + + // For Lazy Loaded room, see algorithm here: + // https://docs.google.com/document/d/11i14UI1cUz-OJ0knD5BFu7fmT6Fo327zvMYqfSAR7xs/edit#heading=h.qif6pkqyjgzn + + RoomState roomState = mRoom.getState(); + + if (!TextUtils.isEmpty(roomState.name)) { + return roomState.name; + } + + if (!TextUtils.isEmpty(roomState.getCanonicalAlias())) { + return roomState.getCanonicalAlias(); + } + + // Temporary patch: use the first alias if available + if (roomState.aliases != null && !roomState.aliases.isEmpty()) { + return roomState.aliases.get(0); + } + + // List of active members, current user excluded + List othersActiveMembers = new ArrayList<>(); + RoomMember currentUser = null; + + int nbOfOtherMembers; + + if (mRoom.getDataHandler().isLazyLoadingEnabled() + && mRoom.isJoined() + && mRoom.getRoomSummary() != null) { + List heroes = mRoom.getRoomSummary().getHeroes(); + + for (String id : heroes) { + RoomMember roomMember = roomState.getMember(id); + + if (roomMember != null) { + othersActiveMembers.add(roomMember); + } + } + } else { + Collection members = roomState.getDisplayableLoadedMembers(); + + for (RoomMember member : members) { + if (!TextUtils.equals(member.membership, RoomMember.MEMBERSHIP_LEAVE)) { + if (TextUtils.equals(member.getUserId(), mRoom.getDataHandler().getUserId())) { + currentUser = member; + } else { + othersActiveMembers.add(member); + } + } + } + + Collections.sort(othersActiveMembers, Comparators.ascComparator); + } + + nbOfOtherMembers = othersActiveMembers.size(); + + String displayName; + + if (mRoom.isInvited()) { + if (currentUser != null + && !othersActiveMembers.isEmpty() + && !TextUtils.isEmpty(currentUser.mSender)) { + // extract who invited us to the room + displayName = context.getString(R.string.room_displayname_invite_from, roomState.getMemberName(currentUser.mSender)); + } else { + displayName = context.getString(R.string.room_displayname_room_invite); + } + } else { + if (nbOfOtherMembers == 0) { + displayName = context.getString(R.string.room_displayname_empty_room); + } else if (nbOfOtherMembers == 1) { + RoomMember member = othersActiveMembers.get(0); + displayName = roomState.getMemberName(member.getUserId()); + } else if (nbOfOtherMembers == 2) { + RoomMember member1 = othersActiveMembers.get(0); + RoomMember member2 = othersActiveMembers.get(1); + + displayName = context.getString(R.string.room_displayname_two_members, + roomState.getMemberName(member1.getUserId()), roomState.getMemberName(member2.getUserId())); + } else { + RoomMember member = othersActiveMembers.get(0); + displayName = context.getResources().getQuantityString(R.plurals.room_displayname_three_and_more_members, + mRoom.getNumberOfJoinedMembers() - 1, + roomState.getMemberName(member.getUserId()), + mRoom.getNumberOfJoinedMembers() - 1); + } + } + + return displayName; + } catch (Exception e) { + Log.e(LOG_TAG, "## Computing room display name failed " + e.getMessage(), e); + } + + return mRoom.getRoomId(); + } +} diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/MXEventTimeline.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/MXEventTimeline.java index a5f1fad5a..47e4af8b3 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/MXEventTimeline.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/MXEventTimeline.java @@ -319,12 +319,13 @@ private void deepCopyState(Direction direction) { /** * Process a state event to keep the internal live and back states up to date. * - * @param event the state event - * @param direction the direction; ie. forwards for live state, backwards for back state + * @param event the state event + * @param direction the direction; ie. forwards for live state, backwards for back state + * @param considerNewContent how the event should affect the state: true for applying, false for un-applying (applying the previous state) * @return true if the event has been processed. */ - private boolean processStateEvent(Event event, Direction direction) { - return mStateHolder.processStateEvent(event, direction); + private boolean processStateEvent(Event event, Direction direction, boolean considerNewContent) { + return mStateHolder.processStateEvent(event, direction, considerNewContent); } /** @@ -470,10 +471,10 @@ private void addPaginationEvents(List events, for (Event stateEvent : stateEvents) { if (direction == Direction.BACKWARDS) { // Enrich the timeline root state with the additional state events observed during back pagination - processStateEvent(stateEvent, Direction.FORWARDS); + processStateEvent(stateEvent, Direction.FORWARDS, true); } - processStateEvent(stateEvent, direction); + processStateEvent(stateEvent, direction, true); } } @@ -482,8 +483,10 @@ private void addPaginationEvents(List events, boolean processedEvent = true; if (event.stateKey != null) { + boolean considerNewContent = direction == Direction.FORWARDS; + deepCopyState(direction); - processedEvent = processStateEvent(event, direction); + processedEvent = processStateEvent(event, direction, considerNewContent); } // Decrypt event if necessary @@ -877,7 +880,7 @@ public void onSuccess(final EventContext eventContext) { protected Void doInBackground(Void... params) { // the state is the one after the latest event of the chunk i.e. the last message of eventContext.eventsAfter for (Event event : eventContext.state) { - processStateEvent(event, Direction.FORWARDS); + processStateEvent(event, Direction.FORWARDS, true); } // init the room states diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/StateEventRedactionChecker.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/StateEventRedactionChecker.java index f4a077805..edc3fab3b 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/StateEventRedactionChecker.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/StateEventRedactionChecker.java @@ -81,7 +81,7 @@ public void onSuccess(List stateEvents) { stateEvent.prune(redactionEvent); stateEvents.set(index, stateEvent); // digest the updated state - mTimelineStateHolder.processStateEvent(stateEvent, EventTimeline.Direction.FORWARDS); + mTimelineStateHolder.processStateEvent(stateEvent, EventTimeline.Direction.FORWARDS, true); isFound = true; break; } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineJoinRoomSyncHandler.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineJoinRoomSyncHandler.java index cfc01bdce..e51cb3420 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineJoinRoomSyncHandler.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineJoinRoomSyncHandler.java @@ -98,7 +98,7 @@ private void handleRoomSyncState(@NonNull final Room room, if (room.getDataHandler().isAlive()) { for (Event event : mRoomSync.state.events) { try { - mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS); + mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS, true); } catch (Exception e) { Log.e(LOG_TAG, "processStateEvent failed " + e.getMessage(), e); } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineLiveEventHandler.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineLiveEventHandler.java index 1e2a15fc7..d8574ec1e 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineLiveEventHandler.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineLiveEventHandler.java @@ -167,7 +167,7 @@ public void handleLiveEvent(@NonNull final Event event, // copy the live state before applying any update mTimelineStateHolder.deepCopyState(EventTimeline.Direction.FORWARDS); // check if the event has been processed - if (!mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS)) { + if (!mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS, true)) { // not processed -> do not warn the application // assume that the event is a duplicated one. return; @@ -236,9 +236,9 @@ private void storeLiveRoomEvent(@NonNull final MXDataHandler dataHandler, } } final RoomState state = mTimelineStateHolder.getState(); - final EventDisplay eventDisplay = new EventDisplay(store.getContext(), indexedEvent, state); + final EventDisplay eventDisplay = new EventDisplay(store.getContext()); // ensure that message can be displayed - if (!TextUtils.isEmpty(eventDisplay.getTextualDisplay())) { + if (!TextUtils.isEmpty(eventDisplay.getTextualDisplay(indexedEvent, state))) { event = indexedEvent; break; } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineStateHolder.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineStateHolder.java index 06efac789..53bd0da39 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineStateHolder.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/data/timeline/TimelineStateHolder.java @@ -93,7 +93,7 @@ public void setBackState(@NonNull final RoomState state) { } /** - * Make a deep copy or the dedicated state. + * Make a deep copy of the dedicated state. * * @param direction the room state direction to deep copy. */ @@ -108,17 +108,31 @@ public void deepCopyState(final EventTimeline.Direction direction) { /** * Process a state event to keep the internal live and back states up to date. * - * @param event the state event - * @param direction the direction; ie. forwards for live state, backwards for back state + * @param event the state event + * @param direction the direction; ie. forwards for live state, backwards for back state + * @param considerNewContent how the event should affect the state: true for applying, false for un-applying (applying the previous state) * @return true if the event has been processed. */ public boolean processStateEvent(@NonNull final Event event, - @NonNull final EventTimeline.Direction direction) { - final RoomState affectedState = direction == EventTimeline.Direction.FORWARDS ? mState : mBackState; - final boolean isProcessed = affectedState.applyState(mStore, event, direction); + @NonNull final EventTimeline.Direction direction, + final boolean considerNewContent) { + final RoomState affectedState; + final IMXStore store; + if (direction == EventTimeline.Direction.FORWARDS) { + affectedState = mState; + store = mStore; + } else { + affectedState = mBackState; + // In the case of backward pagination, we do not want to persist the state event + store = null; + } + + final boolean isProcessed = affectedState.applyState(event, considerNewContent, store); + if (isProcessed && direction == EventTimeline.Direction.FORWARDS) { mStore.storeLiveStateForRoom(mRoomId); } + return isProcessed; } @@ -144,6 +158,4 @@ private void initStates() { mState.setDataHandler(mDataHandler); mState.roomId = mRoomId; } - - } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/fragments/MatrixMessageListFragment.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/fragments/MatrixMessageListFragment.java index 575490de7..2670365d1 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/fragments/MatrixMessageListFragment.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/fragments/MatrixMessageListFragment.java @@ -1791,8 +1791,8 @@ public void onEvent(final Event event, final EventTimeline.Direction direction, // test if the event is displayable // GA issue : the activity can be null if (!hasToRemoved && (null != getActivity())) { - EventDisplay eventDisplay = new EventDisplay(getActivity(), prunedEvent, roomState); - hasToRemoved = TextUtils.isEmpty(eventDisplay.getTextualDisplay()); + EventDisplay eventDisplay = new EventDisplay(getActivity()); + hasToRemoved = TextUtils.isEmpty(eventDisplay.getTextualDisplay(prunedEvent, roomState)); } // event is removed if it has no more content. diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/api/EventsApi.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/api/EventsApi.java index 813c5b43d..d8dd58173 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/api/EventsApi.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/api/EventsApi.java @@ -39,6 +39,7 @@ /** * The events API. + * This interface contains also a lot of miscellaneous requests */ public interface EventsApi { diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/api/ProfileApi.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/api/ProfileApi.java index c3e69d0ec..9cb7b14ba 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/api/ProfileApi.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/api/ProfileApi.java @@ -110,7 +110,7 @@ public interface ProfileApi { * @param refreshParams the refresh token parameters */ @POST(RestClient.URI_API_PREFIX_PATH_R0 + "tokenrefresh") - Call tokenrefresh(@Body TokenRefreshParams refreshParams); + Call tokenRefresh(@Body TokenRefreshParams refreshParams); /** * List all 3PIDs linked to the Matrix user account. diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/EventsRestClient.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/EventsRestClient.java index b36a5b18f..d28219d71 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/EventsRestClient.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/EventsRestClient.java @@ -66,10 +66,6 @@ public EventsRestClient(HomeServerConnectionConfig hsConfig) { super(hsConfig, EventsApi.class, "", false); } - protected EventsRestClient(EventsApi api) { - mApi = api; - } - /** * Retrieves the third party server protocols * diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/GroupsRestClient.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/GroupsRestClient.java index 364cc0d2d..609dd7a74 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/GroupsRestClient.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/GroupsRestClient.java @@ -60,10 +60,6 @@ public GroupsRestClient(HomeServerConnectionConfig hsConfig) { super(hsConfig, GroupsApi.class, RestClient.URI_API_PREFIX_PATH_R0, false); } - protected GroupsRestClient(GroupsApi api) { - mApi = api; - } - /** * Create a group. * diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/LoginRestClient.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/LoginRestClient.java index cdb6fb8be..6344ae769 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/LoginRestClient.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/LoginRestClient.java @@ -136,8 +136,8 @@ public void onRetry() { @Override public void success(JsonObject jsonObject, Response response) { onEventSent(); - mCredentials = gson.fromJson(jsonObject, Credentials.class); - callback.onSuccess(mCredentials); + setCredentials(gson.fromJson(jsonObject, Credentials.class)); + callback.onSuccess(getCredentials()); } }); } @@ -281,8 +281,8 @@ public void onRetry() { @Override public void success(JsonObject jsonObject, Response response) { onEventSent(); - mCredentials = gson.fromJson(jsonObject, Credentials.class); - callback.onSuccess(mCredentials); + setCredentials(gson.fromJson(jsonObject, Credentials.class)); + callback.onSuccess(getCredentials()); } }); } @@ -334,8 +334,8 @@ public void onRetry() { @Override public void success(JsonObject jsonObject, Response response) { onEventSent(); - mCredentials = gson.fromJson(jsonObject, Credentials.class); - callback.onSuccess(mCredentials); + setCredentials(gson.fromJson(jsonObject, Credentials.class)); + callback.onSuccess(getCredentials()); } }); } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/ProfileRestClient.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/ProfileRestClient.java index c678b4e1f..25f36daec 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/ProfileRestClient.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/ProfileRestClient.java @@ -102,7 +102,7 @@ public void updateDisplayname(final String newName, final ApiCallback call // don't retry if the network comes back // let the user chooses what he want to do - mApi.displayname(mCredentials.userId, user) + mApi.displayname(getCredentials().userId, user) .enqueue(new RestAdapterCallback(description, mUnsentEventsManager, callback, new RestAdapterCallback.RequestRetryCallBack() { @Override public void onRetry() { @@ -150,7 +150,7 @@ public void updateAvatarUrl(final String newUrl, final ApiCallback callbac User user = new User(); user.setAvatarUrl(newUrl); - mApi.avatarUrl(mCredentials.userId, user) + mApi.avatarUrl(getCredentials().userId, user) .enqueue(new RestAdapterCallback(description, mUnsentEventsManager, callback, new RestAdapterCallback.RequestRetryCallBack() { @Override public void onRetry() { @@ -297,17 +297,17 @@ public void refreshTokens(final ApiCallback callback) { final String description = "refreshTokens"; TokenRefreshParams params = new TokenRefreshParams(); - params.refresh_token = mCredentials.refreshToken; + params.refresh_token = getCredentials().refreshToken; - mApi.tokenrefresh(params) + mApi.tokenRefresh(params) .enqueue(new RestAdapterCallback(description, mUnsentEventsManager, callback, null) { @Override - public void success(TokenRefreshResponse tokenreponse, Response response) { + public void success(TokenRefreshResponse tokenResponse, Response response) { onEventSent(); - mCredentials.refreshToken = tokenreponse.refresh_token; - mCredentials.accessToken = tokenreponse.access_token; + getCredentials().refreshToken = tokenResponse.refreshToken; + getCredentials().accessToken = tokenResponse.accessToken; if (null != callback) { - callback.onSuccess(mCredentials); + callback.onSuccess(getCredentials()); } } }); diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/RoomsRestClient.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/RoomsRestClient.java index 43930bc1f..0ebcf34f6 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/RoomsRestClient.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/client/RoomsRestClient.java @@ -857,7 +857,7 @@ public void addTag(final String roomId, final String tag, final Double order, fi Map hashMap = new HashMap<>(); hashMap.put("order", order); - mApi.addTag(mCredentials.userId, roomId, tag, hashMap) + mApi.addTag(getCredentials().userId, roomId, tag, hashMap) .enqueue(new RestAdapterCallback(description, mUnsentEventsManager, callback, new RestAdapterCallback.RequestRetryCallBack() { @Override public void onRetry() { @@ -876,7 +876,7 @@ public void onRetry() { public void removeTag(final String roomId, final String tag, final ApiCallback callback) { final String description = "removeTag : roomId " + roomId + " - tag " + tag; - mApi.removeTag(mCredentials.userId, roomId, tag) + mApi.removeTag(getCredentials().userId, roomId, tag) .enqueue(new RestAdapterCallback(description, mUnsentEventsManager, callback, new RestAdapterCallback.RequestRetryCallBack() { @Override public void onRetry() { @@ -898,7 +898,7 @@ public void updateURLPreviewStatus(final String roomId, final boolean status, fi Map params = new HashMap<>(); params.put(AccountDataRestClient.ACCOUNT_DATA_KEY_URL_PREVIEW_DISABLE, !status); - mApi.updateAccountData(mCredentials.userId, roomId, Event.EVENT_TYPE_URL_PREVIEW, params) + mApi.updateAccountData(getCredentials().userId, roomId, Event.EVENT_TYPE_URL_PREVIEW, params) .enqueue(new RestAdapterCallback(description, mUnsentEventsManager, callback, new RestAdapterCallback.RequestRetryCallBack() { @Override public void onRetry() { diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/RoomCreateContent.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/RoomCreateContent.java index 94d3090bd..6fae65aac 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/RoomCreateContent.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/RoomCreateContent.java @@ -26,6 +26,11 @@ public class RoomCreateContent implements Serializable { public String creator; + + // Not used for the moment + // @SerializedName("room_version") + // public String roomVersion; + public Predecessor predecessor; public RoomCreateContent deepCopy() { @@ -35,10 +40,6 @@ public RoomCreateContent deepCopy() { return copy; } - public boolean hasPredecessor() { - return predecessor != null; - } - /** * A link to an old room in case of room versioning */ diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/RoomMember.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/RoomMember.java index 8006c1474..792b3b1a8 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/RoomMember.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/RoomMember.java @@ -21,6 +21,7 @@ import com.google.gson.annotations.SerializedName; +import org.matrix.androidsdk.interfaces.DatedObject; import org.matrix.androidsdk.util.ContentManager; import org.matrix.androidsdk.util.Log; @@ -33,7 +34,7 @@ /** * Class representing a room member: a user with membership information. */ -public class RoomMember implements Externalizable { +public class RoomMember implements Externalizable, DatedObject { private static final String LOG_TAG = RoomMember.class.getSimpleName(); public static final String MEMBERSHIP_JOIN = "join"; @@ -44,7 +45,9 @@ public class RoomMember implements Externalizable { // not supported by the server sync response by computed from the room state events public static final String MEMBERSHIP_KICK = "kick"; + @SerializedName("displayname") public String displayname; + @SerializedName("avatar_url") public String avatarUrl; public String membership; public Invite thirdPartyInvite; @@ -65,6 +68,11 @@ public class RoomMember implements Externalizable { // user which banned or kicked this member public String mSender; + @Override + public long getDate() { + return mOriginServerTs; + } + @Override public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException { if (input.readBoolean()) { diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/login/TokenRefreshResponse.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/login/TokenRefreshResponse.java index 57e5ad39f..8b6d4518a 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/login/TokenRefreshResponse.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/login/TokenRefreshResponse.java @@ -1,8 +1,29 @@ +/* + * Copyright 2018 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.rest.model.login; +import com.google.gson.annotations.SerializedName; + public class TokenRefreshResponse { - public String access_token; - public String refresh_token; + @SerializedName("access_token") + public String accessToken; + + @SerializedName("refresh_token") + public String refreshToken; } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/sync/RoomSyncUnreadNotifications.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/sync/RoomSyncUnreadNotifications.java index d9cf25605..e6b6aa53e 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/sync/RoomSyncUnreadNotifications.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/rest/model/sync/RoomSyncUnreadNotifications.java @@ -1,13 +1,13 @@ -/* +/* * Copyright 2016 OpenMarket Ltd * Copyright 2018 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. @@ -16,19 +16,10 @@ */ package org.matrix.androidsdk.rest.model.sync; -import org.matrix.androidsdk.rest.model.Event; - -import java.util.List; - /** - `MXRoomSyncUnreadNotifications` represents the unread counts for a room. + * `MXRoomSyncUnreadNotifications` represents the unread counts for a room. */ public class RoomSyncUnreadNotifications { - /** - * List of account data events (array of Event). - */ - public List events; - /** * The number of unread messages that match the push notification rules. */ diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/CertUtil.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/CertUtil.java index 85f5b4620..60b638f32 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/CertUtil.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/CertUtil.java @@ -1,5 +1,6 @@ /* * Copyright 2016 OpenMarket Ltd + * Copyright 2018 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. @@ -16,13 +17,16 @@ package org.matrix.androidsdk.ssl; +import android.support.annotation.NonNull; import android.util.Pair; import org.matrix.androidsdk.HomeServerConnectionConfig; import org.matrix.androidsdk.util.Log; import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -144,60 +148,70 @@ public static UnrecognizedCertificateException getCertificateException(Throwable * @return SSLSocket factory */ public static Pair newPinnedSSLSocketFactory(HomeServerConnectionConfig hsConfig) { - try { - X509TrustManager defaultTrustManager = null; - - // If we haven't specified that we wanted to pin the certs, fallback to standard - // X509 checks if fingerprints don't match. - if (!hsConfig.shouldPin()) { - TrustManagerFactory tf = null; + X509TrustManager defaultTrustManager = null; + + // If we haven't specified that we wanted to pin the certs, fallback to standard + // X509 checks if fingerprints don't match. + if (!hsConfig.shouldPin()) { + TrustManagerFactory trustManagerFactory = null; + + // get the PKIX instance + try { + trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); + } catch (NoSuchAlgorithmException e) { + Log.e(LOG_TAG, "## newPinnedSSLSocketFactory() : TrustManagerFactory.getInstance failed " + e.getMessage(), e); + } - // get the PKIX instance + // it doesn't exist, use the default one. + if (trustManagerFactory == null) { try { - tf = TrustManagerFactory.getInstance("PKIX"); - } catch (Exception e) { - Log.e(LOG_TAG, "## newPinnedSSLSocketFactory() : TrustManagerFactory.getInstance failed " + e.getMessage(), e); - } - - // it doesn't exist, use the default one. - if (null == tf) { - try { - tf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - } catch (Exception e) { - Log.e(LOG_TAG, "## addRule : onBingRuleUpdateFailure failed " + e.getMessage(), e); - } + trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + } catch (NoSuchAlgorithmException e) { + Log.e(LOG_TAG, "## newPinnedSSLSocketFactory() : TrustManagerFactory.getInstance with default algorithm failed " + + e.getMessage(), e); } + } - tf.init((KeyStore) null); - TrustManager[] trustManagers = tf.getTrustManagers(); + if (trustManagerFactory != null) { + try { + trustManagerFactory.init((KeyStore) null); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); - for (int i = 0; i < trustManagers.length; i++) { - if (trustManagers[i] instanceof X509TrustManager) { - defaultTrustManager = (X509TrustManager) trustManagers[i]; - break; + for (int i = 0; i < trustManagers.length; i++) { + if (trustManagers[i] instanceof X509TrustManager) { + defaultTrustManager = (X509TrustManager) trustManagers[i]; + break; + } } + } catch (KeyStoreException e) { + Log.e(LOG_TAG, "## newPinnedSSLSocketFactory() : " + e.getMessage(), e); } } + } - TrustManager[] trustPinned = new TrustManager[]{ - new PinnedTrustManager(hsConfig.getAllowedFingerprints(), defaultTrustManager) - }; + X509TrustManager trustManager = new PinnedTrustManager(hsConfig.getAllowedFingerprints(), defaultTrustManager); + + TrustManager[] trustManagers = new TrustManager[]{ + trustManager + }; - SSLSocketFactory sslSocketFactory; + SSLSocketFactory sslSocketFactory; + try { if (hsConfig.forceUsageOfTlsVersions() && hsConfig.getAcceptedTlsVersions() != null) { // Force usage of accepted Tls Versions for Android < 20 - sslSocketFactory = new TLSSocketFactory(trustPinned, hsConfig.getAcceptedTlsVersions()); + sslSocketFactory = new TLSSocketFactory(trustManagers, hsConfig.getAcceptedTlsVersions()); } else { SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, trustPinned, new java.security.SecureRandom()); + sslContext.init(null, trustManagers, new java.security.SecureRandom()); sslSocketFactory = sslContext.getSocketFactory(); } - - return new Pair<>(sslSocketFactory, defaultTrustManager); } catch (Exception e) { + // This is too fatal throw new RuntimeException(e); } + + return new Pair<>(sslSocketFactory, trustManager); } /** @@ -240,9 +254,10 @@ public boolean verify(String hostname, SSLSession session) { * Create a list of accepted TLS specifications for a hs config. * * @param hsConfig the hs config. + * @param url the url of the end point, used to check if we have to enable CLEARTEXT communication. * @return a list of accepted TLS specifications. */ - public static List newConnectionSpecs(HomeServerConnectionConfig hsConfig) { + public static List newConnectionSpecs(@NonNull HomeServerConnectionConfig hsConfig, @NonNull String url) { final ConnectionSpec.Builder builder = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS); final List tlsVersions = hsConfig.getAcceptedTlsVersions(); @@ -261,7 +276,7 @@ public static List newConnectionSpecs(HomeServerConnectionConfig list.add(builder.build()); - if (hsConfig.isHttpConnectionAllowed()) { + if (url.startsWith("http://")) { list.add(ConnectionSpec.CLEARTEXT); } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/Fingerprint.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/Fingerprint.java index 8f2eab1b6..647ba3c97 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/Fingerprint.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/Fingerprint.java @@ -1,5 +1,6 @@ /* * Copyright 2016 OpenMarket Ltd + * Copyright 2018 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. @@ -29,30 +30,27 @@ * Represents a X509 Certificate fingerprint. */ public class Fingerprint { - public enum HashType {SHA1, SHA256} + public enum HashType { + SHA1, + SHA256 + } - private final byte[] mBytes; private final HashType mHashType; + private final byte[] mBytes; private String mDisplayableHexRepr; - public Fingerprint(byte[] bytes, HashType hashType) { - mBytes = bytes; + public Fingerprint(HashType hashType, byte[] bytes) { mHashType = hashType; + mBytes = bytes; mDisplayableHexRepr = null; } public static Fingerprint newSha256Fingerprint(X509Certificate cert) throws CertificateException { - return new Fingerprint( - CertUtil.generateSha256Fingerprint(cert), - HashType.SHA256 - ); + return new Fingerprint(HashType.SHA256, CertUtil.generateSha256Fingerprint(cert)); } public static Fingerprint newSha1Fingerprint(X509Certificate cert) throws CertificateException { - return new Fingerprint( - CertUtil.generateSha1Fingerprint(cert), - HashType.SHA1 - ); + return new Fingerprint(HashType.SHA1, CertUtil.generateSha1Fingerprint(cert)); } public HashType getType() { @@ -91,7 +89,7 @@ public static Fingerprint fromJson(JSONObject obj) throws JSONException { throw new JSONException("Unrecognized hash type: " + hashTypeStr); } - return new Fingerprint(fingerprintBytes, hashType); + return new Fingerprint(hashType, fingerprintBytes); } public boolean matchesCert(X509Certificate cert) throws CertificateException { diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/PinnedTrustManager.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/PinnedTrustManager.java index 5d29a0ead..c1bad3a4f 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/PinnedTrustManager.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/ssl/PinnedTrustManager.java @@ -1,5 +1,6 @@ /* * Copyright 2016 OpenMarket Ltd + * Copyright 2018 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. @@ -20,6 +21,7 @@ import java.security.cert.X509Certificate; import java.util.List; +import javax.annotation.Nullable; import javax.net.ssl.X509TrustManager; /** @@ -28,6 +30,7 @@ */ public class PinnedTrustManager implements X509TrustManager { private final List mFingerprints; + @Nullable private final X509TrustManager mDefaultTrustManager; /** @@ -35,7 +38,7 @@ public class PinnedTrustManager implements X509TrustManager { * @param defaultTrustManager Optional trust manager to fall back on if cert does not match * any of the fingerprints. Can be null. */ - public PinnedTrustManager(List fingerprints, X509TrustManager defaultTrustManager) { + public PinnedTrustManager(List fingerprints, @Nullable X509TrustManager defaultTrustManager) { mFingerprints = fingerprints; mDefaultTrustManager = defaultTrustManager; } @@ -69,7 +72,7 @@ public void checkServerTrusted(X509Certificate[] chain, String s) throws Certifi } } catch (CertificateException e) { // If there is an exception we fall back to checking fingerprints - if (mFingerprints == null || mFingerprints.size() == 0) { + if (mFingerprints == null || mFingerprints.isEmpty()) { throw new UnrecognizedCertificateException(chain[0], Fingerprint.newSha256Fingerprint(chain[0]), e.getCause()); } } diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/util/EventDisplay.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/util/EventDisplay.java index 49c425546..ea94fe035 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/util/EventDisplay.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/util/EventDisplay.java @@ -55,9 +55,7 @@ public class EventDisplay { private static final String MESSAGE_IN_REPLY_TO_LAST_PART = ""; // members - protected final Event mEvent; protected final Context mContext; - protected final RoomState mRoomState; @Nullable protected final HtmlToolbox mHtmlToolbox; @@ -68,15 +66,13 @@ public class EventDisplay { public static final boolean mDisplayRedactedEvents = false; // constructor - public EventDisplay(Context context, Event event, RoomState roomState) { - this(context, event, roomState, null); + public EventDisplay(Context context) { + this(context, null); } // constructor - public EventDisplay(Context context, Event event, RoomState roomState, @Nullable HtmlToolbox htmlToolbox) { + public EventDisplay(Context context, @Nullable HtmlToolbox htmlToolbox) { mContext = context.getApplicationContext(); - mEvent = event; - mRoomState = roomState; mHtmlToolbox = htmlToolbox; } @@ -111,8 +107,8 @@ protected static String getUserDisplayName(String userId, RoomState roomState) { * * @return The text or null if it isn't possible. */ - public CharSequence getTextualDisplay() { - return getTextualDisplay(null); + public CharSequence getTextualDisplay(Event event, RoomState roomState) { + return getTextualDisplay(null, event, roomState); } /** @@ -121,16 +117,16 @@ public CharSequence getTextualDisplay() { * @param displayNameColor the display name highlighted color. * @return The text or null if it isn't possible. */ - public CharSequence getTextualDisplay(Integer displayNameColor) { + public CharSequence getTextualDisplay(Integer displayNameColor, Event event, RoomState roomState) { CharSequence text = null; try { - JsonObject jsonEventContent = mEvent.getContentAsJsonObject(); + JsonObject jsonEventContent = event.getContentAsJsonObject(); - String userDisplayName = getUserDisplayName(mEvent.getSender(), mRoomState); - String eventType = mEvent.getType(); + String userDisplayName = getUserDisplayName(event.getSender(), roomState); + String eventType = event.getType(); - if (mEvent.isCallEvent()) { + if (event.isCallEvent()) { if (Event.EVENT_TYPE_CALL_INVITE.equals(eventType)) { boolean isVideo = false; // detect call type from the sdp @@ -211,11 +207,11 @@ public CharSequence getTextualDisplay(Integer displayNameColor) { } } else if (Event.EVENT_TYPE_MESSAGE_ENCRYPTION.equals(eventType)) { - text = mContext.getString(R.string.notice_end_to_end, userDisplayName, mEvent.getWireEventContent().algorithm); + text = mContext.getString(R.string.notice_end_to_end, userDisplayName, event.getWireEventContent().algorithm); } else if (Event.EVENT_TYPE_MESSAGE_ENCRYPTED.equals(eventType)) { // don't display - if (mEvent.isRedacted()) { - String redactedInfo = EventDisplay.getRedactionMessage(mContext, mEvent, mRoomState); + if (event.isRedacted()) { + String redactedInfo = getRedactionMessage(mContext, event, roomState); if (TextUtils.isEmpty(redactedInfo)) { return null; @@ -226,10 +222,10 @@ public CharSequence getTextualDisplay(Integer displayNameColor) { String message = null; - if (null != mEvent.getCryptoError()) { + if (null != event.getCryptoError()) { String errorDescription; - MXCryptoError error = mEvent.getCryptoError(); + MXCryptoError error = event.getCryptoError(); if (TextUtils.equals(error.errcode, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE)) { errorDescription = mContext.getResources().getString(R.string.notice_crypto_error_unkwown_inbound_session_id); @@ -251,8 +247,8 @@ public CharSequence getTextualDisplay(Integer displayNameColor) { } else if (Event.EVENT_TYPE_STATE_ROOM_TOPIC.equals(eventType)) { String topic = jsonEventContent.getAsJsonPrimitive("topic").getAsString(); - if (mEvent.isRedacted()) { - String redactedInfo = EventDisplay.getRedactionMessage(mContext, mEvent, mRoomState); + if (event.isRedacted()) { + String redactedInfo = getRedactionMessage(mContext, event, roomState); if (TextUtils.isEmpty(redactedInfo)) { return null; @@ -270,8 +266,8 @@ public CharSequence getTextualDisplay(Integer displayNameColor) { JsonPrimitive nameAsJson = jsonEventContent.getAsJsonPrimitive("name"); String roomName = (null == nameAsJson) ? null : nameAsJson.getAsString(); - if (mEvent.isRedacted()) { - String redactedInfo = EventDisplay.getRedactionMessage(mContext, mEvent, mRoomState); + if (event.isRedacted()) { + String redactedInfo = getRedactionMessage(mContext, event, roomState); if (TextUtils.isEmpty(redactedInfo)) { return null; @@ -286,11 +282,11 @@ public CharSequence getTextualDisplay(Integer displayNameColor) { text = mContext.getString(R.string.notice_room_name_removed, userDisplayName); } } else if (Event.EVENT_TYPE_STATE_ROOM_THIRD_PARTY_INVITE.equals(eventType)) { - RoomThirdPartyInvite invite = JsonUtils.toRoomThirdPartyInvite(mEvent.getContent()); + RoomThirdPartyInvite invite = JsonUtils.toRoomThirdPartyInvite(event.getContent()); String displayName = invite.display_name; - if (mEvent.isRedacted()) { - String redactedInfo = EventDisplay.getRedactionMessage(mContext, mEvent, mRoomState); + if (event.isRedacted()) { + String redactedInfo = getRedactionMessage(mContext, event, roomState); if (TextUtils.isEmpty(redactedInfo)) { return null; @@ -301,7 +297,7 @@ public CharSequence getTextualDisplay(Integer displayNameColor) { text = mContext.getString(R.string.notice_room_third_party_invite, userDisplayName, displayName); } else if (Event.EVENT_TYPE_STATE_ROOM_MEMBER.equals(eventType)) { - text = getMembershipNotice(mContext, mEvent, mRoomState); + text = getMembershipNotice(mContext, event, roomState); } } catch (Exception e) { Log.e(LOG_TAG, "getTextualDisplay() " + e.getMessage(), e); @@ -434,7 +430,7 @@ public static String getMembershipNotice(Context context, Event event, RoomState // Check whether the sender has updated his profile (the membership is then unchanged) if (TextUtils.equals(prevMembership, eventContent.membership)) { - String redactedInfo = EventDisplay.getRedactionMessage(context, event, roomState); + String redactedInfo = getRedactionMessage(context, event, roomState); // Is redacted event? if (event.isRedacted()) { @@ -609,12 +605,11 @@ private CharSequence getFormattedMessage(@NonNull final Context context, } // fromHtml formats quotes (> character) with two newlines at the end // remove any newlines at the end of the CharSequence - while (text.charAt(text.length() - 1) == '\n') { - text = text.subSequence(0, text.length() - 2); + while (text.length() > 0 && text.charAt(text.length() - 1) == '\n') { + text = text.subSequence(0, text.length() - 1); } } } return text; } - -} \ No newline at end of file +} diff --git a/matrix-sdk/src/main/java/org/matrix/androidsdk/util/JsonUtils.java b/matrix-sdk/src/main/java/org/matrix/androidsdk/util/JsonUtils.java index 2ab142c0a..74a25e121 100644 --- a/matrix-sdk/src/main/java/org/matrix/androidsdk/util/JsonUtils.java +++ b/matrix-sdk/src/main/java/org/matrix/androidsdk/util/JsonUtils.java @@ -70,6 +70,8 @@ public class JsonUtils { private static final String LOG_TAG = JsonUtils.class.getSimpleName(); + private static final Gson basicGson = new Gson(); + private static final Gson gson = new GsonBuilder() .setFieldNamingStrategy(new MatrixFieldNamingStrategy()) .excludeFieldsWithModifiers(Modifier.PRIVATE, Modifier.STATIC) @@ -101,6 +103,15 @@ public class JsonUtils { .registerTypeAdapter(Boolean.class, new BooleanDeserializer(true)) .create(); + /** + * Provides the basic JSON parser. + * + * @return the basic JSON parser + */ + public static Gson getBasicGson() { + return basicGson; + } + /** * Provides the JSON parser. * diff --git a/matrix-sdk/src/main/res/drawable-xxhdpi/matrix_user.png b/matrix-sdk/src/main/res/drawable-xxhdpi/matrix_user.png deleted file mode 100755 index 4a47aaf96..000000000 Binary files a/matrix-sdk/src/main/res/drawable-xxhdpi/matrix_user.png and /dev/null differ diff --git a/matrix-sdk/src/main/res/layout/adapter_item_icon_and_text.xml b/matrix-sdk/src/main/res/layout/adapter_item_icon_and_text.xml index 4227dd9be..925a27dbd 100644 --- a/matrix-sdk/src/main/res/layout/adapter_item_icon_and_text.xml +++ b/matrix-sdk/src/main/res/layout/adapter_item_icon_and_text.xml @@ -1,20 +1,21 @@ + android:paddingBottom="10dp"> + tools:src="@tools:sample/avatars" /> + android:textColor="@android:color/white" + tools:text="A text here" + tools:textColor="@android:color/black" /> \ No newline at end of file diff --git a/matrix-sdk/src/main/res/values-ar/strings.xml b/matrix-sdk/src/main/res/values-ar/strings.xml index 5d4d33f63..5ec5ec785 100644 --- a/matrix-sdk/src/main/res/values-ar/strings.xml +++ b/matrix-sdk/src/main/res/values-ar/strings.xml @@ -1,5 +1,5 @@ -عُدّة تطوير البرمجيات لِ‍«ماترِكس» على أندرويد + أرسل ⁨%1$s⁩ صورة. @@ -73,4 +73,10 @@ أرسل ملف صوت. أرسل ملفًا. + دعوة من ⁨%s⁩ + غرفة فارغة + + ‏⁨%1$s⁩ و ⁨%2$s⁩ + دعوة إلى غرفة + diff --git a/matrix-sdk/src/main/res/values-bg/strings.xml b/matrix-sdk/src/main/res/values-bg/strings.xml index 70de11ac4..8955f5888 100644 --- a/matrix-sdk/src/main/res/values-bg/strings.xml +++ b/matrix-sdk/src/main/res/values-bg/strings.xml @@ -1,5 +1,5 @@ -Matrix Android SDK + %1$s: %2$s %1$s изпрати снимка. @@ -73,4 +73,15 @@ изпрати аудио файл. изпрати файл. + Покана от %s + Покана за стая + %1$s и %2$s + + + %1$s и 1 друг + %1$s и %2$d други + + + Празна стая + diff --git a/matrix-sdk/src/main/res/values-bs/strings.xml b/matrix-sdk/src/main/res/values-bs/strings.xml index a6b3daec9..6a6ee46d3 100644 --- a/matrix-sdk/src/main/res/values-bs/strings.xml +++ b/matrix-sdk/src/main/res/values-bs/strings.xml @@ -1,2 +1,7 @@ - \ No newline at end of file + + Pozovite iz %s + Poziv u Sobu + %1$s i %2$s + Prazna soba + \ No newline at end of file diff --git a/matrix-sdk/src/main/res/values-ca/strings.xml b/matrix-sdk/src/main/res/values-ca/strings.xml index 539ca6a14..0ab98c0ab 100644 --- a/matrix-sdk/src/main/res/values-ca/strings.xml +++ b/matrix-sdk/src/main/res/values-ca/strings.xml @@ -14,7 +14,7 @@ %1$s vos ha convidat %1$s ha rebutjat la invitació %1$s ha fet fora a %2$s - Android SDK de Matrix + %1$s ha canviat el seu nom visible de %2$s a %3$s %1$s ha eliminat el seu nom visible (%2$s) @@ -64,4 +64,16 @@ %1$s a canviat el seu nom visible a %2$s %s ha iniciat una trucada de vídeo. %s ha iniciat una trucada de veu. - + + + Convideu des de %s + Convideu a la sala + %1$s i %2$s + Sala buida + + %1$s i 1 altre + %1$s i %2$d altres + + + + diff --git a/matrix-sdk/src/main/res/values-da/strings.xml b/matrix-sdk/src/main/res/values-da/strings.xml index d7c37c1dd..acef386ed 100644 --- a/matrix-sdk/src/main/res/values-da/strings.xml +++ b/matrix-sdk/src/main/res/values-da/strings.xml @@ -1,5 +1,5 @@ -Matrix Android SDK + %1$s: %2$s %1$s sendte et billede. @@ -64,4 +64,15 @@ mailadresse Telefonnummer + Invitation fra %s + Invitation til rum + %1$s og %2$s + + + %1$s og 1 anden + %1$s og %2$d andre + + + Tomt rum + diff --git a/matrix-sdk/src/main/res/values-de/strings.xml b/matrix-sdk/src/main/res/values-de/strings.xml index d58c94045..f225b8442 100644 --- a/matrix-sdk/src/main/res/values-de/strings.xml +++ b/matrix-sdk/src/main/res/values-de/strings.xml @@ -1,6 +1,5 @@ - Matrix-Android-SDK %1$s: %2$s %1$s hat ein Bild gesendet. @@ -84,4 +83,16 @@ sandte eine Audio-Datei. sandte eine Datei. + + Einladung von %s + Raumeinladung + %1$s und %2$s + Leerer Raum + + + %1$s und 1 andere(r) + %1$s und %2$d andere + + + diff --git a/matrix-sdk/src/main/res/values-el/strings.xml b/matrix-sdk/src/main/res/values-el/strings.xml index ff855d714..510e8a7dc 100644 --- a/matrix-sdk/src/main/res/values-el/strings.xml +++ b/matrix-sdk/src/main/res/values-el/strings.xml @@ -22,7 +22,6 @@ Ο/Η %1$s άλλαξε το όνομα του δωματίου σε: %2$s Ο/Η %s απάντησε στην κλήση. Ο/Η %s τερμάτισε την κλήση. - SDK για Android του Matrix Ο/Η %s πραγματοποίησε μια κλήση βίντεο. Ο/Η %s πραγματοποίησε μια κλήση ήχου. diff --git a/matrix-sdk/src/main/res/values-es-rMX/strings.xml b/matrix-sdk/src/main/res/values-es-rMX/strings.xml index 57fb929b4..3d80aa993 100644 --- a/matrix-sdk/src/main/res/values-es-rMX/strings.xml +++ b/matrix-sdk/src/main/res/values-es-rMX/strings.xml @@ -1,6 +1,5 @@ - Matrix Android SDK %1$s: %2$s %1$s envió una imagen. @@ -84,4 +83,11 @@ envió un archivo de audio. envió un archivo. + + Invitar de %s + Invitacion de Sala + %1$s y %2$s + Sala vacía + + diff --git a/matrix-sdk/src/main/res/values-es/strings.xml b/matrix-sdk/src/main/res/values-es/strings.xml index a6cc2bd70..4be6ac721 100644 --- a/matrix-sdk/src/main/res/values-es/strings.xml +++ b/matrix-sdk/src/main/res/values-es/strings.xml @@ -1,6 +1,5 @@ - SDK de Matrix Android %1$s: %2$s %1$s envió una imagen. @@ -84,4 +83,16 @@ envió un archivo de audio. envió un archivo. + + Invitación de %s + Invitación a Sala + %1$s y %2$s + Sala vacía + + + %1$s y 1 otro + %1$s y %2$d otros + + + diff --git a/matrix-sdk/src/main/res/values-eu/strings.xml b/matrix-sdk/src/main/res/values-eu/strings.xml index 41d306f67..4f4a27acf 100644 --- a/matrix-sdk/src/main/res/values-eu/strings.xml +++ b/matrix-sdk/src/main/res/values-eu/strings.xml @@ -1,5 +1,5 @@ -Matrix Android SDK + %1$s: %2$s %1$s erabiltzaileak irudi bat bidali du. @@ -73,4 +73,15 @@ audio fitxategi bat bidali du. fitxategi bat bidali du. + %s gelarako gonbidapena + Gela gonbidapena + %1$s eta %2$s + Gela hutsa + + + %1$s eta beste bat + %1$s eta beste %2$d + + + diff --git a/matrix-sdk/src/main/res/values-fi/strings.xml b/matrix-sdk/src/main/res/values-fi/strings.xml index dac28a3bb..2694a33d6 100644 --- a/matrix-sdk/src/main/res/values-fi/strings.xml +++ b/matrix-sdk/src/main/res/values-fi/strings.xml @@ -57,10 +57,18 @@ Sähköpostiosoite Puhelinnumero -Matrix Android SDK + " käyttäjän %1$s toimesta" Takaisinveto epäonnistui %1$s: %2$s "veti takaisin %1$s " - + + + Kutsu käyttäjältä %s + Huonekutsu + %1$s ja %2$s + Tyhjä huone + + + diff --git a/matrix-sdk/src/main/res/values-fr/strings.xml b/matrix-sdk/src/main/res/values-fr/strings.xml index 039e71290..e860c6662 100644 --- a/matrix-sdk/src/main/res/values-fr/strings.xml +++ b/matrix-sdk/src/main/res/values-fr/strings.xml @@ -1,5 +1,5 @@ -SDK Matrix Android + %1$s : %2$s %1$s a envoyé une image. @@ -73,4 +73,15 @@ a envoyé un fichier audio. a envoyé un fichier. + Invitation de %s + Invitation au salon + Salon vide + %1$s et %2$s + + + %1$s et 1 autre + %1$s et %2$d autres + + + diff --git a/matrix-sdk/src/main/res/values-gl/strings.xml b/matrix-sdk/src/main/res/values-gl/strings.xml index 5110fb773..8fc750c4d 100644 --- a/matrix-sdk/src/main/res/values-gl/strings.xml +++ b/matrix-sdk/src/main/res/values-gl/strings.xml @@ -2,7 +2,6 @@ Enderezo de correo Fallo ao subir a páxina - Matrix Android SDK %1$s: %2$s %1$s enviou unha imaxe. @@ -72,4 +71,7 @@ enviar un ficheiro de son. enviar un ficheiro. + %1$s e %2$s + + diff --git a/matrix-sdk/src/main/res/values-hu/strings.xml b/matrix-sdk/src/main/res/values-hu/strings.xml index ccbcead0c..1f544b77b 100644 --- a/matrix-sdk/src/main/res/values-hu/strings.xml +++ b/matrix-sdk/src/main/res/values-hu/strings.xml @@ -1,6 +1,5 @@ -Matrix Android SDK - + %1$s: %2$s %1$s küldött egy képet. @@ -73,4 +72,15 @@ hangfájl elküldve. fájl elküldve. + %s meghívott + Meghívó egy szobába + %1$s és %2$s + Üres szoba + + + %1$s és 1 másik + %1$s és %2$d másik + + + diff --git a/matrix-sdk/src/main/res/values-id/strings.xml b/matrix-sdk/src/main/res/values-id/strings.xml new file mode 100644 index 000000000..157b23d40 --- /dev/null +++ b/matrix-sdk/src/main/res/values-id/strings.xml @@ -0,0 +1,12 @@ + + + Undang dari %s + Undangan Ruang + %1$s dan %2$s + + Ruang kosong + + + %1$s dan %2$d yang lain + + \ No newline at end of file diff --git a/matrix-sdk/src/main/res/values-in/strings.xml b/matrix-sdk/src/main/res/values-in/strings.xml new file mode 100644 index 000000000..157b23d40 --- /dev/null +++ b/matrix-sdk/src/main/res/values-in/strings.xml @@ -0,0 +1,12 @@ + + + Undang dari %s + Undangan Ruang + %1$s dan %2$s + + Ruang kosong + + + %1$s dan %2$d yang lain + + \ No newline at end of file diff --git a/matrix-sdk/src/main/res/values-is/strings.xml b/matrix-sdk/src/main/res/values-is/strings.xml index e665055ac..f84bfb7bb 100644 --- a/matrix-sdk/src/main/res/values-is/strings.xml +++ b/matrix-sdk/src/main/res/values-is/strings.xml @@ -1,6 +1,5 @@ -Matrix Android SDK - + %1$s: %2$s %1$s sendi mynd. %1$s sendi límmerki. @@ -67,4 +66,15 @@ Gat ekki ritstýrt Ekki er í augnablikinu hægt að taka aftur þátt í spjallrás sem er tóm. - + Boð á spjallrás + %1$s og %2$s + + + %1$s og 1 annar + %1$s og %2$d aðrir + + + Tóm spjallrás + Boð frá %s + + diff --git a/matrix-sdk/src/main/res/values-it/strings.xml b/matrix-sdk/src/main/res/values-it/strings.xml index 5348662fe..ce6280b59 100644 --- a/matrix-sdk/src/main/res/values-it/strings.xml +++ b/matrix-sdk/src/main/res/values-it/strings.xml @@ -1,6 +1,5 @@ -Matrix Android SDK - + %1$s: %2$s %1$s ha inviato un\'immagine. @@ -73,4 +72,16 @@ inviato un file audio. inviato un file. + + Invito da %s + Invito nella stanza + %1$s e %2$s + Stanza vuota + + + %1$s e 1 altro + %1$s e %2$d altri + + + diff --git a/matrix-sdk/src/main/res/values-ja/strings.xml b/matrix-sdk/src/main/res/values-ja/strings.xml new file mode 100644 index 000000000..b61f22037 --- /dev/null +++ b/matrix-sdk/src/main/res/values-ja/strings.xml @@ -0,0 +1,38 @@ + + + + %1$s: %2$s + %1$sが画像を送信しました。 + %1$sがスタンプを送信しました。 + + %sの招待 + %1$sが%2$sを招待しました + %1$sがあなたを招待しました + %1$sが参加しました + %1$sが退出しました + %1$sが招待を断りました + %1$sが%2$sとの接続を切断しました + %1$sが%2$sのブロックを解除しました + %14sが%2$sをブロックしました + %14sが%2$sの招待を撤回しました + %1$sがアバターを変更しました + %1$sが表示名を%2$sに設定しました + %1$sが表示名を%2$sから%3$sに変更しました + %1$sが表示名 (%2$s) を削除しました + %1$sがテーマを%2$sに変更しました + %1$sが部屋名を%2$sに変更しました + %sがビデオ通話を開始しました。 + %sが音声通話を開始しました。 + %sが電話に出ました。 + %sが通話を終了しました。 + %sさんからの招待 + 部屋への招待 + %1$sと%2$s + 空の部屋 + + + %1$sと他%2$d名 + + + + diff --git a/matrix-sdk/src/main/res/values-ko/strings.xml b/matrix-sdk/src/main/res/values-ko/strings.xml index 7c468208e..146fed131 100644 --- a/matrix-sdk/src/main/res/values-ko/strings.xml +++ b/matrix-sdk/src/main/res/values-ko/strings.xml @@ -1,6 +1,5 @@ -Matrix 안드로이드 SDK - + %1$s: %2$s %s\'의 초대 diff --git a/matrix-sdk/src/main/res/values-lv/strings.xml b/matrix-sdk/src/main/res/values-lv/strings.xml index 0e7a9cc0b..1347f5633 100644 --- a/matrix-sdk/src/main/res/values-lv/strings.xml +++ b/matrix-sdk/src/main/res/values-lv/strings.xml @@ -1,6 +1,5 @@ -Matrix Android SDK - + %1$s: %2$s %1$s nosūtīja attēlu. @@ -64,4 +63,16 @@ Epasta adrese Telefona numurs + Uzaicinājums no %s + Uzaicinājums uz istabu + %1$s un %2$s + Tukša istaba + + + %1$s un 1 cits + %1$s un %2$d citi + %1$s un %2$d citu + + + diff --git a/matrix-sdk/src/main/res/values-nl/strings.xml b/matrix-sdk/src/main/res/values-nl/strings.xml index 11f8e438d..8b04aab88 100644 --- a/matrix-sdk/src/main/res/values-nl/strings.xml +++ b/matrix-sdk/src/main/res/values-nl/strings.xml @@ -1,6 +1,5 @@ - Matrix Android SDK %1$s: %2$s %1$s stuurde een afbeelding. @@ -82,4 +81,16 @@ verstuurde een audiobestand. verstuurde een bestand. + + Uitnodiging van %s + Ruimte uitnodiging + %1$s en %2$s + Lege ruimte + + + %1$s en 1 andere + %1$s en %2$d anderen + + + diff --git a/matrix-sdk/src/main/res/values-nn/strings.xml b/matrix-sdk/src/main/res/values-nn/strings.xml index b9010f0ce..8f6810e1b 100644 --- a/matrix-sdk/src/main/res/values-nn/strings.xml +++ b/matrix-sdk/src/main/res/values-nn/strings.xml @@ -1,7 +1,6 @@ Kryptert melding - Matrix-Android-SDK %1$s: %2$s %1$s sende eit bilete. @@ -72,4 +71,15 @@ sende ein ljodfil. sende ei fil. + Byd inn frå %s + Rominnbyding + %1$s og %2$s + + + %1$s og 1 til + %1$s og %2$d til + + + Tomt rom + diff --git a/matrix-sdk/src/main/res/values-pl/strings.xml b/matrix-sdk/src/main/res/values-pl/strings.xml index 9cdc88bd1..3e4fdee0a 100644 --- a/matrix-sdk/src/main/res/values-pl/strings.xml +++ b/matrix-sdk/src/main/res/values-pl/strings.xml @@ -1,23 +1,22 @@ -Matrix Android SDK - + %1$s: %2$s - %1$s wysłał zdjęcie. + %1$s wysłał(a) zdjęcie. Zaproszenie od %s - %1$s zaprosił %2$s - %1$s zaprosił Cię - %1$s dołączył - %1$s wyszedł - %1$s odrzucił zaproszenie - %1$s wyrzucił %2$s - %1$s odblokował %2$s - %1$s zablokował %2$s - %1$s zmienił awatar - %1$s zmienił wyświetlaną nazwę na %2$s - %1$s zmienił wyświetlaną nazwę z %2$s na %3$s - %1$s usunął swoją wyświetlaną nazwę (%2$s) - %1$s zmienił temat na: %2$s + %1$s zaprosił(a) %2$s + %1$s zaprosił(a) Cię + %1$s dołączył(a) + %1$s wyszedł(-ła) + %1$s odrzucił(a) zaproszenie + %1$s wyrzucił(a) %2$s + %1$s odblokował(a) %2$s + %1$s zablokował(a) %2$s + %1$s zmienił(a) awatar + %1$s zmienił(a) wyświetlaną nazwę na %2$s + %1$s zmienił(a) wyświetlaną nazwę z %2$s na %3$s + %1$s usunął(-ęła) swoją wyświetlaną nazwę (%2$s) + %1$s zmienił(a) temat na: %2$s Nie udało się wysłać wiadomości Nie udało się wysłać zdjęcia @@ -32,13 +31,30 @@ wszyscy członkowie pokoju. wszyscy. - %1$s zmienił znawę pokoju na: %2$s - %s zakończył rozmowę. - %1$s usunął nazwę pokoju - %1$s usunął temat pokoju + %1$s zmienił(a) znawę pokoju na: %2$s + %s zakończył(a) rozmowę. + %1$s usunął(-ęła) nazwę pokoju + %1$s usunął(-ęła) temat pokoju " [powód: %1$s]" - %1$s wysłał naklejkę. + %1$s wysłał(a) naklejkę. + + %1$s włączył(a) szyfrowanie end-to-end (%2$s) + + %1$s wycofał(a) zaproszenie %2$s + %s odebrał(a) połączenie. + (awatar też został zmieniony) + W odpowiedzi do + + Zaproszenie od %s + Zaproszenie do pokoju + %1$s i %2$s + Pusty pokój - %1$s włączył szyfrowanie end-to-end (%2$s) + + %1$s i jeden inny + %1$s i kilku innych + %1$s i %2$d innych + + - + diff --git a/matrix-sdk/src/main/res/values-pt-rBR/strings.xml b/matrix-sdk/src/main/res/values-pt-rBR/strings.xml index 25e573909..3cee21226 100644 --- a/matrix-sdk/src/main/res/values-pt-rBR/strings.xml +++ b/matrix-sdk/src/main/res/values-pt-rBR/strings.xml @@ -1,6 +1,5 @@ - Matrix Android SDK %1$s: %2$s %1$s enviou uma imagem. @@ -85,4 +84,16 @@ enviou um arquivo de áudio. enviou um arquivo. + + Convite de %s + Convite para sala + %1$s e %2$s + Sala vazia + + + %1$s e 1 outra/o + %1$s e %2$d outras/os + + + diff --git a/matrix-sdk/src/main/res/values-pt/strings.xml b/matrix-sdk/src/main/res/values-pt/strings.xml index 6d8500a6a..5478df56d 100644 --- a/matrix-sdk/src/main/res/values-pt/strings.xml +++ b/matrix-sdk/src/main/res/values-pt/strings.xml @@ -1,6 +1,5 @@ - Matrix Android SDK %1$s: %2$s %1$s enviou uma imagem. @@ -74,6 +73,13 @@ Endereço de e-mail Número de telefone - - + + + Convite de %s + Convite para sala + %1$s e %2$s + Sala vazia + + + diff --git a/matrix-sdk/src/main/res/values-ru/strings.xml b/matrix-sdk/src/main/res/values-ru/strings.xml index 704ea394d..d71ce25be 100644 --- a/matrix-sdk/src/main/res/values-ru/strings.xml +++ b/matrix-sdk/src/main/res/values-ru/strings.xml @@ -1,6 +1,5 @@ - Matrix Android SDK %1$s: %2$s %1$s отправил(а) изображение. @@ -84,4 +83,18 @@ отправил аудиофайл. отправил файл. + + Приглашение от %s + Приглашение в комнату + %1$s и %2$s + Пустая комната + + + %1$s и 1 другой + %1$s и %2$d другие + %1$s и %2$d других + + + + diff --git a/matrix-sdk/src/main/res/values-sk/strings.xml b/matrix-sdk/src/main/res/values-sk/strings.xml index fb98be023..2e3307eaa 100644 --- a/matrix-sdk/src/main/res/values-sk/strings.xml +++ b/matrix-sdk/src/main/res/values-sk/strings.xml @@ -1,6 +1,5 @@ -Matrix Android SDK - + %1$s: %2$s %1$s poslal obrázok. @@ -73,4 +72,17 @@ odoslal zvukový súbor. Odoslal súbor. + Pozvanie od %s + Pozvanie do miestnosti + %1$s a %2$s + Prázdna miestnosť + + + %1$s a 1 ďalší + %1$s a %2$d ďalší + %1$s a %2$d ďalších + + + + diff --git a/matrix-sdk/src/main/res/values-te/strings.xml b/matrix-sdk/src/main/res/values-te/strings.xml index 207e69c03..57527ea63 100644 --- a/matrix-sdk/src/main/res/values-te/strings.xml +++ b/matrix-sdk/src/main/res/values-te/strings.xml @@ -57,11 +57,17 @@ ఇమెయిల్ చిరునామా ఫోను నంబరు -మ్యాట్రిక్స్ అండ్రొఇడ్ ఏస్ డి కె %1$s: %2$s %1$s ఒక చిత్రం పంపారు. %1$s మిమ్మల్ని ఆహ్వానించారు %1$s చేరారు - + + %s నుండి ఆహ్వానించు + %1$s మరియు %2$s + గదికి ఆహ్వానం + ఖాళీ గది + + + diff --git a/matrix-sdk/src/main/res/values-th/strings.xml b/matrix-sdk/src/main/res/values-th/strings.xml index d8374c668..5ddb5276d 100644 --- a/matrix-sdk/src/main/res/values-th/strings.xml +++ b/matrix-sdk/src/main/res/values-th/strings.xml @@ -1,5 +1,4 @@ -Matrix Android SDK - + %1$s: %2$s diff --git a/matrix-sdk/src/main/res/values-uk/strings.xml b/matrix-sdk/src/main/res/values-uk/strings.xml index 24cf67446..18d89e4f3 100644 --- a/matrix-sdk/src/main/res/values-uk/strings.xml +++ b/matrix-sdk/src/main/res/values-uk/strings.xml @@ -1,6 +1,5 @@ -Matrix Android SDK - + %1$s: %2$s %1$s відправив зображення. @@ -8,4 +7,11 @@ %1$s запросив(ла) %2$s Закодоване повідомлення - + + Запрошення від %s + Запрошення до кімнати + %1$s і %2$s + Порожня кімната + + + diff --git a/matrix-sdk/src/main/res/values-zh-rCN/strings.xml b/matrix-sdk/src/main/res/values-zh-rCN/strings.xml index 5d5edf5c4..b0befebab 100644 --- a/matrix-sdk/src/main/res/values-zh-rCN/strings.xml +++ b/matrix-sdk/src/main/res/values-zh-rCN/strings.xml @@ -61,7 +61,6 @@ %1$s 接受了 %2$s 的邀请 无法撤回 - Matrix Android SDK %1$s:%2$s %1$s 发送了一张贴纸。 @@ -73,4 +72,12 @@ 回复 - + 空聊天室 + 来自 %s 的邀请 + 聊天室邀请 + %1$s 和 %2$s + + %1$s 和 与其他 %2$d 个人 + + + diff --git a/matrix-sdk/src/main/res/values-zh-rTW/strings.xml b/matrix-sdk/src/main/res/values-zh-rTW/strings.xml index 516201cbf..38b497598 100644 --- a/matrix-sdk/src/main/res/values-zh-rTW/strings.xml +++ b/matrix-sdk/src/main/res/values-zh-rTW/strings.xml @@ -1,6 +1,5 @@ -Matrix Android 軟體開發工具 - + %1$s:%2$s %1$s 傳送了一張圖片。 @@ -73,4 +72,14 @@ 傳送了音訊檔案。 傳送了檔案。 + 來自%s 的邀請 + 聊天室邀請 + %1$s 和 %2$s + + 空聊天室 + + %1$s 和 和其他 %2$d 個人 + + + diff --git a/matrix-sdk/src/main/res/values/dimens.xml b/matrix-sdk/src/main/res/values/dimens.xml deleted file mode 100644 index f11f7450a..000000000 --- a/matrix-sdk/src/main/res/values/dimens.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/matrix-sdk/src/main/res/values/strings.xml b/matrix-sdk/src/main/res/values/strings.xml index 28e684ed8..1098d54e5 100644 --- a/matrix-sdk/src/main/res/values/strings.xml +++ b/matrix-sdk/src/main/res/values/strings.xml @@ -1,6 +1,4 @@ - Matrix Android SDK - %1$s: %2$s %1$s sent an image. %1$s sent a sticker. @@ -84,4 +82,18 @@ sent an audio file. sent a file. + + Invite from %s + Room Invite + + + %1$s and %2$s + + + %1$s and 1 other + %1$s and %2$d others + + + Empty room + diff --git a/matrix-sdk/src/test/java/org/matrix/androidsdk/data/timeline/TimelinePushWorkerTest.java b/matrix-sdk/src/test/java/org/matrix/androidsdk/data/timeline/TimelinePushWorkerTest.java index 420d6c615..e7301bd5a 100644 --- a/matrix-sdk/src/test/java/org/matrix/androidsdk/data/timeline/TimelinePushWorkerTest.java +++ b/matrix-sdk/src/test/java/org/matrix/androidsdk/data/timeline/TimelinePushWorkerTest.java @@ -26,6 +26,7 @@ import org.matrix.androidsdk.rest.model.Event; import org.matrix.androidsdk.rest.model.bingrules.BingRule; import org.matrix.androidsdk.util.BingRulesManager; +import org.matrix.androidsdk.util.JsonUtils; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @@ -75,7 +76,7 @@ public void triggerPush_WhenEventHasNoLifetime_ShouldTriggerPush() { @Test public void triggerPush_WhenMaxLifetimeIsReached_ShouldNotTriggerPush() { - final Gson gson = new Gson(); + final Gson gson = JsonUtils.getBasicGson(); final BingRule bingRule = Mockito.mock(BingRule.class); Mockito.when(bingRule.shouldNotify()).thenReturn(true); Mockito.when(mBingRulesManager.fulfilledBingRule(Mockito.any(Event.class))).thenReturn(bingRule); diff --git a/matrix-sdk/src/test/java/org/matrix/androidsdk/data/timeline/TimelineStateHolderTest.java b/matrix-sdk/src/test/java/org/matrix/androidsdk/data/timeline/TimelineStateHolderTest.java index 27e07643c..5506c9c3c 100644 --- a/matrix-sdk/src/test/java/org/matrix/androidsdk/data/timeline/TimelineStateHolderTest.java +++ b/matrix-sdk/src/test/java/org/matrix/androidsdk/data/timeline/TimelineStateHolderTest.java @@ -38,7 +38,7 @@ public void processStateEvent_WhenDirectionIsForward__ShouldStoreLiveState() { event.roomId = ROOM_ID; event.stateKey = Event.EVENT_TYPE_STATE_ROOM_NAME; event.type = Event.EVENT_TYPE_STATE_ROOM_NAME; - mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS); + mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS, true); Mockito.verify(mIMXStore, Mockito.times(1)).storeLiveStateForRoom(Mockito.anyString()); } @@ -47,7 +47,7 @@ public void processStateEvent_WhenNoStateKeyIsGiven__ShouldNotBeProcessed() { final Event event = new Event(); event.roomId = ROOM_ID; event.type = Event.EVENT_TYPE_STATE_ROOM_NAME; - Assert.assertFalse(mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS)); + Assert.assertFalse(mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS, true)); } @Test @@ -56,7 +56,7 @@ public void processStateEvent_WithConformingEvent__ShouldBeProcessed() { event.roomId = ROOM_ID; event.type = Event.EVENT_TYPE_STATE_ROOM_NAME; event.stateKey = Event.EVENT_TYPE_STATE_ROOM_NAME; - Assert.assertTrue(mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS)); + Assert.assertTrue(mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS, true)); } @Test @@ -67,8 +67,8 @@ public void processStateEvent_WhenDirectionIsForward__ShouldUseState() { event.type = Event.EVENT_TYPE_STATE_ROOM_NAME; final RoomState state = Mockito.spy(mTimelineStateHolder.getState()); mTimelineStateHolder.setState(state); - mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS); - Mockito.verify(state).applyState(mIMXStore, event, EventTimeline.Direction.FORWARDS); + mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.FORWARDS, true); + Mockito.verify(state).applyState(event, true, mIMXStore); } @Test @@ -79,8 +79,8 @@ public void processStateEvent_WhenDirectionIsBackward__ShouldUseBackState() { event.type = Event.EVENT_TYPE_STATE_ROOM_NAME; final RoomState backState = Mockito.spy(mTimelineStateHolder.getBackState()); mTimelineStateHolder.setBackState(backState); - mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.BACKWARDS); - Mockito.verify(backState).applyState(mIMXStore, event, EventTimeline.Direction.BACKWARDS); + mTimelineStateHolder.processStateEvent(event, EventTimeline.Direction.BACKWARDS, false); + Mockito.verify(backState).applyState(event, false, null); } @Test @@ -104,6 +104,4 @@ public void deepCopyState_WhenDirectionIsBackward__ShouldCopyBackState() { Mockito.verify(backState).deepCopy(); Mockito.verify(state, Mockito.never()).deepCopy(); } - - }