Merge branch 'develop' into feature/bca/rust_flavor

This commit is contained in:
valere 2023-04-13 15:49:20 +02:00
commit 0e503503d2
17 changed files with 69 additions and 31 deletions

View file

@ -23,7 +23,7 @@ jobs:
- name: Run Emoji script - name: Run Emoji script
run: ./tools/import_emojis.py run: ./tools/import_emojis.py
- name: Create Pull Request for Emojis - name: Create Pull Request for Emojis
uses: peter-evans/create-pull-request@v4 uses: peter-evans/create-pull-request@v5
with: with:
commit-message: Sync Emojis commit-message: Sync Emojis
title: Sync Emojis title: Sync Emojis
@ -49,7 +49,7 @@ jobs:
- name: Run SAS String script - name: Run SAS String script
run: ./tools/import_sas_strings.py run: ./tools/import_sas_strings.py
- name: Create Pull Request for SAS Strings - name: Create Pull Request for SAS Strings
uses: peter-evans/create-pull-request@v4 uses: peter-evans/create-pull-request@v5
with: with:
commit-message: Sync SAS Strings commit-message: Sync SAS Strings
title: Sync SAS Strings title: Sync SAS Strings
@ -68,7 +68,7 @@ jobs:
- name: Run analytics import script - name: Run analytics import script
run: ./tools/import_analytic_plan.sh run: ./tools/import_analytic_plan.sh
- name: Create Pull Request for analytics plan - name: Create Pull Request for analytics plan
uses: peter-evans/create-pull-request@v4 uses: peter-evans/create-pull-request@v5
with: with:
commit-message: Sync analytics plan commit-message: Sync analytics plan
title: Sync analytics plan title: Sync analytics plan

2
changelog.d/8307.bugfix Normal file
View file

@ -0,0 +1,2 @@
The new permalink rendering is not applied on permalink created with the potential clientPermalinkBaseUrl

1
changelog.d/8313.bugfix Normal file
View file

@ -0,0 +1 @@
Keep screen on while recording voicebroadcast

View file

@ -48,4 +48,8 @@ class TestPermalinkService : PermalinkService {
MARKDOWN -> "[%2\$s](https://matrix.to/#/%1\$s)" MARKDOWN -> "[%2\$s](https://matrix.to/#/%1\$s)"
} }
} }
override fun isPermalinkSupported(supportedHosts: Array<String>, url: String): Boolean {
return false
}
} }

View file

@ -62,7 +62,7 @@ object MatrixPatterns {
// regex pattern to find permalink with message id. // regex pattern to find permalink with message id.
// Android does not support in URL so extract it. // Android does not support in URL so extract it.
private const val PERMALINK_BASE_REGEX = "https://matrix\\.to/#/" private const val PERMALINK_BASE_REGEX = "https://matrix\\.to/#/"
private const val APP_BASE_REGEX = "https://[A-Z0-9.-]+\\.[A-Z]{2,}/[A-Z]{3,}/#/room/" private const val APP_BASE_REGEX = "https://[A-Z0-9.-]+\\.[A-Z]{2,}/#/(room|user)/"
const val SEP_REGEX = "/" const val SEP_REGEX = "/"
private val PATTERN_CONTAIN_MATRIX_TO_PERMALINK = PERMALINK_BASE_REGEX.toRegex(RegexOption.IGNORE_CASE) private val PATTERN_CONTAIN_MATRIX_TO_PERMALINK = PERMALINK_BASE_REGEX.toRegex(RegexOption.IGNORE_CASE)

View file

@ -97,4 +97,15 @@ interface PermalinkService {
* @return the created template * @return the created template
*/ */
fun createMentionSpanTemplate(type: SpanTemplateType, forceMatrixTo: Boolean = false): String fun createMentionSpanTemplate(type: SpanTemplateType, forceMatrixTo: Boolean = false): String
/**
* Check if the url is a permalink. It must be a matrix.to link
* or a link with host provided by the string-array `permalink_supported_hosts` in the config file
*
* @param supportedHosts the list of hosts supported for permalinks
* @param url the link to check, Ex: "https://matrix.to/#/@benoit:matrix.org"
*
* @return true when url is a permalink
*/
fun isPermalinkSupported(supportedHosts: Array<String>, url: String): Boolean
} }

View file

@ -16,6 +16,7 @@
package org.matrix.android.sdk.internal.session.permalinks package org.matrix.android.sdk.internal.session.permalinks
import androidx.core.net.toUri
import org.matrix.android.sdk.api.session.events.model.Event 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
import javax.inject.Inject import javax.inject.Inject
@ -47,4 +48,9 @@ internal class DefaultPermalinkService @Inject constructor(
override fun createMentionSpanTemplate(type: PermalinkService.SpanTemplateType, forceMatrixTo: Boolean): String { override fun createMentionSpanTemplate(type: PermalinkService.SpanTemplateType, forceMatrixTo: Boolean): String {
return permalinkFactory.createMentionSpanTemplate(type, forceMatrixTo) return permalinkFactory.createMentionSpanTemplate(type, forceMatrixTo)
} }
override fun isPermalinkSupported(supportedHosts: Array<String>, url: String): Boolean {
return url.startsWith(PermalinkService.MATRIX_TO_URL_BASE) ||
supportedHosts.any { url.toUri().host == it }
}
} }

View file

@ -1333,13 +1333,17 @@ class TimelineViewModel @AssistedInject constructor(
computeUnreadState(timelineEvents, roomSummary) computeUnreadState(timelineEvents, roomSummary)
} }
// We don't want live update of unread so we skip when we already had a HasUnread or HasNoUnread // We don't want live update of unread so we skip when we already had a HasUnread or HasNoUnread
// However, we want to update an existing HasUnread, if the readMarkerId hasn't changed, // However, we want to update an existing HasUnread, if the readMarkerId hasn't changed or when we go back in live,
// as we might be loading new events to fill gaps in the timeline. // as we might be loading new events to fill gaps in the timeline.
.distinctUntilChanged { previous, current -> .distinctUntilChanged { previous, current ->
when { when {
previous is UnreadState.Unknown || previous is UnreadState.ReadMarkerNotLoaded -> false previous is UnreadState.Unknown || previous is UnreadState.ReadMarkerNotLoaded -> false
previous is UnreadState.HasUnread && current is UnreadState.HasUnread && previous is UnreadState.HasUnread && current is UnreadState.HasUnread &&
previous.readMarkerId == current.readMarkerId -> false previous.readMarkerId == current.readMarkerId -> false
previous is UnreadState.HasUnread && (
current is UnreadState.HasUnread && previous.firstUnreadEventId != current.firstUnreadEventId ||
current is UnreadState.HasNoUnread
) && timeline?.isLive.orFalse() -> false
current is UnreadState.HasUnread || current is UnreadState.HasNoUnread -> true current is UnreadState.HasUnread || current is UnreadState.HasNoUnread -> true
else -> false else -> false
} }

View file

@ -239,7 +239,7 @@ class AudioMessageHelper @Inject constructor(
val percentage = currentPosition.toFloat() / totalDuration val percentage = currentPosition.toFloat() / totalDuration
playbackTracker.updatePlayingAtPlaybackTime(id, currentPosition, percentage) playbackTracker.updatePlayingAtPlaybackTime(id, currentPosition, percentage)
} else { } else {
playbackTracker.stopPlayback(id) playbackTracker.stopPlaybackOrRecorder(id)
stopPlaybackTicker() stopPlaybackTicker()
} }
} }

View file

