mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
Merge branch 'develop' into michaelk/sonarqube_fixes
This commit is contained in:
commit
9f4f3f482c
1
changelog.d/6123.wip
Normal file
1
changelog.d/6123.wip
Normal file
@ -0,0 +1 @@
|
||||
[Live location sharing] Update entity in DB when a live is timed out
|
1
changelog.d/6140.bugfix
Normal file
1
changelog.d/6140.bugfix
Normal file
@ -0,0 +1 @@
|
||||
Prevent widget web view from reloading on screen / orientation change
|
@ -40,6 +40,9 @@ class RetryTestRule(val retryCount: Int = 3) : TestRule {
|
||||
for (i in 0 until retryCount) {
|
||||
try {
|
||||
base.evaluate()
|
||||
if (i > 0) {
|
||||
println("Retried test $i times")
|
||||
}
|
||||
return
|
||||
} catch (t: Throwable) {
|
||||
caughtThrowable = t
|
||||
|
@ -69,7 +69,7 @@ import java.util.concurrent.CountDownLatch
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@LargeTest
|
||||
@Ignore
|
||||
@Ignore("This test fails with an unhandled exception thrown from a coroutine which terminates the entire test run.")
|
||||
class E2eeSanityTests : InstrumentedTest {
|
||||
|
||||
@get:Rule val rule = RetryTestRule(3)
|
||||
|
@ -25,6 +25,7 @@ import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
@ -46,6 +47,7 @@ import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.RetryTestRule
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.common.TestMatrixCallback
|
||||
import java.util.Collections
|
||||
@ -57,6 +59,8 @@ import java.util.concurrent.CountDownLatch
|
||||
@Ignore
|
||||
class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
@get:Rule val rule = RetryTestRule(3)
|
||||
|
||||
/**
|
||||
* - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
|
||||
* - Check backup keys after having marked one as backed up
|
||||
|
@ -22,6 +22,9 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocati
|
||||
* Aggregation info concerning a live location share.
|
||||
*/
|
||||
data class LiveLocationShareAggregatedSummary(
|
||||
/**
|
||||
* Indicate whether the live is currently running.
|
||||
*/
|
||||
val isActive: Boolean?,
|
||||
val endOfLiveTimestampMillis: Long?,
|
||||
val lastLocationDataContent: MessageBeaconLocationDataContent?,
|
||||
|
@ -31,6 +31,9 @@ internal open class LiveLocationShareAggregatedSummaryEntity(
|
||||
|
||||
var roomId: String = "",
|
||||
|
||||
/**
|
||||
* Indicate whether the live is currently running.
|
||||
*/
|
||||
var isActive: Boolean? = null,
|
||||
|
||||
var endOfLiveTimestampMillis: Long? = null,
|
||||
|
@ -55,3 +55,11 @@ internal fun LiveLocationShareAggregatedSummaryEntity.Companion.getOrCreate(
|
||||
return LiveLocationShareAggregatedSummaryEntity.where(realm, roomId, eventId).findFirst()
|
||||
?: LiveLocationShareAggregatedSummaryEntity.create(realm, roomId, eventId)
|
||||
}
|
||||
|
||||
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.get(
|
||||
realm: Realm,
|
||||
roomId: String,
|
||||
eventId: String,
|
||||
): LiveLocationShareAggregatedSummaryEntity? {
|
||||
return LiveLocationShareAggregatedSummaryEntity.where(realm, roomId, eventId).findFirst()
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ import org.matrix.android.sdk.internal.session.profile.ProfileModule
|
||||
import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker
|
||||
import org.matrix.android.sdk.internal.session.pushers.PushersModule
|
||||
import org.matrix.android.sdk.internal.session.room.RoomModule
|
||||
import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.DeactivateLiveLocationShareWorker
|
||||
import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker
|
||||
import org.matrix.android.sdk.internal.session.room.send.RedactEventWorker
|
||||
import org.matrix.android.sdk.internal.session.room.send.SendEventWorker
|
||||
@ -131,6 +132,8 @@ internal interface SessionComponent {
|
||||
|
||||
fun inject(worker: UpdateTrustWorker)
|
||||
|
||||
fun inject(worker: DeactivateLiveLocationShareWorker)
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
fun create(
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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.aggregation.livelocation
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.WorkerParameters
|
||||
import com.squareup.moshi.JsonClass
|
||||
import io.realm.RealmConfiguration
|
||||
import org.matrix.android.sdk.api.util.md5
|
||||
import org.matrix.android.sdk.internal.SessionManager
|
||||
import org.matrix.android.sdk.internal.database.awaitTransaction
|
||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.query.get
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.session.SessionComponent
|
||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Worker dedicated to update live location summary data so that it is considered as deactivated.
|
||||
* For the context: it is needed since a live location share should be deactivated after a certain timeout.
|
||||
*/
|
||||
internal class DeactivateLiveLocationShareWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
|
||||
SessionSafeCoroutineWorker<DeactivateLiveLocationShareWorker.Params>(
|
||||
context,
|
||||
params,
|
||||
sessionManager,
|
||||
Params::class.java
|
||||
) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class Params(
|
||||
override val sessionId: String,
|
||||
override val lastFailureMessage: String? = null,
|
||||
val eventId: String,
|
||||
val roomId: String
|
||||
) : SessionWorkerParams
|
||||
|
||||
@SessionDatabase
|
||||
@Inject lateinit var realmConfiguration: RealmConfiguration
|
||||
|
||||
override fun injectWith(injector: SessionComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override suspend fun doSafeWork(params: Params): Result {
|
||||
return runCatching {
|
||||
deactivateLiveLocationShare(params)
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
Result.success()
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e("failed to deactivate live, eventId: ${params.eventId}, roomId: ${params.roomId}")
|
||||
Result.failure()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun deactivateLiveLocationShare(params: Params) {
|
||||
awaitTransaction(realmConfiguration) { realm ->
|
||||
val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.get(
|
||||
realm = realm,
|
||||
roomId = params.roomId,
|
||||
eventId = params.eventId
|
||||
)
|
||||
aggregatedSummary?.isActive = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun buildErrorParams(params: Params, message: String): Params {
|
||||
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getWorkName(eventId: String, roomId: String): String {
|
||||
val hash = "$eventId$roomId".md5()
|
||||
return "DeactivateLiveLocationWork-$hash"
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
|
||||
package org.matrix.android.sdk.internal.session.room.aggregation.livelocation
|
||||
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.extensions.orTrue
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
@ -26,17 +27,27 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocati
|
||||
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.di.SessionId
|
||||
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class LiveLocationAggregationProcessor @Inject constructor() {
|
||||
internal class LiveLocationAggregationProcessor @Inject constructor(
|
||||
@SessionId private val sessionId: String,
|
||||
private val workManagerProvider: WorkManagerProvider,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
fun handleBeaconInfo(realm: Realm, event: Event, content: MessageBeaconInfoContent, roomId: String, isLocalEcho: Boolean) {
|
||||
if (event.senderId.isNullOrEmpty() || isLocalEcho) {
|
||||
return
|
||||
}
|
||||
|
||||
val targetEventId = if (content.isLive.orTrue()) {
|
||||
val isLive = content.isLive.orTrue()
|
||||
val targetEventId = if (isLive) {
|
||||
event.eventId
|
||||
} else {
|
||||
// when live is set to false, we use the id of the event that should have been replaced
|
||||
@ -56,8 +67,39 @@ internal class LiveLocationAggregationProcessor @Inject constructor() {
|
||||
|
||||
Timber.d("updating summary of id=$targetEventId with isLive=${content.isLive}")
|
||||
|
||||
aggregatedSummary.endOfLiveTimestampMillis = content.getBestTimestampMillis()?.let { it + (content.timeout ?: 0) }
|
||||
aggregatedSummary.isActive = content.isLive
|
||||
val endOfLiveTimestampMillis = content.getBestTimestampMillis()?.let { it + (content.timeout ?: 0) }
|
||||
aggregatedSummary.endOfLiveTimestampMillis = endOfLiveTimestampMillis
|
||||
aggregatedSummary.isActive = isLive
|
||||
|
||||
if (isLive) {
|
||||
scheduleDeactivationAfterTimeout(targetEventId, roomId, endOfLiveTimestampMillis)
|
||||
} else {
|
||||
cancelDeactivationAfterTimeout(targetEventId, roomId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun scheduleDeactivationAfterTimeout(eventId: String, roomId: String, endOfLiveTimestampMillis: Long?) {
|
||||
endOfLiveTimestampMillis ?: return
|
||||
|
||||
val workParams = DeactivateLiveLocationShareWorker.Params(sessionId = sessionId, eventId = eventId, roomId = roomId)
|
||||
val workData = WorkerParamsFactory.toData(workParams)
|
||||
val workName = DeactivateLiveLocationShareWorker.getWorkName(eventId = eventId, roomId = roomId)
|
||||
val workDelayMillis = (endOfLiveTimestampMillis - clock.epochMillis()).coerceAtLeast(0)
|
||||
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<DeactivateLiveLocationShareWorker>()
|
||||
.setInitialDelay(workDelayMillis, TimeUnit.MILLISECONDS)
|
||||
.setInputData(workData)
|
||||
.build()
|
||||
|
||||
workManagerProvider.workManager.enqueueUniqueWork(
|
||||
workName,
|
||||
ExistingWorkPolicy.REPLACE,
|
||||
workRequest
|
||||
)
|
||||
}
|
||||
|
||||
private fun cancelDeactivationAfterTimeout(eventId: String, roomId: String) {
|
||||
val workName = DeactivateLiveLocationShareWorker.getWorkName(eventId = eventId, roomId = roomId)
|
||||
workManagerProvider.workManager.cancelUniqueWork(workName)
|
||||
}
|
||||
|
||||
fun handleBeaconLocationData(
|
||||
|
@ -27,6 +27,7 @@ import org.matrix.android.sdk.internal.di.MatrixScope
|
||||
import org.matrix.android.sdk.internal.session.content.UploadContentWorker
|
||||
import org.matrix.android.sdk.internal.session.group.GetGroupDataWorker
|
||||
import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker
|
||||
import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.DeactivateLiveLocationShareWorker
|
||||
import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker
|
||||
import org.matrix.android.sdk.internal.session.room.send.RedactEventWorker
|
||||
import org.matrix.android.sdk.internal.session.room.send.SendEventWorker
|
||||
@ -64,9 +65,11 @@ internal class MatrixWorkerFactory @Inject constructor(private val sessionManage
|
||||
SyncWorker(appContext, workerParameters, sessionManager)
|
||||
UpdateTrustWorker::class.java.name ->
|
||||
UpdateTrustWorker(appContext, workerParameters, sessionManager)
|
||||
UploadContentWorker::class.java.name ->
|
||||
UploadContentWorker::class.java.name ->
|
||||
UploadContentWorker(appContext, workerParameters, sessionManager)
|
||||
else -> {
|
||||
DeactivateLiveLocationShareWorker::class.java.name ->
|
||||
DeactivateLiveLocationShareWorker(appContext, workerParameters, sessionManager)
|
||||
else -> {
|
||||
Timber.w("No worker defined on MatrixWorkerFactory for $workerClassName will delegate to default.")
|
||||
// Return null to delegate to the default WorkerFactory.
|
||||
null
|
||||
|
@ -306,7 +306,9 @@
|
||||
android:supportsPictureInPicture="true" />
|
||||
|
||||
<activity android:name=".features.terms.ReviewTermsActivity" />
|
||||
<activity android:name=".features.widgets.WidgetActivity" />
|
||||
<activity android:name=".features.widgets.WidgetActivity"
|
||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" />
|
||||
|
||||
<activity android:name=".features.pin.PinActivity" />
|
||||
<activity android:name=".features.analytics.ui.consent.AnalyticsOptInActivity" />
|
||||
<activity android:name=".features.home.room.detail.search.SearchActivity" />
|
||||
|
@ -33,7 +33,6 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocation
|
||||
import im.vector.app.features.location.INITIAL_MAP_ZOOM_IN_TIMELINE
|
||||
import im.vector.app.features.location.UrlMapProvider
|
||||
import im.vector.app.features.location.toLocationData
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.threeten.bp.LocalDateTime
|
||||
@ -129,7 +128,7 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
|
||||
private fun getViewState(liveLocationShareSummaryData: LiveLocationShareSummaryData?): LiveLocationShareViewState {
|
||||
return when {
|
||||
liveLocationShareSummaryData?.isActive == null -> LiveLocationShareViewState.Unkwown
|
||||
liveLocationShareSummaryData.isActive.not() || isLiveTimedOut(liveLocationShareSummaryData) -> LiveLocationShareViewState.Inactive
|
||||
liveLocationShareSummaryData.isActive.not() -> LiveLocationShareViewState.Inactive
|
||||
liveLocationShareSummaryData.isActive && liveLocationShareSummaryData.lastGeoUri.isNullOrEmpty() -> LiveLocationShareViewState.Loading
|
||||
else ->
|
||||
LiveLocationShareViewState.Running(
|
||||
@ -139,16 +138,6 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
|
||||
}.also { viewState -> Timber.d("computed viewState: $viewState") }
|
||||
}
|
||||
|
||||
private fun isLiveTimedOut(liveLocationShareSummaryData: LiveLocationShareSummaryData): Boolean {
|
||||
return getEndOfLiveDateTime(liveLocationShareSummaryData)
|
||||
?.let { endOfLive ->
|
||||
// this will only cover users with different timezones but not users with manually time set
|
||||
val now = LocalDateTime.now()
|
||||
now.isAfter(endOfLive)
|
||||
}
|
||||
.orFalse()
|
||||
}
|
||||
|
||||
private fun getEndOfLiveDateTime(liveLocationShareSummaryData: LiveLocationShareSummaryData): LocalDateTime? {
|
||||
return liveLocationShareSummaryData.endOfLiveTimestampMillis?.let { DateProvider.toLocalDateTime(timestamp = it) }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user