Merge pull request #1965 from vector-im/feature/share

Share button in rooms gives room ID link without via parameters (#1927)
This commit is contained in:
Benoit Marty 2020-08-20 18:28:30 +02:00 committed by GitHub
commit 9582b9c0fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 280 additions and 113 deletions

View file

@ -6,6 +6,7 @@ Features ✨:
- Conference with Jitsi support (#43) - Conference with Jitsi support (#43)
Improvements 🙌: Improvements 🙌:
- Share button in rooms gives room ID link without via parameters (#1927)
- Give user the possibility to prevent accidental call (#1869) - Give user the possibility to prevent accidental call (#1869)
- Display device information (name, id and key) in Cryptography setting screen (#1784) - Display device information (name, id and key) in Cryptography setting screen (#1784)
- Ensure users do not accidentally ignore other users (#1890) - Ensure users do not accidentally ignore other users (#1890)

View file

@ -19,6 +19,7 @@ package org.matrix.android.sdk.api.session
import androidx.annotation.MainThread import androidx.annotation.MainThread
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.failure.GlobalError
import org.matrix.android.sdk.api.pushrules.PushRuleService import org.matrix.android.sdk.api.pushrules.PushRuleService
@ -35,6 +36,7 @@ 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.homeserver.HomeServerCapabilitiesService
import org.matrix.android.sdk.api.session.identity.IdentityService import org.matrix.android.sdk.api.session.identity.IdentityService
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
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.profile.ProfileService
import org.matrix.android.sdk.api.session.pushers.PushersService import org.matrix.android.sdk.api.session.pushers.PushersService
import org.matrix.android.sdk.api.session.room.RoomDirectoryService import org.matrix.android.sdk.api.session.room.RoomDirectoryService
@ -48,7 +50,6 @@ import org.matrix.android.sdk.api.session.terms.TermsService
import org.matrix.android.sdk.api.session.typing.TypingUsersTracker import org.matrix.android.sdk.api.session.typing.TypingUsersTracker
import org.matrix.android.sdk.api.session.user.UserService import org.matrix.android.sdk.api.session.user.UserService
import org.matrix.android.sdk.api.session.widgets.WidgetService import org.matrix.android.sdk.api.session.widgets.WidgetService
import okhttp3.OkHttpClient
/** /**
* This interface defines interactions with a session. * This interface defines interactions with a session.
@ -195,6 +196,11 @@ interface Session :
*/ */
fun fileService(): FileService fun fileService(): FileService
/**
* Returns the permalink service associated with the session
*/
fun permalinkService(): PermalinkService
/** /**
* Add a listener to the session. * Add a listener to the session.
* @param listener the listener to add. * @param listener the listener to add.

View file

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.permalinks package org.matrix.android.sdk.api.session.permalinks
import android.text.Spannable import android.text.Spannable

View file

@ -15,11 +15,11 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.permalinks package org.matrix.android.sdk.api.session.permalinks
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.view.View import android.view.View
import org.matrix.android.sdk.api.permalinks.MatrixPermalinkSpan.Callback import org.matrix.android.sdk.api.session.permalinks.MatrixPermalinkSpan.Callback
/** /**
* This MatrixPermalinkSpan is a clickable span which use a [Callback] to communicate back. * This MatrixPermalinkSpan is a clickable span which use a [Callback] to communicate back.

View file

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.permalinks package org.matrix.android.sdk.api.session.permalinks
import android.net.Uri import android.net.Uri

View file

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.permalinks package org.matrix.android.sdk.api.session.permalinks
import android.net.Uri import android.net.Uri
import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.MatrixPatterns
@ -37,7 +37,7 @@ object PermalinkParser {
* Turns an uri to a [PermalinkData] * Turns an uri to a [PermalinkData]
*/ */
fun parse(uri: Uri): PermalinkData { fun parse(uri: Uri): PermalinkData {
if (!uri.toString().startsWith(PermalinkFactory.MATRIX_TO_URL_BASE)) { if (!uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE)) {
return PermalinkData.FallbackLink(uri) return PermalinkData.FallbackLink(uri)
} }

View file

@ -15,55 +15,58 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api.permalinks package org.matrix.android.sdk.api.session.permalinks
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
/** /**
* Useful methods to create Matrix permalink (matrix.to links). * Useful methods to create Matrix permalink (matrix.to links).
*/ */
object PermalinkFactory { interface PermalinkService {
companion object {
const val MATRIX_TO_URL_BASE = "https://matrix.to/#/" const val MATRIX_TO_URL_BASE = "https://matrix.to/#/"
}
/** /**
* Creates a permalink for an event. * Creates a permalink for an event.
* Ex: "https://matrix.to/#/!nbzmcXAqpxBXjAdgoX:matrix.org/$1531497316352799BevdV:matrix.org" * Ex: "https://matrix.to/#/!nbzmcXAqpxBXjAdgoX:matrix.org/$1531497316352799BevdV:matrix.org"
* *
* @param event the event * @param event the event
*
* @return the permalink, or null in case of error * @return the permalink, or null in case of error
*/ */
fun createPermalink(event: Event): String? { fun createPermalink(event: Event): String?
if (event.roomId.isNullOrEmpty() || event.eventId.isNullOrEmpty()) {
return null
}
return createPermalink(event.roomId, event.eventId)
}
/** /**
* Creates a permalink for an id (can be a user Id, Room Id, etc.). * Creates a permalink for an id (can be a user Id, etc.).
* For a roomId, consider using [createRoomPermalink]
* Ex: "https://matrix.to/#/@benoit:matrix.org" * Ex: "https://matrix.to/#/@benoit:matrix.org"
* *
* @param id the id * @param id the id
* @return the permalink, or null in case of error * @return the permalink, or null in case of error
*/ */
fun createPermalink(id: String): String? { fun createPermalink(id: String): String?
return if (id.isEmpty()) {
null
} else MATRIX_TO_URL_BASE + escape(id)
}
/** /**
* Creates a permalink for an event. If you have an event you can use [.createPermalink] * Creates a permalink for a roomId, including the via parameters
* Ex: "https://matrix.to/#/!nbzmcXAqpxBXjAdgoX:matrix.org/$1531497316352799BevdV:matrix.org" *
* @param roomId the room id
*
* @return the permalink, or null in case of error
*/
fun createRoomPermalink(roomId: String): String?
/**
* Creates a permalink for an event. If you have an event you can use [createPermalink]
* Ex: "https://matrix.to/#/!nbzmcXAqpxBXjAdgoX:matrix.org/$1531497316352799BevdV:matrix.org?via=matrix.org"
* *
* @param roomId the id of the room * @param roomId the id of the room
* @param eventId the id of the event * @param eventId the id of the event
*
* @return the permalink * @return the permalink
*/ */
fun createPermalink(roomId: String, eventId: String): String { fun createPermalink(roomId: String, eventId: String): String
return MATRIX_TO_URL_BASE + escape(roomId) + "/" + escape(eventId)
}
/** /**
* Extract the linked id from the universal link * Extract the linked id from the universal link
@ -71,31 +74,5 @@ object PermalinkFactory {
* @param url the universal link, Ex: "https://matrix.to/#/@benoit:matrix.org" * @param url the universal link, Ex: "https://matrix.to/#/@benoit:matrix.org"
* @return the id from the url, ex: "@benoit:matrix.org", or null if the url is not a permalink * @return the id from the url, ex: "@benoit:matrix.org", or null if the url is not a permalink
*/ */
fun getLinkedId(url: String): String? { fun getLinkedId(url: String): String?
val isSupported = url.startsWith(MATRIX_TO_URL_BASE)
return if (isSupported) {
url.substring(MATRIX_TO_URL_BASE.length)
} else null
}
/**
* Escape '/' in id, because it is used as a separator
*
* @param id the id to escape
* @return the escaped id
*/
internal fun escape(id: String): String {
return id.replace("/", "%2F")
}
/**
* Unescape '/' in id
*
* @param id the id to escape
* @return the escaped id
*/
internal fun unescape(id: String): String {
return id.replace("%2F", "/")
}
} }

View file

@ -19,6 +19,13 @@ package org.matrix.android.sdk.internal.session
import androidx.annotation.MainThread import androidx.annotation.MainThread
import dagger.Lazy import dagger.Lazy
import io.realm.RealmConfiguration
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.failure.GlobalError
@ -37,6 +44,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.group.GroupService
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService 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.integrationmanager.IntegrationManagerService
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.profile.ProfileService
import org.matrix.android.sdk.api.session.pushers.PushersService import org.matrix.android.sdk.api.session.pushers.PushersService
import org.matrix.android.sdk.api.session.room.RoomDirectoryService import org.matrix.android.sdk.api.session.room.RoomDirectoryService
@ -63,13 +71,6 @@ import org.matrix.android.sdk.internal.session.sync.job.SyncWorker
import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import org.matrix.android.sdk.internal.util.createUIHandler import org.matrix.android.sdk.internal.util.createUIHandler
import io.realm.RealmConfiguration
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider import javax.inject.Provider
@ -96,6 +97,7 @@ internal class DefaultSession @Inject constructor(
private val termsService: Lazy<TermsService>, private val termsService: Lazy<TermsService>,
private val cryptoService: Lazy<DefaultCryptoService>, private val cryptoService: Lazy<DefaultCryptoService>,
private val defaultFileService: Lazy<FileService>, private val defaultFileService: Lazy<FileService>,
private val permalinkService: Lazy<PermalinkService>,
private val secureStorageService: Lazy<SecureStorageService>, private val secureStorageService: Lazy<SecureStorageService>,
private val profileService: Lazy<ProfileService>, private val profileService: Lazy<ProfileService>,
private val widgetService: Lazy<WidgetService>, private val widgetService: Lazy<WidgetService>,
@ -254,6 +256,8 @@ internal class DefaultSession @Inject constructor(
override fun fileService(): FileService = defaultFileService.get() override fun fileService(): FileService = defaultFileService.get()
override fun permalinkService(): PermalinkService = permalinkService.get()
override fun widgetService(): WidgetService = widgetService.get() override fun widgetService(): WidgetService = widgetService.get()
override fun integrationManagerService() = integrationManagerService override fun integrationManagerService() = integrationManagerService

View file

@ -25,6 +25,9 @@ import dagger.Lazy
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.multibindings.IntoSet import dagger.multibindings.IntoSet
import io.realm.RealmConfiguration
import okhttp3.OkHttpClient
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.auth.data.Credentials 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.HomeServerConnectionConfig
@ -35,6 +38,7 @@ import org.matrix.android.sdk.api.session.InitialSyncProgressService
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.accountdata.AccountDataService import org.matrix.android.sdk.api.session.accountdata.AccountDataService
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import org.matrix.android.sdk.api.session.securestorage.SecureStorageService import org.matrix.android.sdk.api.session.securestorage.SecureStorageService
import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService
import org.matrix.android.sdk.api.session.typing.TypingUsersTracker import org.matrix.android.sdk.api.session.typing.TypingUsersTracker
@ -72,6 +76,7 @@ import org.matrix.android.sdk.internal.session.download.DownloadProgressIntercep
import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapabilitiesService import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapabilitiesService
import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService
import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor
import org.matrix.android.sdk.internal.session.room.create.RoomCreateEventProcessor import org.matrix.android.sdk.internal.session.room.create.RoomCreateEventProcessor
import org.matrix.android.sdk.internal.session.room.prune.RedactionEventProcessor import org.matrix.android.sdk.internal.session.room.prune.RedactionEventProcessor
@ -81,9 +86,6 @@ import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker
import org.matrix.android.sdk.internal.session.user.accountdata.DefaultAccountDataService import org.matrix.android.sdk.internal.session.user.accountdata.DefaultAccountDataService
import org.matrix.android.sdk.internal.session.widgets.DefaultWidgetURLFormatter import org.matrix.android.sdk.internal.session.widgets.DefaultWidgetURLFormatter
import org.matrix.android.sdk.internal.util.md5 import org.matrix.android.sdk.internal.util.md5
import io.realm.RealmConfiguration
import okhttp3.OkHttpClient
import org.greenrobot.eventbus.EventBus
import retrofit2.Retrofit import retrofit2.Retrofit
import java.io.File import java.io.File
import javax.inject.Provider import javax.inject.Provider
@ -356,6 +358,9 @@ internal abstract class SessionModule {
@Binds @Binds
abstract fun bindSharedSecretStorageService(service: DefaultSharedSecretStorageService): SharedSecretStorageService abstract fun bindSharedSecretStorageService(service: DefaultSharedSecretStorageService): SharedSecretStorageService
@Binds
abstract fun bindPermalinkService(service: DefaultPermalinkService): PermalinkService
@Binds @Binds
abstract fun bindTypingUsersTracker(tracker: DefaultTypingUsersTracker): TypingUsersTracker abstract fun bindTypingUsersTracker(tracker: DefaultTypingUsersTracker): TypingUsersTracker
} }

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020 New Vector Ltd
* 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.permalinks
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import org.matrix.android.sdk.api.session.permalinks.PermalinkService.Companion.MATRIX_TO_URL_BASE
import javax.inject.Inject
internal class DefaultPermalinkService @Inject constructor(
private val permalinkFactory: PermalinkFactory
) : PermalinkService {
override fun createPermalink(event: Event): String? {
return permalinkFactory.createPermalink(event)
}
override fun createPermalink(id: String): String? {
return permalinkFactory.createPermalink(id)
}
override fun createRoomPermalink(roomId: String): String? {
return permalinkFactory.createRoomPermalink(roomId)
}
override fun createPermalink(roomId: String, eventId: String): String {
return permalinkFactory.createPermalink(roomId, eventId)
}
override fun getLinkedId(url: String): String? {
return url
.takeIf { it.startsWith(MATRIX_TO_URL_BASE) }
?.substring(MATRIX_TO_URL_BASE.length)
?.substringBeforeLast("?")
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2020 New Vector Ltd
* 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.permalinks
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.permalinks.PermalinkService.Companion.MATRIX_TO_URL_BASE
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.room.RoomGetter
import java.net.URLEncoder
import javax.inject.Inject
import javax.inject.Provider
internal class PermalinkFactory @Inject constructor(
@UserId
private val userId: String,
// Use a provider to fix circular Dagger dependency
private val roomGetterProvider: Provider<RoomGetter>
) {
fun createPermalink(event: Event): String? {
if (event.roomId.isNullOrEmpty() || event.eventId.isNullOrEmpty()) {
return null
}
return createPermalink(event.roomId, event.eventId)
}
fun createPermalink(id: String): String? {
return if (id.isEmpty()) {
null
} else MATRIX_TO_URL_BASE + escape(id)
}
fun createRoomPermalink(roomId: String): String? {
return if (roomId.isEmpty()) {
null
} else {
MATRIX_TO_URL_BASE + escape(roomId) + computeViaParams(userId, roomId)
}
}
fun createPermalink(roomId: String, eventId: String): String {
return MATRIX_TO_URL_BASE + escape(roomId) + "/" + escape(eventId) + computeViaParams(userId, roomId)
}
fun getLinkedId(url: String): String? {
val isSupported = url.startsWith(MATRIX_TO_URL_BASE)
return if (isSupported) {
url.substring(MATRIX_TO_URL_BASE.length)
} else null
}
/**
* Compute the via parameters.
* Take up to 3 homeserver domains, taking the most representative one regarding room members and including the
* current user one.
*/
private fun computeViaParams(userId: String, roomId: String): String {
val userHomeserver = userId.substringAfter(":")
return getUserIdsOfJoinedMembers(roomId)
.map { it.substringAfter(":") }
.groupBy { it }
.mapValues { it.value.size }
.toMutableMap()
// Ensure the user homeserver will be included
.apply { this[userHomeserver] = Int.MAX_VALUE }
.let { map -> map.keys.sortedByDescending { map[it] } }
.take(3)
.joinToString(prefix = "?via=", separator = "&via=") { URLEncoder.encode(it, "utf-8") }
}
/**
* Escape '/' in id, because it is used as a separator
*
* @param id the id to escape
* @return the escaped id
*/
private fun escape(id: String): String {
return id.replace("/", "%2F")
}
/**
* Unescape '/' in id
*
* @param id the id to escape
* @return the escaped id
*/
private fun unescape(id: String): String {
return id.replace("%2F", "/")
}
/**
* Get a set of userIds of joined members of a room
*/
private fun getUserIdsOfJoinedMembers(roomId: String): Set<String> {
return roomGetterProvider.get().getRoom(roomId)
?.getRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.JOIN) })
?.map { it.userId }
.orEmpty()
.toSet()
}
}

View file

@ -156,11 +156,15 @@ internal class DefaultRelationService @AssistedInject constructor(
originalTimelineEvent: TimelineEvent, originalTimelineEvent: TimelineEvent,
newBodyText: String, newBodyText: String,
compatibilityBodyText: String): Cancelable { compatibilityBodyText: String): Cancelable {
val event = eventFactory val event = eventFactory.createReplaceTextOfReply(
.createReplaceTextOfReply(roomId, roomId,
replyToEdit, replyToEdit,
originalTimelineEvent, originalTimelineEvent,
newBodyText, true, MessageType.MSGTYPE_TEXT, compatibilityBodyText) newBodyText,
true,
MessageType.MSGTYPE_TEXT,
compatibilityBodyText
)
.also { saveLocalEcho(it) } .also { saveLocalEcho(it) }
return if (cryptoService.isRoomEncrypted(roomId)) { return if (cryptoService.isRoomEncrypted(roomId)) {
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to")) val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))

View file

@ -22,7 +22,6 @@ import android.graphics.Bitmap
import android.media.MediaMetadataRetriever import android.media.MediaMetadataRetriever
import androidx.exifinterface.media.ExifInterface import androidx.exifinterface.media.ExifInterface
import org.matrix.android.sdk.R import org.matrix.android.sdk.R
import org.matrix.android.sdk.api.permalinks.PermalinkFactory
import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
@ -59,8 +58,8 @@ import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.session.room.timeline.isReply import org.matrix.android.sdk.api.session.room.timeline.isReply
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.content.ThumbnailExtractor import org.matrix.android.sdk.internal.session.content.ThumbnailExtractor
import org.matrix.android.sdk.internal.session.permalinks.PermalinkFactory
import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.StringProvider import org.matrix.android.sdk.internal.util.StringProvider
import javax.inject.Inject import javax.inject.Inject
@ -79,8 +78,8 @@ internal class LocalEchoEventFactory @Inject constructor(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val markdownParser: MarkdownParser, private val markdownParser: MarkdownParser,
private val textPillsUtils: TextPillsUtils, private val textPillsUtils: TextPillsUtils,
private val taskExecutor: TaskExecutor, private val localEchoRepository: LocalEchoRepository,
private val localEchoRepository: LocalEchoRepository private val permalinkFactory: PermalinkFactory
) { ) {
fun createTextEvent(roomId: String, msgType: String, text: CharSequence, autoMarkdown: Boolean): Event { fun createTextEvent(roomId: String, msgType: String, text: CharSequence, autoMarkdown: Boolean): Event {
if (msgType == MessageType.MSGTYPE_TEXT || msgType == MessageType.MSGTYPE_EMOTE) { if (msgType == MessageType.MSGTYPE_TEXT || msgType == MessageType.MSGTYPE_EMOTE) {
@ -169,9 +168,8 @@ internal class LocalEchoEventFactory @Inject constructor(
newBodyAutoMarkdown: Boolean, newBodyAutoMarkdown: Boolean,
msgType: String, msgType: String,
compatibilityText: String): Event { compatibilityText: String): Event {
val permalink = PermalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "") val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "")
val userLink = originalEvent.root.senderId?.let { PermalinkFactory.createPermalink(it) } val userLink = originalEvent.root.senderId?.let { permalinkFactory.createPermalink(it) } ?: ""
?: ""
val body = bodyForReply(originalEvent.getLastMessageContent(), originalEvent.isReply()) val body = bodyForReply(originalEvent.getLastMessageContent(), originalEvent.isReply())
val replyFormatted = REPLY_PATTERN.format( val replyFormatted = REPLY_PATTERN.format(
@ -361,12 +359,15 @@ internal class LocalEchoEventFactory @Inject constructor(
return System.currentTimeMillis() return System.currentTimeMillis()
} }
fun createReplyTextEvent(roomId: String, eventReplied: TimelineEvent, replyText: CharSequence, autoMarkdown: Boolean): Event? { fun createReplyTextEvent(roomId: String,
eventReplied: TimelineEvent,
replyText: CharSequence,
autoMarkdown: Boolean): Event? {
// Fallbacks and event representation // Fallbacks and event representation
// TODO Add error/warning logs when any of this is null // TODO Add error/warning logs when any of this is null
val permalink = PermalinkFactory.createPermalink(eventReplied.root) ?: return null val permalink = permalinkFactory.createPermalink(eventReplied.root) ?: return null
val userId = eventReplied.root.senderId ?: return null val userId = eventReplied.root.senderId ?: return null
val userLink = PermalinkFactory.createPermalink(userId) ?: return null val userLink = permalinkFactory.createPermalink(userId) ?: return null
val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.isReply()) val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.isReply())
val replyFormatted = REPLY_PATTERN.format( val replyFormatted = REPLY_PATTERN.format(

View file

@ -163,7 +163,6 @@ import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import org.billcarsonfr.jsonviewer.JSonViewerDialog import org.billcarsonfr.jsonviewer.JSonViewerDialog
import org.commonmark.parser.Parser import org.commonmark.parser.Parser
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.permalinks.PermalinkFactory
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
@ -1608,7 +1607,7 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, composerLayout.text.toString())) roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, composerLayout.text.toString()))
} }
is EventSharedAction.CopyPermalink -> { is EventSharedAction.CopyPermalink -> {
val permalink = PermalinkFactory.createPermalink(roomDetailArgs.roomId, action.eventId) val permalink = session.permalinkService().createPermalink(roomDetailArgs.roomId, action.eventId)
copyToClipboard(requireContext(), permalink, false) copyToClipboard(requireContext(), permalink, false)
showSnackWithMessage(getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT) showSnackWithMessage(getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT)
} }

View file

@ -22,21 +22,22 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.core.resources.UserPreferencesProvider import im.vector.app.core.resources.UserPreferencesProvider
import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.home.room.detail.timeline.item.RoomCreateItem_ import im.vector.app.features.home.room.detail.timeline.item.RoomCreateItem_
import org.matrix.android.sdk.api.permalinks.PermalinkFactory import me.gujun.android.span.span
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import me.gujun.android.span.span
import javax.inject.Inject import javax.inject.Inject
class RoomCreateItemFactory @Inject constructor(private val stringProvider: StringProvider, class RoomCreateItemFactory @Inject constructor(private val stringProvider: StringProvider,
private val userPreferencesProvider: UserPreferencesProvider, private val userPreferencesProvider: UserPreferencesProvider,
private val session: Session,
private val noticeItemFactory: NoticeItemFactory) { private val noticeItemFactory: NoticeItemFactory) {
fun create(event: TimelineEvent, callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? { fun create(event: TimelineEvent, callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? {
val createRoomContent = event.root.getClearContent().toModel<RoomCreateContent>() ?: return null val createRoomContent = event.root.getClearContent().toModel<RoomCreateContent>() ?: return null
val predecessorId = createRoomContent.predecessor?.roomId ?: return defaultRendering(event, callback) val predecessorId = createRoomContent.predecessor?.roomId ?: return defaultRendering(event, callback)
val roomLink = PermalinkFactory.createPermalink(predecessorId) ?: return null val roomLink = session.permalinkService().createRoomPermalink(predecessorId) ?: return null
val text = span { val text = span {
+stringProvider.getString(R.string.room_tombstone_continuation_description) +stringProvider.getString(R.string.room_tombstone_continuation_description)
+"\n" +"\n"

View file

@ -26,12 +26,12 @@ import im.vector.app.core.utils.EvenBetterLinkMovementMethod
import im.vector.app.core.utils.isValidUrl import im.vector.app.core.utils.isValidUrl
import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.html.PillImageSpan import im.vector.app.features.html.PillImageSpan
import org.matrix.android.sdk.api.permalinks.MatrixLinkify
import org.matrix.android.sdk.api.permalinks.MatrixPermalinkSpan
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.session.permalinks.MatrixLinkify
import org.matrix.android.sdk.api.session.permalinks.MatrixPermalinkSpan
fun CharSequence.findPillsAndProcess(scope: CoroutineScope, processBlock: (PillImageSpan) -> Unit) { fun CharSequence.findPillsAndProcess(scope: CoroutineScope, processBlock: (PillImageSpan) -> Unit) {
scope.launch(Dispatchers.Main) { scope.launch(Dispatchers.Main) {

View file

@ -21,15 +21,15 @@ import android.text.style.URLSpan
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.glide.GlideRequests import im.vector.app.core.glide.GlideRequests
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import org.matrix.android.sdk.api.permalinks.PermalinkData
import org.matrix.android.sdk.api.permalinks.PermalinkParser
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.util.MatrixItem
import io.noties.markwon.MarkwonVisitor import io.noties.markwon.MarkwonVisitor
import io.noties.markwon.SpannableBuilder import io.noties.markwon.SpannableBuilder
import io.noties.markwon.html.HtmlTag import io.noties.markwon.html.HtmlTag
import io.noties.markwon.html.MarkwonHtmlRenderer import io.noties.markwon.html.MarkwonHtmlRenderer
import io.noties.markwon.html.tag.LinkHandler import io.noties.markwon.html.tag.LinkHandler
import org.matrix.android.sdk.api.session.permalinks.PermalinkData
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.util.MatrixItem
class MxLinkTagHandler(private val glideRequests: GlideRequests, class MxLinkTagHandler(private val glideRequests: GlideRequests,
private val context: Context, private val context: Context,

View file

@ -20,12 +20,12 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.features.navigation.Navigator import im.vector.app.features.navigation.Navigator
import org.matrix.android.sdk.api.permalinks.PermalinkData
import org.matrix.android.sdk.api.permalinks.PermalinkParser
import org.matrix.android.sdk.api.util.Optional
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.matrix.android.sdk.api.session.permalinks.PermalinkData
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.rx
import javax.inject.Inject import javax.inject.Inject

View file

@ -27,8 +27,16 @@ import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.permalinks.PermalinkFactory
import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
@ -47,15 +55,6 @@ import org.matrix.android.sdk.api.util.toOptional
import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.internal.util.awaitCallback
import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.rx
import org.matrix.android.sdk.rx.unwrap import org.matrix.android.sdk.rx.unwrap
import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState, class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
@ -337,7 +336,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
} }
private fun handleShareRoomMemberProfile() { private fun handleShareRoomMemberProfile() {
PermalinkFactory.createPermalink(initialState.userId)?.let { permalink -> session.permalinkService().createPermalink(initialState.userId)?.let { permalink ->
_viewEvents.post(RoomMemberProfileViewEvents.ShareRoomMemberProfile(permalink)) _viewEvents.post(RoomMemberProfileViewEvents.ShareRoomMemberProfile(permalink))
} }
} }

View file

@ -22,8 +22,11 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.permalinks.PermalinkFactory
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
@ -31,10 +34,6 @@ import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.rx
import org.matrix.android.sdk.rx.unwrap import org.matrix.android.sdk.rx.unwrap
import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import java.util.UUID import java.util.UUID
class RoomProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomProfileViewState, class RoomProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomProfileViewState,
@ -118,7 +117,8 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted private val ini
} }
private fun handleShareRoomProfile() { private fun handleShareRoomProfile() {
PermalinkFactory.createPermalink(initialState.roomId)?.let { permalink -> session.permalinkService().createRoomPermalink(initialState.roomId)
?.let { permalink ->
_viewEvents.post(RoomProfileViewEvents.ShareRoomProfile(permalink)) _viewEvents.post(RoomProfileViewEvents.ShareRoomProfile(permalink))
} }
} }