@ -100,7 +100,7 @@ class AudioMessagePlaybackTracker @Inject constructor() {
} }
} }
fun stopPlayback(id: String) { fun stopPlaybackOrRecorder(id: String) {
val state = getPlaybackState(id) val state = getPlaybackState(id)
if (state !is Listener.State.Error) { if (state !is Listener.State.Error) {
setState(id, Listener.State.Idle) setState(id, Listener.State.Idle)

View file

@ -29,7 +29,7 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.glide.GlideApp import im.vector.app.core.glide.GlideApp
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.html.PillImageSpan import im.vector.app.features.html.PillImageSpan
import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.getRoomSummary import org.matrix.android.sdk.api.session.getRoomSummary
import org.matrix.android.sdk.api.session.getUserOrDefault import org.matrix.android.sdk.api.session.getUserOrDefault
import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.permalinks.PermalinkData
@ -99,7 +99,9 @@ class EventTextRenderer @AssistedInject constructor(
private fun addPermalinksSpans(text: Spannable) { private fun addPermalinksSpans(text: Spannable) {
for (match in Patterns.WEB_URL.toRegex().findAll(text)) { for (match in Patterns.WEB_URL.toRegex().findAll(text)) {
val url = text.substring(match.range) val url = text.substring(match.range)
val matrixItem = if (MatrixPatterns.isPermalink(url)) { val supportedHosts = context.resources.getStringArray(R.array.permalink_supported_hosts)
val isPermalinkSupported = sessionHolder.getSafeActiveSession()?.permalinkService()?.isPermalinkSupported(supportedHosts, url).orFalse()
val matrixItem = if (isPermalinkSupported) {
when (val permalinkData = PermalinkParser.parse(url)) { when (val permalinkData = PermalinkParser.parse(url)) {
is PermalinkData.UserLink -> permalinkData.toMatrixItem() is PermalinkData.UserLink -> permalinkData.toMatrixItem()
is PermalinkData.RoomLink -> permalinkData.toMatrixItem() is PermalinkData.RoomLink -> permalinkData.toMatrixItem()

View file

@ -22,10 +22,12 @@ import android.text.Spanned
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.glide.GlideApp import im.vector.app.core.glide.GlideApp
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import io.noties.markwon.core.spans.LinkSpan import io.noties.markwon.core.spans.LinkSpan
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.getRoomSummary import org.matrix.android.sdk.api.session.getRoomSummary
import org.matrix.android.sdk.api.session.getUser import org.matrix.android.sdk.api.session.getUser
import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.permalinks.PermalinkData
@ -105,12 +107,18 @@ class PillsPostProcessor @AssistedInject constructor(
PillImageSpan(GlideApp.with(context), avatarRenderer, context, matrixItem) PillImageSpan(GlideApp.with(context), avatarRenderer, context, matrixItem)
private fun LinkSpan.createPillSpan(): PillImageSpan? { private fun LinkSpan.createPillSpan(): PillImageSpan? {
val matrixItem = when (val permalinkData = PermalinkParser.parse(url)) { val supportedHosts = context.resources.getStringArray(R.array.permalink_supported_hosts)
is PermalinkData.UserLink -> permalinkData.toMatrixItem() val isPermalinkSupported = sessionHolder.getSafeActiveSession()?.permalinkService()?.isPermalinkSupported(supportedHosts, url).orFalse()
is PermalinkData.RoomLink -> permalinkData.toMatrixItem() if (isPermalinkSupported) {
else -> null val matrixItem = when (val permalinkData = PermalinkParser.parse(url)) {
} ?: return null is PermalinkData.UserLink -> permalinkData.toMatrixItem()
return createPillImageSpan(matrixItem) is PermalinkData.RoomLink -> permalinkData.toMatrixItem()
else -> null
} ?: return null
return createPillImageSpan(matrixItem)
} else {
return null
}
} }
private fun PermalinkData.UserLink.toMatrixItem(): MatrixItem? = private fun PermalinkData.UserLink.toMatrixItem(): MatrixItem? =

View file

@ -18,7 +18,6 @@ package im.vector.app.features.permalink
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
@ -38,7 +37,6 @@ import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.getRoomSummary import org.matrix.android.sdk.api.session.getRoomSummary
import org.matrix.android.sdk.api.session.permalinks.PermalinkData 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.permalinks.PermalinkParser
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import org.matrix.android.sdk.api.session.room.getTimelineEvent import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -68,10 +66,11 @@ class PermalinkHandler @Inject constructor(
navigationInterceptor: NavigationInterceptor? = null, navigationInterceptor: NavigationInterceptor? = null,
buildTask: Boolean = false buildTask: Boolean = false
): Boolean { ): Boolean {
val supportedHosts = fragmentActivity.resources.getStringArray(R.array.permalink_supported_hosts)
return when { return when {
deepLink == null -> false deepLink == null -> false
deepLink.isIgnored() -> true deepLink.isIgnored() -> true
!isPermalinkSupported(fragmentActivity, deepLink.toString()) -> false !activeSessionHolder.getSafeActiveSession()?.permalinkService()?.isPermalinkSupported(supportedHosts, deepLink.toString()).orFalse() -> false
else -> { else -> {
tryOrNull { tryOrNull {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
@ -167,12 +166,6 @@ class PermalinkHandler @Inject constructor(
} }
} }
private fun isPermalinkSupported(context: Context, url: String): Boolean {
return url.startsWith(PermalinkService.MATRIX_TO_URL_BASE) ||
context.resources.getStringArray(R.array.permalink_supported_hosts)
.any { url.toUri().host == it }
}
private suspend fun PermalinkData.RoomLink.getRoomId(): String? { private suspend fun PermalinkData.RoomLink.getRoomId(): String? {
val session = activeSessionHolder.getSafeActiveSession() val session = activeSessionHolder.getSafeActiveSession()
return if (isRoomAlias && session != null) { return if (isRoomAlias && session != null) {

View file

@ -527,7 +527,7 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
State.Idle -> { State.Idle -> {
// restart the playback time if player completed with less than 1s remaining time // restart the playback time if player completed with less than 1s remaining time
if (percentage == null || (playlist.duration - position) < 1000) { if (percentage == null || (playlist.duration - position) < 1000) {
playbackTracker.stopPlayback(id) playbackTracker.stopPlaybackOrRecorder(id)
} else { } else {
playbackTracker.updatePausedAtPlaybackTime(id, position, percentage) playbackTracker.updatePausedAtPlaybackTime(id, position, percentage)
} }

View file

@ -20,6 +20,7 @@ import android.content.Context
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import im.vector.app.core.resources.BuildMeta import im.vector.app.core.resources.BuildMeta
import im.vector.app.features.attachments.toContentAttachmentData import im.vector.app.features.attachments.toContentAttachmentData
import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
import im.vector.app.features.voicebroadcast.VoiceBroadcastFailure import im.vector.app.features.voicebroadcast.VoiceBroadcastFailure
@ -54,6 +55,7 @@ import javax.inject.Inject
class StartVoiceBroadcastUseCase @Inject constructor( class StartVoiceBroadcastUseCase @Inject constructor(
private val session: Session, private val session: Session,
private val voiceBroadcastRecorder: VoiceBroadcastRecorder?, private val voiceBroadcastRecorder: VoiceBroadcastRecorder?,
private val playbackTracker: AudioMessagePlaybackTracker,
private val context: Context, private val context: Context,
private val buildMeta: BuildMeta, private val buildMeta: BuildMeta,
private val getRoomLiveVoiceBroadcastsUseCase: GetRoomLiveVoiceBroadcastsUseCase, private val getRoomLiveVoiceBroadcastsUseCase: GetRoomLiveVoiceBroadcastsUseCase,
@ -106,10 +108,14 @@ class StartVoiceBroadcastUseCase @Inject constructor(
} }
override fun onStateUpdated(state: VoiceBroadcastRecorder.State) { override fun onStateUpdated(state: VoiceBroadcastRecorder.State) {
if (state == VoiceBroadcastRecorder.State.Error) { when (state) {
session.coroutineScope.launch { VoiceBroadcastRecorder.State.Recording -> playbackTracker.updateCurrentRecording(AudioMessagePlaybackTracker.RECORDING_ID, emptyList())
pauseVoiceBroadcastUseCase.execute(room.roomId) VoiceBroadcastRecorder.State.Idle -> playbackTracker.stopPlaybackOrRecorder(AudioMessagePlaybackTracker.RECORDING_ID)
VoiceBroadcastRecorder.State.Error -> {
playbackTracker.stopPlaybackOrRecorder(AudioMessagePlaybackTracker.RECORDING_ID)
session.coroutineScope.launch { pauseVoiceBroadcastUseCase.execute(room.roomId) }
} }
else -> Unit
} }
} }
}) })

View file

@ -47,8 +47,7 @@ class GetVoiceBroadcastStateEventLiveUseCase @Inject constructor(
) { ) {
fun execute(voiceBroadcast: VoiceBroadcast): Flow<Optional<VoiceBroadcastEvent>> { fun execute(voiceBroadcast: VoiceBroadcast): Flow<Optional<VoiceBroadcastEvent>> {
val room = session.getRoom(voiceBroadcast.roomId) ?: error("Unknown roomId: ${voiceBroadcast.roomId}") return getMostRecentVoiceBroadcastEventFlow(voiceBroadcast)
return getMostRecentVoiceBroadcastEventFlow(room, voiceBroadcast)
.onEach { event -> .onEach { event ->
Timber.d( Timber.d(
"## VoiceBroadcast | " + "## VoiceBroadcast | " +
@ -61,7 +60,8 @@ class GetVoiceBroadcastStateEventLiveUseCase @Inject constructor(
/** /**
* Get a flow of the most recent event for the given voice broadcast. * Get a flow of the most recent event for the given voice broadcast.
*/ */
private fun getMostRecentVoiceBroadcastEventFlow(room: Room, voiceBroadcast: VoiceBroadcast): Flow<Optional<VoiceBroadcastEvent>> { private fun getMostRecentVoiceBroadcastEventFlow(voiceBroadcast: VoiceBroadcast): Flow<Optional<VoiceBroadcastEvent>> {
val room = session.getRoom(voiceBroadcast.roomId) ?: error("Unknown roomId: ${voiceBroadcast.roomId}")
val startedEventFlow = room.flow().liveTimelineEvent(voiceBroadcast.voiceBroadcastId) val startedEventFlow = room.flow().liveTimelineEvent(voiceBroadcast.voiceBroadcastId)
// observe started event changes // observe started event changes
return startedEventFlow return startedEventFlow

View file

@ -57,6 +57,7 @@ class StartVoiceBroadcastUseCaseTest {
StartVoiceBroadcastUseCase( StartVoiceBroadcastUseCase(
session = fakeSession, session = fakeSession,
voiceBroadcastRecorder = fakeVoiceBroadcastRecorder, voiceBroadcastRecorder = fakeVoiceBroadcastRecorder,
playbackTracker = mockk(),
context = FakeContext().instance, context = FakeContext().instance,
buildMeta = mockk(), buildMeta = mockk(),
getRoomLiveVoiceBroadcastsUseCase = fakeGetRoomLiveVoiceBroadcastsUseCase, getRoomLiveVoiceBroadcastsUseCase = fakeGetRoomLiveVoiceBroadcastsUseCase,