diff --git a/library/ui-strings/src/main/res/values/donottranslate.xml b/library/ui-strings/src/main/res/values/donottranslate.xml
index bfe751ef5a..910ce31c41 100755
--- a/library/ui-strings/src/main/res/values/donottranslate.xml
+++ b/library/ui-strings/src/main/res/values/donottranslate.xml
@@ -10,6 +10,8 @@
Cut the slack from teams.
+ Crash the application.
+
© MapTiler © OpenStreetMap contributors
diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt
index 80ed311901..64cb0acb2d 100644
--- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt
+++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowSession.kt
@@ -80,6 +80,9 @@ class FlowSession(private val session: Session) {
fun liveSyncState(): Flow {
return session.syncService().getSyncStateLive().asFlow()
+ .startWith(session.coroutineDispatchers.io) {
+ session.syncService().getSyncState()
+ }
}
fun livePushers(): Flow> {
diff --git a/vector/src/main/java/im/vector/app/core/session/EnsureSessionSyncingUseCase.kt b/vector/src/main/java/im/vector/app/core/session/EnsureSessionSyncingUseCase.kt
new file mode 100644
index 0000000000..c53795d18d
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/session/EnsureSessionSyncingUseCase.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2022 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.core.session
+
+import android.content.Context
+import dagger.hilt.android.qualifiers.ApplicationContext
+import im.vector.app.core.di.ActiveSessionHolder
+import im.vector.app.core.extensions.startSyncing
+import org.matrix.android.sdk.api.session.sync.SyncState
+import timber.log.Timber
+import javax.inject.Inject
+
+class EnsureSessionSyncingUseCase @Inject constructor(
+ @ApplicationContext private val context: Context,
+ private val activeSessionHolder: ActiveSessionHolder,
+) {
+ fun execute() {
+ val session = activeSessionHolder.getSafeActiveSession() ?: return
+ if (session.syncService().getSyncState() == SyncState.Idle) {
+ Timber.w("EnsureSessionSyncingUseCase: start syncing")
+ session.startSyncing(context)
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/command/Command.kt b/vector/src/main/java/im/vector/app/features/command/Command.kt
index 324029c45b..b112751f68 100644
--- a/vector/src/main/java/im/vector/app/features/command/Command.kt
+++ b/vector/src/main/java/im/vector/app/features/command/Command.kt
@@ -32,6 +32,7 @@ enum class Command(
val isDevCommand: Boolean,
val isThreadCommand: Boolean
) {
+ CRASH_APP("/crash", null, "", R.string.command_description_crash_application, true, true),
EMOTE("/me", null, "", R.string.command_description_emote, false, true),
BAN_USER("/ban", null, " [reason]", R.string.command_description_ban_user, false, false),
UNBAN_USER("/unban", null, " [reason]", R.string.command_description_unban_user, false, false),
diff --git a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt
index e08bc9fb64..298387c324 100644
--- a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt
+++ b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt
@@ -20,13 +20,16 @@ import im.vector.app.core.extensions.isEmail
import im.vector.app.core.extensions.isMsisdn
import im.vector.app.core.extensions.orEmpty
import im.vector.app.features.home.room.detail.ChatEffect
+import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.MatrixPatterns
import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl
import org.matrix.android.sdk.api.session.identity.ThreePid
import timber.log.Timber
import javax.inject.Inject
-class CommandParser @Inject constructor() {
+class CommandParser @Inject constructor(
+ private val vectorPreferences: VectorPreferences
+) {
/**
* Convert the text message into a Slash command.
@@ -404,6 +407,9 @@ class CommandParser @Inject constructor() {
ParsedCommand.ErrorSyntax(Command.UPGRADE_ROOM)
}
}
+ Command.CRASH_APP.matches(slashCommand) && vectorPreferences.developerMode() -> {
+ throw RuntimeException("Application crashed from user demand")
+ }
else -> {
// Unknown command
ParsedCommand.ErrorUnknownSlashCommand(slashCommand)
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
index 8f16121a30..f961cc5da6 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
@@ -30,6 +30,7 @@ import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase
import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.pushers.RegisterUnifiedPushUseCase
import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase
+import im.vector.app.core.session.EnsureSessionSyncingUseCase
import im.vector.app.features.analytics.AnalyticsConfig
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.extensions.toAnalyticsType
@@ -95,6 +96,7 @@ class HomeActivityViewModel @AssistedInject constructor(
private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase,
private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase,
private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase,
+ private val ensureSessionSyncingUseCase: EnsureSessionSyncingUseCase,
) : VectorViewModel(initialState) {
@AssistedFactory
@@ -118,6 +120,8 @@ class HomeActivityViewModel @AssistedInject constructor(
private fun initialize() {
if (isInitialized) return
isInitialized = true
+ // Ensure Session is syncing
+ ensureSessionSyncingUseCase.execute()
registerUnifiedPushIfNeeded()
cleanupFiles()
observeInitialSync()
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt
index 7fbd5b2bf6..dcf4d87894 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt
@@ -38,8 +38,8 @@ data class HomeDetailViewState(
val notificationCountRooms: Int = 0,
val notificationHighlightRooms: Boolean = false,
val hasUnreadMessages: Boolean = false,
- val syncState: SyncState = SyncState.Idle,
- val incrementalSyncRequestState: SyncRequestState.IncrementalSyncRequestState = SyncRequestState.IncrementalSyncIdle,
+ val syncState: SyncState? = null,
+ val incrementalSyncRequestState: SyncRequestState.IncrementalSyncRequestState? = null,
val pushCounter: Int = 0,
val pstnSupportFlag: Boolean = false,
val forceDialPadTab: Boolean = false
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
index 897594ffad..f4919a5906 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
@@ -60,8 +60,8 @@ data class RoomDetailViewState(
val formattedTypingUsers: String? = null,
val tombstoneEvent: Event? = null,
val joinUpgradedRoomAsync: Async = Uninitialized,
- val syncState: SyncState = SyncState.Idle,
- val incrementalSyncRequestState: SyncRequestState.IncrementalSyncRequestState = SyncRequestState.IncrementalSyncIdle,
+ val syncState: SyncState? = null,
+ val incrementalSyncRequestState: SyncRequestState.IncrementalSyncRequestState? = null,
val pushCounter: Int = 0,
val highlightedEventId: String? = null,
val unreadState: UnreadState = UnreadState.Unknown,
diff --git a/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt b/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt
index a71f445859..a40b70b8a6 100755
--- a/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt
+++ b/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt
@@ -40,8 +40,8 @@ class SyncStateView @JvmOverloads constructor(context: Context, attrs: Attribute
@SuppressLint("SetTextI18n")
fun render(
- newState: SyncState,
- incrementalSyncRequestState: SyncRequestState.IncrementalSyncRequestState,
+ newState: SyncState?,
+ incrementalSyncRequestState: SyncRequestState.IncrementalSyncRequestState?,
pushCounter: Int,
showDebugInfo: Boolean
) {
@@ -64,8 +64,9 @@ class SyncStateView @JvmOverloads constructor(context: Context, attrs: Attribute
}
}
- private fun SyncState.toHumanReadable(): String {
+ private fun SyncState?.toHumanReadable(): String {
return when (this) {
+ null -> "Unknown"
SyncState.Idle -> "Idle"
SyncState.InvalidToken -> "InvalidToken"
SyncState.Killed -> "Killed"
@@ -76,8 +77,9 @@ class SyncStateView @JvmOverloads constructor(context: Context, attrs: Attribute
}
}
- private fun SyncRequestState.IncrementalSyncRequestState.toHumanReadable(): String {
+ private fun SyncRequestState.IncrementalSyncRequestState?.toHumanReadable(): String {
return when (this) {
+ null -> "Unknown"
SyncRequestState.IncrementalSyncIdle -> "Idle"
is SyncRequestState.IncrementalSyncParsing -> "Parsing ${this.rooms} room(s) ${this.toDevice} toDevice(s)"
SyncRequestState.IncrementalSyncError -> "Error"
diff --git a/vector/src/main/res/layout/fragment_timeline.xml b/vector/src/main/res/layout/fragment_timeline.xml
index 6e83dbe8fd..a022ad2744 100644
--- a/vector/src/main/res/layout/fragment_timeline.xml
+++ b/vector/src/main/res/layout/fragment_timeline.xml
@@ -17,7 +17,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
- android:visibility="gone" />
+ android:visibility="gone"
+ tools:visibility="visible" />
+ android:layout_height="match_parent">
+ app:layout_constraintTop_toBottomOf="@id/removeJitsiWidgetView"
+ tools:visibility="visible" />
+
+
-
-
+ android:translationZ="10dp"
+ android:visibility="visible" />
diff --git a/vector/src/test/java/im/vector/app/features/command/CommandParserTest.kt b/vector/src/test/java/im/vector/app/features/command/CommandParserTest.kt
index f502db85ca..ef6a99aa49 100644
--- a/vector/src/test/java/im/vector/app/features/command/CommandParserTest.kt
+++ b/vector/src/test/java/im/vector/app/features/command/CommandParserTest.kt
@@ -16,12 +16,15 @@
package im.vector.app.features.command
+import im.vector.app.test.fakes.FakeVectorPreferences
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
private const val A_SPACE_ID = "!my-space-id"
class CommandParserTest {
+ private val fakeVectorPreferences = FakeVectorPreferences()
+
@Test
fun parseSlashCommandEmpty() {
test("/", ParsedCommand.ErrorEmptySlashCommand)
@@ -70,7 +73,7 @@ class CommandParserTest {
}
private fun test(message: String, expectedResult: ParsedCommand) {
- val commandParser = CommandParser()
+ val commandParser = CommandParser(fakeVectorPreferences.instance)
val result = commandParser.parseSlashCommand(message, null, false)
result shouldBeEqualTo expectedResult
}