mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 17:35:54 +03:00
Merge pull request #5639 from vector-im/feature/dla/uisi_match_web_implementation
Align Autorageshake with web implementation
This commit is contained in:
commit
454a65602b
10 changed files with 69 additions and 95 deletions
1
changelog.d/5596.bugfix
Normal file
1
changelog.d/5596.bugfix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Align auto-reporting of decryption errors implementation with web client.
|
1
changelog.d/5639.sdk
Normal file
1
changelog.d/5639.sdk
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Include original event in live decryption listeners and update sync status naming to InitialSyncProgressing for clarity.
|
|
@ -25,9 +25,9 @@ interface LiveEventListener {
|
||||||
|
|
||||||
fun onPaginatedEvent(roomId: String, event: Event)
|
fun onPaginatedEvent(roomId: String, event: Event)
|
||||||
|
|
||||||
fun onEventDecrypted(eventId: String, roomId: String, clearEvent: JsonDict)
|
fun onEventDecrypted(event: Event, clearEvent: JsonDict)
|
||||||
|
|
||||||
fun onEventDecryptionError(eventId: String, roomId: String, throwable: Throwable)
|
fun onEventDecryptionError(event: Event, throwable: Throwable)
|
||||||
|
|
||||||
fun onLiveToDeviceEvent(event: Event)
|
fun onLiveToDeviceEvent(event: Event)
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ interface SyncStatusService {
|
||||||
abstract class InitialSyncStatus : Status()
|
abstract class InitialSyncStatus : Status()
|
||||||
|
|
||||||
object Idle : InitialSyncStatus()
|
object Idle : InitialSyncStatus()
|
||||||
data class Progressing(
|
data class InitialSyncProgressing(
|
||||||
val initSyncStep: InitSyncStep,
|
val initSyncStep: InitSyncStep,
|
||||||
val percentProgress: Int = 0
|
val percentProgress: Int = 0
|
||||||
) : InitialSyncStatus()
|
) : InitialSyncStatus()
|
||||||
|
|
|
@ -71,7 +71,7 @@ internal class StreamEventsManager @Inject constructor() {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
tryOrNull {
|
tryOrNull {
|
||||||
it.onEventDecrypted(event.eventId ?: "", event.roomId ?: "", result.clearEvent)
|
it.onEventDecrypted(event, result.clearEvent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ internal class StreamEventsManager @Inject constructor() {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
tryOrNull {
|
tryOrNull {
|
||||||
it.onEventDecryptionError(event.eventId ?: "", event.roomId ?: "", error)
|
it.onEventDecryptionError(event, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ internal class DefaultSyncStatusService @Inject constructor() :
|
||||||
// Update the progress of the leaf and all its parents
|
// Update the progress of the leaf and all its parents
|
||||||
leaf.setProgress(progress)
|
leaf.setProgress(progress)
|
||||||
// Then update the live data using leaf wording and root progress
|
// Then update the live data using leaf wording and root progress
|
||||||
status.postValue(SyncStatusService.Status.Progressing(leaf.initSyncStep, root.currentProgress.toInt()))
|
status.postValue(SyncStatusService.Status.InitialSyncProgressing(leaf.initSyncStep, root.currentProgress.toInt()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,11 @@
|
||||||
package im.vector.app
|
package im.vector.app
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import androidx.lifecycle.asFlow
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.features.rageshake.BugReporter
|
import im.vector.app.features.rageshake.BugReporter
|
||||||
import im.vector.app.features.rageshake.ReportType
|
import im.vector.app.features.rageshake.ReportType
|
||||||
|
import im.vector.app.features.session.coroutineScope
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -34,6 +36,7 @@ import kotlinx.coroutines.launch
|
||||||
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.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
|
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
@ -62,10 +65,11 @@ class AutoRageShaker @Inject constructor(
|
||||||
|
|
||||||
private val e2eDetectedFlow = MutableSharedFlow<E2EMessageDetected>(replay = 0)
|
private val e2eDetectedFlow = MutableSharedFlow<E2EMessageDetected>(replay = 0)
|
||||||
private val matchingRSRequestFlow = MutableSharedFlow<Event>(replay = 0)
|
private val matchingRSRequestFlow = MutableSharedFlow<Event>(replay = 0)
|
||||||
|
private var hasSynced = false
|
||||||
|
private var preferenceEnabled = false
|
||||||
fun initialize() {
|
fun initialize() {
|
||||||
observeActiveSession()
|
observeActiveSession()
|
||||||
enable(vectorPreferences.labsAutoReportUISI())
|
preferenceEnabled = vectorPreferences.labsAutoReportUISI()
|
||||||
// It's a singleton...
|
// It's a singleton...
|
||||||
vectorPreferences.subscribeToChanges(this)
|
vectorPreferences.subscribeToChanges(this)
|
||||||
|
|
||||||
|
@ -74,7 +78,7 @@ class AutoRageShaker @Inject constructor(
|
||||||
e2eDetectedFlow
|
e2eDetectedFlow
|
||||||
.onEach {
|
.onEach {
|
||||||
sendRageShake(it)
|
sendRageShake(it)
|
||||||
delay(2_000)
|
delay(60_000)
|
||||||
}
|
}
|
||||||
.catch { cause ->
|
.catch { cause ->
|
||||||
Timber.w(cause, "Failed to RS")
|
Timber.w(cause, "Failed to RS")
|
||||||
|
@ -84,7 +88,7 @@ class AutoRageShaker @Inject constructor(
|
||||||
matchingRSRequestFlow
|
matchingRSRequestFlow
|
||||||
.onEach {
|
.onEach {
|
||||||
sendMatchingRageShake(it)
|
sendMatchingRageShake(it)
|
||||||
delay(2_000)
|
delay(60_000)
|
||||||
}
|
}
|
||||||
.catch { cause ->
|
.catch { cause ->
|
||||||
Timber.w(cause, "Failed to send matching rageshake")
|
Timber.w(cause, "Failed to send matching rageshake")
|
||||||
|
@ -93,14 +97,7 @@ class AutoRageShaker @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||||
enable(vectorPreferences.labsAutoReportUISI())
|
preferenceEnabled = vectorPreferences.labsAutoReportUISI()
|
||||||
}
|
|
||||||
|
|
||||||
var _enabled = false
|
|
||||||
fun enable(enabled: Boolean) {
|
|
||||||
if (enabled == _enabled) return
|
|
||||||
_enabled = enabled
|
|
||||||
detector.enabled = enabled
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeActiveSession() {
|
private fun observeActiveSession() {
|
||||||
|
@ -115,7 +112,6 @@ class AutoRageShaker @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decryptionErrorDetected(target: E2EMessageDetected) {
|
fun decryptionErrorDetected(target: E2EMessageDetected) {
|
||||||
if (target.source == UISIEventSource.INITIAL_SYNC) return
|
|
||||||
if (activeSessionHolder.getSafeActiveSession()?.sessionId != currentActiveSessionId) return
|
if (activeSessionHolder.getSafeActiveSession()?.sessionId != currentActiveSessionId) return
|
||||||
val shouldSendRS = synchronized(alreadyReportedUisi) {
|
val shouldSendRS = synchronized(alreadyReportedUisi) {
|
||||||
val reportInfo = ReportInfo(target.roomId, target.sessionId)
|
val reportInfo = ReportInfo(target.roomId, target.sessionId)
|
||||||
|
@ -148,7 +144,6 @@ class AutoRageShaker @Inject constructor(
|
||||||
append("\"room_id\": \"${target.roomId}\",")
|
append("\"room_id\": \"${target.roomId}\",")
|
||||||
append("\"sender_key\": \"${target.senderKey}\",")
|
append("\"sender_key\": \"${target.senderKey}\",")
|
||||||
append("\"device_id\": \"${target.senderDeviceId}\",")
|
append("\"device_id\": \"${target.senderDeviceId}\",")
|
||||||
append("\"source\": \"${target.source}\",")
|
|
||||||
append("\"user_id\": \"${target.senderUserId}\",")
|
append("\"user_id\": \"${target.senderUserId}\",")
|
||||||
append("\"session_id\": \"${target.sessionId}\"")
|
append("\"session_id\": \"${target.sessionId}\"")
|
||||||
append("}")
|
append("}")
|
||||||
|
@ -245,6 +240,9 @@ class AutoRageShaker @Inject constructor(
|
||||||
override val reciprocateToDeviceEventType: String
|
override val reciprocateToDeviceEventType: String
|
||||||
get() = AUTO_RS_REQUEST
|
get() = AUTO_RS_REQUEST
|
||||||
|
|
||||||
|
override val enabled: Boolean
|
||||||
|
get() = this@AutoRageShaker.preferenceEnabled && this@AutoRageShaker.hasSynced
|
||||||
|
|
||||||
override fun uisiDetected(source: E2EMessageDetected) {
|
override fun uisiDetected(source: E2EMessageDetected) {
|
||||||
decryptionErrorDetected(source)
|
decryptionErrorDetected(source)
|
||||||
}
|
}
|
||||||
|
@ -261,7 +259,14 @@ class AutoRageShaker @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.currentActiveSessionId = sessionId
|
this.currentActiveSessionId = sessionId
|
||||||
this.detector.enabled = _enabled
|
|
||||||
|
hasSynced = session.hasAlreadySynced()
|
||||||
|
session.getSyncStatusLive()
|
||||||
|
.asFlow()
|
||||||
|
.onEach {
|
||||||
|
hasSynced = it !is SyncStatusService.Status.InitialSyncProgressing
|
||||||
|
}
|
||||||
|
.launchIn(session.coroutineScope)
|
||||||
activeSessionIds.add(sessionId)
|
activeSessionIds.add(sessionId)
|
||||||
session.addListener(this)
|
session.addListener(this)
|
||||||
session.addEventStreamListener(detector)
|
session.addEventStreamListener(detector)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.app
|
package im.vector.app
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.LiveEventListener
|
import org.matrix.android.sdk.api.session.LiveEventListener
|
||||||
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.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
|
@ -26,23 +27,17 @@ import java.util.Timer
|
||||||
import java.util.TimerTask
|
import java.util.TimerTask
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
enum class UISIEventSource {
|
|
||||||
INITIAL_SYNC,
|
|
||||||
INCREMENTAL_SYNC,
|
|
||||||
PAGINATION
|
|
||||||
}
|
|
||||||
|
|
||||||
data class E2EMessageDetected(
|
data class E2EMessageDetected(
|
||||||
val eventId: String,
|
val eventId: String,
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val senderUserId: String,
|
val senderUserId: String,
|
||||||
val senderDeviceId: String,
|
val senderDeviceId: String,
|
||||||
val senderKey: String,
|
val senderKey: String,
|
||||||
val sessionId: String,
|
val sessionId: String
|
||||||
val source: UISIEventSource) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromEvent(event: Event, roomId: String, source: UISIEventSource): E2EMessageDetected {
|
fun fromEvent(event: Event, roomId: String): E2EMessageDetected {
|
||||||
val encryptedContent = event.content.toModel<EncryptedEventContent>()
|
val encryptedContent = event.content.toModel<EncryptedEventContent>()
|
||||||
|
|
||||||
return E2EMessageDetected(
|
return E2EMessageDetected(
|
||||||
|
@ -51,8 +46,7 @@ data class E2EMessageDetected(
|
||||||
senderUserId = event.senderId ?: "",
|
senderUserId = event.senderId ?: "",
|
||||||
senderDeviceId = encryptedContent?.deviceId ?: "",
|
senderDeviceId = encryptedContent?.deviceId ?: "",
|
||||||
senderKey = encryptedContent?.senderKey ?: "",
|
senderKey = encryptedContent?.senderKey ?: "",
|
||||||
sessionId = encryptedContent?.sessionId ?: "",
|
sessionId = encryptedContent?.sessionId ?: ""
|
||||||
source = source
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +55,7 @@ data class E2EMessageDetected(
|
||||||
class UISIDetector : LiveEventListener {
|
class UISIDetector : LiveEventListener {
|
||||||
|
|
||||||
interface UISIDetectorCallback {
|
interface UISIDetectorCallback {
|
||||||
|
val enabled: Boolean
|
||||||
val reciprocateToDeviceEventType: String
|
val reciprocateToDeviceEventType: String
|
||||||
fun uisiDetected(source: E2EMessageDetected)
|
fun uisiDetected(source: E2EMessageDetected)
|
||||||
fun uisiReciprocateRequest(source: Event)
|
fun uisiReciprocateRequest(source: Event)
|
||||||
|
@ -68,30 +63,16 @@ class UISIDetector : LiveEventListener {
|
||||||
|
|
||||||
var callback: UISIDetectorCallback? = null
|
var callback: UISIDetectorCallback? = null
|
||||||
|
|
||||||
private val trackedEvents = mutableListOf<Pair<E2EMessageDetected, TimerTask>>()
|
private val trackedEvents = mutableMapOf<String, TimerTask>()
|
||||||
private val executor = Executors.newSingleThreadExecutor()
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
private val timer = Timer()
|
private val timer = Timer()
|
||||||
private val timeoutMillis = 30_000L
|
private val timeoutMillis = 30_000L
|
||||||
var enabled = false
|
private val enabled: Boolean get() = callback?.enabled.orFalse()
|
||||||
|
|
||||||
override fun onLiveEvent(roomId: String, event: Event) {
|
override fun onEventDecrypted(event: Event, clearEvent: JsonDict) {
|
||||||
if (!enabled) return
|
val eventId = event.eventId
|
||||||
if (!event.isEncrypted()) return
|
val roomId = event.roomId
|
||||||
executor.execute {
|
if (!enabled || eventId == null || roomId == null) return
|
||||||
handleEventReceived(E2EMessageDetected.fromEvent(event, roomId, UISIEventSource.INCREMENTAL_SYNC))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPaginatedEvent(roomId: String, event: Event) {
|
|
||||||
if (!enabled) return
|
|
||||||
if (!event.isEncrypted()) return
|
|
||||||
executor.execute {
|
|
||||||
handleEventReceived(E2EMessageDetected.fromEvent(event, roomId, UISIEventSource.PAGINATION))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onEventDecrypted(eventId: String, roomId: String, clearEvent: JsonDict) {
|
|
||||||
if (!enabled) return
|
|
||||||
executor.execute {
|
executor.execute {
|
||||||
unTrack(eventId, roomId)
|
unTrack(eventId, roomId)
|
||||||
}
|
}
|
||||||
|
@ -104,41 +85,35 @@ class UISIDetector : LiveEventListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEventDecryptionError(eventId: String, roomId: String, throwable: Throwable) {
|
override fun onEventDecryptionError(event: Event, throwable: Throwable) {
|
||||||
if (!enabled) return
|
val eventId = event.eventId
|
||||||
executor.execute {
|
val roomId = event.roomId
|
||||||
unTrack(eventId, roomId)?.let {
|
if (!enabled || eventId == null || roomId == null) return
|
||||||
triggerUISI(it)
|
|
||||||
}
|
|
||||||
// if (throwable is MXCryptoError.OlmError) {
|
|
||||||
// if (throwable.olmException.message == "UNKNOWN_MESSAGE_INDEX") {
|
|
||||||
// unTrack(eventId, roomId)?.let {
|
|
||||||
// triggerUISI(it)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleEventReceived(detectorEvent: E2EMessageDetected) {
|
val trackerId: String = trackerId(eventId, roomId)
|
||||||
if (!enabled) return
|
if (trackedEvents.containsKey(trackerId)) {
|
||||||
if (trackedEvents.any { it.first == detectorEvent }) {
|
Timber.w("## UISIDetector: Event $eventId is already tracked")
|
||||||
Timber.w("## UISIDetector: Event ${detectorEvent.eventId} is already tracked")
|
return
|
||||||
} else {
|
}
|
||||||
// track it and start timer
|
// track it and start timer
|
||||||
val timeoutTask = object : TimerTask() {
|
val timeoutTask = object : TimerTask() {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
executor.execute {
|
executor.execute {
|
||||||
unTrack(detectorEvent.eventId, detectorEvent.roomId)
|
unTrack(eventId, roomId)
|
||||||
Timber.v("## UISIDetector: Timeout on ${detectorEvent.eventId} ")
|
Timber.v("## UISIDetector: Timeout on $eventId")
|
||||||
triggerUISI(detectorEvent)
|
triggerUISI(E2EMessageDetected.fromEvent(event, roomId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trackedEvents.add(detectorEvent to timeoutTask)
|
trackedEvents[trackerId] = timeoutTask
|
||||||
timer.schedule(timeoutTask, timeoutMillis)
|
timer.schedule(timeoutTask, timeoutMillis)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
override fun onLiveEvent(roomId: String, event: Event) { }
|
||||||
|
|
||||||
|
override fun onPaginatedEvent(roomId: String, event: Event) { }
|
||||||
|
|
||||||
|
private fun trackerId(eventId: String, roomId: String): String = "$roomId-$eventId"
|
||||||
|
|
||||||
private fun triggerUISI(source: E2EMessageDetected) {
|
private fun triggerUISI(source: E2EMessageDetected) {
|
||||||
if (!enabled) return
|
if (!enabled) return
|
||||||
|
@ -146,15 +121,7 @@ class UISIDetector : LiveEventListener {
|
||||||
callback?.uisiDetected(source)
|
callback?.uisiDetected(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun unTrack(eventId: String, roomId: String): E2EMessageDetected? {
|
private fun unTrack(eventId: String, roomId: String) {
|
||||||
val index = trackedEvents.indexOfFirst { it.first.eventId == eventId && it.first.roomId == roomId }
|
trackedEvents.remove(trackerId(eventId, roomId))?.cancel()
|
||||||
return if (index != -1) {
|
|
||||||
trackedEvents.removeAt(index).let {
|
|
||||||
it.second.cancel()
|
|
||||||
it.first
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -352,7 +352,7 @@ class HomeActivity :
|
||||||
|
|
||||||
private fun renderState(state: HomeActivityViewState) {
|
private fun renderState(state: HomeActivityViewState) {
|
||||||
when (val status = state.syncStatusServiceStatus) {
|
when (val status = state.syncStatusServiceStatus) {
|
||||||
is SyncStatusService.Status.Progressing -> {
|
is SyncStatusService.Status.InitialSyncProgressing -> {
|
||||||
val initSyncStepStr = initSyncStepFormatter.format(status.initSyncStep)
|
val initSyncStepStr = initSyncStepFormatter.format(status.initSyncStep)
|
||||||
Timber.v("$initSyncStepStr ${status.percentProgress}")
|
Timber.v("$initSyncStepStr ${status.percentProgress}")
|
||||||
views.waitingView.root.setOnClickListener {
|
views.waitingView.root.setOnClickListener {
|
||||||
|
|
|
@ -179,7 +179,7 @@ class HomeActivityViewModel @AssistedInject constructor(
|
||||||
.asFlow()
|
.asFlow()
|
||||||
.onEach { status ->
|
.onEach { status ->
|
||||||
when (status) {
|
when (status) {
|
||||||
is SyncStatusService.Status.Progressing -> {
|
is SyncStatusService.Status.InitialSyncProgressing -> {
|
||||||
// Schedule a check of the bootstrap when the init sync will be finished
|
// Schedule a check of the bootstrap when the init sync will be finished
|
||||||
checkBootstrap = true
|
checkBootstrap = true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue