Skip to content

Commit

Permalink
Add a setting to be able to always appear offline (#5583)
Browse files Browse the repository at this point in the history
Add a setting to be able to always appear offline
Move presence enabling to a app-only preference
  • Loading branch information
aringenbach committed Apr 12, 2022
1 parent 454a656 commit 047a45d
Show file tree
Hide file tree
Showing 18 changed files with 116 additions and 46 deletions.
1 change: 1 addition & 0 deletions changelog.d/5582.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a setting to be able to always appear offline
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@ data class MatrixConfiguration(
* RoomDisplayNameFallbackProvider to provide default room display name.
*/
val roomDisplayNameFallbackProvider: RoomDisplayNameFallbackProvider,
/**
* True to enable presence information sync (if available). False to disable regardless of server setting.
*/
val presenceSyncEnabled: Boolean = true,
/**
* Thread messages default enable/disabled value
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import android.content.Context
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.internal.session.sync.SyncPresence
import javax.inject.Inject

/**
Expand All @@ -45,7 +46,29 @@ class LightweightSettingsStorage @Inject constructor(
return sdkDefaultPrefs.getBoolean(MATRIX_SDK_SETTINGS_THREAD_MESSAGES_ENABLED, matrixConfiguration.threadMessagesEnabledDefault)
}

/**
* Set the presence status sent on syncs when the application is in foreground.
*
* @param presence the presence status that should be sent on sync
*/
internal fun setSyncPresenceStatus(presence: SyncPresence) {
sdkDefaultPrefs.edit {
putString(MATRIX_SDK_SETTINGS_FOREGROUND_PRESENCE_STATUS, presence.value)
}
}

/**
* Get the presence status that should be sent on syncs when the application is in foreground.
*
* @return the presence status that should be sent on sync
*/
internal fun getSyncPresenceStatus(): SyncPresence {
val presenceString = sdkDefaultPrefs.getString(MATRIX_SDK_SETTINGS_FOREGROUND_PRESENCE_STATUS, SyncPresence.Online.value)
return SyncPresence.from(presenceString) ?: SyncPresence.Online
}

companion object {
const val MATRIX_SDK_SETTINGS_THREAD_MESSAGES_ENABLED = "MATRIX_SDK_SETTINGS_THREAD_MESSAGES_ENABLED"
private const val MATRIX_SDK_SETTINGS_FOREGROUND_PRESENCE_STATUS = "MATRIX_SDK_SETTINGS_FOREGROUND_PRESENCE_STATUS"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,22 @@ package org.matrix.android.sdk.internal.session.presence.service
import org.matrix.android.sdk.api.session.presence.PresenceService
import org.matrix.android.sdk.api.session.presence.model.PresenceEnum
import org.matrix.android.sdk.api.session.presence.model.UserPresence
import org.matrix.android.sdk.internal.database.lightweight.LightweightSettingsStorage
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.presence.service.task.GetPresenceTask
import org.matrix.android.sdk.internal.session.presence.service.task.SetPresenceTask
import org.matrix.android.sdk.internal.session.sync.SyncPresence
import javax.inject.Inject

internal class DefaultPresenceService @Inject constructor(
@UserId private val userId: String,
private val setPresenceTask: SetPresenceTask,
private val getPresenceTask: GetPresenceTask
private val getPresenceTask: GetPresenceTask,
private val lightweightSettingsStorage: LightweightSettingsStorage
) : PresenceService {

override suspend fun setMyPresence(presence: PresenceEnum, statusMsg: String?) {
lightweightSettingsStorage.setSyncPresenceStatus(SyncPresence.from(presence))
setPresenceTask.execute(SetPresenceTask.Params(userId, presence, statusMsg))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.matrix.android.sdk.internal.session.sync

import org.matrix.android.sdk.api.session.presence.model.PresenceEnum

/**
* For `set_presence` parameter in the /sync request
*
Expand All @@ -27,5 +29,16 @@ package org.matrix.android.sdk.internal.session.sync
enum class SyncPresence(val value: String) {
Offline("offline"),
Online("online"),
Unavailable("unavailable")
Unavailable("unavailable");

companion object {
fun from(presenceEnum: PresenceEnum): SyncPresence {
return when (presenceEnum) {
PresenceEnum.ONLINE -> Online
PresenceEnum.OFFLINE -> Offline
PresenceEnum.UNAVAILABLE -> Unavailable
}
}
fun from(s: String?): SyncPresence? = values().find { it.value == s }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,24 @@ import javax.inject.Inject
internal class PresenceSyncHandler @Inject constructor(private val matrixConfiguration: MatrixConfiguration) {

fun handle(realm: Realm, presenceSyncResponse: PresenceSyncResponse?) {
if (matrixConfiguration.presenceSyncEnabled) {
presenceSyncResponse?.events
?.filter { event -> event.type == EventType.PRESENCE }
?.forEach { event ->
val content = event.getPresenceContent() ?: return@forEach
val userId = event.senderId ?: return@forEach
val userPresenceEntity = UserPresenceEntity(
userId = userId,
lastActiveAgo = content.lastActiveAgo,
statusMessage = content.statusMessage,
isCurrentlyActive = content.isCurrentlyActive,
avatarUrl = content.avatarUrl,
displayName = content.displayName
).also {
it.presence = content.presence
}

storePresenceToDB(realm, userPresenceEntity)
presenceSyncResponse?.events
?.filter { event -> event.type == EventType.PRESENCE }
?.forEach { event ->
val content = event.getPresenceContent() ?: return@forEach
val userId = event.senderId ?: return@forEach
val userPresenceEntity = UserPresenceEntity(
userId = userId,
lastActiveAgo = content.lastActiveAgo,
statusMessage = content.statusMessage,
isCurrentlyActive = content.isCurrentlyActive,
avatarUrl = content.avatarUrl,
displayName = content.displayName
).also {
it.presence = content.presence
}
}

storePresenceToDB(realm, userPresenceEntity)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ import org.matrix.android.sdk.api.logger.LoggerTag
import org.matrix.android.sdk.api.session.call.MxCall
import org.matrix.android.sdk.api.session.sync.SyncState
import org.matrix.android.sdk.api.session.sync.model.SyncResponse
import org.matrix.android.sdk.internal.database.lightweight.LightweightSettingsStorage
import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker
import org.matrix.android.sdk.internal.session.call.ActiveCallHandler
import org.matrix.android.sdk.internal.session.sync.SyncPresence
import org.matrix.android.sdk.internal.session.sync.SyncTask
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.util.Debouncer
Expand All @@ -59,7 +59,8 @@ private val loggerTag = LoggerTag("SyncThread", LoggerTag.SYNC)
internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
private val networkConnectivityChecker: NetworkConnectivityChecker,
private val backgroundDetectionObserver: BackgroundDetectionObserver,
private val activeCallHandler: ActiveCallHandler
private val activeCallHandler: ActiveCallHandler,
private val lightweightSettingsStorage: LightweightSettingsStorage
) : Thread("SyncThread"), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {

private var state: SyncState = SyncState.Idle
Expand Down Expand Up @@ -182,7 +183,8 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
else -> DEFAULT_LONG_POOL_TIMEOUT
}
Timber.tag(loggerTag.value).d("Execute sync request with timeout $timeout")
val params = SyncTask.Params(timeout, SyncPresence.Online, afterPause = afterPause)
val presence = lightweightSettingsStorage.getSyncPresenceStatus()
val params = SyncTask.Params(timeout, presence, afterPause = afterPause)
val sync = syncScope.launch {
previousSyncResponseHasToDevice = doSync(params)
}
Expand Down
2 changes: 2 additions & 0 deletions vector-config/src/main/res/values/config-settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

<bool name="settings_interface_bubble_visible">true</bool>
<bool name="settings_interface_bubble_default">false</bool>
<bool name="settings_presence_visible">false</bool>
<bool name="settings_presence_user_always_appears_offline_default">false</bool>

<!-- Level 1: Voice and video -->

Expand Down
1 change: 0 additions & 1 deletion vector/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ android {

buildConfigField "Boolean", "enableLocationSharing", "true"
buildConfigField "String", "mapTilerKey", "\"fU3vlMsMn4Jb6dnEIFsx\""
buildConfigField "Boolean", "PRESENCE_SYNC_ENABLED", "true"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ object VectorStaticModule {
applicationFlavor = BuildConfig.FLAVOR_DESCRIPTION,
roomDisplayNameFallbackProvider = vectorRoomDisplayNameFallbackProvider,
threadMessagesEnabledDefault = vectorPreferences.areThreadMessagesEnabled(),
presenceSyncEnabled = BuildConfig.PRESENCE_SYNC_ENABLED
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.billcarsonfr.jsonviewer.JSonViewerDialog
import org.commonmark.parser.Parser
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.events.model.EventType
Expand Down Expand Up @@ -266,8 +265,7 @@ class TimelineFragment @Inject constructor(
private val pillsPostProcessorFactory: PillsPostProcessor.Factory,
private val callManager: WebRtcCallManager,
private val audioMessagePlaybackTracker: AudioMessagePlaybackTracker,
private val clock: Clock,
private val matrixConfiguration: MatrixConfiguration
private val clock: Clock
) :
VectorBaseFragment<FragmentTimelineBinding>(),
TimelineEventController.Callback,
Expand Down Expand Up @@ -1650,7 +1648,7 @@ class TimelineFragment @Inject constructor(
views.includeRoomToolbar.roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN
views.includeRoomToolbar.roomToolbarTitleView.text = roomSummary.displayName
avatarRenderer.render(roomSummary.toMatrixItem(), views.includeRoomToolbar.roomToolbarAvatarImageView)
val showPresence = roomSummary.isDirect && matrixConfiguration.presenceSyncEnabled
val showPresence = roomSummary.isDirect
views.includeRoomToolbar.roomToolbarPresenceImageView.render(showPresence, roomSummary.directUserPresence)
val shieldView = if (showPresence) views.includeRoomToolbar.roomToolbarTitleShield else views.includeRoomToolbar.roomToolbarAvatarShield
shieldView.render(roomSummary.roomEncryptionTrustLevel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter
import im.vector.app.features.home.room.typing.TypingHelper
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary
Expand All @@ -42,8 +41,7 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
private val stringProvider: StringProvider,
private val typingHelper: TypingHelper,
private val avatarRenderer: AvatarRenderer,
private val errorFormatter: ErrorFormatter,
private val matrixConfiguration: MatrixConfiguration) {
private val errorFormatter: ErrorFormatter) {

fun create(roomSummary: RoomSummary,
roomChangeMembershipStates: Map<String, ChangeMembershipState>,
Expand Down Expand Up @@ -127,7 +125,7 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
// We do not display shield in the room list anymore
// .encryptionTrustLevel(roomSummary.roomEncryptionTrustLevel)
.izPublic(roomSummary.isPublic)
.showPresence(roomSummary.isDirect && matrixConfiguration.presenceSyncEnabled)
.showPresence(roomSummary.isDirect)
.userPresence(roomSummary.directUserPresence)
.matrixItem(roomSummary.toMatrixItem())
.lastEventTime(latestEventTime)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
import org.matrix.android.sdk.api.util.toMatrixItem
import timber.log.Timber
Expand All @@ -69,7 +68,6 @@ class RoomProfileFragment @Inject constructor(
private val roomProfileController: RoomProfileController,
private val avatarRenderer: AvatarRenderer,
private val roomDetailPendingActionStore: RoomDetailPendingActionStore,
private val matrixConfiguration: MatrixConfiguration
) :
VectorBaseFragment<FragmentMatrixProfileBinding>(),
RoomProfileController.Callback {
Expand Down Expand Up @@ -224,7 +222,7 @@ class RoomProfileFragment @Inject constructor(
avatarRenderer.render(matrixItem, views.matrixProfileToolbarAvatarImageView)
headerViews.roomProfileDecorationImageView.render(it.roomEncryptionTrustLevel)
views.matrixProfileDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel)
headerViews.roomProfilePresenceImageView.render(it.isDirect && matrixConfiguration.presenceSyncEnabled, it.directUserPresence)
headerViews.roomProfilePresenceImageView.render(it.isDirect, it.directUserPresence)
headerViews.roomProfilePublicImageView.isVisible = it.isPublic && !it.isDirect
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.home.AvatarRenderer
import me.gujun.android.span.span
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
Expand All @@ -40,8 +39,7 @@ class RoomMemberListController @Inject constructor(
private val avatarRenderer: AvatarRenderer,
private val stringProvider: StringProvider,
private val colorProvider: ColorProvider,
private val roomMemberSummaryFilter: RoomMemberSummaryFilter,
private val matrixConfiguration: MatrixConfiguration
private val roomMemberSummaryFilter: RoomMemberSummaryFilter
) : TypedEpoxyController<RoomMemberListViewState>() {

interface Callback {
Expand Down Expand Up @@ -124,7 +122,6 @@ class RoomMemberListController @Inject constructor(
host: RoomMemberListController,
data: RoomMemberListViewState) {
val powerLabel = stringProvider.getString(powerLevelCategory.titleRes)
val presenceSyncEnabled = matrixConfiguration.presenceSyncEnabled

profileMatrixItemWithPowerLevelWithPresence {
id(roomMember.userId)
Expand All @@ -134,7 +131,7 @@ class RoomMemberListController @Inject constructor(
clickListener {
host.callback?.onRoomMemberClicked(roomMember)
}
showPresence(presenceSyncEnabled)
showPresence(true)
userPresence(roomMember.userPresence)
powerLevelLabel(
span {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
private const val SETTINGS_ENABLE_CHAT_EFFECTS = "SETTINGS_ENABLE_CHAT_EFFECTS"
private const val SETTINGS_SHOW_EMOJI_KEYBOARD = "SETTINGS_SHOW_EMOJI_KEYBOARD"
private const val SETTINGS_LABS_ENABLE_LATEX_MATHS = "SETTINGS_LABS_ENABLE_LATEX_MATHS"
const val SETTINGS_PRESENCE_USER_ALWAYS_APPEARS_OFFLINE = "SETTINGS_PRESENCE_USER_ALWAYS_APPEARS_OFFLINE"

// Room directory
private const val SETTINGS_ROOM_DIRECTORY_SHOW_ALL_PUBLIC_ROOMS = "SETTINGS_ROOM_DIRECTORY_SHOW_ALL_PUBLIC_ROOMS"
Expand Down Expand Up @@ -865,6 +866,16 @@ class VectorPreferences @Inject constructor(private val context: Context) {
return defaultPrefs.getBoolean(SETTINGS_INTERFACE_BUBBLE_KEY, getDefault(R.bool.settings_interface_bubble_default))
}

/**
* Tells if user should always appear offline or not.
*
* @return true if user should always appear offline
*/
fun userAlwaysAppearsOffline(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_PRESENCE_USER_ALWAYS_APPEARS_OFFLINE,
getDefault(R.bool.settings_presence_user_always_appears_offline_default))
}

/**
* Update the rage shake enabled status.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import android.content.Context
import android.os.Bundle
import android.widget.CheckedTextView
import androidx.core.view.children
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.BuildConfig
Expand All @@ -36,6 +37,8 @@ import im.vector.app.features.MainActivityArgs
import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.configuration.VectorConfiguration
import im.vector.app.features.themes.ThemeUtils
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.presence.model.PresenceEnum
import javax.inject.Inject

class VectorSettingsPreferencesFragment @Inject constructor(
Expand Down Expand Up @@ -78,6 +81,17 @@ class VectorSettingsPreferencesFragment @Inject constructor(
}
}

findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_PRESENCE_USER_ALWAYS_APPEARS_OFFLINE)!!.let { pref ->
pref.isChecked = vectorPreferences.userAlwaysAppearsOffline()
pref.setOnPreferenceChangeListener { _, newValue ->
val presenceOfflineModeEnabled = newValue as? Boolean ?: false
lifecycleScope.launch {
session.setMyPresence(if (presenceOfflineModeEnabled) PresenceEnum.OFFLINE else PresenceEnum.ONLINE)
}
true
}
}

findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME)!!.let { pref ->
pref.isChecked = vectorPreferences.prefSpacesShowAllRoomInHome()
pref.setOnPreferenceChangeListener { _, _ ->
Expand Down
Loading

0 comments on commit 047a45d

Please sign in to comment.