Merge pull request #4046 from vector-im/feature/bma/incr_sync_investigation
Incr sync investigation
This commit is contained in:
commit
b52f2b0422
|
@ -0,0 +1 @@
|
|||
Push and syncs: add debug info on room list and on room detail screen and improves the log format.
|
|
@ -0,0 +1 @@
|
|||
InitialSyncProgressService has been renamed to SyncStatusService and its function getInitialSyncProgressStatus() has been renamed to getSyncStatusLive()
|
|
@ -7,8 +7,8 @@ ext.versions = [
|
|||
'targetCompat' : JavaVersion.VERSION_11,
|
||||
]
|
||||
|
||||
// Ref: https://kotlinlang.org/releases.html
|
||||
def gradle = "7.0.2"
|
||||
// Ref: https://kotlinlang.org/releases.html
|
||||
def kotlin = "1.5.30"
|
||||
def kotlinCoroutines = "1.5.1"
|
||||
def dagger = "2.38.1"
|
||||
|
@ -55,6 +55,8 @@ ext.libs = [
|
|||
'lifecycleExtensions' : "androidx.lifecycle:lifecycle-extensions:$lifecycle",
|
||||
'lifecycleJava8' : "androidx.lifecycle:lifecycle-common-java8:$lifecycle",
|
||||
'lifecycleLivedata' : "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1",
|
||||
'datastore' : "androidx.datastore:datastore:1.0.0",
|
||||
'datastorepreferences' : "androidx.datastore:datastore-preferences:1.0.0",
|
||||
'pagingRuntimeKtx' : "androidx.paging:paging-runtime-ktx:2.1.2",
|
||||
'coreTesting' : "androidx.arch.core:core-testing:2.1.0",
|
||||
'testCore' : "androidx.test:core:$androidxTest",
|
||||
|
|
|
@ -24,6 +24,7 @@ package org.matrix.android.sdk.api.logger
|
|||
*/
|
||||
open class LoggerTag(_value: String, parentTag: LoggerTag? = null) {
|
||||
|
||||
object SYNC : LoggerTag("SYNC")
|
||||
object VOIP : LoggerTag("VOIP")
|
||||
|
||||
val value: String = if (parentTag == null) {
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.matrix.android.sdk.api.session.file.FileService
|
|||
import org.matrix.android.sdk.api.session.group.GroupService
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
|
||||
import org.matrix.android.sdk.api.session.identity.IdentityService
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
||||
import org.matrix.android.sdk.api.session.media.MediaService
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
||||
|
@ -75,7 +75,7 @@ interface Session :
|
|||
ProfileService,
|
||||
PushRuleService,
|
||||
PushersService,
|
||||
InitialSyncProgressService,
|
||||
SyncStatusService,
|
||||
HomeServerCapabilitiesService,
|
||||
SecureStorageService,
|
||||
AccountService {
|
||||
|
|
|
@ -17,15 +17,33 @@ package org.matrix.android.sdk.api.session.initsync
|
|||
|
||||
import androidx.lifecycle.LiveData
|
||||
|
||||
interface InitialSyncProgressService {
|
||||
interface SyncStatusService {
|
||||
|
||||
fun getInitialSyncProgressStatus(): LiveData<Status>
|
||||
fun getSyncStatusLive(): LiveData<Status>
|
||||
|
||||
sealed class Status {
|
||||
object Idle : Status()
|
||||
/**
|
||||
* For initial sync
|
||||
*/
|
||||
abstract class InitialSyncStatus: Status()
|
||||
|
||||
object Idle : InitialSyncStatus()
|
||||
data class Progressing(
|
||||
val initSyncStep: InitSyncStep,
|
||||
val percentProgress: Int = 0
|
||||
) : Status()
|
||||
) : InitialSyncStatus()
|
||||
|
||||
/**
|
||||
* For incremental sync
|
||||
*/
|
||||
abstract class IncrementalSyncStatus: Status()
|
||||
|
||||
object IncrementalSyncIdle : IncrementalSyncStatus()
|
||||
data class IncrementalSyncParsing(
|
||||
val rooms: Int,
|
||||
val toDevice: Int
|
||||
) : IncrementalSyncStatus()
|
||||
object IncrementalSyncError : IncrementalSyncStatus()
|
||||
object IncrementalSyncDone : IncrementalSyncStatus()
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker
|
|||
import org.matrix.android.sdk.api.session.file.FileService
|
||||
import org.matrix.android.sdk.api.session.group.GroupService
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
|
||||
import org.matrix.android.sdk.api.session.media.MediaService
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
||||
|
@ -115,7 +115,7 @@ internal class DefaultSession @Inject constructor(
|
|||
private val contentUploadProgressTracker: ContentUploadStateTracker,
|
||||
private val typingUsersTracker: TypingUsersTracker,
|
||||
private val contentDownloadStateTracker: ContentDownloadStateTracker,
|
||||
private val initialSyncProgressService: Lazy<InitialSyncProgressService>,
|
||||
private val syncStatusService: Lazy<SyncStatusService>,
|
||||
private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>,
|
||||
private val accountDataService: Lazy<SessionAccountDataService>,
|
||||
private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
|
||||
|
@ -141,7 +141,7 @@ internal class DefaultSession @Inject constructor(
|
|||
PushersService by pushersService.get(),
|
||||
EventService by eventService.get(),
|
||||
TermsService by termsService.get(),
|
||||
InitialSyncProgressService by initialSyncProgressService.get(),
|
||||
SyncStatusService by syncStatusService.get(),
|
||||
SecureStorageService by secureStorageService.get(),
|
||||
HomeServerCapabilitiesService by homeServerCapabilitiesService.get(),
|
||||
ProfileService by profileService.get(),
|
||||
|
|
|
@ -37,7 +37,7 @@ import org.matrix.android.sdk.api.session.SessionLifecycleObserver
|
|||
import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService
|
||||
import org.matrix.android.sdk.api.session.events.EventService
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.openid.OpenIdService
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
|
||||
import org.matrix.android.sdk.api.session.securestorage.SecureStorageService
|
||||
|
@ -81,7 +81,7 @@ import org.matrix.android.sdk.internal.session.download.DownloadProgressIntercep
|
|||
import org.matrix.android.sdk.internal.session.events.DefaultEventService
|
||||
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.initsync.DefaultInitialSyncProgressService
|
||||
import org.matrix.android.sdk.internal.session.initsync.DefaultSyncStatusService
|
||||
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
|
||||
import org.matrix.android.sdk.internal.session.openid.DefaultOpenIdService
|
||||
import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService
|
||||
|
@ -355,7 +355,7 @@ internal abstract class SessionModule {
|
|||
abstract fun bindEventSenderProcessorAsSessionLifecycleObserver(processor: EventSenderProcessorCoroutine): SessionLifecycleObserver
|
||||
|
||||
@Binds
|
||||
abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService
|
||||
abstract fun bindSyncStatusService(service: DefaultSyncStatusService): SyncStatusService
|
||||
|
||||
@Binds
|
||||
abstract fun bindSecureStorageService(service: DefaultSecureStorageService): SecureStorageService
|
||||
|
|
|
@ -18,23 +18,28 @@ package org.matrix.android.sdk.internal.session.initsync
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.matrix.android.sdk.api.session.initsync.InitSyncStep
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import javax.inject.Inject
|
||||
|
||||
@SessionScope
|
||||
internal class DefaultInitialSyncProgressService @Inject constructor()
|
||||
: InitialSyncProgressService,
|
||||
internal class DefaultSyncStatusService @Inject constructor()
|
||||
: SyncStatusService,
|
||||
ProgressReporter {
|
||||
|
||||
private val status = MutableLiveData<InitialSyncProgressService.Status>()
|
||||
private val status = MutableLiveData<SyncStatusService.Status>()
|
||||
|
||||
private var rootTask: TaskInfo? = null
|
||||
|
||||
override fun getInitialSyncProgressStatus(): LiveData<InitialSyncProgressService.Status> {
|
||||
override fun getSyncStatusLive(): LiveData<SyncStatusService.Status> {
|
||||
return status
|
||||
}
|
||||
|
||||
// Only to be used for incremental sync
|
||||
fun setStatus(newStatus: SyncStatusService.Status.IncrementalSyncStatus) {
|
||||
status.postValue(newStatus)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a rootTask
|
||||
*/
|
||||
|
@ -67,7 +72,7 @@ internal class DefaultInitialSyncProgressService @Inject constructor()
|
|||
// Update the progress of the leaf and all its parents
|
||||
leaf.setProgress(progress)
|
||||
// Then update the live data using leaf wording and root progress
|
||||
status.postValue(InitialSyncProgressService.Status.Progressing(leaf.initSyncStep, root.currentProgress.toInt()))
|
||||
status.postValue(SyncStatusService.Status.Progressing(leaf.initSyncStep, root.currentProgress.toInt()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,13 +87,13 @@ internal class DefaultInitialSyncProgressService @Inject constructor()
|
|||
// And close it
|
||||
endedTask.parent.child = null
|
||||
} else {
|
||||
status.postValue(InitialSyncProgressService.Status.Idle)
|
||||
status.postValue(SyncStatusService.Status.Idle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun endAll() {
|
||||
rootTask = null
|
||||
status.postValue(InitialSyncProgressService.Status.Idle)
|
||||
status.postValue(SyncStatusService.Status.Idle)
|
||||
}
|
||||
}
|
|
@ -17,7 +17,9 @@
|
|||
package org.matrix.android.sdk.internal.session.sync
|
||||
|
||||
import okhttp3.ResponseBody
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.initsync.InitSyncStep
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
|
@ -26,7 +28,7 @@ import org.matrix.android.sdk.internal.network.executeRequest
|
|||
import org.matrix.android.sdk.internal.network.toFailure
|
||||
import org.matrix.android.sdk.internal.session.filter.FilterRepository
|
||||
import org.matrix.android.sdk.internal.session.homeserver.GetHomeServerCapabilitiesTask
|
||||
import org.matrix.android.sdk.internal.session.initsync.DefaultInitialSyncProgressService
|
||||
import org.matrix.android.sdk.internal.session.initsync.DefaultSyncStatusService
|
||||
import org.matrix.android.sdk.internal.session.initsync.reportSubtask
|
||||
import org.matrix.android.sdk.internal.session.sync.model.LazyRoomSyncEphemeral
|
||||
import org.matrix.android.sdk.internal.session.sync.parsing.InitialSyncResponseParser
|
||||
|
@ -40,6 +42,8 @@ import java.io.File
|
|||
import java.net.SocketTimeoutException
|
||||
import javax.inject.Inject
|
||||
|
||||
private val loggerTag = LoggerTag("SyncTask", LoggerTag.SYNC)
|
||||
|
||||
internal interface SyncTask : Task<SyncTask.Params, Unit> {
|
||||
|
||||
data class Params(
|
||||
|
@ -53,7 +57,7 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
@UserId private val userId: String,
|
||||
private val filterRepository: FilterRepository,
|
||||
private val syncResponseHandler: SyncResponseHandler,
|
||||
private val initialSyncProgressService: DefaultInitialSyncProgressService,
|
||||
private val defaultSyncStatusService: DefaultSyncStatusService,
|
||||
private val syncTokenStore: SyncTokenStore,
|
||||
private val getHomeServerCapabilitiesTask: GetHomeServerCapabilitiesTask,
|
||||
private val userStore: UserStore,
|
||||
|
@ -75,7 +79,7 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun doSync(params: SyncTask.Params) {
|
||||
Timber.v("Sync task started on Thread: ${Thread.currentThread().name}")
|
||||
Timber.tag(loggerTag.value).d("Sync task started on Thread: ${Thread.currentThread().name}")
|
||||
|
||||
val requestParams = HashMap<String, String>()
|
||||
var timeout = 0L
|
||||
|
@ -92,7 +96,7 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
if (isInitialSync) {
|
||||
// We might want to get the user information in parallel too
|
||||
userStore.createOrUpdate(userId)
|
||||
initialSyncProgressService.startRoot(InitSyncStep.ImportingAccount, 100)
|
||||
defaultSyncStatusService.startRoot(InitSyncStep.ImportingAccount, 100)
|
||||
}
|
||||
// Maybe refresh the homeserver capabilities data we know
|
||||
getHomeServerCapabilitiesTask.execute(GetHomeServerCapabilitiesTask.Params(forceRefresh = false))
|
||||
|
@ -100,20 +104,20 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT)
|
||||
|
||||
if (isInitialSync) {
|
||||
Timber.d("INIT_SYNC with filter: ${requestParams["filter"]}")
|
||||
Timber.tag(loggerTag.value).d("INIT_SYNC with filter: ${requestParams["filter"]}")
|
||||
val initSyncStrategy = initialSyncStrategy
|
||||
logDuration("INIT_SYNC strategy: $initSyncStrategy") {
|
||||
logDuration("INIT_SYNC strategy: $initSyncStrategy", loggerTag) {
|
||||
if (initSyncStrategy is InitialSyncStrategy.Optimized) {
|
||||
roomSyncEphemeralTemporaryStore.reset()
|
||||
workingDir.mkdirs()
|
||||
val file = downloadInitSyncResponse(requestParams)
|
||||
reportSubtask(initialSyncProgressService, InitSyncStep.ImportingAccount, 1, 0.7F) {
|
||||
reportSubtask(defaultSyncStatusService, InitSyncStep.ImportingAccount, 1, 0.7F) {
|
||||
handleSyncFile(file, initSyncStrategy)
|
||||
}
|
||||
// Delete all files
|
||||
workingDir.deleteRecursively()
|
||||
} else {
|
||||
val syncResponse = logDuration("INIT_SYNC Request") {
|
||||
val syncResponse = logDuration("INIT_SYNC Request", loggerTag) {
|
||||
executeRequest(globalErrorReceiver) {
|
||||
syncAPI.sync(
|
||||
params = requestParams,
|
||||
|
@ -122,43 +126,60 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
logDuration("INIT_SYNC Database insertion") {
|
||||
syncResponseHandler.handleResponse(syncResponse, token, initialSyncProgressService)
|
||||
logDuration("INIT_SYNC Database insertion", loggerTag) {
|
||||
syncResponseHandler.handleResponse(syncResponse, token, defaultSyncStatusService)
|
||||
}
|
||||
}
|
||||
}
|
||||
initialSyncProgressService.endAll()
|
||||
defaultSyncStatusService.endAll()
|
||||
} else {
|
||||
val syncResponse = executeRequest(globalErrorReceiver) {
|
||||
Timber.tag(loggerTag.value).d("Start incremental sync request")
|
||||
defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncIdle)
|
||||
val syncResponse = try {
|
||||
executeRequest(globalErrorReceiver) {
|
||||
syncAPI.sync(
|
||||
params = requestParams,
|
||||
readTimeOut = readTimeOut
|
||||
)
|
||||
}
|
||||
syncResponseHandler.handleResponse(syncResponse, token, null)
|
||||
} catch (throwable: Throwable) {
|
||||
Timber.tag(loggerTag.value).e(throwable, "Incremental sync request error")
|
||||
defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncError)
|
||||
throw throwable
|
||||
}
|
||||
Timber.v("Sync task finished on Thread: ${Thread.currentThread().name}")
|
||||
val nbRooms = syncResponse.rooms?.invite.orEmpty().size + syncResponse.rooms?.join.orEmpty().size + syncResponse.rooms?.leave.orEmpty().size
|
||||
val nbToDevice = syncResponse.toDevice?.events.orEmpty().size
|
||||
Timber.tag(loggerTag.value).d("Incremental sync request parsing, $nbRooms room(s) $nbToDevice toDevice(s)")
|
||||
defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncParsing(
|
||||
rooms = nbRooms,
|
||||
toDevice = nbToDevice
|
||||
))
|
||||
syncResponseHandler.handleResponse(syncResponse, token, null)
|
||||
Timber.tag(loggerTag.value).d("Incremental sync done")
|
||||
defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncDone)
|
||||
}
|
||||
Timber.tag(loggerTag.value).d("Sync task finished on Thread: ${Thread.currentThread().name}")
|
||||
}
|
||||
|
||||
private suspend fun downloadInitSyncResponse(requestParams: Map<String, String>): File {
|
||||
val workingFile = File(workingDir, "initSync.json")
|
||||
val status = initialSyncStatusRepository.getStep()
|
||||
if (workingFile.exists() && status >= InitialSyncStatus.STEP_DOWNLOADED) {
|
||||
Timber.d("INIT_SYNC file is already here")
|
||||
reportSubtask(initialSyncProgressService, InitSyncStep.Downloading, 1, 0.3f) {
|
||||
Timber.tag(loggerTag.value).d("INIT_SYNC file is already here")
|
||||
reportSubtask(defaultSyncStatusService, InitSyncStep.Downloading, 1, 0.3f) {
|
||||
// Empty task
|
||||
}
|
||||
} else {
|
||||
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_DOWNLOADING)
|
||||
val syncResponse = logDuration("INIT_SYNC Perform server request") {
|
||||
reportSubtask(initialSyncProgressService, InitSyncStep.ServerComputing, 1, 0.2f) {
|
||||
val syncResponse = logDuration("INIT_SYNC Perform server request", loggerTag) {
|
||||
reportSubtask(defaultSyncStatusService, InitSyncStep.ServerComputing, 1, 0.2f) {
|
||||
getSyncResponse(requestParams, MAX_NUMBER_OF_RETRY_AFTER_TIMEOUT)
|
||||
}
|
||||
}
|
||||
|
||||
if (syncResponse.isSuccessful) {
|
||||
logDuration("INIT_SYNC Download and save to file") {
|
||||
reportSubtask(initialSyncProgressService, InitSyncStep.Downloading, 1, 0.1f) {
|
||||
logDuration("INIT_SYNC Download and save to file", loggerTag) {
|
||||
reportSubtask(defaultSyncStatusService, InitSyncStep.Downloading, 1, 0.1f) {
|
||||
syncResponse.body()?.byteStream()?.use { inputStream ->
|
||||
workingFile.outputStream().use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
|
@ -168,7 +189,7 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
}
|
||||
} else {
|
||||
throw syncResponse.toFailure(globalErrorReceiver)
|
||||
.also { Timber.w("INIT_SYNC request failure: $this") }
|
||||
.also { Timber.tag(loggerTag.value).w("INIT_SYNC request failure: $this") }
|
||||
}
|
||||
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_DOWNLOADED)
|
||||
}
|
||||
|
@ -185,9 +206,9 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
).awaitResponse()
|
||||
} catch (throwable: Throwable) {
|
||||
if (throwable is SocketTimeoutException && retry > 0) {
|
||||
Timber.w("INIT_SYNC timeout retry left: $retry")
|
||||
Timber.tag(loggerTag.value).w("INIT_SYNC timeout retry left: $retry")
|
||||
} else {
|
||||
Timber.e(throwable, "INIT_SYNC timeout, no retry left, or other error")
|
||||
Timber.tag(loggerTag.value).e(throwable, "INIT_SYNC timeout, no retry left, or other error")
|
||||
throw throwable
|
||||
}
|
||||
}
|
||||
|
@ -195,18 +216,18 @@ internal class DefaultSyncTask @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun handleSyncFile(workingFile: File, initSyncStrategy: InitialSyncStrategy.Optimized) {
|
||||
logDuration("INIT_SYNC handleSyncFile()") {
|
||||
val syncResponse = logDuration("INIT_SYNC Read file and parse") {
|
||||
logDuration("INIT_SYNC handleSyncFile()", loggerTag) {
|
||||
val syncResponse = logDuration("INIT_SYNC Read file and parse", loggerTag) {
|
||||
syncResponseParser.parse(initSyncStrategy, workingFile)
|
||||
}
|
||||
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_PARSED)
|
||||
// Log some stats
|
||||
val nbOfJoinedRooms = syncResponse.rooms?.join?.size ?: 0
|
||||
val nbOfJoinedRoomsInFile = syncResponse.rooms?.join?.values?.count { it.ephemeral is LazyRoomSyncEphemeral.Stored }
|
||||
Timber.d("INIT_SYNC $nbOfJoinedRooms rooms, $nbOfJoinedRoomsInFile ephemeral stored into files")
|
||||
Timber.tag(loggerTag.value).d("INIT_SYNC $nbOfJoinedRooms rooms, $nbOfJoinedRoomsInFile ephemeral stored into files")
|
||||
|
||||
logDuration("INIT_SYNC Database insertion") {
|
||||
syncResponseHandler.handleResponse(syncResponse, null, initialSyncProgressService)
|
||||
logDuration("INIT_SYNC Database insertion", loggerTag) {
|
||||
syncResponseHandler.handleResponse(syncResponse, null, defaultSyncStatusService)
|
||||
}
|
||||
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_SUCCESS)
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import kotlinx.coroutines.cancelChildren
|
|||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.call.MxCall
|
||||
import org.matrix.android.sdk.internal.session.call.ActiveCallHandler
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncPresence
|
||||
|
@ -49,6 +50,8 @@ import kotlin.concurrent.schedule
|
|||
private const val RETRY_WAIT_TIME_MS = 10_000L
|
||||
private const val DEFAULT_LONG_POOL_TIMEOUT = 30_000L
|
||||
|
||||
private val loggerTag = LoggerTag("SyncThread", LoggerTag.SYNC)
|
||||
|
||||
internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||
private val networkConnectivityChecker: NetworkConnectivityChecker,
|
||||
private val backgroundDetectionObserver: BackgroundDetectionObserver,
|
||||
|
@ -83,7 +86,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
|
||||
fun restart() = synchronized(lock) {
|
||||
if (!isStarted) {
|
||||
Timber.v("Resume sync...")
|
||||
Timber.tag(loggerTag.value).d("Resume sync...")
|
||||
isStarted = true
|
||||
// Check again server availability and the token validity
|
||||
canReachServer = true
|
||||
|
@ -94,7 +97,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
|
||||
fun pause() = synchronized(lock) {
|
||||
if (isStarted) {
|
||||
Timber.v("Pause sync...")
|
||||
Timber.tag(loggerTag.value).d("Pause sync...")
|
||||
isStarted = false
|
||||
retryNoNetworkTask?.cancel()
|
||||
syncScope.coroutineContext.cancelChildren()
|
||||
|
@ -102,7 +105,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
}
|
||||
|
||||
fun kill() = synchronized(lock) {
|
||||
Timber.v("Kill sync...")
|
||||
Timber.tag(loggerTag.value).d("Kill sync...")
|
||||
updateStateTo(SyncState.Killing)
|
||||
retryNoNetworkTask?.cancel()
|
||||
syncScope.coroutineContext.cancelChildren()
|
||||
|
@ -124,21 +127,21 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
}
|
||||
|
||||
override fun run() {
|
||||
Timber.v("Start syncing...")
|
||||
Timber.tag(loggerTag.value).d("Start syncing...")
|
||||
|
||||
isStarted = true
|
||||
networkConnectivityChecker.register(this)
|
||||
backgroundDetectionObserver.register(this)
|
||||
registerActiveCallsObserver()
|
||||
while (state != SyncState.Killing) {
|
||||
Timber.v("Entering loop, state: $state")
|
||||
Timber.tag(loggerTag.value).d("Entering loop, state: $state")
|
||||
if (!isStarted) {
|
||||
Timber.v("Sync is Paused. Waiting...")
|
||||
Timber.tag(loggerTag.value).d("Sync is Paused. Waiting...")
|
||||
updateStateTo(SyncState.Paused)
|
||||
synchronized(lock) { lock.wait() }
|
||||
Timber.v("...unlocked")
|
||||
Timber.tag(loggerTag.value).d("...unlocked")
|
||||
} else if (!canReachServer) {
|
||||
Timber.v("No network. Waiting...")
|
||||
Timber.tag(loggerTag.value).d("No network. Waiting...")
|
||||
updateStateTo(SyncState.NoNetwork)
|
||||
// We force retrying in RETRY_WAIT_TIME_MS maximum. Otherwise it will be unlocked by onConnectivityChanged() or restart()
|
||||
retryNoNetworkTask = Timer(SyncState.NoNetwork.toString(), false).schedule(RETRY_WAIT_TIME_MS) {
|
||||
|
@ -148,19 +151,19 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
}
|
||||
}
|
||||
synchronized(lock) { lock.wait() }
|
||||
Timber.v("...retry")
|
||||
Timber.tag(loggerTag.value).d("...retry")
|
||||
} else if (!isTokenValid) {
|
||||
Timber.v("Token is invalid. Waiting...")
|
||||
Timber.tag(loggerTag.value).d("Token is invalid. Waiting...")
|
||||
updateStateTo(SyncState.InvalidToken)
|
||||
synchronized(lock) { lock.wait() }
|
||||
Timber.v("...unlocked")
|
||||
Timber.tag(loggerTag.value).d("...unlocked")
|
||||
} else {
|
||||
if (state !is SyncState.Running) {
|
||||
updateStateTo(SyncState.Running(afterPause = true))
|
||||
}
|
||||
// No timeout after a pause
|
||||
val timeout = state.let { if (it is SyncState.Running && it.afterPause) 0 else DEFAULT_LONG_POOL_TIMEOUT }
|
||||
Timber.v("Execute sync request with timeout $timeout")
|
||||
Timber.tag(loggerTag.value).d("Execute sync request with timeout $timeout")
|
||||
val params = SyncTask.Params(timeout, SyncPresence.Online)
|
||||
val sync = syncScope.launch {
|
||||
doSync(params)
|
||||
|
@ -168,10 +171,10 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
runBlocking {
|
||||
sync.join()
|
||||
}
|
||||
Timber.v("...Continue")
|
||||
Timber.tag(loggerTag.value).d("...Continue")
|
||||
}
|
||||
}
|
||||
Timber.v("Sync killed")
|
||||
Timber.tag(loggerTag.value).d("Sync killed")
|
||||
updateStateTo(SyncState.Killed)
|
||||
backgroundDetectionObserver.unregister(this)
|
||||
networkConnectivityChecker.unregister(this)
|
||||
|
@ -199,19 +202,19 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
}
|
||||
if (failure is Failure.NetworkConnection && failure.cause is SocketTimeoutException) {
|
||||
// Timeout are not critical
|
||||
Timber.v("Timeout")
|
||||
Timber.tag(loggerTag.value).d("Timeout")
|
||||
} else if (failure is CancellationException) {
|
||||
Timber.v("Cancelled")
|
||||
Timber.tag(loggerTag.value).d("Cancelled")
|
||||
} else if (failure.isTokenError()) {
|
||||
// No token or invalid token, stop the thread
|
||||
Timber.w(failure, "Token error")
|
||||
Timber.tag(loggerTag.value).w(failure, "Token error")
|
||||
isStarted = false
|
||||
isTokenValid = false
|
||||
} else {
|
||||
Timber.e(failure)
|
||||
Timber.tag(loggerTag.value).e(failure)
|
||||
if (failure !is Failure.NetworkConnection || failure.cause is JsonEncodingException) {
|
||||
// Wait 10s before retrying
|
||||
Timber.v("Wait 10s")
|
||||
Timber.tag(loggerTag.value).d("Wait 10s")
|
||||
delay(RETRY_WAIT_TIME_MS)
|
||||
}
|
||||
}
|
||||
|
@ -225,7 +228,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
|||
}
|
||||
|
||||
private fun updateStateTo(newState: SyncState) {
|
||||
Timber.v("Update state from $state to $newState")
|
||||
Timber.tag(loggerTag.value).d("Update state from $state to $newState")
|
||||
if (newState == state) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.matrix.android.sdk.internal.util
|
||||
|
||||
import org.matrix.android.sdk.BuildConfig
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import timber.log.Timber
|
||||
|
||||
internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String {
|
||||
|
@ -32,14 +33,15 @@ internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String {
|
|||
}
|
||||
|
||||
internal suspend fun <T> logDuration(message: String,
|
||||
loggerTag: LoggerTag,
|
||||
block: suspend () -> T): T {
|
||||
Timber.d("$message -- BEGIN")
|
||||
Timber.tag(loggerTag.value).d("$message -- BEGIN")
|
||||
val start = System.currentTimeMillis()
|
||||
val result = logRamUsage(message) {
|
||||
block()
|
||||
}
|
||||
val duration = System.currentTimeMillis() - start
|
||||
Timber.d("$message -- END duration: $duration ms")
|
||||
Timber.tag(loggerTag.value).d("$message -- END duration: $duration ms")
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -345,6 +345,9 @@ dependencies {
|
|||
implementation libs.androidx.lifecycleExtensions
|
||||
implementation libs.androidx.lifecycleLivedata
|
||||
|
||||
implementation libs.androidx.datastore
|
||||
implementation libs.androidx.datastorepreferences
|
||||
|
||||
|
||||
// Log
|
||||
implementation libs.jakewharton.timber
|
||||
|
@ -406,7 +409,7 @@ dependencies {
|
|||
// To convert voice message on old platforms
|
||||
implementation 'com.arthenica:ffmpeg-kit-audio:4.4.LTS'
|
||||
|
||||
//Alerter
|
||||
// Alerter
|
||||
implementation 'com.tapadoo.android:alerter:7.0.1'
|
||||
|
||||
implementation 'com.otaliastudios:autocomplete:1.1.0'
|
||||
|
|
|
@ -39,17 +39,22 @@ import im.vector.app.features.notifications.NotifiableMessageEvent
|
|||
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
import im.vector.app.features.notifications.SimpleNotifiableEvent
|
||||
import im.vector.app.features.settings.VectorDataStore
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import im.vector.app.push.fcm.FcmHelper
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.pushrules.Action
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import timber.log.Timber
|
||||
|
||||
private val loggerTag = LoggerTag("Push", LoggerTag.SYNC)
|
||||
|
||||
/**
|
||||
* Class extending FirebaseMessagingService.
|
||||
*/
|
||||
|
@ -60,6 +65,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
private lateinit var pusherManager: PushersManager
|
||||
private lateinit var activeSessionHolder: ActiveSessionHolder
|
||||
private lateinit var vectorPreferences: VectorPreferences
|
||||
private lateinit var vectorDataStore: VectorDataStore
|
||||
private lateinit var wifiDetector: WifiDetector
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob())
|
||||
|
@ -77,6 +83,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
pusherManager = pusherManager()
|
||||
activeSessionHolder = activeSessionHolder()
|
||||
vectorPreferences = vectorPreferences()
|
||||
vectorDataStore = vectorDataStore()
|
||||
wifiDetector = wifiDetector()
|
||||
}
|
||||
}
|
||||
|
@ -88,9 +95,13 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
*/
|
||||
override fun onMessageReceived(message: RemoteMessage) {
|
||||
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
|
||||
Timber.d("## onMessageReceived() %s", message.data.toString())
|
||||
Timber.tag(loggerTag.value).d("## onMessageReceived() %s", message.data.toString())
|
||||
}
|
||||
Timber.tag(loggerTag.value).d("## onMessageReceived() from FCM with priority %s", message.priority)
|
||||
|
||||
runBlocking {
|
||||
vectorDataStore.incrementPushCounter()
|
||||
}
|
||||
Timber.d("## onMessageReceived() from FCM with priority %s", message.priority)
|
||||
|
||||
// Diagnostic Push
|
||||
if (message.data["event_id"] == PushersManager.TEST_EVENT_ID) {
|
||||
|
@ -100,14 +111,14 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
}
|
||||
|
||||
if (!vectorPreferences.areNotificationEnabledForDevice()) {
|
||||
Timber.i("Notification are disabled for this device")
|
||||
Timber.tag(loggerTag.value).i("Notification are disabled for this device")
|
||||
return
|
||||
}
|
||||
|
||||
mUIHandler.post {
|
||||
if (ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||
// we are in foreground, let the sync do the things?
|
||||
Timber.d("PUSH received in a foreground state, ignore")
|
||||
Timber.tag(loggerTag.value).d("PUSH received in a foreground state, ignore")
|
||||
} else {
|
||||
onMessageReceivedInternal(message.data)
|
||||
}
|
||||
|
@ -121,7 +132,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
* you retrieve the token.
|
||||
*/
|
||||
override fun onNewToken(refreshedToken: String) {
|
||||
Timber.i("onNewToken: FCM Token has been updated")
|
||||
Timber.tag(loggerTag.value).i("onNewToken: FCM Token has been updated")
|
||||
FcmHelper.storeFcmToken(this, refreshedToken)
|
||||
if (vectorPreferences.areNotificationEnabledForDevice() && activeSessionHolder.hasActiveSession()) {
|
||||
pusherManager.registerPusherWithFcmKey(refreshedToken)
|
||||
|
@ -138,7 +149,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
* It is recommended that the app do a full sync with the app server after receiving this call.
|
||||
*/
|
||||
override fun onDeletedMessages() {
|
||||
Timber.v("## onDeletedMessages()")
|
||||
Timber.tag(loggerTag.value).v("## onDeletedMessages()")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,9 +161,9 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
private fun onMessageReceivedInternal(data: Map<String, String>) {
|
||||
try {
|
||||
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
|
||||
Timber.d("## onMessageReceivedInternal() : $data")
|
||||
Timber.tag(loggerTag.value).d("## onMessageReceivedInternal() : $data")
|
||||
} else {
|
||||
Timber.d("## onMessageReceivedInternal() : $data")
|
||||
Timber.tag(loggerTag.value).d("## onMessageReceivedInternal()")
|
||||
}
|
||||
|
||||
// update the badge counter
|
||||
|
@ -162,24 +173,24 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
val session = activeSessionHolder.getSafeActiveSession()
|
||||
|
||||
if (session == null) {
|
||||
Timber.w("## Can't sync from push, no current session")
|
||||
Timber.tag(loggerTag.value).w("## Can't sync from push, no current session")
|
||||
} else {
|
||||
val eventId = data["event_id"]
|
||||
val roomId = data["room_id"]
|
||||
|
||||
if (isEventAlreadyKnown(eventId, roomId)) {
|
||||
Timber.d("Ignoring push, event already known")
|
||||
Timber.tag(loggerTag.value).d("Ignoring push, event already known")
|
||||
} else {
|
||||
// Try to get the Event content faster
|
||||
Timber.d("Requesting event in fast lane")
|
||||
Timber.tag(loggerTag.value).d("Requesting event in fast lane")
|
||||
getEventFastLane(session, roomId, eventId)
|
||||
|
||||
Timber.d("Requesting background sync")
|
||||
Timber.tag(loggerTag.value).d("Requesting background sync")
|
||||
session.requireBackgroundSync()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## onMessageReceivedInternal() failed")
|
||||
Timber.tag(loggerTag.value).e(e, "## onMessageReceivedInternal() failed")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,18 +204,18 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
}
|
||||
|
||||
if (wifiDetector.isConnectedToWifi().not()) {
|
||||
Timber.d("No WiFi network, do not get Event")
|
||||
Timber.tag(loggerTag.value).d("No WiFi network, do not get Event")
|
||||
return
|
||||
}
|
||||
|
||||
coroutineScope.launch {
|
||||
Timber.d("Fast lane: start request")
|
||||
Timber.tag(loggerTag.value).d("Fast lane: start request")
|
||||
val event = tryOrNull { session.getEvent(roomId, eventId) } ?: return@launch
|
||||
|
||||
val resolvedEvent = notifiableEventResolver.resolveInMemoryEvent(session, event)
|
||||
|
||||
resolvedEvent
|
||||
?.also { Timber.d("Fast lane: notify drawer") }
|
||||
?.also { Timber.tag(loggerTag.value).d("Fast lane: notify drawer") }
|
||||
?.let {
|
||||
it.isPushGatewayEvent = true
|
||||
notificationDrawerManager.onNotifiableEventReceived(it)
|
||||
|
@ -222,7 +233,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
val room = session.getRoom(roomId) ?: return false
|
||||
return room.getTimeLineEvent(eventId) != null
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## isEventAlreadyKnown() : failed to check if the event was already defined")
|
||||
Timber.tag(loggerTag.value).e(e, "## isEventAlreadyKnown() : failed to check if the event was already defined")
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
@ -230,7 +241,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
|
||||
private fun handleNotificationWithoutSyncingMode(data: Map<String, String>, session: Session?) {
|
||||
if (session == null) {
|
||||
Timber.e("## handleNotificationWithoutSyncingMode cannot find session")
|
||||
Timber.tag(loggerTag.value).e("## handleNotificationWithoutSyncingMode cannot find session")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -263,9 +274,9 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
val notifiableEvent = notifiableEventResolver.resolveEvent(event, session)
|
||||
|
||||
if (notifiableEvent == null) {
|
||||
Timber.e("Unsupported notifiable event $eventId")
|
||||
Timber.tag(loggerTag.value).e("Unsupported notifiable event $eventId")
|
||||
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
|
||||
Timber.e("--> $event")
|
||||
Timber.tag(loggerTag.value).e("--> $event")
|
||||
}
|
||||
} else {
|
||||
if (notifiableEvent is NotifiableMessageEvent) {
|
||||
|
|
|
@ -58,6 +58,7 @@ import im.vector.app.features.rageshake.VectorFileLogger
|
|||
import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler
|
||||
import im.vector.app.features.reactions.data.EmojiDataSource
|
||||
import im.vector.app.features.session.SessionListener
|
||||
import im.vector.app.features.settings.VectorDataStore
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import im.vector.app.features.ui.UiStateRepository
|
||||
import org.matrix.android.sdk.api.Matrix
|
||||
|
@ -145,6 +146,8 @@ interface VectorComponent {
|
|||
|
||||
fun vectorPreferences(): VectorPreferences
|
||||
|
||||
fun vectorDataStore(): VectorDataStore
|
||||
|
||||
fun wifiDetector(): WifiDetector
|
||||
|
||||
fun vectorFileLogger(): VectorFileLogger
|
||||
|
|
|
@ -70,7 +70,7 @@ import im.vector.app.features.workers.signout.ServerBackupStatusViewState
|
|||
import im.vector.app.push.fcm.FcmHelper
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import org.matrix.android.sdk.internal.session.sync.InitialSyncStrategy
|
||||
|
@ -308,11 +308,11 @@ class HomeActivity :
|
|||
}
|
||||
|
||||
private fun renderState(state: HomeActivityViewState) {
|
||||
when (val status = state.initialSyncProgressServiceStatus) {
|
||||
is InitialSyncProgressService.Status.Idle -> {
|
||||
when (val status = state.syncStatusServiceStatus) {
|
||||
is SyncStatusService.Status.Idle -> {
|
||||
views.waitingView.root.isVisible = false
|
||||
}
|
||||
is InitialSyncProgressService.Status.Progressing -> {
|
||||
is SyncStatusService.Status.Progressing -> {
|
||||
val initSyncStepStr = initSyncStepFormatter.format(status.initSyncStep)
|
||||
Timber.v("$initSyncStepStr ${status.percentProgress}")
|
||||
views.waitingView.root.setOnClickListener {
|
||||
|
@ -330,6 +330,7 @@ class HomeActivity :
|
|||
}
|
||||
views.waitingView.root.isVisible = true
|
||||
}
|
||||
else -> Unit
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
|
@ -474,8 +475,8 @@ class HomeActivity :
|
|||
override fun getMenuRes() = R.menu.home
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
||||
menu.findItem(R.id.menu_home_init_sync_legacy)?.isVisible = vectorPreferences.developerMode()
|
||||
menu.findItem(R.id.menu_home_init_sync_optimized)?.isVisible = vectorPreferences.developerMode()
|
||||
menu.findItem(R.id.menu_home_init_sync_legacy).isVisible = vectorPreferences.developerMode()
|
||||
menu.findItem(R.id.menu_home_init_sync_optimized).isVisible = vectorPreferences.developerMode()
|
||||
return super.onPrepareOptionsMenu(menu)
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
|||
import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.pushrules.RuleIds
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
|
@ -122,25 +122,26 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||
private fun observeInitialSync() {
|
||||
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
||||
|
||||
session.getInitialSyncProgressStatus()
|
||||
session.getSyncStatusLive()
|
||||
.asObservable()
|
||||
.subscribe { status ->
|
||||
when (status) {
|
||||
is InitialSyncProgressService.Status.Progressing -> {
|
||||
is SyncStatusService.Status.Progressing -> {
|
||||
// Schedule a check of the bootstrap when the init sync will be finished
|
||||
checkBootstrap = true
|
||||
}
|
||||
is InitialSyncProgressService.Status.Idle -> {
|
||||
is SyncStatusService.Status.Idle -> {
|
||||
if (checkBootstrap) {
|
||||
checkBootstrap = false
|
||||
maybeBootstrapCrossSigningAfterInitialSync()
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
initialSyncProgressServiceStatus = status
|
||||
syncStatusServiceStatus = status
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
package im.vector.app.features.home
|
||||
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
|
||||
data class HomeActivityViewState(
|
||||
val initialSyncProgressServiceStatus: InitialSyncProgressService.Status = InitialSyncProgressService.Status.Idle
|
||||
val syncStatusServiceStatus: SyncStatusService.Status = SyncStatusService.Status.Idle
|
||||
) : MvRxState
|
||||
|
|
|
@ -440,7 +440,11 @@ class HomeDetailFragment @Inject constructor(
|
|||
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_people).render(it.notificationCountPeople, it.notificationHighlightPeople)
|
||||
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_rooms).render(it.notificationCountRooms, it.notificationHighlightRooms)
|
||||
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_notification).render(it.notificationCountCatchup, it.notificationHighlightCatchup)
|
||||
views.syncStateView.render(it.syncState)
|
||||
views.syncStateView.render(
|
||||
it.syncState,
|
||||
it.incrementalSyncStatus,
|
||||
it.pushCounter,
|
||||
vectorPreferences.developerShowDebugInfo())
|
||||
|
||||
hasUnreadRooms = it.hasUnreadMessages
|
||||
}
|
||||
|
|
|
@ -33,13 +33,16 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager
|
|||
import im.vector.app.features.createdirect.DirectRoomHelper
|
||||
import im.vector.app.features.invite.AutoAcceptInvites
|
||||
import im.vector.app.features.invite.showInvites
|
||||
import im.vector.app.features.settings.VectorDataStore
|
||||
import im.vector.app.features.ui.UiStateRepository
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
|
||||
import org.matrix.android.sdk.api.query.RoomCategoryFilter
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.room.RoomSortOrder
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
|
@ -56,10 +59,11 @@ import java.util.concurrent.TimeUnit
|
|||
class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: HomeDetailViewState,
|
||||
private val session: Session,
|
||||
private val uiStateRepository: UiStateRepository,
|
||||
private val vectorDataStore: VectorDataStore,
|
||||
private val callManager: WebRtcCallManager,
|
||||
private val directRoomHelper: DirectRoomHelper,
|
||||
private val appStateHandler: AppStateHandler,
|
||||
private val autoAcceptInvites: AutoAcceptInvites)
|
||||
private val autoAcceptInvites: AutoAcceptInvites)
|
||||
: VectorViewModel<HomeDetailViewState, HomeDetailAction, HomeDetailViewEvents>(initialState),
|
||||
CallProtocolsChecker.Listener {
|
||||
|
||||
|
@ -89,6 +93,7 @@ private val autoAcceptInvites: AutoAcceptInvites)
|
|||
observeRoomGroupingMethod()
|
||||
observeRoomSummaries()
|
||||
updateShowDialPadTab()
|
||||
observeDataStore()
|
||||
callManager.addProtocolsCheckerListener(this)
|
||||
session.rx().liveUser(session.myUserId).execute {
|
||||
copy(
|
||||
|
@ -97,6 +102,18 @@ private val autoAcceptInvites: AutoAcceptInvites)
|
|||
}
|
||||
}
|
||||
|
||||
private fun observeDataStore() {
|
||||
viewModelScope.launch {
|
||||
vectorDataStore.pushCounterFlow.collect { nbOfPush ->
|
||||
setState {
|
||||
copy(
|
||||
pushCounter = nbOfPush
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: HomeDetailAction) {
|
||||
when (action) {
|
||||
is HomeDetailAction.SwitchTab -> handleSwitchTab(action)
|
||||
|
@ -173,6 +190,17 @@ private val autoAcceptInvites: AutoAcceptInvites)
|
|||
}
|
||||
}
|
||||
.disposeOnClear()
|
||||
|
||||
session.getSyncStatusLive()
|
||||
.asObservable()
|
||||
.subscribe {
|
||||
if (it is SyncStatusService.Status.IncrementalSyncStatus) {
|
||||
setState {
|
||||
copy(incrementalSyncStatus = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
.disposeOnClear()
|
||||
}
|
||||
|
||||
private fun observeRoomGroupingMethod() {
|
||||
|
|
|
@ -22,6 +22,7 @@ import com.airbnb.mvrx.MvRxState
|
|||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.app.R
|
||||
import im.vector.app.RoomGroupingMethod
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.sync.SyncState
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
@ -39,6 +40,8 @@ data class HomeDetailViewState(
|
|||
val notificationHighlightRooms: Boolean = false,
|
||||
val hasUnreadMessages: Boolean = false,
|
||||
val syncState: SyncState = SyncState.Idle,
|
||||
val incrementalSyncStatus: SyncStatusService.Status.IncrementalSyncStatus = SyncStatusService.Status.IncrementalSyncIdle,
|
||||
val pushCounter: Int = 0,
|
||||
val showDialPadTab: Boolean = false
|
||||
) : MvRxState
|
||||
|
||||
|
|
|
@ -387,8 +387,17 @@ class RoomDetailFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
roomDetailViewModel.selectSubscribe(RoomDetailViewState::syncState) { syncState ->
|
||||
views.syncStateView.render(syncState)
|
||||
roomDetailViewModel.selectSubscribe(
|
||||
RoomDetailViewState::syncState,
|
||||
RoomDetailViewState::incrementalSyncStatus,
|
||||
RoomDetailViewState::pushCounter
|
||||
) { syncState, incrementalSyncStatus, pushCounter ->
|
||||
views.syncStateView.render(
|
||||
syncState,
|
||||
incrementalSyncStatus,
|
||||
pushCounter,
|
||||
vectorPreferences.developerShowDebugInfo()
|
||||
)
|
||||
}
|
||||
|
||||
roomDetailViewModel.observeViewEvents {
|
||||
|
|
|
@ -38,9 +38,9 @@ import im.vector.app.core.extensions.exhaustive
|
|||
import im.vector.app.core.mvrx.runCatchingToAsync
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.call.conference.JitsiActiveConferenceHolder
|
||||
import im.vector.app.features.attachments.toContentAttachmentData
|
||||
import im.vector.app.features.call.conference.ConferenceEvent
|
||||
import im.vector.app.features.call.conference.JitsiActiveConferenceHolder
|
||||
import im.vector.app.features.call.conference.JitsiService
|
||||
import im.vector.app.features.call.lookup.CallProtocolsChecker
|
||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||
|
@ -57,12 +57,14 @@ import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
|||
import im.vector.app.features.home.room.typing.TypingHelper
|
||||
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
|
||||
import im.vector.app.features.session.coroutineScope
|
||||
import im.vector.app.features.settings.VectorDataStore
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import im.vector.app.features.voice.VoicePlayerHelper
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.rxkotlin.subscribeBy
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.commonmark.parser.Parser
|
||||
|
@ -80,6 +82,7 @@ import org.matrix.android.sdk.api.session.events.model.isTextMessage
|
|||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.file.FileService
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
|
@ -102,6 +105,7 @@ import org.matrix.android.sdk.api.session.space.CreateSpaceParams
|
|||
import org.matrix.android.sdk.api.session.widgets.model.WidgetType
|
||||
import org.matrix.android.sdk.api.util.toOptional
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
|
||||
import org.matrix.android.sdk.rx.asObservable
|
||||
import org.matrix.android.sdk.rx.rx
|
||||
import org.matrix.android.sdk.rx.unwrap
|
||||
import timber.log.Timber
|
||||
|
@ -111,6 +115,7 @@ import java.util.concurrent.atomic.AtomicBoolean
|
|||
class RoomDetailViewModel @AssistedInject constructor(
|
||||
@Assisted private val initialState: RoomDetailViewState,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val vectorDataStore: VectorDataStore,
|
||||
private val stringProvider: StringProvider,
|
||||
private val rainbowGenerator: RainbowGenerator,
|
||||
private val session: Session,
|
||||
|
@ -174,6 +179,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
observeSummaryState()
|
||||
getUnreadState()
|
||||
observeSyncState()
|
||||
observeDataStore()
|
||||
observeEventDisplayedActions()
|
||||
loadDraftIfAny()
|
||||
observeUnreadState()
|
||||
|
@ -198,6 +204,18 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun observeDataStore() {
|
||||
viewModelScope.launch {
|
||||
vectorDataStore.pushCounterFlow.collect { nbOfPush ->
|
||||
setState {
|
||||
copy(
|
||||
pushCounter = nbOfPush
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareForEncryption() {
|
||||
// check if there is not already a call made, or if there has been an error
|
||||
if (prepareToEncrypt.shouldLoad) {
|
||||
|
@ -1493,6 +1511,17 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
.disposeOnClear()
|
||||
|
||||
session.getSyncStatusLive()
|
||||
.asObservable()
|
||||
.subscribe { it ->
|
||||
if (it is SyncStatusService.Status.IncrementalSyncStatus) {
|
||||
setState {
|
||||
copy(incrementalSyncStatus = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
.disposeOnClear()
|
||||
}
|
||||
|
||||
private fun observeRoomSummary() {
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.airbnb.mvrx.MvRxState
|
|||
import com.airbnb.mvrx.Uninitialized
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
|
@ -77,6 +78,8 @@ data class RoomDetailViewState(
|
|||
val tombstoneEvent: Event? = null,
|
||||
val joinUpgradedRoomAsync: Async<String> = Uninitialized,
|
||||
val syncState: SyncState = SyncState.Idle,
|
||||
val incrementalSyncStatus: SyncStatusService.Status.IncrementalSyncStatus = SyncStatusService.Status.IncrementalSyncIdle,
|
||||
val pushCounter: Int = 0,
|
||||
val highlightedEventId: String? = null,
|
||||
val unreadState: UnreadState = UnreadState.Unknown,
|
||||
val canShowJumpToReadMarker: Boolean = true,
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* 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 im.vector.app.features.settings
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
|
||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "vector_settings")
|
||||
|
||||
class VectorDataStore @Inject constructor(
|
||||
private val context: Context
|
||||
) {
|
||||
|
||||
private val pushCounter = intPreferencesKey("push_counter")
|
||||
|
||||
val pushCounterFlow: Flow<Int> = context.dataStore.data.map { preferences ->
|
||||
preferences[pushCounter] ?: 0
|
||||
}
|
||||
|
||||
suspend fun incrementPushCounter() {
|
||||
context.dataStore.edit { settings ->
|
||||
val currentCounterValue = settings[pushCounter] ?: 0
|
||||
settings[pushCounter] = currentCounterValue + 1
|
||||
}
|
||||
}
|
||||
}
|
|
@ -159,6 +159,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
|||
private const val SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY = "SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY"
|
||||
private const val SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY = "SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY"
|
||||
private const val SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY"
|
||||
private const val SETTINGS_DEVELOPER_MODE_SHOW_INFO_ON_SCREEN_KEY = "SETTINGS_DEVELOPER_MODE_SHOW_INFO_ON_SCREEN_KEY"
|
||||
|
||||
// SETTINGS_LABS_HIDE_TECHNICAL_E2E_ERRORS
|
||||
private const val SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM = "SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM"
|
||||
|
@ -312,6 +313,10 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
|||
return defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY, false)
|
||||
}
|
||||
|
||||
fun developerShowDebugInfo(): Boolean {
|
||||
return developerMode() && defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_SHOW_INFO_ON_SCREEN_KEY, false)
|
||||
}
|
||||
|
||||
fun shouldShowHiddenEvents(): Boolean {
|
||||
return developerMode() && defaultPrefs.getBoolean(SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY, false)
|
||||
}
|
||||
|
|
|
@ -16,27 +16,41 @@
|
|||
|
||||
package im.vector.app.features.sync.widget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.view.isVisible
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.utils.isAirplaneModeOn
|
||||
import im.vector.app.databinding.ViewSyncStateBinding
|
||||
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.sync.SyncState
|
||||
|
||||
class SyncStateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
|
||||
: FrameLayout(context, attrs, defStyle) {
|
||||
: LinearLayout(context, attrs, defStyle) {
|
||||
|
||||
private val views: ViewSyncStateBinding
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.view_sync_state, this)
|
||||
views = ViewSyncStateBinding.bind(this)
|
||||
orientation = VERTICAL
|
||||
}
|
||||
|
||||
fun render(newState: SyncState) {
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun render(newState: SyncState,
|
||||
incrementalSyncStatus: SyncStatusService.Status.IncrementalSyncStatus,
|
||||
pushCounter: Int,
|
||||
showDebugInfo: Boolean
|
||||
) {
|
||||
views.syncStateDebugInfo.isVisible = showDebugInfo
|
||||
if (showDebugInfo) {
|
||||
views.syncStateDebugInfoText.text =
|
||||
"Sync thread : ${newState.toHumanReadable()}\nSync request: ${incrementalSyncStatus.toHumanReadable()}"
|
||||
views.syncStateDebugInfoPushCounter.text =
|
||||
"Push: $pushCounter"
|
||||
}
|
||||
views.syncStateProgressBar.isVisible = newState is SyncState.Running && newState.afterPause
|
||||
|
||||
if (newState == SyncState.NoNetwork) {
|
||||
|
@ -48,4 +62,26 @@ class SyncStateView @JvmOverloads constructor(context: Context, attrs: Attribute
|
|||
views.syncStateNoNetworkAirplane.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun SyncState.toHumanReadable(): String {
|
||||
return when (this) {
|
||||
SyncState.Idle -> "Idle"
|
||||
SyncState.InvalidToken -> "InvalidToken"
|
||||
SyncState.Killed -> "Killed"
|
||||
SyncState.Killing -> "Killing"
|
||||
SyncState.NoNetwork -> "NoNetwork"
|
||||
SyncState.Paused -> "Paused"
|
||||
is SyncState.Running -> "$this"
|
||||
}
|
||||
}
|
||||
|
||||
private fun SyncStatusService.Status.IncrementalSyncStatus.toHumanReadable(): String {
|
||||
return when (this) {
|
||||
SyncStatusService.Status.IncrementalSyncIdle -> "Idle"
|
||||
is SyncStatusService.Status.IncrementalSyncParsing -> "Parsing ${this.rooms} room(s) ${this.toDevice} toDevice(s)"
|
||||
SyncStatusService.Status.IncrementalSyncError -> "Error"
|
||||
SyncStatusService.Status.IncrementalSyncDone -> "Done"
|
||||
else -> "?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,34 @@
|
|||
tools:orientation="vertical"
|
||||
tools:parentTag="android.widget.LinearLayout">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/syncStateDebugInfo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="2dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/syncStateDebugInfoText"
|
||||
style="@style/Widget.Vector.TextView.Caption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="monospace"
|
||||
tools:text="debug info" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/syncStateDebugInfoPushCounter"
|
||||
style="@style/Widget.Vector.TextView.Caption"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:fontFamily="monospace"
|
||||
android:textStyle="bold"
|
||||
tools:text="123" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Trick to remove surrounding padding (clip from wrapping frame) -->
|
||||
<FrameLayout
|
||||
android:id="@+id/syncStateProgressBar"
|
||||
|
|
|
@ -2659,6 +2659,9 @@
|
|||
<!-- Note to translators: the translation MUST contain the string "${app_name}", which will be replaced by the application name -->
|
||||
<string name="template_settings_developer_mode_fail_fast_summary">${app_name} may crash more often when an unexpected error occurs</string>
|
||||
|
||||
<string name="settings_developer_mode_show_info_on_screen_title">Show debug info on screen</string>
|
||||
<string name="settings_developer_mode_show_info_on_screen_summary">Show some useful info to help debugging the application</string>
|
||||
|
||||
<string name="command_description_shrug">Prepends ¯\\_(ツ)_/¯ to a plain-text message</string>
|
||||
|
||||
<string name="create_room_encryption_title">"Enable encryption"</string>
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
<im.vector.app.core.preference.VectorSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
|
||||
android:icon="@drawable/ic_verification_glasses"
|
||||
android:key="SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
|
||||
android:summary="@string/settings_developer_mode_summary"
|
||||
android:title="@string/settings_developer_mode" />
|
||||
|
||||
|
@ -17,6 +17,13 @@
|
|||
android:key="SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY"
|
||||
android:title="@string/settings_labs_show_hidden_events_in_timeline" />
|
||||
|
||||
<im.vector.app.core.preference.VectorSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:dependency="SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
|
||||
android:key="SETTINGS_DEVELOPER_MODE_SHOW_INFO_ON_SCREEN_KEY"
|
||||
android:summary="@string/settings_developer_mode_show_info_on_screen_summary"
|
||||
android:title="@string/settings_developer_mode_show_info_on_screen_title" />
|
||||
|
||||
<im.vector.app.core.preference.VectorSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:dependency="SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
|
||||
|
|
Loading…
Reference in New Issue