+
+ /**
+ * Get Raw Url Preview data from the homeserver. There is no cache management for this request
+ * @param url The url to get the preview data from
+ * @param timestamp The optional timestamp
+ */
+ suspend fun getRawPreviewUrl(url: String, timestamp: Long?): JsonDict
+
+ /**
+ * Get Url Preview data from the homeserver, or from cache, depending on the cache strategy
+ * @param url The url to get the preview data from
+ * @param timestamp The optional timestamp. Note that this parameter is not taken into account
+ * if the data is already in cache and the cache strategy allow to use it
+ * @param cacheStrategy the cache strategy, see the type for more details
+ */
+ suspend fun getPreviewUrl(url: String, timestamp: Long?, cacheStrategy: CacheStrategy): PreviewUrlData
+
+ /**
+ * Clear the cache of all retrieved UrlPreview data
+ */
+ suspend fun clearCache()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/media/PreviewUrlData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/media/PreviewUrlData.kt
new file mode 100644
index 0000000000..33fc8b052b
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/media/PreviewUrlData.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.api.session.media
+
+/**
+ * Facility data class to get the common field of a PreviewUrl response form the server
+ *
+ * Example of return data for the url `https://matrix.org`:
+ *
+ * {
+ * "matrix:image:size": 112805,
+ * "og:description": "Matrix is an open standard for interoperable, decentralised, real-time communication",
+ * "og:image": "mxc://matrix.org/2020-12-03_uFqjagCCTJbaaJxb",
+ * "og:image:alt": "Matrix is an open standard for interoperable, decentralised, real-time communication",
+ * "og:image:height": 467,
+ * "og:image:type": "image/jpeg",
+ * "og:image:width": 911,
+ * "og:locale": "en_US",
+ * "og:site_name": "Matrix.org",
+ * "og:title": "Matrix.org",
+ * "og:type": "website",
+ * "og:url": "https://matrix.org"
+ * }
+ *
+ */
+data class PreviewUrlData(
+ // Value of field "og:url". If not provided, this is the value passed in parameter
+ val url: String,
+ // Value of field "og:site_name"
+ val siteName: String?,
+ // Value of field "og:title"
+ val title: String?,
+ // Value of field "og:description"
+ val description: String?,
+ // Value of field "og:image"
+ val mxcUrl: String?
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
index ac1d726d03..aefc086b43 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
@@ -25,6 +25,7 @@ interface PermalinkService {
companion object {
const val MATRIX_TO_URL_BASE = "https://matrix.to/#/"
+ const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://"
}
/**
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt
index 477bef66cf..5f02b77a1e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt
@@ -18,12 +18,15 @@ package org.matrix.android.sdk.api.session.room
import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
+import org.matrix.android.sdk.api.session.room.peeking.PeekResult
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.Optional
+import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
/**
* This interface defines methods to get rooms. It's implemented at the session level.
@@ -120,7 +123,7 @@ interface RoomService {
*/
fun getRoomIdByAlias(roomAlias: String,
searchOnServer: Boolean,
- callback: MatrixCallback>): Cancelable
+ callback: MatrixCallback>): Cancelable
/**
* Delete a room alias
@@ -163,4 +166,16 @@ interface RoomService {
* @return a LiveData of the optional found room member
*/
fun getRoomMemberLive(userId: String, roomId: String): LiveData>
+
+ /**
+ * Get some state events about a room
+ */
+ fun getRoomState(roomId: String, callback: MatrixCallback>)
+
+ /**
+ * Use this if you want to get information from a room that you are not yet in (or invited)
+ * It might be possible to get some information on this room if it is public or if guest access is allowed
+ * This call will try to gather some information on this room, but it could fail and get nothing more
+ */
+ fun peekRoom(roomIdOrAlias: String, callback: MatrixCallback)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt
index 859f7fd104..73e27b64e3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt
@@ -20,6 +20,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
+import org.matrix.android.sdk.api.util.MimeTypes
import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo
@JsonClass(generateAdapter = true)
@@ -54,5 +55,5 @@ data class MessageImageContent(
@Json(name = "file") override val encryptedFileInfo: EncryptedFileInfo? = null
) : MessageImageInfoContent {
override val mimeType: String?
- get() = encryptedFileInfo?.mimetype ?: info?.mimeType ?: "image/*"
+ get() = encryptedFileInfo?.mimetype ?: info?.mimeType ?: MimeTypes.Images
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt
index 0f133323b0..a2b4e135d1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt
@@ -33,4 +33,7 @@ object MessageType {
// Add, in local, a fake message type in order to StickerMessage can inherit Message class
// Because sticker isn't a message type but a event type without msgtype field
const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
+
+ const val MSGTYPE_CONFETTI = "nic.custom.confetti"
+ const val MSGTYPE_SNOW = "nic.custom.snow"
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/peeking/PeekResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/peeking/PeekResult.kt
new file mode 100644
index 0000000000..db70dadef3
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/peeking/PeekResult.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.api.session.room.peeking
+
+sealed class PeekResult {
+ data class Success(
+ val roomId: String,
+ val alias: String?,
+ val name: String?,
+ val topic: String?,
+ val avatarUrl: String?,
+ val numJoinedMembers: Int?,
+ val viaServers: List
+ ) : PeekResult()
+
+ data class PeekingNotAllowed(
+ val roomId: String,
+ val alias: String?,
+ val viaServers: List
+ ) : PeekResult()
+
+ object UnknownAlias : PeekResult()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt
index 74e3faf38a..444366e912 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt
@@ -18,13 +18,11 @@ package org.matrix.android.sdk.api.session.room.state
import android.net.Uri
import androidx.lifecycle.LiveData
-import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
-import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.api.util.Optional
@@ -33,41 +31,41 @@ interface StateService {
/**
* Update the topic of the room
*/
- fun updateTopic(topic: String, callback: MatrixCallback): Cancelable
+ suspend fun updateTopic(topic: String)
/**
* Update the name of the room
*/
- fun updateName(name: String, callback: MatrixCallback): Cancelable
+ suspend fun updateName(name: String)
/**
* Update the canonical alias of the room
* @param alias the canonical alias, or null to reset the canonical alias of this room
* @param altAliases the alternative aliases for this room. It should include the canonical alias if any.
*/
- fun updateCanonicalAlias(alias: String?, altAliases: List, callback: MatrixCallback): Cancelable
+ suspend fun updateCanonicalAlias(alias: String?, altAliases: List)
/**
* Update the history readability of the room
*/
- fun updateHistoryReadability(readability: RoomHistoryVisibility, callback: MatrixCallback): Cancelable
+ suspend fun updateHistoryReadability(readability: RoomHistoryVisibility)
/**
* Update the join rule and/or the guest access
*/
- fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?, callback: MatrixCallback): Cancelable
+ suspend fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?)
/**
* Update the avatar of the room
*/
- fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback): Cancelable
+ suspend fun updateAvatar(avatarUri: Uri, fileName: String)
/**
* Delete the avatar of the room
*/
- fun deleteAvatar(callback: MatrixCallback): Cancelable
+ suspend fun deleteAvatar()
- fun sendStateEvent(eventType: String, stateKey: String?, body: JsonDict, callback: MatrixCallback): Cancelable
+ suspend fun sendStateEvent(eventType: String, stateKey: String?, body: JsonDict)
fun getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event?
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/EventTypeFilter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/EventTypeFilter.kt
new file mode 100644
index 0000000000..18faa6a452
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/EventTypeFilter.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.api.session.room.timeline
+
+data class EventTypeFilter(
+ /**
+ * Allowed event type.
+ */
+ val eventType: String,
+ /**
+ * Allowed state key. Set null if you want to allow all events,
+ * otherwise allowed events will be filtered according to the given stateKey.
+ */
+ val stateKey: String?
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEventFilters.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEventFilters.kt
index c751632286..4415c8e4b3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEventFilters.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEventFilters.kt
@@ -36,5 +36,5 @@ data class TimelineEventFilters(
/**
* If [filterTypes] is true, the list of types allowed by the list.
*/
- val allowedTypes: List = emptyList()
+ val allowedTypes: List = emptyList()
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/MimeTypes.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/MimeTypes.kt
new file mode 100644
index 0000000000..c74999b4ab
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/MimeTypes.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.api.util
+
+import org.matrix.android.sdk.api.extensions.orFalse
+
+// The Android SDK does not provide constant for mime type, add some of them here
+object MimeTypes {
+ const val Any: String = "*/*"
+ const val OctetStream = "application/octet-stream"
+
+ const val Images = "image/*"
+
+ const val Png = "image/png"
+ const val BadJpg = "image/jpg"
+ const val Jpeg = "image/jpeg"
+ const val Gif = "image/gif"
+
+ fun String?.normalizeMimeType() = if (this == BadJpg) Jpeg else this
+
+ fun String?.isMimeTypeImage() = this?.startsWith("image/").orFalse()
+ fun String?.isMimeTypeVideo() = this?.startsWith("video/").orFalse()
+ fun String?.isMimeTypeAudio() = this?.startsWith("audio/").orFalse()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
index c6d610188e..2ec8900f7c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
@@ -33,6 +33,7 @@ import org.matrix.android.sdk.internal.di.AuthDatabase
import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter
import org.matrix.android.sdk.internal.wellknown.WellknownModule
import io.realm.RealmConfiguration
+import org.matrix.android.sdk.api.auth.HomeServerHistoryService
import java.io.File
@Module(includes = [WellknownModule::class])
@@ -80,4 +81,7 @@ internal abstract class AuthModule {
@Binds
abstract fun bindDirectLoginTask(task: DefaultDirectLoginTask): DirectLoginTask
+
+ @Binds
+ abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 3d5a0efcd4..55f053de8d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.data.LoginFlowResult
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.auth.login.LoginWizard
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
@@ -278,6 +279,7 @@ internal class DefaultAuthenticationService @Inject constructor(
}
return LoginFlowResult.Success(
loginFlowResponse.flows.orEmpty().mapNotNull { it.type },
+ loginFlowResponse.flows.orEmpty().firstOrNull { it.type == LoginFlowTypes.SSO }?.ssoIdentityProvider,
versions.isLoginAndRegistrationSupportedBySdk(),
homeServerUrl,
!versions.isSupportedBySdk()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultHomeServerHistoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultHomeServerHistoryService.kt
new file mode 100644
index 0000000000..7415938ebc
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultHomeServerHistoryService.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.auth
+
+import com.zhuinden.monarchy.Monarchy
+import io.realm.kotlin.where
+import org.matrix.android.sdk.api.auth.HomeServerHistoryService
+import org.matrix.android.sdk.internal.database.model.KnownServerUrlEntity
+import org.matrix.android.sdk.internal.di.GlobalDatabase
+import javax.inject.Inject
+
+class DefaultHomeServerHistoryService @Inject constructor(
+ @GlobalDatabase private val monarchy: Monarchy
+) : HomeServerHistoryService {
+
+ override fun getKnownServersUrls(): List {
+ return monarchy.fetchAllMappedSync(
+ { realm ->
+ realm.where()
+ },
+ { it.url }
+ )
+ }
+
+ override fun addHomeServerToHistory(url: String) {
+ monarchy.writeAsync { realm ->
+ KnownServerUrlEntity(url).let {
+ realm.insertOrUpdate(it)
+ }
+ }
+ }
+
+ override fun clearHistory() {
+ monarchy.runTransactionSync { it.where().findAll().deleteAllFromRealm() }
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt
index 8acdee3608..2b26115f30 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginFlowResponse.kt
@@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.auth.data
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
@JsonClass(generateAdapter = true)
internal data class LoginFlowResponse(
@@ -34,5 +35,13 @@ internal data class LoginFlow(
* The login type. This is supplied as the type when logging in.
*/
@Json(name = "type")
- val type: String?
+ val type: String?,
+
+ /**
+ * Augments m.login.sso flow discovery definition to include metadata on the supported IDPs
+ * the client can show a button for each of the supported providers
+ * See MSC #2858
+ */
+ @Json(name = "org.matrix.msc2858.identity_providers")
+ val ssoIdentityProvider: List?
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/LocalizedFlowDataLoginTerms.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/LocalizedFlowDataLoginTerms.kt
index 1e18887008..5d119bb617 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/LocalizedFlowDataLoginTerms.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/LocalizedFlowDataLoginTerms.kt
@@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.auth.registration
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
/**
* This class represent a localized privacy policy for registration Flow.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/RegistrationFlowResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/RegistrationFlowResponse.kt
index 5b105c4d40..3461a4d738 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/RegistrationFlowResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/RegistrationFlowResponse.kt
@@ -51,6 +51,18 @@ data class RegistrationFlowResponse(
* The information that the client will need to know in order to use a given type of authentication.
* For each login stage type presented, that type may be present as a key in this dictionary.
* For example, the public key of reCAPTCHA stage could be given here.
+ * other example
+ * "params": {
+ * "m.login.sso": {
+ * "identity_providers": [
+ * {
+ * "id": "google",
+ * "name": "Google",
+ * "icon": "https://..."
+ * }
+ * ]
+ * }
+ * }
*/
@Json(name = "params")
val params: JsonDict? = null
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/ElementToDecrypt.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/ElementToDecrypt.kt
index b77006aa3a..c071384df4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/ElementToDecrypt.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/ElementToDecrypt.kt
@@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.crypto.attachments
import android.os.Parcelable
import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
fun EncryptedFileInfo.toElementToDecrypt(): ElementToDecrypt? {
// Check the validity of some fields
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
index 973388da49..b970ec60e2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
@@ -20,6 +20,7 @@ import io.realm.DynamicRealm
import io.realm.RealmMigration
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
+import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import timber.log.Timber
import javax.inject.Inject
@@ -27,7 +28,7 @@ import javax.inject.Inject
class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
companion object {
- const val SESSION_STORE_SCHEMA_VERSION = 5L
+ const val SESSION_STORE_SCHEMA_VERSION = 6L
}
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
@@ -38,6 +39,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
if (oldVersion <= 2) migrateTo3(realm)
if (oldVersion <= 3) migrateTo4(realm)
if (oldVersion <= 4) migrateTo5(realm)
+ if (oldVersion <= 5) migrateTo6(realm)
}
private fun migrateTo1(realm: DynamicRealm) {
@@ -89,4 +91,18 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
?.removeField("adminE2EByDefault")
?.removeField("preferredJitsiDomain")
}
+
+ private fun migrateTo6(realm: DynamicRealm) {
+ Timber.d("Step 5 -> 6")
+ realm.schema.create("PreviewUrlCacheEntity")
+ .addField(PreviewUrlCacheEntityFields.URL, String::class.java)
+ .setRequired(PreviewUrlCacheEntityFields.URL, true)
+ .addPrimaryKey(PreviewUrlCacheEntityFields.URL)
+ .addField(PreviewUrlCacheEntityFields.URL_FROM_SERVER, String::class.java)
+ .addField(PreviewUrlCacheEntityFields.SITE_NAME, String::class.java)
+ .addField(PreviewUrlCacheEntityFields.TITLE, String::class.java)
+ .addField(PreviewUrlCacheEntityFields.DESCRIPTION, String::class.java)
+ .addField(PreviewUrlCacheEntityFields.MXC_URL, String::class.java)
+ .addField(PreviewUrlCacheEntityFields.LAST_UPDATED_TIMESTAMP, Long::class.java)
+ }
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/KnownServerUrlEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/KnownServerUrlEntity.kt
new file mode 100644
index 0000000000..1ebdc22ab4
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/KnownServerUrlEntity.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.database.model
+
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+
+internal open class KnownServerUrlEntity(
+ @PrimaryKey
+ var url: String = ""
+) : RealmObject() {
+ companion object
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PreviewUrlCacheEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PreviewUrlCacheEntity.kt
new file mode 100644
index 0000000000..b1e0b64405
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PreviewUrlCacheEntity.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.database.model
+
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+
+internal open class PreviewUrlCacheEntity(
+ @PrimaryKey
+ var url: String = "",
+
+ var urlFromServer: String? = null,
+ var siteName: String? = null,
+ var title: String? = null,
+ var description: String? = null,
+ var mxcUrl: String? = null,
+
+ var lastUpdatedTimestamp: Long = 0L
+) : RealmObject() {
+
+ companion object
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt
index f62312f8fc..bca2c42c9e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/SessionRealmModule.kt
@@ -48,6 +48,7 @@ import io.realm.annotations.RealmModule
PushRulesEntity::class,
PushRuleEntity::class,
PushConditionEntity::class,
+ PreviewUrlCacheEntity::class,
PusherEntity::class,
PusherDataEntity::class,
ReadReceiptsSummaryEntity::class,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/PreviewUrlCacheEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/PreviewUrlCacheEntityQueries.kt
new file mode 100644
index 0000000000..a139c17439
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/PreviewUrlCacheEntityQueries.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.database.query
+
+import io.realm.Realm
+import io.realm.kotlin.createObject
+import io.realm.kotlin.where
+import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntity
+import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
+
+/**
+ * Get the current PreviewUrlCacheEntity, return null if it does not exist
+ */
+internal fun PreviewUrlCacheEntity.Companion.get(realm: Realm, url: String): PreviewUrlCacheEntity? {
+ return realm.where()
+ .equalTo(PreviewUrlCacheEntityFields.URL, url)
+ .findFirst()
+}
+
+/**
+ * Get the current PreviewUrlCacheEntity, create one if it does not exist
+ */
+internal fun PreviewUrlCacheEntity.Companion.getOrCreate(realm: Realm, url: String): PreviewUrlCacheEntity {
+ return get(realm, url) ?: realm.createObject(url)
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt
index 41a13c785d..148232cf94 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt
@@ -71,8 +71,23 @@ internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
}
internal fun RealmQuery.filterEvents(filters: TimelineEventFilters): RealmQuery {
- if (filters.filterTypes) {
- `in`(TimelineEventEntityFields.ROOT.TYPE, filters.allowedTypes.toTypedArray())
+ if (filters.filterTypes && filters.allowedTypes.isNotEmpty()) {
+ beginGroup()
+ filters.allowedTypes.forEachIndexed { index, filter ->
+ if (filter.stateKey == null) {
+ equalTo(TimelineEventEntityFields.ROOT.TYPE, filter.eventType)
+ } else {
+ beginGroup()
+ equalTo(TimelineEventEntityFields.ROOT.TYPE, filter.eventType)
+ and()
+ equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, filter.stateKey)
+ endGroup()
+ }
+ if (index != filters.allowedTypes.size - 1) {
+ or()
+ }
+ }
+ endGroup()
}
if (filters.filterUseless) {
not()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
index d3f08fde36..9d6fa29bb2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
@@ -25,6 +25,7 @@ import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.auth.AuthenticationService
+import org.matrix.android.sdk.api.auth.HomeServerHistoryService
import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.auth.AuthModule
@@ -62,6 +63,8 @@ internal interface MatrixComponent {
fun rawService(): RawService
+ fun homeServerHistoryService(): HomeServerHistoryService
+
fun context(): Context
fun matrixConfiguration(): MatrixConfiguration
@@ -71,9 +74,6 @@ internal interface MatrixComponent {
@CacheDirectory
fun cacheDir(): File
- @ExternalFilesDirectory
- fun externalFilesDir(): File?
-
fun olmManager(): OlmManager
fun taskExecutor(): TaskExecutor
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
index 71cbd8f1a1..b58fb3e683 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
@@ -57,13 +57,6 @@ internal object MatrixModule {
return context.cacheDir
}
- @JvmStatic
- @Provides
- @ExternalFilesDirectory
- fun providesExternalFilesDir(context: Context): File? {
- return context.getExternalFilesDir(null)
- }
-
@JvmStatic
@Provides
@MatrixScope
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
index e6cec7f7ac..2535a5347a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
@@ -16,14 +16,15 @@
package org.matrix.android.sdk.internal.network
-import org.matrix.android.sdk.api.failure.Failure
-import org.matrix.android.sdk.api.failure.shouldBeRetried
-import org.matrix.android.sdk.internal.network.ssl.CertUtil
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.delay
import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.api.failure.shouldBeRetried
+import org.matrix.android.sdk.internal.network.ssl.CertUtil
import retrofit2.Call
import retrofit2.awaitResponse
+import timber.log.Timber
import java.io.IOException
internal suspend inline fun executeRequest(eventBus: EventBus?,
@@ -49,6 +50,9 @@ internal class Request(private val eventBus: EventBus?) {
throw response.toFailure(eventBus)
}
} catch (exception: Throwable) {
+ // Log some details about the request which has failed
+ Timber.e("Exception when executing request ${apiCall.request().method} ${apiCall.request().url.toString().substringBefore("?")}")
+
// Check if this is a certificateException
CertUtil.getCertificateException(exception)
// TODO Support certificate error once logged
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultCleanRawCacheTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/CleanRawCacheTask.kt
similarity index 100%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultCleanRawCacheTask.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/CleanRawCacheTask.kt
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt
index 3b0d7546e5..42b826de16 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt
@@ -16,7 +16,7 @@
package org.matrix.android.sdk.internal.raw
-import org.matrix.android.sdk.api.raw.RawCacheStrategy
+import org.matrix.android.sdk.api.cache.CacheStrategy
import org.matrix.android.sdk.api.raw.RawService
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@@ -25,15 +25,15 @@ internal class DefaultRawService @Inject constructor(
private val getUrlTask: GetUrlTask,
private val cleanRawCacheTask: CleanRawCacheTask
) : RawService {
- override suspend fun getUrl(url: String, rawCacheStrategy: RawCacheStrategy): String {
- return getUrlTask.execute(GetUrlTask.Params(url, rawCacheStrategy))
+ override suspend fun getUrl(url: String, cacheStrategy: CacheStrategy): String {
+ return getUrlTask.execute(GetUrlTask.Params(url, cacheStrategy))
}
override suspend fun getWellknown(userId: String): String {
val homeServerDomain = userId.substringAfter(":")
return getUrl(
"https://$homeServerDomain/.well-known/matrix/client",
- RawCacheStrategy.TtlCache(TimeUnit.HOURS.toMillis(8), false)
+ CacheStrategy.TtlCache(TimeUnit.HOURS.toMillis(8), false)
)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultGetUrlTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GetUrlTask.kt
similarity index 86%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultGetUrlTask.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GetUrlTask.kt
index 1f4ca6d627..16633d90ef 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultGetUrlTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GetUrlTask.kt
@@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.raw
import com.zhuinden.monarchy.Monarchy
import okhttp3.ResponseBody
-import org.matrix.android.sdk.api.raw.RawCacheStrategy
+import org.matrix.android.sdk.api.cache.CacheStrategy
import org.matrix.android.sdk.internal.database.model.RawCacheEntity
import org.matrix.android.sdk.internal.database.query.get
import org.matrix.android.sdk.internal.database.query.getOrCreate
@@ -32,7 +32,7 @@ import javax.inject.Inject
internal interface GetUrlTask : Task {
data class Params(
val url: String,
- val rawCacheStrategy: RawCacheStrategy
+ val cacheStrategy: CacheStrategy
)
}
@@ -42,14 +42,14 @@ internal class DefaultGetUrlTask @Inject constructor(
) : GetUrlTask {
override suspend fun execute(params: GetUrlTask.Params): String {
- return when (params.rawCacheStrategy) {
- RawCacheStrategy.NoCache -> doRequest(params.url)
- is RawCacheStrategy.TtlCache -> doRequestWithCache(
+ return when (params.cacheStrategy) {
+ CacheStrategy.NoCache -> doRequest(params.url)
+ is CacheStrategy.TtlCache -> doRequestWithCache(
params.url,
- params.rawCacheStrategy.validityDurationInMillis,
- params.rawCacheStrategy.strict
+ params.cacheStrategy.validityDurationInMillis,
+ params.cacheStrategy.strict
)
- RawCacheStrategy.InfiniteCache -> doRequestWithCache(
+ CacheStrategy.InfiniteCache -> doRequestWithCache(
params.url,
Long.MAX_VALUE,
true
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GlobalRealmMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GlobalRealmMigration.kt
new file mode 100644
index 0000000000..49bcc72181
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GlobalRealmMigration.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.raw
+
+import io.realm.DynamicRealm
+import io.realm.RealmMigration
+import org.matrix.android.sdk.internal.database.model.KnownServerUrlEntityFields
+import timber.log.Timber
+
+internal object GlobalRealmMigration : RealmMigration {
+
+ // Current schema version
+ const val SCHEMA_VERSION = 1L
+
+ override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
+ Timber.d("Migrating Auth Realm from $oldVersion to $newVersion")
+
+ if (oldVersion <= 0) migrateTo1(realm)
+ }
+
+ private fun migrateTo1(realm: DynamicRealm) {
+ realm.schema.create("KnownServerUrlEntity")
+ .addField(KnownServerUrlEntityFields.URL, String::class.java)
+ .addPrimaryKey(KnownServerUrlEntityFields.URL)
+ .setRequired(KnownServerUrlEntityFields.URL, true)
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GlobalRealmModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GlobalRealmModule.kt
index e4e4160193..770a49c904 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GlobalRealmModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GlobalRealmModule.kt
@@ -17,6 +17,7 @@
package org.matrix.android.sdk.internal.raw
import io.realm.annotations.RealmModule
+import org.matrix.android.sdk.internal.database.model.KnownServerUrlEntity
import org.matrix.android.sdk.internal.database.model.RawCacheEntity
/**
@@ -24,6 +25,7 @@ import org.matrix.android.sdk.internal.database.model.RawCacheEntity
*/
@RealmModule(library = true,
classes = [
- RawCacheEntity::class
+ RawCacheEntity::class,
+ KnownServerUrlEntity::class
])
internal class GlobalRealmModule
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawModule.kt
index aee2a52818..50721b809a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawModule.kt
@@ -57,6 +57,9 @@ internal abstract class RawModule {
realmKeysUtils.configureEncryption(this, DB_ALIAS)
}
.name("matrix-sdk-global.realm")
+ .schemaVersion(GlobalRealmMigration.SCHEMA_VERSION)
+ .migration(GlobalRealmMigration)
+ .allowWritesOnUiThread(true)
.modules(GlobalRealmModule())
.build()
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
index 861ae7c7ee..07cde3da60 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
@@ -21,6 +21,10 @@ import android.net.Uri
import android.webkit.MimeTypeMap
import androidx.core.content.FileProvider
import arrow.core.Try
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import okhttp3.OkHttpClient
+import okhttp3.Request
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
@@ -29,35 +33,21 @@ import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.NoOpCancellable
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments
-import org.matrix.android.sdk.internal.di.CacheDirectory
-import org.matrix.android.sdk.internal.di.ExternalFilesDirectory
import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificateWithProgress
import org.matrix.android.sdk.internal.session.download.DownloadProgressInterceptor.Companion.DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
+import org.matrix.android.sdk.internal.util.md5
import org.matrix.android.sdk.internal.util.toCancelable
import org.matrix.android.sdk.internal.util.writeToFile
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import okhttp3.OkHttpClient
-import okhttp3.Request
-import okio.buffer
-import okio.sink
-import okio.source
import timber.log.Timber
import java.io.File
import java.io.IOException
-import java.io.InputStream
-import java.net.URLEncoder
import javax.inject.Inject
internal class DefaultFileService @Inject constructor(
private val context: Context,
- @CacheDirectory
- private val cacheDirectory: File,
- @ExternalFilesDirectory
- private val externalFilesDirectory: File?,
@SessionDownloadsDirectory
private val sessionCacheDirectory: File,
private val contentUrlResolver: ContentUrlResolver,
@@ -67,9 +57,17 @@ internal class DefaultFileService @Inject constructor(
private val taskExecutor: TaskExecutor
) : FileService {
- private fun String.safeFileName() = URLEncoder.encode(this, Charsets.US_ASCII.displayName())
+ // Legacy folder, will be deleted
+ private val legacyFolder = File(sessionCacheDirectory, "MF")
+ // Folder to store downloaded files (not decrypted)
+ private val downloadFolder = File(sessionCacheDirectory, "F")
+ // Folder to store decrypted files
+ private val decryptedFolder = File(downloadFolder, "D")
- private val downloadFolder = File(sessionCacheDirectory, "MF")
+ init {
+ // Clear the legacy downloaded files
+ legacyFolder.deleteRecursively()
+ }
/**
* Retain ongoing downloads to avoid re-downloading and already downloading file
@@ -81,28 +79,26 @@ internal class DefaultFileService @Inject constructor(
* Download file in the cache folder, and eventually decrypt it
* TODO looks like files are copied 3 times
*/
- override fun downloadFile(downloadMode: FileService.DownloadMode,
- id: String,
- fileName: String,
+ override fun downloadFile(fileName: String,
mimeType: String?,
url: String?,
elementToDecrypt: ElementToDecrypt?,
callback: MatrixCallback): Cancelable {
- val unwrappedUrl = url ?: return NoOpCancellable.also {
+ url ?: return NoOpCancellable.also {
callback.onFailure(IllegalArgumentException("url is null"))
}
- Timber.v("## FileService downloadFile $unwrappedUrl")
+ Timber.v("## FileService downloadFile $url")
synchronized(ongoing) {
- val existing = ongoing[unwrappedUrl]
+ val existing = ongoing[url]
if (existing != null) {
Timber.v("## FileService downloadFile is already downloading.. ")
existing.add(callback)
return NoOpCancellable
} else {
// mark as tracked
- ongoing[unwrappedUrl] = ArrayList()
+ ongoing[url] = ArrayList()
// and proceed to download
}
}
@@ -110,15 +106,15 @@ internal class DefaultFileService @Inject constructor(
return taskExecutor.executorScope.launch(coroutineDispatchers.main) {
withContext(coroutineDispatchers.io) {
Try {
- if (!downloadFolder.exists()) {
- downloadFolder.mkdirs()
+ if (!decryptedFolder.exists()) {
+ decryptedFolder.mkdirs()
}
// ensure we use unique file name by using URL (mapped to suitable file name)
// Also we need to add extension for the FileProvider, if not it lot's of app that it's
// shared with will not function well (even if mime type is passed in the intent)
- File(downloadFolder, fileForUrl(unwrappedUrl, mimeType))
- }.flatMap { destFile ->
- if (!destFile.exists()) {
+ getFiles(url, fileName, mimeType, elementToDecrypt != null)
+ }.flatMap { cachedFiles ->
+ if (!cachedFiles.file.exists()) {
val resolvedUrl = contentUrlResolver.resolveFullSize(url) ?: return@flatMap Try.Failure(IllegalArgumentException("url is null"))
val request = Request.Builder()
@@ -141,79 +137,153 @@ internal class DefaultFileService @Inject constructor(
Timber.v("Response size ${response.body?.contentLength()} - Stream available: ${!source.exhausted()}")
- if (elementToDecrypt != null) {
- Timber.v("## FileService: decrypt file")
- val decryptSuccess = destFile.outputStream().buffered().use {
- MXEncryptedAttachments.decryptAttachment(
- source.inputStream(),
- elementToDecrypt,
- it
- )
- }
- response.close()
- if (!decryptSuccess) {
- return@flatMap Try.Failure(IllegalStateException("Decryption error"))
- }
- } else {
- writeToFile(source.inputStream(), destFile)
- response.close()
- }
+ // Write the file to cache (encrypted version if the file is encrypted)
+ writeToFile(source.inputStream(), cachedFiles.file)
+ response.close()
} else {
Timber.v("## FileService: cache hit for $url")
}
- Try.just(copyFile(destFile, downloadMode))
+ Try.just(cachedFiles)
}
- }.fold({
- callback.onFailure(it)
- // notify concurrent requests
- val toNotify = synchronized(ongoing) {
- ongoing[unwrappedUrl]?.also {
- ongoing.remove(unwrappedUrl)
+ }.flatMap { cachedFiles ->
+ // Decrypt if necessary
+ if (cachedFiles.decryptedFile != null) {
+ if (!cachedFiles.decryptedFile.exists()) {
+ Timber.v("## FileService: decrypt file")
+ // Ensure the parent folder exists
+ cachedFiles.decryptedFile.parentFile?.mkdirs()
+ val decryptSuccess = cachedFiles.file.inputStream().use { inputStream ->
+ cachedFiles.decryptedFile.outputStream().buffered().use { outputStream ->
+ MXEncryptedAttachments.decryptAttachment(
+ inputStream,
+ elementToDecrypt,
+ outputStream
+ )
+ }
+ }
+ if (!decryptSuccess) {
+ return@flatMap Try.Failure(IllegalStateException("Decryption error"))
+ }
+ } else {
+ Timber.v("## FileService: cache hit for decrypted file")
}
+ Try.just(cachedFiles.decryptedFile)
+ } else {
+ // Clear file
+ Try.just(cachedFiles.file)
}
- toNotify?.forEach { otherCallbacks ->
- tryOrNull { otherCallbacks.onFailure(it) }
- }
- }, { file ->
- callback.onSuccess(file)
- // notify concurrent requests
- val toNotify = synchronized(ongoing) {
- ongoing[unwrappedUrl]?.also {
- ongoing.remove(unwrappedUrl)
+ }.fold(
+ { throwable ->
+ callback.onFailure(throwable)
+ // notify concurrent requests
+ val toNotify = synchronized(ongoing) {
+ ongoing[url]?.also {
+ ongoing.remove(url)
+ }
+ }
+ toNotify?.forEach { otherCallbacks ->
+ tryOrNull { otherCallbacks.onFailure(throwable) }
+ }
+ },
+ { file ->
+ callback.onSuccess(file)
+ // notify concurrent requests
+ val toNotify = synchronized(ongoing) {
+ ongoing[url]?.also {
+ ongoing.remove(url)
+ }
+ }
+ Timber.v("## FileService additional to notify ${toNotify?.size ?: 0} ")
+ toNotify?.forEach { otherCallbacks ->
+ tryOrNull { otherCallbacks.onSuccess(file) }
+ }
}
- }
- Timber.v("## FileService additional to notify ${toNotify?.size ?: 0} ")
- toNotify?.forEach { otherCallbacks ->
- tryOrNull { otherCallbacks.onSuccess(file) }
- }
- })
+ )
}.toCancelable()
}
- fun storeDataFor(url: String, mimeType: String?, inputStream: InputStream) {
- val file = File(downloadFolder, fileForUrl(url, mimeType))
- val source = inputStream.source().buffer()
- file.sink().buffer().let { sink ->
- source.use { input ->
- sink.use { output ->
- output.writeAll(input)
+ fun storeDataFor(mxcUrl: String,
+ filename: String?,
+ mimeType: String?,
+ originalFile: File,
+ encryptedFile: File?) {
+ val files = getFiles(mxcUrl, filename, mimeType, encryptedFile != null)
+ if (encryptedFile != null) {
+ // We switch the two files here, original file it the decrypted file
+ files.decryptedFile?.let { originalFile.copyTo(it) }
+ encryptedFile.copyTo(files.file)
+ } else {
+ // Just copy the original file
+ originalFile.copyTo(files.file)
+ }
+ }
+
+ private fun safeFileName(fileName: String?, mimeType: String?): String {
+ return buildString {
+ // filename has to be safe for the Android System
+ val result = fileName
+ ?.replace("[^a-z A-Z0-9\\\\.\\-]".toRegex(), "_")
+ ?.takeIf { it.isNotEmpty() }
+ ?: DEFAULT_FILENAME
+ append(result)
+ // Check that the extension is correct regarding the mimeType
+ val extensionFromMime = mimeType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) }
+ if (extensionFromMime != null) {
+ // Compare
+ val fileExtension = result.substringAfterLast(delimiter = ".", missingDelimiterValue = "")
+ if (fileExtension.isEmpty() || fileExtension != extensionFromMime) {
+ // Missing extension, or diff in extension, add the one provided by the mimetype
+ append(".")
+ append(extensionFromMime)
}
}
}
}
- private fun fileForUrl(url: String, mimeType: String?): String {
- val extension = mimeType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) }
- return if (extension != null) "${url.safeFileName()}.$extension" else url.safeFileName()
+ override fun isFileInCache(mxcUrl: String?,
+ fileName: String,
+ mimeType: String?,
+ elementToDecrypt: ElementToDecrypt?): Boolean {
+ return fileState(mxcUrl, fileName, mimeType, elementToDecrypt) == FileService.FileState.IN_CACHE
}
- override fun isFileInCache(mxcUrl: String, mimeType: String?): Boolean {
- return File(downloadFolder, fileForUrl(mxcUrl, mimeType)).exists()
+ internal data class CachedFiles(
+ // This is the downloaded file. Can be clear or encrypted
+ val file: File,
+ // This is the decrypted file. Null if the original file is not encrypted
+ val decryptedFile: File?
+ ) {
+ fun getClearFile(): File = decryptedFile ?: file
}
- override fun fileState(mxcUrl: String, mimeType: String?): FileService.FileState {
- if (isFileInCache(mxcUrl, mimeType)) return FileService.FileState.IN_CACHE
+ private fun getFiles(mxcUrl: String,
+ fileName: String?,
+ mimeType: String?,
+ isEncrypted: Boolean): CachedFiles {
+ val hashFolder = mxcUrl.md5()
+ val safeFileName = safeFileName(fileName, mimeType)
+ return if (isEncrypted) {
+ // Encrypted file
+ CachedFiles(
+ File(downloadFolder, "$hashFolder/$ENCRYPTED_FILENAME"),
+ File(decryptedFolder, "$hashFolder/$safeFileName")
+ )
+ } else {
+ // Clear file
+ CachedFiles(
+ File(downloadFolder, "$hashFolder/$safeFileName"),
+ null
+ )
+ }
+ }
+
+ override fun fileState(mxcUrl: String?,
+ fileName: String,
+ mimeType: String?,
+ elementToDecrypt: ElementToDecrypt?): FileService.FileState {
+ mxcUrl ?: return FileService.FileState.UNKNOWN
+ if (getFiles(mxcUrl, fileName, mimeType, elementToDecrypt != null).file.exists()) return FileService.FileState.IN_CACHE
val isDownloading = synchronized(ongoing) {
ongoing[mxcUrl] != null
}
@@ -224,26 +294,18 @@ internal class DefaultFileService @Inject constructor(
* Use this URI and pass it to intent using flag Intent.FLAG_GRANT_READ_URI_PERMISSION
* (if not other app won't be able to access it)
*/
- override fun getTemporarySharableURI(mxcUrl: String, mimeType: String?): Uri? {
+ override fun getTemporarySharableURI(mxcUrl: String?,
+ fileName: String,
+ mimeType: String?,
+ elementToDecrypt: ElementToDecrypt?): Uri? {
+ mxcUrl ?: return null
// this string could be extracted no?
val authority = "${context.packageName}.mx-sdk.fileprovider"
- val targetFile = File(downloadFolder, fileForUrl(mxcUrl, mimeType))
+ val targetFile = getFiles(mxcUrl, fileName, mimeType, elementToDecrypt != null).getClearFile()
if (!targetFile.exists()) return null
return FileProvider.getUriForFile(context, authority, targetFile)
}
- private fun copyFile(file: File, downloadMode: FileService.DownloadMode): File {
- // TODO some of this seems outdated, will need to be re-worked
- return when (downloadMode) {
- FileService.DownloadMode.TO_EXPORT ->
- file.copyTo(File(externalFilesDirectory, file.name), true)
- FileService.DownloadMode.FOR_EXTERNAL_SHARE ->
- file.copyTo(File(File(cacheDirectory, "ext_share"), file.name), true)
- FileService.DownloadMode.FOR_INTERNAL_USE ->
- file
- }
- }
-
override fun getCacheSize(): Int {
return downloadFolder.walkTopDown()
.onEnter {
@@ -256,4 +318,14 @@ internal class DefaultFileService @Inject constructor(
override fun clearCache() {
downloadFolder.deleteRecursively()
}
+
+ override fun clearDecryptedCache() {
+ decryptedFolder.deleteRecursively()
+ }
+
+ companion object {
+ private const val ENCRYPTED_FILENAME = "encrypted.bin"
+ // The extension would be added from the mimetype
+ private const val DEFAULT_FILENAME = "file"
+ }
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt
index 25345e953c..c5f3f65a34 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt
@@ -43,6 +43,7 @@ import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.session.group.GroupService
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
+import org.matrix.android.sdk.api.session.media.MediaService
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import org.matrix.android.sdk.api.session.profile.ProfileService
import org.matrix.android.sdk.api.session.pushers.PushersService
@@ -102,6 +103,7 @@ internal class DefaultSession @Inject constructor(
private val permalinkService: Lazy,
private val secureStorageService: Lazy,
private val profileService: Lazy,
+ private val mediaService: Lazy,
private val widgetService: Lazy,
private val syncThreadProvider: Provider,
private val contentUrlResolver: ContentUrlResolver,
@@ -263,6 +265,8 @@ internal class DefaultSession @Inject constructor(
override fun widgetService(): WidgetService = widgetService.get()
+ override fun mediaService(): MediaService = mediaService.get()
+
override fun integrationManagerService() = integrationManagerService
override fun callSignalingService(): CallSignalingService = callSignalingService.get()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt
index e6fd5a7a0c..659fcc8f5c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt
@@ -40,6 +40,7 @@ import org.matrix.android.sdk.internal.session.group.GroupModule
import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesModule
import org.matrix.android.sdk.internal.session.identity.IdentityModule
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManagerModule
+import org.matrix.android.sdk.internal.session.media.MediaModule
import org.matrix.android.sdk.internal.session.openid.OpenIdModule
import org.matrix.android.sdk.internal.session.profile.ProfileModule
import org.matrix.android.sdk.internal.session.pushers.AddHttpPusherWorker
@@ -75,6 +76,7 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
GroupModule::class,
ContentModule::class,
CacheModule::class,
+ MediaModule::class,
CryptoModule::class,
PushersModule::class,
OpenIdModule::class,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
index 32949d60c4..96b44917bd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
@@ -50,6 +50,7 @@ import org.matrix.android.sdk.internal.database.EventInsertLiveObserver
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.SessionRealmConfigurationFactory
import org.matrix.android.sdk.internal.di.Authenticated
+import org.matrix.android.sdk.internal.di.CacheDirectory
import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory
@@ -169,9 +170,9 @@ internal abstract class SessionModule {
@JvmStatic
@Provides
@SessionDownloadsDirectory
- fun providesCacheDir(@SessionId sessionId: String,
- context: Context): File {
- return File(context.cacheDir, "downloads/$sessionId")
+ fun providesDownloadsCacheDir(@SessionId sessionId: String,
+ @CacheDirectory cacheFile: File): File {
+ return File(cacheFile, "downloads/$sessionId")
}
@JvmStatic
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ContentUploadResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ContentUploadResponse.kt
index b5de26b39d..1ebe5b2eb6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ContentUploadResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ContentUploadResponse.kt
@@ -20,6 +20,9 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
-data class ContentUploadResponse(
+internal data class ContentUploadResponse(
+ /**
+ * Required. The MXC URI to the uploaded content.
+ */
@Json(name = "content_uri") val contentUri: String
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt
index 8c3aad6a1f..4b31db59b1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
+import org.matrix.android.sdk.api.util.MimeTypes
import timber.log.Timber
import java.io.ByteArrayOutputStream
@@ -58,7 +59,7 @@ internal object ThumbnailExtractor {
height = thumbnailHeight,
size = thumbnailSize.toLong(),
bytes = outputStream.toByteArray(),
- mimeType = "image/jpeg"
+ mimeType = MimeTypes.Jpeg
)
thumbnail.recycle()
outputStream.reset()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
index 4a30d6c1e6..672d407d25 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
@@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent
import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
+import org.matrix.android.sdk.api.util.MimeTypes
import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments
import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
@@ -151,7 +152,10 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
params.attachment.size
)
- if (attachment.type == ContentAttachmentData.Type.IMAGE && params.compressBeforeSending) {
+ if (attachment.type == ContentAttachmentData.Type.IMAGE
+ // Do not compress gif
+ && attachment.mimeType != MimeTypes.Gif
+ && params.compressBeforeSending) {
fileToUpload = imageCompressor.compress(context, workingFile, MAX_IMAGE_SIZE, MAX_IMAGE_SIZE)
.also { compressedFile ->
// Get new Bitmap size
@@ -174,14 +178,15 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
}
}
+ val encryptedFile: File?
val contentUploadResponse = if (params.isEncrypted) {
Timber.v("## FileService: Encrypt file")
- val tmpEncrypted = File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir)
+ encryptedFile = File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir)
.also { filesToDelete.add(it) }
uploadedFileEncryptedFileInfo =
- MXEncryptedAttachments.encrypt(fileToUpload.inputStream(), attachment.getSafeMimeType(), tmpEncrypted) { read, total ->
+ MXEncryptedAttachments.encrypt(fileToUpload.inputStream(), attachment.getSafeMimeType(), encryptedFile) { read, total ->
notifyTracker(params) {
contentUploadStateTracker.setEncrypting(it, read.toLong(), total.toLong())
}
@@ -190,18 +195,23 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
Timber.v("## FileService: Uploading file")
fileUploader
- .uploadFile(tmpEncrypted, attachment.name, "application/octet-stream", progressListener)
+ .uploadFile(encryptedFile, attachment.name, MimeTypes.OctetStream, progressListener)
} else {
Timber.v("## FileService: Clear file")
+ encryptedFile = null
fileUploader
.uploadFile(fileToUpload, attachment.name, attachment.getSafeMimeType(), progressListener)
}
Timber.v("## FileService: Update cache storage for ${contentUploadResponse.contentUri}")
try {
- context.contentResolver.openInputStream(attachment.queryUri)?.let {
- fileService.storeDataFor(contentUploadResponse.contentUri, params.attachment.getSafeMimeType(), it)
- }
+ fileService.storeDataFor(
+ mxcUrl = contentUploadResponse.contentUri,
+ filename = params.attachment.name,
+ mimeType = params.attachment.getSafeMimeType(),
+ originalFile = workingFile,
+ encryptedFile = encryptedFile
+ )
Timber.v("## FileService: cache storage updated")
} catch (failure: Throwable) {
Timber.e(failure, "## FileService: Failed to update file cache")
@@ -252,7 +262,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
val encryptionResult = MXEncryptedAttachments.encryptAttachment(thumbnailData.bytes.inputStream(), thumbnailData.mimeType)
val contentUploadResponse = fileUploader.uploadByteArray(encryptionResult.encryptedByteArray,
"thumb_${params.attachment.name}",
- "application/octet-stream",
+ MimeTypes.OctetStream,
thumbnailProgressListener)
UploadThumbnailResult(
contentUploadResponse.contentUri,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/DefaultSaveFilterTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt
similarity index 100%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/DefaultSaveFilterTask.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGetGroupDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataTask.kt
similarity index 100%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGetGroupDataTask.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataTask.kt
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/CapabilitiesAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/CapabilitiesAPI.kt
index 39b6608de3..8242edac84 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/CapabilitiesAPI.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/CapabilitiesAPI.kt
@@ -22,19 +22,12 @@ import retrofit2.Call
import retrofit2.http.GET
internal interface CapabilitiesAPI {
-
/**
* Request the homeserver capabilities
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "capabilities")
fun getCapabilities(): Call
- /**
- * Request the upload capabilities
- */
- @GET(NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "config")
- fun getUploadCapabilities(): Call
-
/**
* Request the versions
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultGetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
similarity index 89%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultGetHomeServerCapabilitiesTask.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index 8d289dfda5..f3686b02d3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultGetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -29,6 +29,8 @@ import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManagerConfigExtractor
+import org.matrix.android.sdk.internal.session.media.GetMediaConfigResult
+import org.matrix.android.sdk.internal.session.media.MediaAPI
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.wellknown.GetWellknownTask
@@ -40,6 +42,7 @@ internal interface GetHomeServerCapabilitiesTask : Task
internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
private val capabilitiesAPI: CapabilitiesAPI,
+ private val mediaAPI: MediaAPI,
@SessionDatabase private val monarchy: Monarchy,
private val eventBus: EventBus,
private val getWellknownTask: GetWellknownTask,
@@ -67,9 +70,9 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
}
}.getOrNull()
- val uploadCapabilities = runCatching {
- executeRequest(eventBus) {
- apiCall = capabilitiesAPI.getUploadCapabilities()
+ val mediaConfig = runCatching {
+ executeRequest(eventBus) {
+ apiCall = mediaAPI.getMediaConfig()
}
}.getOrNull()
@@ -83,11 +86,11 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
getWellknownTask.execute(GetWellknownTask.Params(userId, homeServerConnectionConfig))
}.getOrNull()
- insertInDb(capabilities, uploadCapabilities, versions, wellknownResult)
+ insertInDb(capabilities, mediaConfig, versions, wellknownResult)
}
private suspend fun insertInDb(getCapabilitiesResult: GetCapabilitiesResult?,
- getUploadCapabilitiesResult: GetUploadCapabilitiesResult?,
+ getMediaConfigResult: GetMediaConfigResult?,
getVersionResult: Versions?,
getWellknownResult: WellknownResult?) {
monarchy.awaitTransaction { realm ->
@@ -97,8 +100,8 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
homeServerCapabilitiesEntity.canChangePassword = getCapabilitiesResult.canChangePassword()
}
- if (getUploadCapabilitiesResult != null) {
- homeServerCapabilitiesEntity.maxUploadFileSize = getUploadCapabilitiesResult.maxUploadSize
+ if (getMediaConfigResult != null) {
+ homeServerCapabilitiesEntity.maxUploadFileSize = getMediaConfigResult.maxUploadSize
?: HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt
index 3b0d514cf3..a03bef9501 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt
@@ -93,7 +93,7 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor(
} catch (failure: Throwable) {
// Catch invalid hash pepper and retry
if (canRetry && failure is Failure.ServerError && failure.error.code == MatrixError.M_INVALID_PEPPER) {
- // This is not documented, by the error can contain the new pepper!
+ // This is not documented, but the error can contain the new pepper!
if (!failure.error.newLookupPepper.isNullOrEmpty()) {
// Store it and use it right now
hashDetailResponse.copy(pepper = failure.error.newLookupPepper)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/ClearPreviewUrlCacheTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/ClearPreviewUrlCacheTask.kt
new file mode 100644
index 0000000000..004b622c64
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/ClearPreviewUrlCacheTask.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.session.media
+
+import com.zhuinden.monarchy.Monarchy
+import io.realm.kotlin.where
+import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntity
+import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.task.Task
+import org.matrix.android.sdk.internal.util.awaitTransaction
+import javax.inject.Inject
+
+internal interface ClearPreviewUrlCacheTask : Task
+
+internal class DefaultClearPreviewUrlCacheTask @Inject constructor(
+ @SessionDatabase private val monarchy: Monarchy
+) : ClearPreviewUrlCacheTask {
+
+ override suspend fun execute(params: Unit) {
+ monarchy.awaitTransaction { realm ->
+ realm.where()
+ .findAll()
+ .deleteAllFromRealm()
+ }
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/DefaultMediaService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/DefaultMediaService.kt
new file mode 100644
index 0000000000..1a400ccfcf
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/DefaultMediaService.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.session.media
+
+import androidx.collection.LruCache
+import org.matrix.android.sdk.api.cache.CacheStrategy
+import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.media.MediaService
+import org.matrix.android.sdk.api.session.media.PreviewUrlData
+import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.internal.util.getOrPut
+import javax.inject.Inject
+
+internal class DefaultMediaService @Inject constructor(
+ private val clearPreviewUrlCacheTask: ClearPreviewUrlCacheTask,
+ private val getPreviewUrlTask: GetPreviewUrlTask,
+ private val getRawPreviewUrlTask: GetRawPreviewUrlTask,
+ private val urlsExtractor: UrlsExtractor
+) : MediaService {
+ // Cache of extracted URLs
+ private val extractedUrlsCache = LruCache>(1_000)
+
+ override fun extractUrls(event: Event): List {
+ return extractedUrlsCache.getOrPut(event.cacheKey()) { urlsExtractor.extract(event) }
+ }
+
+ private fun Event.cacheKey() = "${eventId ?: ""}-${roomId ?: ""}"
+
+ override suspend fun getRawPreviewUrl(url: String, timestamp: Long?): JsonDict {
+ return getRawPreviewUrlTask.execute(GetRawPreviewUrlTask.Params(url, timestamp))
+ }
+
+ override suspend fun getPreviewUrl(url: String, timestamp: Long?, cacheStrategy: CacheStrategy): PreviewUrlData {
+ return getPreviewUrlTask.execute(GetPreviewUrlTask.Params(url, timestamp, cacheStrategy))
+ }
+
+ override suspend fun clearCache() {
+ extractedUrlsCache.evictAll()
+ clearPreviewUrlCacheTask.execute(Unit)
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetUploadCapabilitiesResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetMediaConfigResult.kt
similarity index 86%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetUploadCapabilitiesResult.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetMediaConfigResult.kt
index 92903bf96e..fece6c06c6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetUploadCapabilitiesResult.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetMediaConfigResult.kt
@@ -5,7 +5,7 @@
* 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
+ * 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,
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package org.matrix.android.sdk.internal.session.homeserver
+package org.matrix.android.sdk.internal.session.media
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
-internal data class GetUploadCapabilitiesResult(
+internal data class GetMediaConfigResult(
/**
* The maximum size an upload can be in bytes. Clients SHOULD use this as a guide when uploading content.
* If not listed or null, the size limit should be treated as unknown.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt
new file mode 100644
index 0000000000..69cdfa8faa
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.session.media
+
+import com.zhuinden.monarchy.Monarchy
+import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.api.cache.CacheStrategy
+import org.matrix.android.sdk.api.session.media.PreviewUrlData
+import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntity
+import org.matrix.android.sdk.internal.database.query.get
+import org.matrix.android.sdk.internal.database.query.getOrCreate
+import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.network.executeRequest
+import org.matrix.android.sdk.internal.task.Task
+import org.matrix.android.sdk.internal.util.awaitTransaction
+import java.util.Date
+import javax.inject.Inject
+
+internal interface GetPreviewUrlTask : Task {
+ data class Params(
+ val url: String,
+ val timestamp: Long?,
+ val cacheStrategy: CacheStrategy
+ )
+}
+
+internal class DefaultGetPreviewUrlTask @Inject constructor(
+ private val mediaAPI: MediaAPI,
+ private val eventBus: EventBus,
+ @SessionDatabase private val monarchy: Monarchy
+) : GetPreviewUrlTask {
+
+ override suspend fun execute(params: GetPreviewUrlTask.Params): PreviewUrlData {
+ return when (params.cacheStrategy) {
+ CacheStrategy.NoCache -> doRequest(params.url, params.timestamp)
+ is CacheStrategy.TtlCache -> doRequestWithCache(
+ params.url,
+ params.timestamp,
+ params.cacheStrategy.validityDurationInMillis,
+ params.cacheStrategy.strict
+ )
+ CacheStrategy.InfiniteCache -> doRequestWithCache(
+ params.url,
+ params.timestamp,
+ Long.MAX_VALUE,
+ true
+ )
+ }
+ }
+
+ private suspend fun doRequest(url: String, timestamp: Long?): PreviewUrlData {
+ return executeRequest(eventBus) {
+ apiCall = mediaAPI.getPreviewUrlData(url, timestamp)
+ }
+ .toPreviewUrlData(url)
+ }
+
+ private fun JsonDict.toPreviewUrlData(url: String): PreviewUrlData {
+ return PreviewUrlData(
+ url = (get("og:url") as? String) ?: url,
+ siteName = get("og:site_name") as? String,
+ title = get("og:title") as? String,
+ description = get("og:description") as? String,
+ mxcUrl = get("og:image") as? String
+ )
+ }
+
+ private suspend fun doRequestWithCache(url: String, timestamp: Long?, validityDurationInMillis: Long, strict: Boolean): PreviewUrlData {
+ // Get data from cache
+ var dataFromCache: PreviewUrlData? = null
+ var isCacheValid = false
+ monarchy.doWithRealm { realm ->
+ val entity = PreviewUrlCacheEntity.get(realm, url)
+ dataFromCache = entity?.toDomain()
+ isCacheValid = entity != null && Date().time < entity.lastUpdatedTimestamp + validityDurationInMillis
+ }
+
+ val finalDataFromCache = dataFromCache
+ if (finalDataFromCache != null && isCacheValid) {
+ return finalDataFromCache
+ }
+
+ // No cache or outdated cache
+ val data = try {
+ doRequest(url, timestamp)
+ } catch (throwable: Throwable) {
+ // In case of error, we can return value from cache even if outdated
+ return finalDataFromCache
+ ?.takeIf { !strict }
+ ?: throw throwable
+ }
+
+ // Store cache
+ monarchy.awaitTransaction { realm ->
+ val previewUrlCacheEntity = PreviewUrlCacheEntity.getOrCreate(realm, url)
+ previewUrlCacheEntity.urlFromServer = data.url
+ previewUrlCacheEntity.siteName = data.siteName
+ previewUrlCacheEntity.title = data.title
+ previewUrlCacheEntity.description = data.description
+ previewUrlCacheEntity.mxcUrl = data.mxcUrl
+
+ previewUrlCacheEntity.lastUpdatedTimestamp = Date().time
+ }
+
+ return data
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetRawPreviewUrlTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetRawPreviewUrlTask.kt
new file mode 100644
index 0000000000..6c5dad2422
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetRawPreviewUrlTask.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.session.media
+
+import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.internal.network.executeRequest
+import org.matrix.android.sdk.internal.task.Task
+import javax.inject.Inject
+
+internal interface GetRawPreviewUrlTask : Task {
+ data class Params(
+ val url: String,
+ val timestamp: Long?
+ )
+}
+
+internal class DefaultGetRawPreviewUrlTask @Inject constructor(
+ private val mediaAPI: MediaAPI,
+ private val eventBus: EventBus
+) : GetRawPreviewUrlTask {
+
+ override suspend fun execute(params: GetRawPreviewUrlTask.Params): JsonDict {
+ return executeRequest(eventBus) {
+ apiCall = mediaAPI.getPreviewUrlData(params.url, params.timestamp)
+ }
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/MediaAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/MediaAPI.kt
new file mode 100644
index 0000000000..bbb4f1e06a
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/MediaAPI.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.session.media
+
+import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.internal.network.NetworkConstants
+import retrofit2.Call
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+internal interface MediaAPI {
+ /**
+ * Retrieve the configuration of the content repository
+ * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-media-r0-config
+ */
+ @GET(NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "config")
+ fun getMediaConfig(): Call
+
+ /**
+ * Get information about a URL for the client. Typically this is called when a client
+ * sees a URL in a message and wants to render a preview for the user.
+ * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-media-r0-preview-url
+ * @param url Required. The URL to get a preview of.
+ * @param ts The preferred point in time to return a preview for. The server may return a newer version
+ * if it does not have the requested version available.
+ */
+ @GET(NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "preview_url")
+ fun getPreviewUrlData(@Query("url") url: String, @Query("ts") ts: Long?): Call
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/MediaModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/MediaModule.kt
new file mode 100644
index 0000000000..bc58b3f444
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/MediaModule.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.session.media
+
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import org.matrix.android.sdk.api.session.media.MediaService
+import org.matrix.android.sdk.internal.session.SessionScope
+import retrofit2.Retrofit
+
+@Module
+internal abstract class MediaModule {
+
+ @Module
+ companion object {
+ @Provides
+ @JvmStatic
+ @SessionScope
+ fun providesMediaAPI(retrofit: Retrofit): MediaAPI {
+ return retrofit.create(MediaAPI::class.java)
+ }
+ }
+
+ @Binds
+ abstract fun bindMediaService(service: DefaultMediaService): MediaService
+
+ @Binds
+ abstract fun bindGetRawPreviewUrlTask(task: DefaultGetRawPreviewUrlTask): GetRawPreviewUrlTask
+
+ @Binds
+ abstract fun bindGetPreviewUrlTask(task: DefaultGetPreviewUrlTask): GetPreviewUrlTask
+
+ @Binds
+ abstract fun bindClearMediaCacheTask(task: DefaultClearPreviewUrlCacheTask): ClearPreviewUrlCacheTask
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/PreviewUrlMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/PreviewUrlMapper.kt
new file mode 100644
index 0000000000..dd1a9ead26
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/PreviewUrlMapper.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.session.media
+
+import org.matrix.android.sdk.api.session.media.PreviewUrlData
+import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntity
+
+/**
+ * PreviewUrlCacheEntity -> PreviewUrlData
+ */
+internal fun PreviewUrlCacheEntity.toDomain() = PreviewUrlData(
+ url = urlFromServer ?: url,
+ siteName = siteName,
+ title = title,
+ description = description,
+ mxcUrl = mxcUrl
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt
new file mode 100644
index 0000000000..e531d6af9f
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.session.media
+
+import android.util.Patterns
+import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.events.model.EventType
+import org.matrix.android.sdk.api.session.events.model.toModel
+import org.matrix.android.sdk.api.session.room.model.message.MessageContent
+import org.matrix.android.sdk.api.session.room.model.message.MessageType
+import javax.inject.Inject
+
+internal class UrlsExtractor @Inject constructor() {
+ // Sadly Patterns.WEB_URL_WITH_PROTOCOL is not public so filter the protocol later
+ private val urlRegex = Patterns.WEB_URL.toRegex()
+
+ fun extract(event: Event): List {
+ return event.takeIf { it.getClearType() == EventType.MESSAGE }
+ ?.getClearContent()
+ ?.toModel()
+ ?.takeIf {
+ it.msgType == MessageType.MSGTYPE_TEXT
+ || it.msgType == MessageType.MSGTYPE_NOTICE
+ || it.msgType == MessageType.MSGTYPE_EMOTE
+ }
+ ?.body
+ ?.let { urlRegex.findAll(it) }
+ ?.map { it.value }
+ ?.filter { it.startsWith("https://") || it.startsWith("http://") }
+ ?.distinct()
+ ?.toList()
+ .orEmpty()
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt
index 5265e4f17d..500d43408e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt
@@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.profile.ProfileService
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.api.util.MimeTypes
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity
import org.matrix.android.sdk.internal.database.model.UserThreePidEntity
@@ -80,7 +81,7 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto
override fun updateAvatar(userId: String, newAvatarUri: Uri, fileName: String, matrixCallback: MatrixCallback): Cancelable {
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, matrixCallback) {
- val response = fileUploader.uploadFromUri(newAvatarUri, fileName, "image/jpeg")
+ val response = fileUploader.uploadFromUri(newAvatarUri, fileName, MimeTypes.Jpeg)
setAvatarUrlTask.execute(SetAvatarUrlTask.Params(userId = userId, newAvatarUrl = response.contentUri))
userStore.updateAvatar(userId, response.contentUri)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt
index 9ec985e0b6..383dd876d3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt
@@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.RoomService
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
@@ -27,6 +28,7 @@ import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
+import org.matrix.android.sdk.api.session.room.peeking.PeekResult
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional
@@ -35,10 +37,13 @@ import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFie
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.room.alias.DeleteRoomAliasTask
import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask
+import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
import org.matrix.android.sdk.internal.session.room.membership.joining.JoinRoomTask
+import org.matrix.android.sdk.internal.session.room.peeking.PeekRoomTask
+import org.matrix.android.sdk.internal.session.room.peeking.ResolveRoomStateTask
import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask
@@ -55,6 +60,8 @@ internal class DefaultRoomService @Inject constructor(
private val updateBreadcrumbsTask: UpdateBreadcrumbsTask,
private val roomIdByAliasTask: GetRoomIdByAliasTask,
private val deleteRoomAliasTask: DeleteRoomAliasTask,
+ private val resolveRoomStateTask: ResolveRoomStateTask,
+ private val peekRoomTask: PeekRoomTask,
private val roomGetter: RoomGetter,
private val roomSummaryDataSource: RoomSummaryDataSource,
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
@@ -119,7 +126,7 @@ internal class DefaultRoomService @Inject constructor(
.executeBy(taskExecutor)
}
- override fun getRoomIdByAlias(roomAlias: String, searchOnServer: Boolean, callback: MatrixCallback>): Cancelable {
+ override fun getRoomIdByAlias(roomAlias: String, searchOnServer: Boolean, callback: MatrixCallback>): Cancelable {
return roomIdByAliasTask
.configureWith(GetRoomIdByAliasTask.Params(roomAlias, searchOnServer)) {
this.callback = callback
@@ -154,4 +161,20 @@ internal class DefaultRoomService @Inject constructor(
results.firstOrNull().toOptional()
}
}
+
+ override fun getRoomState(roomId: String, callback: MatrixCallback>) {
+ resolveRoomStateTask
+ .configureWith(ResolveRoomStateTask.Params(roomId)) {
+ this.callback = callback
+ }
+ .executeBy(taskExecutor)
+ }
+
+ override fun peekRoom(roomIdOrAlias: String, callback: MatrixCallback) {
+ peekRoomTask
+ .configureWith(PeekRoomTask.Params(roomIdOrAlias)) {
+ this.callback = callback
+ }
+ .executeBy(taskExecutor)
+ }
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt
index 955a251b52..aa92c1cb3b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt
@@ -183,7 +183,7 @@ internal interface RoomAPI {
@Body body: ThreePidInviteBody): Call
/**
- * Send a generic state events
+ * Send a generic state event
*
* @param roomId the room id.
* @param stateEventType the state event type
@@ -195,7 +195,7 @@ internal interface RoomAPI {
@Body params: JsonDict): Call
/**
- * Send a generic state events
+ * Send a generic state event
*
* @param roomId the room id.
* @param stateEventType the state event type
@@ -208,6 +208,13 @@ internal interface RoomAPI {
@Path("state_key") stateKey: String,
@Body params: JsonDict): Call
+ /**
+ * Get state events of a room
+ * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-state
+ */
+ @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/state")
+ fun getRoomState(@Path("roomId") roomId: String) : Call>
+
/**
* Send a relation event to a room.
*
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt
index 3a94396a61..92f4ea2aea 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt
@@ -57,6 +57,10 @@ import org.matrix.android.sdk.internal.session.room.membership.leaving.DefaultLe
import org.matrix.android.sdk.internal.session.room.membership.leaving.LeaveRoomTask
import org.matrix.android.sdk.internal.session.room.membership.threepid.DefaultInviteThreePidTask
import org.matrix.android.sdk.internal.session.room.membership.threepid.InviteThreePidTask
+import org.matrix.android.sdk.internal.session.room.peeking.DefaultPeekRoomTask
+import org.matrix.android.sdk.internal.session.room.peeking.DefaultResolveRoomStateTask
+import org.matrix.android.sdk.internal.session.room.peeking.PeekRoomTask
+import org.matrix.android.sdk.internal.session.room.peeking.ResolveRoomStateTask
import org.matrix.android.sdk.internal.session.room.read.DefaultMarkAllRoomsReadTask
import org.matrix.android.sdk.internal.session.room.read.DefaultSetReadMarkersTask
import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
@@ -223,4 +227,10 @@ internal abstract class RoomModule {
@Binds
abstract fun bindDeleteTagFromRoomTask(task: DefaultDeleteTagFromRoomTask): DeleteTagFromRoomTask
+
+ @Binds
+ abstract fun bindResolveRoomStateTask(task: DefaultResolveRoomStateTask): ResolveRoomStateTask
+
+ @Binds
+ abstract fun bindPeekRoomTask(task: DefaultPeekRoomTask): PeekRoomTask
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt
index 3c47ee6ef0..543d605707 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt
@@ -29,7 +29,7 @@ import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
import org.matrix.android.sdk.internal.task.Task
import javax.inject.Inject
-internal interface GetRoomIdByAliasTask : Task> {
+internal interface GetRoomIdByAliasTask : Task> {
data class Params(
val roomAlias: String,
val searchOnServer: Boolean
@@ -42,21 +42,21 @@ internal class DefaultGetRoomIdByAliasTask @Inject constructor(
private val eventBus: EventBus
) : GetRoomIdByAliasTask {
- override suspend fun execute(params: GetRoomIdByAliasTask.Params): Optional {
- var roomId = Realm.getInstance(monarchy.realmConfiguration).use {
+ override suspend fun execute(params: GetRoomIdByAliasTask.Params): Optional {
+ val roomId = Realm.getInstance(monarchy.realmConfiguration).use {
RoomSummaryEntity.findByAlias(it, params.roomAlias)?.roomId
}
return if (roomId != null) {
- Optional.from(roomId)
+ Optional.from(RoomAliasDescription(roomId))
} else if (!params.searchOnServer) {
- Optional.from(null)
+ Optional.from(null)
} else {
- roomId = tryOrNull("## Failed to get roomId from alias") {
+ val description = tryOrNull("## Failed to get roomId from alias") {
executeRequest(eventBus) {
apiCall = directoryAPI.getRoomIdByAlias(params.roomAlias)
}
- }?.roomId
- Optional.from(roomId)
+ }
+ Optional.from(description)
}
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasDescription.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasDescription.kt
index ada3839fa0..d1f93c50be 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasDescription.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasDescription.kt
@@ -20,7 +20,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
-internal data class RoomAliasDescription(
+data class RoomAliasDescription(
/**
* The room ID for this alias.
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt
index 79ff9db087..fb840b4eb3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt
@@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
import org.matrix.android.sdk.api.session.identity.toMedium
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
+import org.matrix.android.sdk.api.util.MimeTypes
import org.matrix.android.sdk.internal.crypto.DeviceListManager
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.di.AuthenticatedIdentity
@@ -96,7 +97,7 @@ internal class CreateRoomBodyBuilder @Inject constructor(
fileUploader.uploadFromUri(
uri = avatarUri,
filename = UUID.randomUUID().toString(),
- mimeType = "image/jpeg")
+ mimeType = MimeTypes.Jpeg)
}
?.let { response ->
Event(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/PeekRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/PeekRoomTask.kt
new file mode 100644
index 0000000000..5a82d74537
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/PeekRoomTask.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.session.room.peeking
+
+import org.matrix.android.sdk.api.MatrixPatterns
+import org.matrix.android.sdk.api.session.events.model.EventType
+import org.matrix.android.sdk.api.session.events.model.toModel
+import org.matrix.android.sdk.api.session.room.model.RoomAvatarContent
+import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
+import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
+import org.matrix.android.sdk.api.session.room.model.RoomNameContent
+import org.matrix.android.sdk.api.session.room.model.RoomTopicContent
+import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsFilter
+import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
+import org.matrix.android.sdk.api.session.room.peeking.PeekResult
+import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask
+import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
+import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
+import org.matrix.android.sdk.internal.task.Task
+import javax.inject.Inject
+
+internal interface PeekRoomTask : Task {
+ data class Params(
+ val roomIdOrAlias: String
+ )
+}
+
+internal class DefaultPeekRoomTask @Inject constructor(
+ private val getRoomIdByAliasTask: GetRoomIdByAliasTask,
+ private val getRoomDirectoryVisibilityTask: GetRoomDirectoryVisibilityTask,
+ private val getPublicRoomTask: GetPublicRoomTask,
+ private val resolveRoomStateTask: ResolveRoomStateTask
+) : PeekRoomTask {
+
+ override suspend fun execute(params: PeekRoomTask.Params): PeekResult {
+ val roomId: String
+ val serverList: List
+ val isAlias = MatrixPatterns.isRoomAlias(params.roomIdOrAlias)
+ if (isAlias) {
+ // get alias description
+ val aliasDescription = getRoomIdByAliasTask
+ .execute(GetRoomIdByAliasTask.Params(params.roomIdOrAlias, true))
+ .getOrNull()
+ ?: return PeekResult.UnknownAlias
+
+ roomId = aliasDescription.roomId
+ serverList = aliasDescription.servers
+ } else {
+ roomId = params.roomIdOrAlias
+ serverList = emptyList()
+ }
+
+ // Is it a public room?
+ val publicRepoResult = when (getRoomDirectoryVisibilityTask.execute(GetRoomDirectoryVisibilityTask.Params(roomId))) {
+ RoomDirectoryVisibility.PRIVATE -> {
+ // We cannot resolve this room :/
+ null
+ }
+ RoomDirectoryVisibility.PUBLIC -> {
+ // Try to find it in directory
+ val filter = if (isAlias) PublicRoomsFilter(searchTerm = params.roomIdOrAlias.substring(1))
+ else null
+
+ getPublicRoomTask.execute(GetPublicRoomTask.Params(
+ server = serverList.firstOrNull(),
+ publicRoomsParams = PublicRoomsParams(
+ filter = filter,
+ limit = 20.takeIf { filter != null } ?: 100
+ )
+ )).chunk?.firstOrNull { it.roomId == roomId }
+ }
+ }
+
+ if (publicRepoResult != null) {
+ return PeekResult.Success(
+ roomId = roomId,
+ alias = publicRepoResult.getPrimaryAlias() ?: params.roomIdOrAlias.takeIf { isAlias },
+ avatarUrl = publicRepoResult.avatarUrl,
+ name = publicRepoResult.name,
+ topic = publicRepoResult.topic,
+ numJoinedMembers = publicRepoResult.numJoinedMembers,
+ viaServers = serverList
+ )
+ }
+
+ // mm... try to peek state ? maybe the room is not public but yet allow guest to get events?
+ // this could be slow
+ try {
+ val stateEvents = resolveRoomStateTask.execute(ResolveRoomStateTask.Params(roomId))
+ val name = stateEvents
+ .lastOrNull { it.type == EventType.STATE_ROOM_NAME && it.stateKey == "" }
+ ?.let { it.content?.toModel()?.name }
+
+ val topic = stateEvents
+ .lastOrNull { it.type == EventType.STATE_ROOM_TOPIC && it.stateKey == "" }
+ ?.let { it.content?.toModel()?.topic }
+
+ val avatarUrl = stateEvents
+ .lastOrNull { it.type == EventType.STATE_ROOM_AVATAR }
+ ?.let { it.content?.toModel()?.avatarUrl }
+
+ val alias = stateEvents
+ .lastOrNull { it.type == EventType.STATE_ROOM_CANONICAL_ALIAS }
+ ?.let { it.content?.toModel()?.canonicalAlias }
+
+ // not sure if it's the right way to do that :/
+ val memberCount = stateEvents
+ .filter { it.type == EventType.STATE_ROOM_MEMBER && it.stateKey?.isNotEmpty() == true }
+ .distinctBy { it.stateKey }
+ .count()
+
+ return PeekResult.Success(
+ roomId = roomId,
+ alias = alias,
+ avatarUrl = avatarUrl,
+ name = name,
+ topic = topic,
+ numJoinedMembers = memberCount,
+ viaServers = serverList
+ )
+ } catch (failure: Throwable) {
+ // Would be M_FORBIDDEN if cannot peek :/
+ // User XXX not in room !XXX, and room previews are disabled
+ return PeekResult.PeekingNotAllowed(
+ roomId = roomId,
+ alias = params.roomIdOrAlias.takeIf { isAlias },
+ viaServers = serverList
+ )
+ }
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/ResolveRoomStateTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/ResolveRoomStateTask.kt
new file mode 100644
index 0000000000..03ea2408f0
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/peeking/ResolveRoomStateTask.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.session.room.peeking
+
+import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.internal.network.executeRequest
+import org.matrix.android.sdk.internal.session.room.RoomAPI
+import org.matrix.android.sdk.internal.task.Task
+import javax.inject.Inject
+
+internal interface ResolveRoomStateTask : Task> {
+ data class Params(
+ val roomId: String
+ )
+}
+
+internal class DefaultResolveRoomStateTask @Inject constructor(
+ private val roomAPI: RoomAPI,
+ private val eventBus: EventBus
+) : ResolveRoomStateTask {
+
+ override suspend fun execute(params: ResolveRoomStateTask.Params): List {
+ return executeRequest(eventBus) {
+ apiCall = roomAPI.getRoomState(params.roomId)
+ }
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
index b13ce15da6..8828f3dfed 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
@@ -177,7 +177,7 @@ internal class DefaultSendService @AssistedInject constructor(
val attachmentData = ContentAttachmentData(
size = messageContent.info!!.size,
mimeType = messageContent.info.mimeType!!,
- name = messageContent.body,
+ name = messageContent.getFileName(),
queryUri = Uri.parse(messageContent.url),
type = ContentAttachmentData.Type.FILE
)
@@ -210,6 +210,8 @@ internal class DefaultSendService @AssistedInject constructor(
override fun cancelSend(eventId: String) {
cancelSendTracker.markLocalEchoForCancel(eventId, roomId)
+ // This is maybe the current task, so cancel it too
+ eventSenderProcessor.cancel(eventId, roomId)
taskExecutor.executorScope.launch {
localEchoRepository.deleteFailedEcho(roomId, eventId)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt
index 62e225c624..5014d94558 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt
@@ -16,6 +16,7 @@
package org.matrix.android.sdk.internal.session.room.send.queue
+import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.auth.data.SessionParams
@@ -106,17 +107,21 @@ internal class EventSenderProcessor @Inject constructor(
// non blocking add to queue
sendingQueue.add(task)
markAsManaged(task)
- return object : Cancelable {
- override fun cancel() {
- task.cancel()
- }
- }
+ return task
+ }
+
+ fun cancel(eventId: String, roomId: String) {
+ (currentTask as? SendEventQueuedTask)
+ ?.takeIf { it -> it.event.eventId == eventId && it.event.roomId == roomId }
+ ?.cancel()
}
companion object {
private const val RETRY_WAIT_TIME_MS = 10_000L
}
+ private var currentTask: QueuedTask? = null
+
private var sendingQueue = LinkedBlockingQueue()
private var networkAvailableLock = Object()
@@ -129,6 +134,7 @@ internal class EventSenderProcessor @Inject constructor(
while (!isInterrupted) {
Timber.v("## SendThread wait for task to process")
val task = sendingQueue.take()
+ .also { currentTask = it }
Timber.v("## SendThread Found task to process $task")
if (task.isCancelled()) {
@@ -183,6 +189,10 @@ internal class EventSenderProcessor @Inject constructor(
task.onTaskFailed()
throw InterruptedException()
}
+ exception is CancellationException -> {
+ Timber.v("## SendThread task has been cancelled")
+ break@retryLoop
+ }
else -> {
Timber.v("## SendThread retryLoop Un-Retryable error, try next task")
// this task is in error, check next one?
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt
index e69c65ec4c..dfbac347d9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt
@@ -17,7 +17,6 @@
package org.matrix.android.sdk.internal.session.room.send.queue
import android.content.Context
-import org.matrix.android.sdk.api.auth.data.sessionId
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.room.send.SendState
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt
index bccbc97ff4..9a7fcd8d91 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt
@@ -16,14 +16,26 @@
package org.matrix.android.sdk.internal.session.room.send.queue
-abstract class QueuedTask {
+import org.matrix.android.sdk.api.util.Cancelable
+
+abstract class QueuedTask : Cancelable {
var retryCount = 0
- abstract suspend fun execute()
+ private var hasBeenCancelled: Boolean = false
+
+ suspend fun execute() {
+ if (!isCancelled()) {
+ doExecute()
+ }
+ }
+
+ abstract suspend fun doExecute()
abstract fun onTaskFailed()
- abstract fun isCancelled() : Boolean
+ open fun isCancelled() = hasBeenCancelled
- abstract fun cancel()
+ final override fun cancel() {
+ hasBeenCancelled = true
+ }
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt
index a3c19a1f7c..8e7ba2f155 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt
@@ -22,20 +22,18 @@ import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
internal class RedactQueuedTask(
- val toRedactEventId: String,
+ private val toRedactEventId: String,
val redactionLocalEchoId: String,
- val roomId: String,
- val reason: String?,
- val redactEventTask: RedactEventTask,
- val localEchoRepository: LocalEchoRepository,
- val cancelSendTracker: CancelSendTracker
+ private val roomId: String,
+ private val reason: String?,
+ private val redactEventTask: RedactEventTask,
+ private val localEchoRepository: LocalEchoRepository,
+ private val cancelSendTracker: CancelSendTracker
) : QueuedTask() {
- private var _isCancelled: Boolean = false
+ override fun toString() = "[RedactQueuedTask $redactionLocalEchoId]"
- override fun toString() = "[RedactEventRunnableTask $redactionLocalEchoId]"
-
- override suspend fun execute() {
+ override suspend fun doExecute() {
redactEventTask.execute(RedactEventTask.Params(redactionLocalEchoId, roomId, toRedactEventId, reason))
}
@@ -44,10 +42,6 @@ internal class RedactQueuedTask(
}
override fun isCancelled(): Boolean {
- return _isCancelled || cancelSendTracker.isCancelRequestedFor(redactionLocalEchoId, roomId)
- }
-
- override fun cancel() {
- _isCancelled = true
+ return super.isCancelled() || cancelSendTracker.isCancelRequestedFor(redactionLocalEchoId, roomId)
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt
index 21a4145a9d..ea097082c7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt
@@ -33,11 +33,9 @@ internal class SendEventQueuedTask(
val cancelSendTracker: CancelSendTracker
) : QueuedTask() {
- private var _isCancelled: Boolean = false
+ override fun toString() = "[SendEventQueuedTask ${event.eventId}]"
- override fun toString() = "[SendEventRunnableTask ${event.eventId}]"
-
- override suspend fun execute() {
+ override suspend fun doExecute() {
sendEventTask.execute(SendEventTask.Params(event, encrypt))
}
@@ -56,10 +54,6 @@ internal class SendEventQueuedTask(
}
override fun isCancelled(): Boolean {
- return _isCancelled || cancelSendTracker.isCancelRequestedFor(event.eventId, event.roomId)
- }
-
- override fun cancel() {
- _isCancelled = true
+ return super.isCancelled() || cancelSendTracker.isCancelRequestedFor(event.eventId, event.roomId)
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
index 6015d945c4..b546584450 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
@@ -20,7 +20,6 @@ import android.net.Uri
import androidx.lifecycle.LiveData
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
-import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
@@ -32,22 +31,15 @@ import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
import org.matrix.android.sdk.api.session.room.state.StateService
-import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.api.util.MimeTypes
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.session.content.FileUploader
import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask
-import org.matrix.android.sdk.internal.task.TaskExecutor
-import org.matrix.android.sdk.internal.task.configureWith
-import org.matrix.android.sdk.internal.task.launchToCallback
-import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
-import org.matrix.android.sdk.internal.util.awaitCallback
internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String,
private val stateEventDataSource: StateEventDataSource,
- private val taskExecutor: TaskExecutor,
private val sendStateTask: SendStateTask,
- private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val fileUploader: FileUploader,
private val addRoomAliasTask: AddRoomAliasTask
) : StateService {
@@ -73,45 +65,38 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
return stateEventDataSource.getStateEventsLive(roomId, eventTypes, stateKey)
}
- override fun sendStateEvent(
+ override suspend fun sendStateEvent(
eventType: String,
stateKey: String?,
- body: JsonDict,
- callback: MatrixCallback
- ): Cancelable {
+ body: JsonDict
+ ) {
val params = SendStateTask.Params(
roomId = roomId,
stateKey = stateKey,
eventType = eventType,
body = body
)
- return sendStateTask
- .configureWith(params) {
- this.callback = callback
- }
- .executeBy(taskExecutor)
+ sendStateTask.execute(params)
}
- override fun updateTopic(topic: String, callback: MatrixCallback): Cancelable {
- return sendStateEvent(
+ override suspend fun updateTopic(topic: String) {
+ sendStateEvent(
eventType = EventType.STATE_ROOM_TOPIC,
body = mapOf("topic" to topic),
- callback = callback,
stateKey = null
)
}
- override fun updateName(name: String, callback: MatrixCallback): Cancelable {
- return sendStateEvent(
+ override suspend fun updateName(name: String) {
+ sendStateEvent(
eventType = EventType.STATE_ROOM_NAME,
body = mapOf("name" to name),
- callback = callback,
stateKey = null
)
}
- override fun updateCanonicalAlias(alias: String?, altAliases: List, callback: MatrixCallback): Cancelable {
- return sendStateEvent(
+ override suspend fun updateCanonicalAlias(alias: String?, altAliases: List) {
+ sendStateEvent(
eventType = EventType.STATE_ROOM_CANONICAL_ALIAS,
body = RoomCanonicalAliasContent(
canonicalAlias = alias,
@@ -123,64 +108,48 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
// Sort for the cleanup
.sorted()
).toContent(),
- callback = callback,
stateKey = null
)
}
- override fun updateHistoryReadability(readability: RoomHistoryVisibility, callback: MatrixCallback): Cancelable {
- return sendStateEvent(
+ override suspend fun updateHistoryReadability(readability: RoomHistoryVisibility) {
+ sendStateEvent(
eventType = EventType.STATE_ROOM_HISTORY_VISIBILITY,
body = mapOf("history_visibility" to readability),
- callback = callback,
stateKey = null
)
}
- override fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?, callback: MatrixCallback): Cancelable {
- return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
- if (joinRules != null) {
- awaitCallback {
- sendStateEvent(
- eventType = EventType.STATE_ROOM_JOIN_RULES,
- body = RoomJoinRulesContent(joinRules).toContent(),
- callback = it,
- stateKey = null
- )
- }
- }
- if (guestAccess != null) {
- awaitCallback {
- sendStateEvent(
- eventType = EventType.STATE_ROOM_GUEST_ACCESS,
- body = RoomGuestAccessContent(guestAccess).toContent(),
- callback = it,
- stateKey = null
- )
- }
- }
+ override suspend fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?) {
+ if (joinRules != null) {
+ sendStateEvent(
+ eventType = EventType.STATE_ROOM_JOIN_RULES,
+ body = RoomJoinRulesContent(joinRules).toContent(),
+ stateKey = null
+ )
+ }
+ if (guestAccess != null) {
+ sendStateEvent(
+ eventType = EventType.STATE_ROOM_GUEST_ACCESS,
+ body = RoomGuestAccessContent(guestAccess).toContent(),
+ stateKey = null
+ )
}
}
- override fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback): Cancelable {
- return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
- val response = fileUploader.uploadFromUri(avatarUri, fileName, "image/jpeg")
- awaitCallback {
- sendStateEvent(
- eventType = EventType.STATE_ROOM_AVATAR,
- body = mapOf("url" to response.contentUri),
- callback = it,
- stateKey = null
- )
- }
- }
+ override suspend fun updateAvatar(avatarUri: Uri, fileName: String) {
+ val response = fileUploader.uploadFromUri(avatarUri, fileName, MimeTypes.Jpeg)
+ sendStateEvent(
+ eventType = EventType.STATE_ROOM_AVATAR,
+ body = mapOf("url" to response.contentUri),
+ stateKey = null
+ )
}
- override fun deleteAvatar(callback: MatrixCallback): Cancelable {
- return sendStateEvent(
+ override suspend fun deleteAvatar() {
+ sendStateEvent(
eventType = EventType.STATE_ROOM_AVATAR,
body = emptyMap(),
- callback = callback,
stateKey = null
)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryEventsHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryEventsHelper.kt
index a3862b001b..7437a686da 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryEventsHelper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryEventsHelper.kt
@@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.summary
import io.realm.Realm
import org.matrix.android.sdk.api.session.room.summary.RoomSummaryConstants
+import org.matrix.android.sdk.api.session.room.timeline.EventTypeFilter
import org.matrix.android.sdk.api.session.room.timeline.TimelineEventFilters
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
import org.matrix.android.sdk.internal.database.query.latestEvent
@@ -26,7 +27,7 @@ internal object RoomSummaryEventsHelper {
private val previewFilters = TimelineEventFilters(
filterTypes = true,
- allowedTypes = RoomSummaryConstants.PREVIEWABLE_TYPES,
+ allowedTypes = RoomSummaryConstants.PREVIEWABLE_TYPES.map { EventTypeFilter(eventType = it, stateKey = null) },
filterUseless = true,
filterRedacted = false,
filterEdits = true
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
index 995a21aa23..86b0497bd0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
@@ -784,19 +784,20 @@ internal class DefaultTimeline(
}
private fun List.filterEventsWithSettings(): List {
- return filter {
- val filterType = !settings.filters.filterTypes || settings.filters.allowedTypes.contains(it.root.type)
+ return filter { event ->
+ val filterType = !settings.filters.filterTypes
+ || settings.filters.allowedTypes.any { it.eventType == event.root.type && (it.stateKey == null || it.stateKey == event.root.senderId) }
if (!filterType) return@filter false
- val filterEdits = if (settings.filters.filterEdits && it.root.getClearType() == EventType.MESSAGE) {
- val messageContent = it.root.getClearContent().toModel()
+ val filterEdits = if (settings.filters.filterEdits && event.root.getClearType() == EventType.MESSAGE) {
+ val messageContent = event.root.getClearContent().toModel()
messageContent?.relatesTo?.type != RelationType.REPLACE && messageContent?.relatesTo?.type != RelationType.RESPONSE
} else {
true
}
if (!filterEdits) return@filter false
- val filterRedacted = settings.filters.filterRedacted && it.root.isRedacted()
+ val filterRedacted = settings.filters.filterRedacted && event.root.isRedacted()
!filterRedacted
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultGetContextOfEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetContextOfEventTask.kt
similarity index 100%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultGetContextOfEventTask.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetContextOfEventTask.kt
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultPaginationTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationTask.kt
similarity index 100%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultPaginationTask.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationTask.kt
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt
index 3dcc5e21b1..fa517bebf2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt
@@ -151,8 +151,25 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu
beginGroup()
var needOr = false
if (settings.filters.filterTypes) {
- val allowedTypes = settings.filters.allowedTypes.toTypedArray()
- not().`in`("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", allowedTypes)
+ beginGroup()
+ // Events: A, B, C, D, (E and S1), F, G, (H and S1), I
+ // Allowed: A, B, C, (E and S1), G, (H and S2)
+ // Result: D, F, H, I
+ settings.filters.allowedTypes.forEachIndexed { index, filter ->
+ if (filter.stateKey == null) {
+ notEqualTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", filter.eventType)
+ } else {
+ beginGroup()
+ notEqualTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", filter.eventType)
+ or()
+ notEqualTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.STATE_KEY}", filter.stateKey)
+ endGroup()
+ }
+ if (index != settings.filters.allowedTypes.size - 1) {
+ and()
+ }
+ }
+ endGroup()
needOr = true
}
if (settings.filters.filterUseless) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
index 74cba5e796..424c24663c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
@@ -25,7 +25,6 @@ import org.matrix.android.sdk.api.failure.isTokenError
import org.matrix.android.sdk.api.session.sync.SyncState
import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker
import org.matrix.android.sdk.internal.session.sync.SyncTask
-import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.util.Debouncer
import org.matrix.android.sdk.internal.util.createUIHandler
@@ -50,14 +49,13 @@ private const val RETRY_WAIT_TIME_MS = 10_000L
private const val DEFAULT_LONG_POOL_TIMEOUT = 30_000L
internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
- private val typingUsersTracker: DefaultTypingUsersTracker,
private val networkConnectivityChecker: NetworkConnectivityChecker,
private val backgroundDetectionObserver: BackgroundDetectionObserver,
private val activeCallHandler: ActiveCallHandler
) : Thread("SyncThread"), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
private var state: SyncState = SyncState.Idle
- private var liveState = MutableLiveData(state)
+ private var liveState = MutableLiveData(state)
private val lock = Object()
private val syncScope = CoroutineScope(SupervisorJob())
private val debouncer = Debouncer(createUIHandler())
@@ -231,7 +229,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
return
}
state = newState
- debouncer.debounce("post_state", Runnable {
+ debouncer.debounce("post_state", {
liveState.value = newState
}, 150)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/FileSaver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/FileSaver.kt
index 4dc54d3b19..fb5e3a5774 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/FileSaver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/FileSaver.kt
@@ -25,6 +25,9 @@ import java.io.InputStream
*/
@WorkerThread
fun writeToFile(inputStream: InputStream, outputFile: File) {
+ // Ensure the parent folder exists, else it will crash
+ outputFile.parentFile?.mkdirs()
+
outputFile.outputStream().use {
inputStream.copyTo(it)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LruCache.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LruCache.kt
new file mode 100644
index 0000000000..0998601db6
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LruCache.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
+ *
+ * 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.android.sdk.internal.util
+
+import androidx.collection.LruCache
+
+@Suppress("NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER")
+internal inline fun LruCache.getOrPut(key: K, defaultValue: () -> V): V {
+ return get(key) ?: defaultValue().also { put(key, it) }
+}
diff --git a/matrix-sdk-android/src/main/res/values-bg/strings_sas.xml b/matrix-sdk-android/src/main/res/values-bg/strings_sas.xml
new file mode 100644
index 0000000000..e2ee9faefc
--- /dev/null
+++ b/matrix-sdk-android/src/main/res/values-bg/strings_sas.xml
@@ -0,0 +1,68 @@
+
+
+
+ Куче
+ Котка
+ Лъв
+ Кон
+ Еднорог
+ Прасе
+ Слон
+ Заек
+ Панда
+ Петел
+ Пингвин
+ Костенурка
+ Риба
+ Октопод
+ Пеперуда
+ Цвете
+ Дърво
+ Кактус
+ Гъба
+ Глобус
+ Луна
+ Облак
+ Огън
+ Банан
+ Ябълка
+ Ягода
+ Царевица
+ Пица
+ Торта
+ Сърце
+ Усмивка
+ Робот
+ Шапка
+ Очила
+ Гаечен ключ
+ Дядо Коледа
+ Палец нагоре
+ Чадър
+ Пясъчен часовник
+ Часовник
+ Подарък
+ Лампа
+ Книга
+ Молив
+ Кламер
+ Ножици
+ Катинар
+ Ключ
+ Чук
+ Телефон
+ Флаг
+ Влак
+ Колело
+ Самолет
+ Ракета
+ Трофей
+ Топка
+ Китара
+ Тромпет
+ Звънец
+ Котва
+ Слушалки
+ Папка
+ Кабърче
+
diff --git a/matrix-sdk-android/src/main/res/values-ca/strings_sas.xml b/matrix-sdk-android/src/main/res/values-ca/strings_sas.xml
new file mode 100644
index 0000000000..acc0dcbc72
--- /dev/null
+++ b/matrix-sdk-android/src/main/res/values-ca/strings_sas.xml
@@ -0,0 +1,68 @@
+
+
+
+ Gos
+ Gat
+ Lleó
+ Cavall
+ Unicorn
+ Porc
+ Elefant
+ Conill
+ Panda
+ Gall
+ Pingüí
+ Tortuga
+ Peix
+ Pop
+ Papallona
+ Flor
+ Arbre
+ Cactus
+ Bolet
+ Globus terraqüi
+ Lluna
+ Núvol
+ Foc
+ Plàtan
+ Poma
+ Maduixa
+ Blat de moro
+ Pizza
+ Pastís
+ Cor
+ Somrient
+ Robot
+ Barret
+ Ulleres
+ Clau anglesa
+ Pare Noél
+ Polzes amunt
+ Paraigües
+ Rellotge de sorra
+ Rellotge
+ Regal
+ Bombeta
+ Llibre
+ Llapis
+ Clip
+ Tisores
+ Cadenat
+ Clau
+ Martell
+ Telèfon
+ Bandera
+ Tren
+ Bicicleta
+ Avió
+ Coet
+ Trofeu
+ Pilota
+ Guitarra
+ Trompeta
+ Campana
+ Àncora
+ Auriculars
+ Carpeta
+ Xinxeta
+
diff --git a/matrix-sdk-android/src/main/res/values-cs/strings.xml b/matrix-sdk-android/src/main/res/values-cs/strings.xml
index ebf7590596..50dea12b09 100644
--- a/matrix-sdk-android/src/main/res/values-cs/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-cs/strings.xml
@@ -218,4 +218,28 @@
%1$s vstoupili
Založili jste diskusi
%1$s založil diskusi
+ Prázdná místnost (byla %s)
+
+ - %1$s, %2$s, %3$s a %4$d další
+ - %1$s, %2$s, %3$s a %4$d další
+ - %1$s, %2$s, %3$s a %4$d dalších
+
+ %1$s, %2$s, %3$s a %4$s
+ %1$s, %2$s a %3$s
+ 🎉 Účast všech serverů je zakázána! Tuto místnost již nelze použít.
+ Beze změny.
+ • Servery shodující se doslovně s IP jsou nyní zakázány.
+ • Servery shodující se doslovně s IP jsou nyní povoleny.
+ • Servery shodující se s %s byly odstraněny ze seznamu povolených.
+ • Servery shodující se s %s jsou nyní povoleny.
+ • Servery shodující se s %s byly odstraněny ze seznamu zakázaných.
+ • Servery shodující se s %s jsou nyní zakázány.
+ Změnili jste ACL serveru pro tuto místnost.
+ %s změnili ACL serveru pro tuto místnost.
+ • Server shodující se doslovně s IP je povolen.
+ • Server shodující se doslovně s IP je zakázán.
+ • Server shodující se s %s je povolen.
+ • Server shodující se s %s je zakázán.
+ Nastavili jste ACL serveru pro tuto místnost.
+ %s nastavili ACL serveru pro tuto místnost.
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/res/values-de/strings.xml b/matrix-sdk-android/src/main/res/values-de/strings.xml
index 4c574d578a..bdeeafccb6 100644
--- a/matrix-sdk-android/src/main/res/values-de/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-de/strings.xml
@@ -56,7 +56,7 @@
E-Mail-Adresse
Telefonnummer
- %1$s sandte einen Sticker.
+ %1$s hat einen Sticker gesendet.
Einladung von %s
Raumeinladung
diff --git a/matrix-sdk-android/src/main/res/values-et/strings.xml b/matrix-sdk-android/src/main/res/values-et/strings.xml
index 71ee50f075..957c0b9955 100644
--- a/matrix-sdk-android/src/main/res/values-et/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-et/strings.xml
@@ -213,4 +213,27 @@
%1$s liitus
Sina alustasid vestlust
%1$s alustas vestlust
+ Tühi jututuba (oli %s)
+
+ - %1$s, %2$s, %3$s ja %4$d muu
+ - %1$s, %2$s, %3$s ja %4$d muud
+
+ %1$s, %2$s, %3$s ja %4$s
+ %1$s, %2$s ja %3$s
+ 🎉 Kõikide serverite osalemine on keelatud! Seda jututuba ei saa enam kasutada.
+ Muudatusi ei ole.
+ • Nüüd on keelatud serverid, mille ip-aadress vastab mustrile.
+ • Nüüd on lubatud serverid, mille ip-aadress vastab mustrile.
+ • Server, mille nimes leidub %s, eemaldati lubatud serverite loendist.
+ • Nüüd on lubatud serverid, mille nimes leidub %s.
+ • Server, mille nimes leidub %s eemaldati keeluloendist.
+ • Keelatud on server, mille nimes leidub %s.
+ Sina muutsid selle jututoa jaoks serverite pääsuloendit.
+ %s muutis selle jututoa jaoks serverite pääsuloendit.
+ Sina kirjeldasid selle jututoa jaoks serverite pääsuloendi.
+ %s kirjeldas selle jututoa jaoks serverite pääsuloendi.
+ • Keelatud on serverid, mille ip-aadress vastab mustrile.
+ • Lubatud on serverid, mille ip-aadress vastab mustrile.
+ • Lubatud on serverid, mille nimes leidub %s.
+ • Keelatud on serverid, mille nimes leidub %s.
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/res/values-fa/strings.xml b/matrix-sdk-android/src/main/res/values-fa/strings.xml
index 11a786f5ac..8f8059067e 100644
--- a/matrix-sdk-android/src/main/res/values-fa/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-fa/strings.xml
@@ -213,4 +213,9 @@
%1$s پیوست
گفتوگو را ایجاد کردید
%1$s گفتوگو را ایجاد کرد
+
+ - %1$s، %2$s، %3$s و %4$d نفر دیگر
+ - %1$s، %2$s، %3$s و %4$d نفر دیگر
+
+ %1$s، %2$s و %3$s
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/res/values-fi/strings.xml b/matrix-sdk-android/src/main/res/values-fi/strings.xml
index fccd22d3b6..1e3788476f 100644
--- a/matrix-sdk-android/src/main/res/values-fi/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-fi/strings.xml
@@ -1,7 +1,6 @@
-
+
%1$s lähetti kuvan.
-
Käyttäjän %s kutsu
%1$s kutsui käyttäjän %2$s
%1$s kutsui sinut
@@ -29,11 +28,9 @@
kaikki.
tuntematon (%s).
%1$s otti käyttöön osapuolten välisen salauksen (%2$s)
-
%1$s lähetti VoIP-konferenssipyynnön
VoIP-konferenssi alkoi
VoIP-konferenssi päättyi
-
(myös kuva vaihdettiin)
%1$s poisti huoneen nimen
%1$s poisti huoneen aiheen
@@ -42,44 +39,31 @@
%1$s hyväksyi kutsun käyttäjän %2$s puolesta
** Salauksen purku epäonnistui: %s **
Lähettäjän laite ei ole lähettänyt avaimia tähän viestiin.
-
Viestin lähetys epäonnistui
-
Kuvan lataaminen epäonnistui
-
Verkkovirhe
Matrix-virhe
-
Tällä hetkellä ei ole mahdollista liittyä uudelleen tyhjään huoneeseen.
-
Salattu viesti
-
Sähköpostiosoite
Puhelinnumero
-
-
Takaisinveto epäonnistui
%1$s: %2$s
-
- Kutsu käyttäjältä %s
+ Kutsu käyttäjältä %s
+
Huonekutsu
%1$s ja %2$s
Tyhjä huone
-
-
%1$s lähetti tarran.
-
- %1$s ja yksi muu
- %1$s ja %2$d muuta
-
Viesti poistettu
%1$s poisti viestin
Viesti poistettu [syy: %1$s]
%1$s poisti viestin [syy: %2$s]
-
Alkusynkronointi:
\nTuodaan tiliä…
Alkusynkronointi:
@@ -96,12 +80,9 @@
\nTuodaan yhteisöjä
Alkusynkronointi:
\nTuodaan tilin tietoja
-
%s päivitti tämän huoneen.
-
Lähetetään viestiä…
Tyhjennä lähetysjono
-
%1$s veti takaisin käyttäjän %2$s liittymiskutsun huoneeseen
Henkilön %1$s kutsu. Syy: %2$s
%1$s kutsui henkilön %2$s. Syy: %3$s
@@ -116,28 +97,117 @@
%1$s kumosi kutsun liittyä huoneeseen käyttäjälle %2$s. Syy: %3$s
%1$s hyväksyi kutsun liityäkseen huoneeseen %2$s. Syy: %3$s
%1$s veti takaisin käyttäjän %2$s kutsun. Syy: %3$s
-
- %1$s lisäsi tälle huoneelle osoitteen %2$s.
- %1$s lisäsi tälle huoneelle osoitteet %2$s.
-
- %1$s poisti tältä huoneelta osoitteen %2$s.
- %1$s poisti tältä huoneelta osoitteet %3$s.
-
%1$s lisäsi tälle huoneelle osoitteen %2$s ja poisti osoitteen %3$s.
-
%1$s asetti tämän huoneen pääosoitteeksi %2$s.
%1$s poisti tämän huoneen pääosoitteen.
-
%1$s salli vieraiden liittyä huoneeseen.
%1$s esti vieraita liittymästä huoneeseen.
-
%1$s laittoi päälle osapuolten välisen salauksen.
%1$s laittoi päälle osapuolisten välisen salauksen (tuntematon algoritmi %2$s).
-
%s haluaa varmentaa salausavaimesi, mutta asiakasohjelmasi ei tue keskustelun aikana tapahtuvaa avainten varmennusta. Joudut käyttämään perinteistä varmennustapaa.
-
-
+ Hyväksyit käyttäjän %1$s kutsun. Syy: %2$s
+ Peruutit kutsun liittyä huoneeseen käyttäjältä %1$s. Syy: %2$s
+ Lähetit kutsun liittyä huoneeseen käyttäjälle %1$s. Syy: %2$s
+ Estit käyttäjän %1$s. Syy: %2$s
+ Peruutit eston %1$s. Syy: %2$s
+ Poistit käyttäjän %1$s. Syy: %2$s
+ Hylkäsit kutsun. Syy: %1$s
+ Lähdit. Syy: %1$s
+ %1$s lähti. Syy: %2$s
+ Poistuit huoneesta. Syy: %1$s
+ Liityit. Syy: %1$s
+ %1$s liittyi. Syy: %2$s
+ Liityit ryhmään. Syy: %1$s
+ Kutsuit %1$s. Syy: %2$s
+ Kutsusi. Syy: %1$s
+ Tyhjä huone (oli %s)
+ %1$s, %2$s, %3$s ja %4$s
+ %1$s, %2$s ja %3$s
+ Mukautettu
+ Mukautettu (%1$d)
+ Oletus
+ Valvoja
+ Ylläpitäjä
+ %1$s muutti %2$s sovelmaa
+ Poistit %1$s sovelman
+ %1$s poisti %2$s sovelman
+ Lisäsit %1$s sovelman
+ %1$s lisäsi %2$s sovelman
+ Muutit %1$s sovelmaa
+ Hyväksyit kutsun henkilölle %1$s
+ Peruutit kutsun henkilöltä %1$s
+ %1$s peruutti kutsun henkilöltä %2$s
+ Peruutit henkilön %1$s kutsun liittyä ryhmään
+ Kutsuit %1$s
+ %1$s kutsui %2$s
+ Lähetit henkilölle %1$s kutsun liittyä huoneeseen
+ Päivitit profiilisi %1$s
+ Poistit huoneen profiilikuvan
+ %1$s poisti huoneen profiilikuvan
+ Poistit huoneen aiheen
+ Poistit huoneen nimen
+ Pyysit ryhmäpuhelua
+ 🎉 Kaikki palvelimet on estetty osallistumasta! Tätä huonetta ei voi enää käyttää.
+ Ei muutosta.
+ • Palvelimet jotka %s poistettiin estolistalta.
+ • Palvelimen haku %s on nyt kielletty.
+ • Palvelimen haku %s on sallittu.
+ • Palvelimen haku %s on kielletty.
+ %1$s on estänyt vieraita liittymästä huoneeseen.
+ Estit vieraita liittymästä huoneeseen.
+ Annoit vieraille luvan liittyä huoneeseen.
+ Annoit vieraille luvan liittyä tänne.
+ %1$s on antanut vieraille luvan liittyä tänne.
+ Poistit tämän huoneen pääosoitteen.
+ Otit käyttöön päästä päähän -salauksen.
+ Olet estänyt vieraiden liittymisen huoneeseen.
+ Otit päästä päähän -salauksen käyttöön (tuntematon algoritmi %1$s).
+ Päivitit tässä.
+ %s päivitti täällä.
+ Päivitit tämän huoneen.
+ Otit päästä päähän -salauksen käyttöön (%1$s)
+ Teit tulevista viesteistä näkyviä käyttäjälle %1$s
+ %1$s teki tulevista viesteistä näkyviä käyttäjälle %2$s
+ Teit tulevan huonehistorian näkyväksi %1$s
+ Lopetit puhelun.
+ Vastasit puheluun.
+ Lähetit tietoja puhelun valmistelemiseksi.
+ %s lähetti tietoja puhelun valmistelemiseksi.
+ Aloitit äänipuhelun.
+ Aloitit videopuhelun.
+ Vaihdoit huoneen nimeksi: %1$s
+ Vaihdoit huoneen profiilikuvaa
+ %1$s muutti huoneen profiilikuvaa
+ Vaihdoit aiheen: %1$s
+ Poistit nimimerkkisi (se oli %1$s)
+ Vaihdoit nimimerkkisi %1$s nimeen %2$s
+ Asetit nimimerkiksesi %1$s
+ Kutsusi
+ Vaihdoit profiilikuvaasi
+ Peruutit %1$sn kutsun
+ Estit %1$s
+ Poistit eston %1$s
+ Poistit %1$s
+ Hylkäsit kutsun
+ Poistuit huoneesta
+ %1$s poistui huoneesta
+ Poistuit huoneesta
+ Liityit
+ %1$s liittyi
+ Liityit huoneeseen
+ Kutsuit %1$s
+ Loit keskustelun
+ %1$s loi keskustelun
+ Loit huoneen
+ %1$s loi huoneen
+ Lähetit tarran.
+ Lähetit kuvan.
+
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/res/values-fr/strings.xml b/matrix-sdk-android/src/main/res/values-fr/strings.xml
index 9c5fa7d3b1..f49c54a8ba 100644
--- a/matrix-sdk-android/src/main/res/values-fr/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-fr/strings.xml
@@ -213,4 +213,27 @@
Vous avez exclus %1$s
Vous avez révoqué l\'exclusion de %1$s
Vous avez expulsé %1$s. Raison : %2$s
+ Salon vide (était %s)
+
+ - %1$s, %2$s, %3$s et %4$d autre
+ - %1$s, %2$s, %3$s et %4$d autres
+
+ %1$s, %2$s, %3$s et %4$s
+ %1$s, %2$s et %3$s
+ 🎉 Tous les serveurs sont interdits de participer ! Ce salon ne peut plus être utilisé.
+ Aucun changement.
+ • Les serveurs correspondant à des IP littérales sont maintenant interdits.
+ • Les serveurs correspondant à %s sont interdits.
+ • Les serveurs correspondants à des IP littérales sont interdites.
+ • Les serveurs correspondants à des IP littérales sont autorisés.
+ • Les serveurs correspondants à des IP littérales sont maintenant autorisées.
+ • Les serveurs correspondant à %s sont supprimés de la liste autorisée.
+ • les serveur correspondant à %s sont maintenant autorisés.
+ • Les serveurs correspondant à %s étaient supprimés de la liste des interdits.
+ • Les serveurs correspondant à %s sont maintenant interdits.
+ Vous avez changé les droits ACL du serveur pour ce salon.
+ %s a changé les droits ACL du serveur pour ce salon.
+ • Les serveurs correspondant à %s sont autorisés.
+ Vous avez paramétré les ACL pour ce salon.
+ %s paramètre les autorisations étendues (ACL) du serveur pour ce salon.
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/res/values-it/strings.xml b/matrix-sdk-android/src/main/res/values-it/strings.xml
index 5eab8c57df..c1a5cf85cb 100644
--- a/matrix-sdk-android/src/main/res/values-it/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-it/strings.xml
@@ -214,4 +214,27 @@
%1$s è entrato
Hai creato la discussione
%1$s ha creato la discussione
+ Stanza vuota (era %s)
+
+ - %1$s, %2$s, %3$s e %4$d altro
+ - %1$s, %2$s, %3$s e altri %4$d
+
+ %1$s, %2$s, %3$s e %4$s
+ %1$s, %2$s e %3$s
+ 🎉 Tutti i server sono banditi dalla partecipazione! Questa stanza non può più essere usata.
+ Nessuna modifica.
+ • I server che corrispondono a IP letterali ora sono banditi.
+ • I server che corrispondono a IP letterali ora sono permessi.
+ • I server che corrispondono a %s sono stati rimossi dalla lista dei consentiti.
+ • I server che corrispondono a %s ora sono permessi.
+ • I server che corrispondono a %s sono stati rimossi dalla lista di ban.
+ • I server che corrispondono a %s ora sono banditi.
+ Hai cambiato le ACL del server per questa stanza.
+ %s ha cambiato le ACL del server per questa stanza.
+ • I server che corrispondono a IP letterali sono banditi.
+ • I server che corrispondono a IP letterali sono permessi.
+ • I server che corrispondono a %s sono permessi.
+ • I server che corrispondono a %s sono banditi.
+ Hai impostato le ACL del server per questa stanza.
+ %s ha impostato le ACL del server per questa stanza.
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml b/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml
index ed9f91cdb3..e6c93cb55c 100644
--- a/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml
@@ -221,4 +221,27 @@
%1$s entrou
Você criou a sala
%1$s criou a sala
+ Sala vazia (era %s)
+
+ - %1$s, %2$s, %3$s e %4$d outro
+ - %1$s, %2$s, %3$s e %4$d outros
+
+ %1$s, %2$s, %3$s e %4$s
+ %1$s, %2$s e %3$s
+ 🎉 Todos os servidores estão proibidos de participar! Esta sala não pode mais ser usada.
+ Nenhuma alteração.
+ • Servidores correspondentes aos IP literais agora estão banidos.
+ • Servidores correspondentes aos IP literais agora estão permitidos.
+ • Servidores correspondentes à %s foram removidos da lista de permitidos.
+ • Servidores correspondentes à %s agora são permitidos.
+ • Servidores correspondente à %s foram removidos da lista de banidos.
+ • Servidores correspondentes à %s foram banidos.
+ Você alterou a lista de controle de acesso (ACL) do servidor para esta sala.
+ %s alterou a lista de controle de acesso (ACL) do servidor para esta sala.
+ • Servidores correspondentes aos IP literais estão banidos.
+ • Servidores correspondentes aos IP literais estão permitidos.
+ • Servidores correspondentes à %s estão permitidos.
+ • Servidores correspondentes à %s estão banidos.
+ Você definiu a lista de controle de acesso (ACL) do servidor para esta sala.
+ %s definiu a lista de controle de acesso (ACL) do servidor para esta sala.
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/res/values-ru/strings.xml b/matrix-sdk-android/src/main/res/values-ru/strings.xml
index ef9cda1dc2..5ef5a4f447 100644
--- a/matrix-sdk-android/src/main/res/values-ru/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-ru/strings.xml
@@ -225,6 +225,31 @@
%1$s вошел(ла)
Вы создали обсуждение
%1$s создал(а) обсуждение
- Вы обновили.
- %s обновлена.
+ Вы обновили эту комнату.
+ %s обновил(а) эту комнату.
+
+ - %1$s, %2$s, %3$s и %4$d другой
+ - %1$s, %2$s, %3$s и %4$d других
+ - %1$s, %2$s, %3$s и %4$d другие
+ - %1$s, %2$s, %3$s и %4$d другие
+
+ %1$s, %2$s, %3$s и %4$s
+ %1$s, %2$s и %3$s
+ 🎉 Всем серверам запрещено участвовать! Эта комната больше не может быть использована.
+ Без изменений.
+ Пустая комната (была %s)
+ • Соответствующий сервер %s заблокирован.
+ • Сервер, соответствующий буквальным IP-адресам, теперь запрещён.
+ • Сервер, соответствующий буквальным IP-адресам, теперь разрешён.
+ • Сервер, соответствующий %s, теперь запрещён.
+ • Сервер, соответствующий %s, теперь разрешён.
+ • Сервер, соответствующий %s, был удалён из списка блокировки.
+ • Сервер, соответствующий буквальным IP-адресам, запрещён.
+ • Сервер, соответствующий буквальным IP-адресам, разрешён.
+ • Сервер, соответствующий %s, разрешён.
+ • Сервер, соответствующий %s, был удалён из разрешённого списка.
+ Вы изменили права доступа сервера (ACL) для этой комнаты.
+ %s изменил права доступа сервера (ACL) для этой комнаты.
+ Вы настроили права доступа сервера (ACL) для этой комнаты.
+ %s устанавливает права доступа сервера (ACL) для этой комнаты.
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/res/values-sq/strings.xml b/matrix-sdk-android/src/main/res/values-sq/strings.xml
index 4055b35025..58ba8877bb 100644
--- a/matrix-sdk-android/src/main/res/values-sq/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-sq/strings.xml
@@ -213,4 +213,23 @@
%1$s erdhi
Krijuat diskutimin
%1$s krijoi diskutimin
+ Dhomë e zbrazët (was %s)
+
+ - %1$s, %2$s, %3$s dhe %4$d tjetër
+ - %1$s, %2$s, %3$s dhe %4$d të tjerë
+
+ %1$s, %2$s, %3$s dhe %4$s
+ %1$s, %2$s dhe %3$s
+ 🎉 U është penguar pjesëmarrja krejt shërbyesve! Kjo dhomë s’mund të përdoret më.
+ Pa ndryshim.
+ Ndryshuat ACL-ra shërbyesi për këtë dhomë.
+ %s ndryshoi ACL-ra shërbyesi për këtë dhomë.
+ Ujdisët ACL-ra shërbyesi për këtë dhomë.
+ %s ujdisi ACL-ra shërbyesi për këtë dhomë.
+ • Shërbyes që kanë përputhje me %s u hoqën nga lista e të lejuarve.
+ • Shërbyesit që kanë përputhje me %s tani janë të lejuar.
+ • Shërbyesit që kanë përputhje me %s u hoqën nga lista e ndalimeve.
+ • Shërbyesit që kanë përputhje me %s tani janë të ndaluar.
+ • Shërbyesit që kanë përputhje me %s janë të ndaluar.
+ • Shërbyesit që kanë përputhje me %s janë të ndaluar.
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/res/values-uk/strings.xml b/matrix-sdk-android/src/main/res/values-uk/strings.xml
index eb5071f190..2477487379 100644
--- a/matrix-sdk-android/src/main/res/values-uk/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-uk/strings.xml
@@ -1,39 +1,34 @@
-
+
%1$s: %2$s
%1$s надіслав(ла) зображення.
-
%s запрошення
%1$s запросив(ла) %2$s
Зашифроване повідомлення
-
Запрошення від %s
Запрошення до кімнати
%1$s і %2$s
Порожня кімната
-
-
%1$s надіслав(ла) наліпку.
-
%1$s запросив(ла) Вас
- %1$s приєднався(лась)
+ %1$s приєднується
%1$s покинув(ла)
%1$s відхилив(ла) запрошення
%1$s копнув(ла) %2$s
%1$s розблокував(ла) %2$s
%1$s заблокував(ла) %2$s
%1$s відкликав(ла) запрошення для %2$s
- %1$s змінив(ла) свій аватар
- %1$s встановив(ла) собі ім’я %2$s
- %1$s змінив(ла) своє ім’я з %2$s на %3$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
%1$s змінив(ла) назву кімнати на: %2$s
%s розпочав(ла) відеодзвінок.
%s розпочав(ла) голосовий дзвінок.
%s відповів(ла) на дзвінок.
- %s завершив(ла) дзвінок.
+ %s завершує дзвінок.
%1$s зробив(ла) майбутню історію кімнати видимою для %2$s
усіх співрозмовників, з моменту їх запрошення.
усіх співрозмовників, з моменту їх приєднання.
@@ -41,44 +36,33 @@
будь-кого.
невідомо (%s).
%1$s увімкнув(ла) наскрізне шифрування (%2$s)
-
%1$s запросив(ла) VoIP конференцію
VoIP конференція розпочалась
VoIP конференція завершилась
-
(аватар також змінено)
%1$s прибрав(ла) назву кімнати
%1$s прибрав(ла) тему кімнати
%1$s оновив(ла) свій профіль %2$s
%1$s надіслав(ла) запрошення %2$s приєднатися до кімнати
%1$s прийняв(ла) запрошення у %2$s
-
** Неможливо розшифрувати: %s **
Пристрій відправника не надіслав нам ключ для цього повідомлення.
-
Неможливо відредагувати
Не вдалося надіслати повідомлення
-
Не вдалося завантажити зображення
-
Помилка мережі
Помилка Matrix
-
Наразі неможливо переприєднатися до порожньої кімнати.
-
Адреса електронної пошти
Номер телефону
-
- %1$s та 1 інший
- %1$s та %2$d інші
- %1$s та %2$d інших
-
+
-
%s вдосконалили цю кімнату.
-
Повідомлення видалено
%1$s видалили повідомлення
Повідомлення видалено [причина: %1$s]
-
+
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml b/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml
index b3de5910a5..08050b400d 100644
--- a/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml
@@ -208,4 +208,26 @@
%1$s 已加入
您已建立此討論
%1$s 已建立此討論
+ 空的聊天室(曾為 %s)
+
+ - %1$s, %2$s, %3$s 與 %4$d 個其他
+
+ %1$s, %2$s, %3$s 與 %4$s
+ %1$s, %2$s 與 %3$s
+ 🎉 禁止所有伺服器參與!無法再使用此聊天室。
+ 無變更。
+ • 禁止伺服器符合 IP 文字。
+ • 允許伺服器符合 IP 文字。
+ • 伺服器符合 %s 已從允許清單中移除。
+ • 允許伺服器符合 %s。
+ • 伺服器符合 %s 已從禁止清單中移除。
+ • 現在禁止伺服器符合 %s。
+ 您為此聊天室變更了伺服器 ACL。
+ %s 為此聊天是變更了伺服器 ACL。
+ • 禁止伺服器符合 IP 文字。
+ • 允許伺服器符合 IP 文字。
+ • 已允許伺服器符合 %s。
+ • 已禁止伺服器符合 %s。
+ 您為此聊天是設定了伺服器 ACL。
+ %s 為此聊天是設定了伺服器 ACL。
\ No newline at end of file
diff --git a/multipicker/build.gradle b/multipicker/build.gradle
index 7c29a5539f..c58c4586b2 100644
--- a/multipicker/build.gradle
+++ b/multipicker/build.gradle
@@ -16,7 +16,7 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-parcelize'
android {
compileSdkVersion 29
diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt
index c7a477cd42..ff5f69db5c 100644
--- a/tools/check/forbidden_strings_in_code.txt
+++ b/tools/check/forbidden_strings_in_code.txt
@@ -109,9 +109,6 @@ import retrofit2\.adapter\.rxjava\.HttpException
### This is generally not necessary, no need to reset the padding if there is no drawable
setCompoundDrawablePadding\(0\)
-### Deprecated use class form SDK API 26
-ButterKnife\.findById\(
-
# Change thread with Rx
# DISABLED
#runOnUiThread
@@ -164,7 +161,7 @@ Formatter\.formatShortFileSize===1
# android\.text\.TextUtils
### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
-enum class===85
+enum class===86
### Do not import temporary legacy classes
import org.matrix.android.sdk.internal.legacy.riot===3
@@ -175,3 +172,6 @@ getSystemService\(Context
### Use DefaultSharedPreferences.getInstance() instead for better performance
PreferenceManager\.getDefaultSharedPreferences==2
+
+### Use ViewBindings
+# findViewById
diff --git a/tools/templates/ElementFeature/root/src/app_package/Fragment.kt.ftl b/tools/templates/ElementFeature/root/src/app_package/Fragment.kt.ftl
index 7e6eac65c8..38f8132d24 100644
--- a/tools/templates/ElementFeature/root/src/app_package/Fragment.kt.ftl
+++ b/tools/templates/ElementFeature/root/src/app_package/Fragment.kt.ftl
@@ -3,7 +3,7 @@ package ${escapeKotlinIdentifiers(packageName)}
import android.os.Bundle
<#if createFragmentArgs>
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
import com.airbnb.mvrx.args
#if>
import android.view.View
@@ -36,8 +36,8 @@ class ${fragmentClass} @Inject constructor(
}
override fun onDestroyView() {
- super.onDestroyView()
// Clear your view, unsubscribe...
+ super.onDestroyView()
}
override fun invalidate() = withState(viewModel) { state ->
diff --git a/vector/build.gradle b/vector/build.gradle
index 69f8daf47b..496582e41c 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -3,21 +3,17 @@ import com.android.build.OutputFile
apply plugin: 'com.android.application'
apply plugin: 'com.google.android.gms.oss-licenses-plugin'
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
kapt {
correctErrorTypes = true
}
-androidExtensions {
- experimental = true
-}
-
// Note: 2 digits max for each value
ext.versionMajor = 1
ext.versionMinor = 0
-ext.versionPatch = 12
+ext.versionPatch = 14
static def getGitTimestamp() {
def cmd = 'git show -s --format=%ct'
@@ -283,6 +279,10 @@ android {
java.srcDirs += "src/sharedTest/java"
}
}
+
+ buildFeatures {
+ viewBinding true
+ }
}
dependencies {
@@ -390,10 +390,6 @@ dependencies {
implementation 'com.otaliastudios:autocomplete:1.1.0'
- // Butterknife
- implementation 'com.jakewharton:butterknife:10.2.0'
- kapt 'com.jakewharton:butterknife-compiler:10.2.0'
-
// Shake detection
implementation 'com.squareup:seismic:1.0.2'
@@ -414,6 +410,9 @@ dependencies {
// Badge for compatibility
implementation 'me.leolin:ShortcutBadger:1.1.22@aar'
+ // Chat effects
+ implementation 'nl.dionsegijn:konfetti:1.2.5'
+ implementation 'com.github.jetradarmobile:android-snowfall:1.2.0'
// DI
implementation "com.google.dagger:dagger:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
@@ -444,6 +443,10 @@ dependencies {
implementation 'com.google.zxing:core:3.3.3'
implementation 'me.dm7.barcodescanner:zxing:1.9.13'
+ // Emoji Keyboard
+ implementation 'com.vanniktech:emoji-material:0.7.0'
+ implementation 'com.vanniktech:emoji-google:0.7.0'
+
// TESTS
testImplementation 'junit:junit:4.13'
testImplementation "org.amshove.kluent:kluent-android:$kluent_version"
diff --git a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
index 6b30700116..cc4724e8f3 100644
--- a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
+++ b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
@@ -76,7 +76,9 @@ class UiAllScreensSanityTest {
private val uiTestBase = UiTestBase()
- // Last passing: 2020-11-09
+ // Last passing:
+ // 2020-11-09
+ // 2020-12-16 After ViewBinding huge change
@Test
fun allScreensTest() {
// Create an account
diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugMaterialThemeActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/DebugMaterialThemeActivity.kt
index 6c4bb925dd..8df1feab1e 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/DebugMaterialThemeActivity.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/DebugMaterialThemeActivity.kt
@@ -24,26 +24,27 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.snackbar.Snackbar
import im.vector.app.R
import im.vector.app.core.utils.toast
-import kotlinx.android.synthetic.debug.activity_test_material_theme.*
+import im.vector.app.databinding.ActivityTestMaterialThemeBinding
// Rendering is not the same with VectorBaseActivity
abstract class DebugMaterialThemeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_test_material_theme)
+ val views = ActivityTestMaterialThemeBinding.inflate(layoutInflater)
+ setContentView(views.root)
- debugShowSnackbar.setOnClickListener {
- Snackbar.make(debugMaterialCoordinator, "Snackbar!", Snackbar.LENGTH_SHORT)
+ views.debugShowSnackbar.setOnClickListener {
+ Snackbar.make(views.coordinatorLayout, "Snackbar!", Snackbar.LENGTH_SHORT)
.setAction("Action") { }
.show()
}
- debugShowToast.setOnClickListener {
+ views.debugShowToast.setOnClickListener {
toast("Toast")
}
- debugShowDialog.setOnClickListener {
+ views.debugShowDialog.setOnClickListener {
AlertDialog.Builder(this)
.setMessage("Dialog content")
.setIcon(R.drawable.ic_settings_x)
@@ -53,7 +54,7 @@ abstract class DebugMaterialThemeActivity : AppCompatActivity() {
.show()
}
- debugShowBottomSheet.setOnClickListener {
+ views.debugShowBottomSheet.setOnClickListener {
BottomSheetDialogFragment().show(supportFragmentManager, "TAG")
}
}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
index 9cca462d1a..8699b238ec 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
@@ -24,7 +24,6 @@ import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.Person
import androidx.core.content.getSystemService
-import butterknife.OnClick
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ScreenComponent
@@ -35,16 +34,17 @@ import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
import im.vector.app.core.utils.allGranted
import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.toast
+import im.vector.app.databinding.ActivityDebugMenuBinding
import im.vector.app.features.debug.sas.DebugSasEmojiActivity
import im.vector.app.features.qrcode.QrCodeScannerActivity
import org.matrix.android.sdk.internal.crypto.verification.qrcode.toQrCodeData
-import kotlinx.android.synthetic.debug.activity_debug_menu.*
+
import timber.log.Timber
import javax.inject.Inject
-class DebugMenuActivity : VectorBaseActivity() {
+class DebugMenuActivity : VectorBaseActivity() {
- override fun getLayoutRes() = R.layout.activity_debug_menu
+ override fun getBinding() = ActivityDebugMenuBinding.inflate(layoutInflater)
@Inject
lateinit var activeSessionHolder: ActiveSessionHolder
@@ -66,24 +66,32 @@ class DebugMenuActivity : VectorBaseActivity() {
val string = buffer.toString(Charsets.ISO_8859_1)
renderQrCode(string)
+ setupViews()
+ }
+
+ private fun setupViews() {
+ views.debugTestTextViewLink.setOnClickListener { testTextViewLink() }
+ views.debugShowSasEmoji.setOnClickListener { showSasEmoji() }
+ views.debugTestNotification.setOnClickListener { testNotification() }
+ views.debugTestMaterialThemeLight.setOnClickListener { testMaterialThemeLight() }
+ views.debugTestMaterialThemeDark.setOnClickListener { testMaterialThemeDark() }
+ views.debugTestCrash.setOnClickListener { testCrash() }
+ views.debugScanQrCode.setOnClickListener { scanQRCode() }
}
private fun renderQrCode(text: String) {
- debug_qr_code.setData(text)
+ views.debugQrCode.setData(text)
}
- @OnClick(R.id.debug_test_text_view_link)
- fun testTextViewLink() {
+ private fun testTextViewLink() {
startActivity(Intent(this, TestLinkifyActivity::class.java))
}
- @OnClick(R.id.debug_show_sas_emoji)
- fun showSasEmoji() {
+ private fun showSasEmoji() {
startActivity(Intent(this, DebugSasEmojiActivity::class.java))
}
- @OnClick(R.id.debug_test_notification)
- fun testNotification() {
+ private fun testNotification() {
val notificationManager = getSystemService()!!
// Create channel first
@@ -166,23 +174,19 @@ class DebugMenuActivity : VectorBaseActivity() {
)
}
- @OnClick(R.id.debug_test_material_theme_light)
- fun testMaterialThemeLight() {
+ private fun testMaterialThemeLight() {
startActivity(Intent(this, DebugMaterialThemeLightActivity::class.java))
}
- @OnClick(R.id.debug_test_material_theme_dark)
- fun testMaterialThemeDark() {
+ private fun testMaterialThemeDark() {
startActivity(Intent(this, DebugMaterialThemeDarkActivity::class.java))
}
- @OnClick(R.id.debug_test_crash)
- fun testCrash() {
+ private fun testCrash() {
throw RuntimeException("Application crashed from user demand")
}
- @OnClick(R.id.debug_scan_qr_code)
- fun scanQRCode() {
+ private fun scanQRCode() {
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) {
doScanQRCode()
}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/TestLinkifyActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/TestLinkifyActivity.kt
index fd28aabd49..88e55d6760 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/TestLinkifyActivity.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/TestLinkifyActivity.kt
@@ -19,28 +19,18 @@ package im.vector.app.features.debug
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
-import android.widget.LinearLayout
-import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
-import androidx.coordinatorlayout.widget.CoordinatorLayout
-import butterknife.BindView
-import butterknife.ButterKnife
import im.vector.app.R
+import im.vector.app.databinding.ActivityTestLinkifyBinding
+import im.vector.app.databinding.ItemTestLinkifyBinding
class TestLinkifyActivity : AppCompatActivity() {
- @BindView(R.id.test_linkify_content_view)
- lateinit var scrollContent: LinearLayout
-
- @BindView(R.id.test_linkify_coordinator)
- lateinit var coordinatorLayout: CoordinatorLayout
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_test_linkify)
- ButterKnife.bind(this)
-
- scrollContent.removeAllViews()
+ val views = ActivityTestLinkifyBinding.inflate(layoutInflater)
+ setContentView(views.root)
+ views.testLinkifyContentView.removeAllViews()
listOf(
"https://www.html5rocks.com/en/tutorials/webrtc/basics/ |",
@@ -89,43 +79,42 @@ class TestLinkifyActivity : AppCompatActivity() {
)
.forEach { textContent ->
val item = LayoutInflater.from(this)
- .inflate(R.layout.item_test_linkify, scrollContent, false)
-
- item.findViewById(R.id.test_linkify_auto_text)
- ?.apply {
- text = textContent
- /* TODO Use BetterLinkMovementMethod when the other PR is merged
- movementMethod = MatrixLinkMovementMethod(object : MockMessageAdapterActionListener() {
- override fun onURLClick(uri: Uri?) {
- Snackbar.make(coordinatorLayout, "URI Clicked: $uri", Snackbar.LENGTH_LONG)
- .setAction("open") {
- openUrlInExternalBrowser(this@TestLinkifyActivity, uri)
- }
- .show()
- }
- })
- */
+ .inflate(R.layout.item_test_linkify, views.testLinkifyContentView, false)
+ val subViews = ItemTestLinkifyBinding.bind(item)
+ subViews.testLinkifyAutoText.apply {
+ text = textContent
+ /* TODO Use BetterLinkMovementMethod when the other PR is merged
+ movementMethod = MatrixLinkMovementMethod(object : MockMessageAdapterActionListener() {
+ override fun onURLClick(uri: Uri?) {
+ Snackbar.make(coordinatorLayout, "URI Clicked: $uri", Snackbar.LENGTH_LONG)
+ .setAction("open") {
+ openUrlInExternalBrowser(this@TestLinkifyActivity, uri)
+ }
+ .show()
}
+ })
+ */
+ }
- item.findViewById(R.id.test_linkify_custom_text)
- ?.apply {
- text = textContent
- /* TODO Use BetterLinkMovementMethod when the other PR is merged
- movementMethod = MatrixLinkMovementMethod(object : MockMessageAdapterActionListener() {
- override fun onURLClick(uri: Uri?) {
- Snackbar.make(coordinatorLayout, "URI Clicked: $uri", Snackbar.LENGTH_LONG)
- .setAction("open") {
- openUrlInExternalBrowser(this@TestLinkifyActivity, uri)
- }
- .show()
- }
- })
- */
-
- // TODO Call VectorLinkify.addLinks(text)
+ subViews.testLinkifyCustomText.apply {
+ text = textContent
+ /* TODO Use BetterLinkMovementMethod when the other PR is merged
+ movementMethod = MatrixLinkMovementMethod(object : MockMessageAdapterActionListener() {
+ override fun onURLClick(uri: Uri?) {
+ Snackbar.make(coordinatorLayout, "URI Clicked: $uri", Snackbar.LENGTH_LONG)
+ .setAction("open") {
+ openUrlInExternalBrowser(this@TestLinkifyActivity, uri)
+ }
+ .show()
}
+ })
+ */
- scrollContent.addView(item, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))
+ // TODO Call VectorLinkify.addLinks(text)
+ }
+
+ views.testLinkifyContentView
+ .addView(item, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))
}
}
}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/sas/DebugSasEmojiActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/sas/DebugSasEmojiActivity.kt
index 869058eff6..4d7005aca8 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/sas/DebugSasEmojiActivity.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/sas/DebugSasEmojiActivity.kt
@@ -18,24 +18,26 @@ package im.vector.app.features.debug.sas
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
-import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
+import im.vector.app.databinding.FragmentGenericRecyclerBinding
import org.matrix.android.sdk.api.crypto.getAllVerificationEmojis
-import kotlinx.android.synthetic.main.fragment_generic_recycler.*
class DebugSasEmojiActivity : AppCompatActivity() {
+ private lateinit var views: FragmentGenericRecyclerBinding
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.fragment_generic_recycler)
+ views = FragmentGenericRecyclerBinding.inflate(layoutInflater)
+ setContentView(views.root)
val controller = SasEmojiController()
- genericRecyclerView.configureWith(controller)
+ views.genericRecyclerView.configureWith(controller)
controller.setData(SasState(getAllVerificationEmojis()))
}
override fun onDestroy() {
- genericRecyclerView.cleanup()
+ views.genericRecyclerView.cleanup()
super.onDestroy()
}
}
diff --git a/vector/src/debug/res/layout/activity_debug_menu.xml b/vector/src/debug/res/layout/activity_debug_menu.xml
index 9a95085a07..6a41488987 100644
--- a/vector/src/debug/res/layout/activity_debug_menu.xml
+++ b/vector/src/debug/res/layout/activity_debug_menu.xml
@@ -1,6 +1,7 @@
-
+
-
-
+
@@ -190,15 +192,25 @@
-
+
+
+
+
+
diff --git a/vector/src/main/assets/open_source_licenses.html b/vector/src/main/assets/open_source_licenses.html
index ac80b0d691..17d79e3655 100755
--- a/vector/src/main/assets/open_source_licenses.html
+++ b/vector/src/main/assets/open_source_licenses.html
@@ -312,11 +312,6 @@ SOFTWARE.
Copyright (c) 2017
-
- Butterknife
-
- Copyright 2013 Jake Wharton
-
seismic
@@ -347,11 +342,6 @@ SOFTWARE.
Copyright 2017 Gabriel Ittner.
-
- Android-multipicker-library
-
- Copyright 2018 Kumar Bibek
-
htmlcompressor
@@ -390,6 +380,16 @@ SOFTWARE.
Copyright 2018, Aleksandr Nikiforov
+
+ Emoji
+
+ Copyright (C) 2016 - Niklas Baudy, Ruben Gees, Mario Đanić and contributors
+
+
+ JetradarMobile / android-snowfall
+
+ Copyright 2016 JetRadar
+
Apache License
@@ -576,5 +576,14 @@ Apache License
+
+ ISC License
+
+ DanielMartinus / Konfetti
+
+ Copyright (c) 2017 Dion Segijn
+
+
+