Allow using the latest user avatar and display name for all messages in the timeline
Signed-off-by: Jorge Martín Espinosa <jorgem@element.io>
This commit is contained in:
parent
f54c865cf4
commit
6a523ccc38
|
@ -0,0 +1 @@
|
||||||
|
Allow using the latest user Avatar and name for all messages in the timeline
|
|
@ -32,6 +32,10 @@ data class TimelineSettings(
|
||||||
* The root thread eventId if this is a thread timeline, or null if this is NOT a thread timeline
|
* The root thread eventId if this is a thread timeline, or null if this is NOT a thread timeline
|
||||||
*/
|
*/
|
||||||
val rootThreadEventId: String? = null,
|
val rootThreadEventId: String? = null,
|
||||||
|
/**
|
||||||
|
* If true Sender Info shown in room will get the latest data information (avatar + displayName)
|
||||||
|
*/
|
||||||
|
val useLiveSenderInfo: Boolean = false,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.matrix.android.sdk.api.settings.LightweightSettingsStorage
|
||||||
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
|
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
||||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
||||||
import org.matrix.android.sdk.internal.task.SemaphoreCoroutineSequencer
|
import org.matrix.android.sdk.internal.task.SemaphoreCoroutineSequencer
|
||||||
|
@ -59,6 +60,7 @@ internal class DefaultTimeline(
|
||||||
private val settings: TimelineSettings,
|
private val settings: TimelineSettings,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val clock: Clock,
|
private val clock: Clock,
|
||||||
|
stateEventDataSource: StateEventDataSource,
|
||||||
paginationTask: PaginationTask,
|
paginationTask: PaginationTask,
|
||||||
getEventTask: GetContextOfEventTask,
|
getEventTask: GetContextOfEventTask,
|
||||||
fetchTokenAndPaginateTask: FetchTokenAndPaginateTask,
|
fetchTokenAndPaginateTask: FetchTokenAndPaginateTask,
|
||||||
|
@ -106,7 +108,9 @@ internal class DefaultTimeline(
|
||||||
onEventsUpdated = this::sendSignalToPostSnapshot,
|
onEventsUpdated = this::sendSignalToPostSnapshot,
|
||||||
onEventsDeleted = this::onEventsDeleted,
|
onEventsDeleted = this::onEventsDeleted,
|
||||||
onLimitedTimeline = this::onLimitedTimeline,
|
onLimitedTimeline = this::onLimitedTimeline,
|
||||||
onNewTimelineEvents = this::onNewTimelineEvents
|
onNewTimelineEvents = this::onNewTimelineEvents,
|
||||||
|
stateEventDataSource = stateEventDataSource,
|
||||||
|
matrixCoroutineDispatchers = coroutineDispatchers,
|
||||||
)
|
)
|
||||||
|
|
||||||
private var strategy: LoadTimelineStrategy = buildStrategy(LoadTimelineStrategy.Mode.Live)
|
private var strategy: LoadTimelineStrategy = buildStrategy(LoadTimelineStrategy.Mode.Live)
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
||||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
||||||
import org.matrix.android.sdk.internal.util.time.Clock
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
|
@ -53,6 +54,7 @@ internal class DefaultTimelineService @AssistedInject constructor(
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val timelineEventDataSource: TimelineEventDataSource,
|
private val timelineEventDataSource: TimelineEventDataSource,
|
||||||
private val clock: Clock,
|
private val clock: Clock,
|
||||||
|
private val stateEventDataSource: StateEventDataSource,
|
||||||
) : TimelineService {
|
) : TimelineService {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
@ -78,7 +80,8 @@ internal class DefaultTimelineService @AssistedInject constructor(
|
||||||
getEventTask = contextOfEventTask,
|
getEventTask = contextOfEventTask,
|
||||||
threadsAwarenessHandler = threadsAwarenessHandler,
|
threadsAwarenessHandler = threadsAwarenessHandler,
|
||||||
lightweightSettingsStorage = lightweightSettingsStorage,
|
lightweightSettingsStorage = lightweightSettingsStorage,
|
||||||
clock = clock
|
clock = clock,
|
||||||
|
stateEventDataSource = stateEventDataSource,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* 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.timeline
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||||
|
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to observe and query the live room state.
|
||||||
|
*/
|
||||||
|
internal class LiveRoomStateListener(
|
||||||
|
roomId: String,
|
||||||
|
stateEventDataSource: StateEventDataSource,
|
||||||
|
private val mainDispatcher: CoroutineDispatcher,
|
||||||
|
) {
|
||||||
|
private val roomStateObserver = Observer<List<Event>> { stateEvents ->
|
||||||
|
stateEvents.map { event ->
|
||||||
|
val memberContent = event.getFixedRoomMemberContent() ?: return@map
|
||||||
|
val stateKey = event.stateKey ?: return@map
|
||||||
|
liveRoomState[stateKey] = memberContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private val stateEventsLiveData: LiveData<List<Event>> by lazy {
|
||||||
|
stateEventDataSource.getStateEventsLive(
|
||||||
|
roomId = roomId,
|
||||||
|
eventTypes = setOf(EventType.STATE_ROOM_MEMBER),
|
||||||
|
stateKey = QueryStringValue.NoCondition,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val liveRoomState = mutableMapOf<String, RoomMemberContent>()
|
||||||
|
|
||||||
|
suspend fun start() = withContext(mainDispatcher) {
|
||||||
|
stateEventsLiveData.observeForever(roomStateObserver)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun stop() = withContext(mainDispatcher) {
|
||||||
|
if (stateEventsLiveData.hasActiveObservers()) {
|
||||||
|
stateEventsLiveData.removeObserver(roomStateObserver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLiveState(stateKey: String): RoomMemberContent? = liveRoomState[stateKey]
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import io.realm.RealmConfiguration
|
||||||
import io.realm.RealmResults
|
import io.realm.RealmResults
|
||||||
import io.realm.kotlin.createObject
|
import io.realm.kotlin.createObject
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import org.matrix.android.sdk.api.failure.MatrixError
|
import org.matrix.android.sdk.api.failure.MatrixError
|
||||||
|
@ -41,6 +42,7 @@ import org.matrix.android.sdk.internal.database.query.findAllIncludingEvents
|
||||||
import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfThread
|
import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfThread
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
||||||
import org.matrix.android.sdk.internal.util.time.Clock
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -100,7 +102,9 @@ internal class LoadTimelineStrategy constructor(
|
||||||
val onEventsUpdated: (Boolean) -> Unit,
|
val onEventsUpdated: (Boolean) -> Unit,
|
||||||
val onEventsDeleted: () -> Unit,
|
val onEventsDeleted: () -> Unit,
|
||||||
val onLimitedTimeline: () -> Unit,
|
val onLimitedTimeline: () -> Unit,
|
||||||
val onNewTimelineEvents: (List<String>) -> Unit
|
val onNewTimelineEvents: (List<String>) -> Unit,
|
||||||
|
val stateEventDataSource: StateEventDataSource,
|
||||||
|
val matrixCoroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
)
|
)
|
||||||
|
|
||||||
private var getContextLatch: CompletableDeferred<Unit>? = null
|
private var getContextLatch: CompletableDeferred<Unit>? = null
|
||||||
|
@ -165,7 +169,13 @@ internal class LoadTimelineStrategy constructor(
|
||||||
onEventsUpdated = dependencies.onEventsUpdated
|
onEventsUpdated = dependencies.onEventsUpdated
|
||||||
)
|
)
|
||||||
|
|
||||||
fun onStart() {
|
private val liveRoomStateListener = LiveRoomStateListener(
|
||||||
|
roomId,
|
||||||
|
dependencies.stateEventDataSource,
|
||||||
|
dependencies.matrixCoroutineDispatchers.main
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun onStart() {
|
||||||
dependencies.eventDecryptor.start()
|
dependencies.eventDecryptor.start()
|
||||||
dependencies.timelineInput.listeners.add(timelineInputListener)
|
dependencies.timelineInput.listeners.add(timelineInputListener)
|
||||||
val realm = dependencies.realm.get()
|
val realm = dependencies.realm.get()
|
||||||
|
@ -174,9 +184,13 @@ internal class LoadTimelineStrategy constructor(
|
||||||
it.addChangeListener(chunkEntityListener)
|
it.addChangeListener(chunkEntityListener)
|
||||||
timelineChunk = it.createTimelineChunk()
|
timelineChunk = it.createTimelineChunk()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dependencies.timelineSettings.useLiveSenderInfo) {
|
||||||
|
liveRoomStateListener.start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onStop() {
|
suspend fun onStop() {
|
||||||
dependencies.eventDecryptor.destroy()
|
dependencies.eventDecryptor.destroy()
|
||||||
dependencies.timelineInput.listeners.remove(timelineInputListener)
|
dependencies.timelineInput.listeners.remove(timelineInputListener)
|
||||||
chunkEntity?.removeChangeListener(chunkEntityListener)
|
chunkEntity?.removeChangeListener(chunkEntityListener)
|
||||||
|
@ -188,6 +202,9 @@ internal class LoadTimelineStrategy constructor(
|
||||||
if (mode is Mode.Thread) {
|
if (mode is Mode.Thread) {
|
||||||
clearThreadChunkEntity(dependencies.realm.get(), mode.rootThreadEventId)
|
clearThreadChunkEntity(dependencies.realm.get(), mode.rootThreadEventId)
|
||||||
}
|
}
|
||||||
|
if (dependencies.timelineSettings.useLiveSenderInfo) {
|
||||||
|
liveRoomStateListener.stop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadMore(count: Int, direction: Timeline.Direction, fetchOnServerIfNeeded: Boolean = true): LoadMoreResult {
|
suspend fun loadMore(count: Int, direction: Timeline.Direction, fetchOnServerIfNeeded: Boolean = true): LoadMoreResult {
|
||||||
|
@ -222,7 +239,22 @@ internal class LoadTimelineStrategy constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildSnapshot(): List<TimelineEvent> {
|
fun buildSnapshot(): List<TimelineEvent> {
|
||||||
return buildSendingEvents() + timelineChunk?.builtItems(includesNext = true, includesPrev = true).orEmpty()
|
val events = buildSendingEvents() + timelineChunk?.builtItems(includesNext = true, includesPrev = true).orEmpty()
|
||||||
|
return if (dependencies.timelineSettings.useLiveSenderInfo) {
|
||||||
|
events.map(this::applyLiveRoomState)
|
||||||
|
} else {
|
||||||
|
events
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyLiveRoomState(event: TimelineEvent): TimelineEvent {
|
||||||
|
val updatedState = liveRoomStateListener.getLiveState(event.senderInfo.userId)
|
||||||
|
return if (updatedState != null) {
|
||||||
|
val updatedSenderInfo = event.senderInfo.copy(avatarUrl = updatedState.avatarUrl, displayName = updatedState.displayName)
|
||||||
|
event.copy(senderInfo = updatedSenderInfo)
|
||||||
|
} else {
|
||||||
|
event
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildSendingEvents(): List<TimelineEvent> {
|
private fun buildSendingEvents(): List<TimelineEvent> {
|
||||||
|
|
|
@ -136,6 +136,7 @@ internal class TimelineChunk(
|
||||||
val prevEvents = prevChunk?.builtItems(includesNext = false, includesPrev = true).orEmpty()
|
val prevEvents = prevChunk?.builtItems(includesNext = false, includesPrev = true).orEmpty()
|
||||||
deepBuiltItems.addAll(prevEvents)
|
deepBuiltItems.addAll(prevEvents)
|
||||||
}
|
}
|
||||||
|
|
||||||
return deepBuiltItems
|
return deepBuiltItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,8 @@
|
||||||
|
|
||||||
<!-- Level 1: Labs -->
|
<!-- Level 1: Labs -->
|
||||||
<bool name="settings_labs_thread_messages_default">false</bool>
|
<bool name="settings_labs_thread_messages_default">false</bool>
|
||||||
|
<bool name="settings_timeline_show_live_sender_info_visible">true</bool>
|
||||||
|
<bool name="settings_timeline_show_live_sender_info_default">false</bool>
|
||||||
<!-- Level 1: Advanced settings -->
|
<!-- Level 1: Advanced settings -->
|
||||||
|
|
||||||
<!-- Level 1: Help and about -->
|
<!-- Level 1: Help and about -->
|
||||||
|
|
|
@ -52,4 +52,8 @@ class UserPreferencesProvider @Inject constructor(private val vectorPreferences:
|
||||||
fun areThreadMessagesEnabled(): Boolean {
|
fun areThreadMessagesEnabled(): Boolean {
|
||||||
return vectorPreferences.areThreadMessagesEnabled()
|
return vectorPreferences.areThreadMessagesEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showLiveSenderInfo(): Boolean {
|
||||||
|
return vectorPreferences.showLiveSenderInfo()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,8 @@ class TimelineSettingsFactory @Inject constructor(private val userPreferencesPro
|
||||||
return TimelineSettings(
|
return TimelineSettings(
|
||||||
initialSize = 30,
|
initialSize = 30,
|
||||||
buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts(),
|
buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts(),
|
||||||
rootThreadEventId = rootThreadEventId
|
rootThreadEventId = rootThreadEventId,
|
||||||
|
useLiveSenderInfo = userPreferencesProvider.showLiveSenderInfo()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,6 +211,9 @@ class VectorPreferences @Inject constructor(
|
||||||
const val SETTINGS_LABS_ENABLE_THREAD_MESSAGES = "SETTINGS_LABS_ENABLE_THREAD_MESSAGES_FINAL"
|
const val SETTINGS_LABS_ENABLE_THREAD_MESSAGES = "SETTINGS_LABS_ENABLE_THREAD_MESSAGES_FINAL"
|
||||||
const val SETTINGS_THREAD_MESSAGES_SYNCED = "SETTINGS_THREAD_MESSAGES_SYNCED"
|
const val SETTINGS_THREAD_MESSAGES_SYNCED = "SETTINGS_THREAD_MESSAGES_SYNCED"
|
||||||
|
|
||||||
|
// This key will be used to enable user for displaying live user info or not.
|
||||||
|
const val SETTINGS_TIMELINE_SHOW_LIVE_SENDER_INFO = "SETTINGS_TIMELINE_SHOW_LIVE_SENDER_INFO"
|
||||||
|
|
||||||
// Possible values for TAKE_PHOTO_VIDEO_MODE
|
// Possible values for TAKE_PHOTO_VIDEO_MODE
|
||||||
const val TAKE_PHOTO_VIDEO_MODE_ALWAYS_ASK = 0
|
const val TAKE_PHOTO_VIDEO_MODE_ALWAYS_ASK = 0
|
||||||
const val TAKE_PHOTO_VIDEO_MODE_PHOTO = 1
|
const val TAKE_PHOTO_VIDEO_MODE_PHOTO = 1
|
||||||
|
@ -1039,9 +1042,6 @@ class VectorPreferences @Inject constructor(
|
||||||
return defaultPrefs.getBoolean(SETTINGS_LABS_RENDER_LOCATIONS_IN_TIMELINE, true)
|
return defaultPrefs.getBoolean(SETTINGS_LABS_RENDER_LOCATIONS_IN_TIMELINE, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether or not thread messages are enabled
|
|
||||||
*/
|
|
||||||
fun areThreadMessagesEnabled(): Boolean {
|
fun areThreadMessagesEnabled(): Boolean {
|
||||||
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_THREAD_MESSAGES, getDefault(R.bool.settings_labs_thread_messages_default))
|
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_THREAD_MESSAGES, getDefault(R.bool.settings_labs_thread_messages_default))
|
||||||
}
|
}
|
||||||
|
@ -1091,4 +1091,8 @@ class VectorPreferences @Inject constructor(
|
||||||
.putBoolean(SETTINGS_THREAD_MESSAGES_SYNCED, shouldMigrate)
|
.putBoolean(SETTINGS_THREAD_MESSAGES_SYNCED, shouldMigrate)
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showLiveSenderInfo(): Boolean {
|
||||||
|
return defaultPrefs.getBoolean(SETTINGS_TIMELINE_SHOW_LIVE_SENDER_INFO, getDefault(R.bool.settings_timeline_show_live_sender_info_default))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2852,6 +2852,8 @@
|
||||||
<string name="labs_auto_report_uisi_desc">Your system will automatically send logs when an unable to decrypt error occurs</string>
|
<string name="labs_auto_report_uisi_desc">Your system will automatically send logs when an unable to decrypt error occurs</string>
|
||||||
<string name="labs_enable_thread_messages">Enable Thread Messages</string>
|
<string name="labs_enable_thread_messages">Enable Thread Messages</string>
|
||||||
<string name="labs_enable_thread_messages_desc">Note: app will be restarted</string>
|
<string name="labs_enable_thread_messages_desc">Note: app will be restarted</string>
|
||||||
|
<string name="settings_show_latest_profile">Show latest user info</string>
|
||||||
|
<string name="settings_show_latest_profile_description">Show the latest profile info (avatar and display name) for all the messages.</string>
|
||||||
|
|
||||||
<string name="user_invites_you">%s invites you</string>
|
<string name="user_invites_you">%s invites you</string>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<!--<im.vector.app.core.preference.VectorPreferenceCategory-->
|
<!--<im.vector.app.core.preference.VectorPreferenceCategory-->
|
||||||
<!--android:key="SETTINGS_LABS_PREFERENCE_KEY"-->
|
<!--android:key="SETTINGS_LABS_PREFERENCE_KEY"-->
|
||||||
|
|
|
@ -88,6 +88,13 @@
|
||||||
android:title="@string/message_bubbles"
|
android:title="@string/message_bubbles"
|
||||||
app:isPreferenceVisible="@bool/settings_interface_bubble_visible" />
|
app:isPreferenceVisible="@bool/settings_interface_bubble_visible" />
|
||||||
|
|
||||||
|
<im.vector.app.core.preference.VectorSwitchPreference
|
||||||
|
android:defaultValue="@bool/settings_timeline_show_live_sender_info_default"
|
||||||
|
app:isPreferenceVisible="@bool/settings_timeline_show_live_sender_info_visible"
|
||||||
|
android:key="SETTINGS_TIMELINE_SHOW_LIVE_SENDER_INFO"
|
||||||
|
android:summary="@string/settings_show_latest_profile_description"
|
||||||
|
android:title="@string/settings_show_latest_profile" />
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorSwitchPreference
|
<im.vector.app.core.preference.VectorSwitchPreference
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:key="SETTINGS_SHOW_URL_PREVIEW_KEY"
|
android:key="SETTINGS_SHOW_URL_PREVIEW_KEY"
|
||||||
|
|
Loading…
Reference in New Issue