Merge pull request #829 from vector-im/feature/stab

Feature/stab
This commit is contained in:
Benoit Marty 2020-01-13 15:13:07 +01:00 committed by GitHub
commit 3c682430d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 139 additions and 132 deletions

View File

@ -5,7 +5,7 @@ Features ✨:
- -
Improvements 🙌: Improvements 🙌:
- - Render events m.room.encryption and m.room.guest_access in the timeline
Other changes: Other changes:
- -
@ -17,7 +17,7 @@ Translations 🗣:
- -
Build 🧱: Build 🧱:
- - Change the way versionCode is computed (#827)
Changes in RiotX 0.12.0 (2020-01-09) Changes in RiotX 0.12.0 (2020-01-09)
=================================================== ===================================================

View File

@ -91,7 +91,8 @@ object EventType {
STATE_ROOM_CANONICAL_ALIAS, STATE_ROOM_CANONICAL_ALIAS,
STATE_ROOM_HISTORY_VISIBILITY, STATE_ROOM_HISTORY_VISIBILITY,
STATE_ROOM_RELATED_GROUPS, STATE_ROOM_RELATED_GROUPS,
STATE_ROOM_PINNED_EVENT STATE_ROOM_PINNED_EVENT,
STATE_ROOM_ENCRYPTION
) )
fun isStateEvent(type: String): Boolean { fun isStateEvent(type: String): Boolean {

View File

@ -28,17 +28,20 @@ fun roomMemberQueryParams(init: (RoomMemberQueryParams.Builder.() -> Unit) = {})
*/ */
data class RoomMemberQueryParams( data class RoomMemberQueryParams(
val displayName: QueryStringValue, val displayName: QueryStringValue,
val memberships: List<Membership> val memberships: List<Membership>,
val excludeSelf: Boolean
) { ) {
class Builder { class Builder {
var displayName: QueryStringValue = QueryStringValue.IsNotEmpty var displayName: QueryStringValue = QueryStringValue.IsNotEmpty
var memberships: List<Membership> = Membership.all() var memberships: List<Membership> = Membership.all()
var excludeSelf: Boolean = false
fun build() = RoomMemberQueryParams( fun build() = RoomMemberQueryParams(
displayName = displayName, displayName = displayName,
memberships = memberships memberships = memberships,
excludeSelf = excludeSelf
) )
} }
} }

View File

@ -0,0 +1,37 @@
/*
* Copyright 2019 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.matrix.android.api.session.room.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Class representing the EventType.STATE_ROOM_GUEST_ACCESS state event content
* Ref: https://matrix.org/docs/spec/client_server/latest#m-room-guest-access
*/
@JsonClass(generateAdapter = true)
data class RoomGuestAccessContent(
// Required. Whether guests can join the room. One of: ["can_join", "forbidden"]
@Json(name = "guest_access") val guestAccess: GuestAccess? = null
)
enum class GuestAccess(val value: String) {
@Json(name = "can_join")
CanJoin("can_join"),
@Json(name = "forbidden")
Forbidden("forbidden")
}

View File

@ -28,6 +28,8 @@ import im.vector.matrix.android.api.session.room.model.PowerLevels
import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
import im.vector.matrix.android.internal.auth.data.ThreePidMedium import im.vector.matrix.android.internal.auth.data.ThreePidMedium
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import timber.log.Timber
/** /**
* Parameter to create a room, with facilities functions to configure it * Parameter to create a room, with facilities functions to configure it
@ -88,7 +90,7 @@ class CreateRoomParams {
* A list of state events to set in the new room. * A list of state events to set in the new room.
* This allows the user to override the default state events set in the new room. * This allows the user to override the default state events set in the new room.
* The expected format of the state events are an object with type, state_key and content keys set. * The expected format of the state events are an object with type, state_key and content keys set.
* Takes precedence over events set by presets, but gets overriden by name and topic keys. * Takes precedence over events set by presets, but gets overridden by name and topic keys.
*/ */
@Json(name = "initial_state") @Json(name = "initial_state")
var initialStates: MutableList<Event>? = null var initialStates: MutableList<Event>? = null
@ -120,14 +122,14 @@ class CreateRoomParams {
* *
* @param algorithm the algorithm * @param algorithm the algorithm
*/ */
fun addCryptoAlgorithm(algorithm: String) { fun enableEncryptionWithAlgorithm(algorithm: String) {
if (algorithm.isNotBlank()) { if (algorithm == MXCRYPTO_ALGORITHM_MEGOLM) {
val contentMap = HashMap<String, String>() val contentMap = mapOf("algorithm" to algorithm)
contentMap["algorithm"] = algorithm
val algoEvent = Event(type = EventType.STATE_ROOM_ENCRYPTION, val algoEvent = Event(
stateKey = "", type = EventType.STATE_ROOM_ENCRYPTION,
content = contentMap.toContent() stateKey = "",
content = contentMap.toContent()
) )
if (null == initialStates) { if (null == initialStates) {
@ -135,6 +137,8 @@ class CreateRoomParams {
} else { } else {
initialStates!!.add(algoEvent) initialStates!!.add(algoEvent)
} }
} else {
Timber.e("Unsupported algorithm: $algorithm")
} }
} }
@ -145,15 +149,15 @@ class CreateRoomParams {
*/ */
fun setHistoryVisibility(historyVisibility: RoomHistoryVisibility?) { fun setHistoryVisibility(historyVisibility: RoomHistoryVisibility?) {
// Remove the existing value if any. // Remove the existing value if any.
initialStates?.removeAll { it.getClearType() == EventType.STATE_ROOM_HISTORY_VISIBILITY } initialStates?.removeAll { it.type == EventType.STATE_ROOM_HISTORY_VISIBILITY }
if (historyVisibility != null) { if (historyVisibility != null) {
val contentMap = HashMap<String, RoomHistoryVisibility>() val contentMap = mapOf("history_visibility" to historyVisibility)
contentMap["history_visibility"] = historyVisibility
val historyVisibilityEvent = Event(type = EventType.STATE_ROOM_HISTORY_VISIBILITY, val historyVisibilityEvent = Event(
stateKey = "", type = EventType.STATE_ROOM_HISTORY_VISIBILITY,
content = contentMap.toContent()) stateKey = "",
content = contentMap.toContent())
if (null == initialStates) { if (null == initialStates) {
initialStates = mutableListOf(historyVisibilityEvent) initialStates = mutableListOf(historyVisibilityEvent)
@ -192,8 +196,8 @@ class CreateRoomParams {
*/ */
fun isDirect(): Boolean { fun isDirect(): Boolean {
return preset == CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT return preset == CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT
&& isDirect == true && isDirect == true
&& (1 == getInviteCount() || 1 == getInvite3PidCount()) && (1 == getInviteCount() || 1 == getInvite3PidCount())
} }
/** /**
@ -218,8 +222,8 @@ class CreateRoomParams {
invite3pids = ArrayList() invite3pids = ArrayList()
} }
val pid = Invite3Pid(idServer = hsConfig.identityServerUri.host!!, val pid = Invite3Pid(idServer = hsConfig.identityServerUri.host!!,
medium = ThreePidMedium.EMAIL, medium = ThreePidMedium.EMAIL,
address = id) address = id)
invite3pids!!.add(pid) invite3pids!!.add(pid)
} else if (isUserId(id)) { } else if (isUserId(id)) {

View File

@ -29,6 +29,7 @@ import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.RoomMemberEntity import im.vector.matrix.android.internal.database.model.RoomMemberEntity
import im.vector.matrix.android.internal.database.model.RoomMemberEntityFields import im.vector.matrix.android.internal.database.model.RoomMemberEntityFields
import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.query.process import im.vector.matrix.android.internal.query.process
import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
@ -39,13 +40,16 @@ import im.vector.matrix.android.internal.util.fetchCopied
import io.realm.Realm import io.realm.Realm
import io.realm.RealmQuery import io.realm.RealmQuery
internal class DefaultMembershipService @AssistedInject constructor(@Assisted private val roomId: String, internal class DefaultMembershipService @AssistedInject constructor(
private val monarchy: Monarchy, @Assisted private val roomId: String,
private val taskExecutor: TaskExecutor, private val monarchy: Monarchy,
private val loadRoomMembersTask: LoadRoomMembersTask, private val taskExecutor: TaskExecutor,
private val inviteTask: InviteTask, private val loadRoomMembersTask: LoadRoomMembersTask,
private val joinTask: JoinRoomTask, private val inviteTask: InviteTask,
private val leaveRoomTask: LeaveRoomTask private val joinTask: JoinRoomTask,
private val leaveRoomTask: LeaveRoomTask,
@UserId
private val userId: String
) : MembershipService { ) : MembershipService {
@AssistedInject.Factory @AssistedInject.Factory
@ -95,6 +99,11 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr
return RoomMembers(realm, roomId).queryRoomMembersEvent() return RoomMembers(realm, roomId).queryRoomMembersEvent()
.process(RoomMemberEntityFields.MEMBERSHIP_STR, queryParams.memberships) .process(RoomMemberEntityFields.MEMBERSHIP_STR, queryParams.memberships)
.process(RoomMemberEntityFields.DISPLAY_NAME, queryParams.displayName) .process(RoomMemberEntityFields.DISPLAY_NAME, queryParams.displayName)
.apply {
if (queryParams.excludeSelf) {
notEqualTo(RoomMemberEntityFields.USER_ID, userId)
}
}
} }
override fun getNumberOfJoinedMembers(): Int { override fun getNumberOfJoinedMembers(): Int {

View File

@ -272,4 +272,7 @@
<string name="notice_room_canonical_alias_set">"%1$s set the main address for this room to %2$s."</string> <string name="notice_room_canonical_alias_set">"%1$s set the main address for this room to %2$s."</string>
<string name="notice_room_canonical_alias_unset">"%1$s removed the main address for this room."</string> <string name="notice_room_canonical_alias_unset">"%1$s removed the main address for this room."</string>
<string name="notice_room_guest_access_can_join">"%1$s has allowed guests to join the room."</string>
<string name="notice_room_guest_access_forbidden">"%1$s has prevented guests from joining the room."</string>
</resources> </resources>

View File

@ -24,12 +24,16 @@ static def getGitTimestamp() {
} }
static def generateVersionCodeFromTimestamp() { static def generateVersionCodeFromTimestamp() {
// It's unix timestamp divided by 10: It's incremented by one every 10 seconds. // It's unix timestamp, minus timestamp of October 3rd 2018 (first commit date) divided by 100: It's incremented by one every 100 seconds.
return (getGitTimestamp() / 10).toInteger() // plus 20_000_000 for compatibility reason with the previous way the Version Code was computed
// Note that the result will be multiplied by 10 when adding the digit for the arch
return ((getGitTimestamp() - 1_538_524_800 ) / 100).toInteger() + 20_000_000
} }
def generateVersionCodeFromVersionName() { def generateVersionCodeFromVersionName() {
return versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch // plus 4_000_000 for compatibility reason with the previous way the Version Code was computed
// Note that the result will be multiplied by 10 when adding the digit for the arch
return (versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch) + 4_000_000
} }
def getVersionCode() { def getVersionCode() {
@ -77,8 +81,8 @@ project.android.buildTypes.all { buildType ->
] ]
} }
// map for the version codes // map for the version codes last digit
// x86 must have greater values than arm, see https://software.intel.com/en-us/android/articles/google-play-supports-cpu-architecture-filtering-for-multiple-apk // x86 must have greater values than arm
// 64 bits have greater value than 32 bits // 64 bits have greater value than 32 bits
ext.abiVersionCodes = ["armeabi-v7a": 1, "arm64-v8a": 2, "x86": 3, "x86_64": 4].withDefault { 0 } ext.abiVersionCodes = ["armeabi-v7a": 1, "arm64-v8a": 2, "x86": 3, "x86_64": 4].withDefault { 0 }
@ -144,7 +148,7 @@ android {
variant.outputs.each { output -> variant.outputs.each { output ->
def baseAbiVersionCode = project.ext.abiVersionCodes.get(output.getFilter(OutputFile.ABI)) def baseAbiVersionCode = project.ext.abiVersionCodes.get(output.getFilter(OutputFile.ABI))
// Known limitation: it does not modify the value in the BuildConfig.java generated file // Known limitation: it does not modify the value in the BuildConfig.java generated file
output.versionCodeOverride = baseAbiVersionCode * 10_000_000 + variant.versionCode output.versionCodeOverride = variant.versionCode * 10 + baseAbiVersionCode
} }
} }
} }

View File

@ -16,14 +16,13 @@
package im.vector.riotx.core.error package im.vector.riotx.core.error
import im.vector.riotx.BuildConfig
import timber.log.Timber import timber.log.Timber
/** /**
* throw in debug, only log in production. As this method does not always throw, next statement should be a return * throw in debug, only log in production. As this method does not always throw, next statement should be a return
*/ */
fun fatalError(message: String) { fun fatalError(message: String, failFast: Boolean) {
if (BuildConfig.DEBUG) { if (failFast) {
error(message) error(message)
} else { } else {
Timber.e(message) Timber.e(message)

View File

@ -16,7 +16,6 @@
package im.vector.riotx.core.rx package im.vector.riotx.core.rx
import im.vector.riotx.BuildConfig
import im.vector.riotx.features.settings.VectorPreferences import im.vector.riotx.features.settings.VectorPreferences
import io.reactivex.plugins.RxJavaPlugins import io.reactivex.plugins.RxJavaPlugins
import timber.log.Timber import timber.log.Timber
@ -33,8 +32,8 @@ class RxConfig @Inject constructor(
RxJavaPlugins.setErrorHandler { throwable -> RxJavaPlugins.setErrorHandler { throwable ->
Timber.e(throwable, "RxError") Timber.e(throwable, "RxError")
// Avoid crash in production // Avoid crash in production, except if user wants it
if (BuildConfig.DEBUG || vectorPreferences.failFast()) { if (vectorPreferences.failFast()) {
throw throwable throw throwable
} }
} }

View File

@ -63,6 +63,7 @@ class AutocompleteMemberPresenter @AssistedInject constructor(context: Context,
QueryStringValue.Contains(query.toString(), QueryStringValue.Case.INSENSITIVE) QueryStringValue.Contains(query.toString(), QueryStringValue.Case.INSENSITIVE)
} }
memberships = listOf(Membership.JOIN) memberships = listOf(Membership.JOIN)
excludeSelf = true
} }
val members = room.getRoomMembers(queryParams) val members = room.getRoomMembers(queryParams)
.asSequence() .asSequence()

View File

@ -55,7 +55,6 @@ import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.matrix.rx.unwrap import im.vector.matrix.rx.unwrap
import im.vector.riotx.BuildConfig
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
@ -308,7 +307,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
fun isMenuItemVisible(@IdRes itemId: Int) = when (itemId) { fun isMenuItemVisible(@IdRes itemId: Int) = when (itemId) {
R.id.clear_message_queue -> R.id.clear_message_queue ->
/* For now always disable on production, worker cancellation is not working properly */ /* For now always disable on production, worker cancellation is not working properly */
timeline.pendingEventCount() > 0 && BuildConfig.DEBUG timeline.pendingEventCount() > 0 && vectorPreferences.developerMode()
R.id.resend_all -> timeline.failedToDeliverEventCount() > 0 R.id.resend_all -> timeline.failedToDeliverEventCount() > 0
R.id.clear_all -> timeline.failedToDeliverEventCount() > 0 R.id.clear_all -> timeline.failedToDeliverEventCount() > 0
else -> false else -> false

View File

@ -47,8 +47,8 @@ class DefaultItemFactory @Inject constructor(private val avatarSizeProvider: Ava
fun create(event: TimelineEvent, fun create(event: TimelineEvent,
highlight: Boolean, highlight: Boolean,
callback: TimelineEventController.Callback?, callback: TimelineEventController.Callback?,
exception: Exception? = null): DefaultItem { throwable: Throwable? = null): DefaultItem {
val text = if (exception == null) { val text = if (throwable == null) {
"${event.root.getClearType()} events are not yet handled" "${event.root.getClearType()} events are not yet handled"
} else { } else {
"an exception occurred when rendering the event ${event.root.eventId}" "an exception occurred when rendering the event ${event.root.eventId}"

View File

@ -1,75 +0,0 @@
/*
* Copyright 2019 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.riotx.features.home.room.detail.timeline.factory
import android.view.View
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.crypto.model.event.EncryptionEventContent
import im.vector.riotx.R
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotx.features.home.room.detail.timeline.helper.AvatarSizeProvider
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.riotx.features.home.room.detail.timeline.item.NoticeItem
import im.vector.riotx.features.home.room.detail.timeline.item.NoticeItem_
import javax.inject.Inject
class EncryptionItemFactory @Inject constructor(private val stringProvider: StringProvider,
private val avatarRenderer: AvatarRenderer,
private val avatarSizeProvider: AvatarSizeProvider) {
fun create(event: TimelineEvent,
highlight: Boolean,
callback: TimelineEventController.Callback?): NoticeItem? {
val text = buildNoticeText(event.root, event.getDisambiguatedDisplayName()) ?: return null
val informationData = MessageInformationData(
eventId = event.root.eventId ?: "?",
senderId = event.root.senderId ?: "",
sendState = event.root.sendState,
avatarUrl = event.senderAvatar,
memberName = event.getDisambiguatedDisplayName(),
showInformation = false
)
val attributes = NoticeItem.Attributes(
avatarRenderer = avatarRenderer,
informationData = informationData,
noticeText = text,
itemLongClickListener = View.OnLongClickListener { view ->
callback?.onEventLongClicked(informationData, null, view) ?: false
},
readReceiptsCallback = callback
)
return NoticeItem_()
.leftGuideline(avatarSizeProvider.leftGuideline)
.highlighted(highlight)
.attributes(attributes)
}
private fun buildNoticeText(event: Event, senderName: String?): CharSequence? {
return when {
EventType.STATE_ROOM_ENCRYPTION == event.getClearType() -> {
val content = event.content.toModel<EncryptionEventContent>() ?: return null
stringProvider.getString(R.string.notice_end_to_end, senderName, content.algorithm)
}
else -> null
}
}
}

View File

@ -24,11 +24,12 @@ import im.vector.riotx.features.home.room.detail.timeline.TimelineEventControlle
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class TimelineItemFactory @Inject constructor(private val messageItemFactory: MessageItemFactory, class TimelineItemFactory @Inject constructor(
private val encryptedItemFactory: EncryptedItemFactory, private val messageItemFactory: MessageItemFactory,
private val noticeItemFactory: NoticeItemFactory, private val encryptedItemFactory: EncryptedItemFactory,
private val defaultItemFactory: DefaultItemFactory, private val noticeItemFactory: NoticeItemFactory,
private val roomCreateItemFactory: RoomCreateItemFactory) { private val defaultItemFactory: DefaultItemFactory,
private val roomCreateItemFactory: RoomCreateItemFactory) {
fun create(event: TimelineEvent, fun create(event: TimelineEvent,
nextEvent: TimelineEvent?, nextEvent: TimelineEvent?,
@ -49,6 +50,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
EventType.STATE_ROOM_CANONICAL_ALIAS, EventType.STATE_ROOM_CANONICAL_ALIAS,
EventType.STATE_ROOM_JOIN_RULES, EventType.STATE_ROOM_JOIN_RULES,
EventType.STATE_ROOM_HISTORY_VISIBILITY, EventType.STATE_ROOM_HISTORY_VISIBILITY,
EventType.STATE_ROOM_GUEST_ACCESS,
EventType.CALL_INVITE, EventType.CALL_INVITE,
EventType.CALL_HANGUP, EventType.CALL_HANGUP,
EventType.CALL_ANSWER, EventType.CALL_ANSWER,
@ -74,9 +76,9 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
null null
} }
} }
} catch (e: Exception) { } catch (throwable: Throwable) {
Timber.e(e, "failed to create message item") Timber.e(throwable, "failed to create message item")
defaultItemFactory.create(event, highlight, callback, e) defaultItemFactory.create(event, highlight, callback, throwable)
} }
return (computedModel ?: EmptyItem_()) return (computedModel ?: EmptyItem_())
} }

View File

@ -22,6 +22,7 @@ import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.* import im.vector.matrix.android.api.session.room.model.*
import im.vector.matrix.android.api.session.room.model.call.CallInviteContent import im.vector.matrix.android.api.session.room.model.call.CallInviteContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.crypto.model.event.EncryptionEventContent
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
@ -40,6 +41,8 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
EventType.STATE_ROOM_ALIASES -> formatRoomAliasesEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName()) EventType.STATE_ROOM_ALIASES -> formatRoomAliasesEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
EventType.STATE_ROOM_CANONICAL_ALIAS -> formatRoomCanonicalAliasEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName()) EventType.STATE_ROOM_CANONICAL_ALIAS -> formatRoomCanonicalAliasEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
EventType.STATE_ROOM_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName()) EventType.STATE_ROOM_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
EventType.STATE_ROOM_GUEST_ACCESS -> formatRoomGuestAccessEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
EventType.STATE_ROOM_ENCRYPTION -> formatRoomEncryptionEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
EventType.STATE_ROOM_TOMBSTONE -> formatRoomTombstoneEvent(timelineEvent.getDisambiguatedDisplayName()) EventType.STATE_ROOM_TOMBSTONE -> formatRoomTombstoneEvent(timelineEvent.getDisambiguatedDisplayName())
EventType.CALL_INVITE, EventType.CALL_INVITE,
EventType.CALL_HANGUP, EventType.CALL_HANGUP,
@ -166,6 +169,20 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
?: sp.getString(R.string.notice_room_canonical_alias_unset, senderName) ?: sp.getString(R.string.notice_room_canonical_alias_unset, senderName)
} }
private fun formatRoomGuestAccessEvent(event: Event, senderName: String?): String? {
val eventContent: RoomGuestAccessContent? = event.getClearContent().toModel()
return when (eventContent?.guestAccess) {
GuestAccess.CanJoin -> sp.getString(R.string.notice_room_guest_access_can_join, senderName)
GuestAccess.Forbidden -> sp.getString(R.string.notice_room_guest_access_forbidden, senderName)
else -> null
}
}
private fun formatRoomEncryptionEvent(event: Event, senderName: String?): CharSequence? {
val content = event.content.toModel<EncryptionEventContent>() ?: return null
return sp.getString(R.string.notice_end_to_end, senderName, content.algorithm)
}
private fun buildProfileNotice(event: Event, senderName: String?, eventContent: RoomMemberContent?, prevEventContent: RoomMemberContent?): String { private fun buildProfileNotice(event: Event, senderName: String?, eventContent: RoomMemberContent?, prevEventContent: RoomMemberContent?): String {
val displayText = StringBuilder() val displayText = StringBuilder()
// Check display name has been changed // Check display name has been changed

View File

@ -35,6 +35,7 @@ object TimelineDisplayableEvents {
EventType.CALL_ANSWER, EventType.CALL_ANSWER,
EventType.ENCRYPTED, EventType.ENCRYPTED,
EventType.STATE_ROOM_ENCRYPTION, EventType.STATE_ROOM_ENCRYPTION,
EventType.STATE_ROOM_GUEST_ACCESS,
EventType.STATE_ROOM_THIRD_PARTY_INVITE, EventType.STATE_ROOM_THIRD_PARTY_INVITE,
EventType.STICKER, EventType.STICKER,
EventType.STATE_ROOM_CREATE, EventType.STATE_ROOM_CREATE,

View File

@ -36,6 +36,7 @@ import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewActivity import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewActivity
import im.vector.riotx.features.settings.VectorPreferences
import im.vector.riotx.features.settings.VectorSettingsActivity import im.vector.riotx.features.settings.VectorSettingsActivity
import im.vector.riotx.features.share.SharedData import im.vector.riotx.features.share.SharedData
import timber.log.Timber import timber.log.Timber
@ -44,12 +45,13 @@ import javax.inject.Singleton
@Singleton @Singleton
class DefaultNavigator @Inject constructor( class DefaultNavigator @Inject constructor(
private val sessionHolder: ActiveSessionHolder private val sessionHolder: ActiveSessionHolder,
private val vectorPreferences: VectorPreferences
) : Navigator { ) : Navigator {
override fun openRoom(context: Context, roomId: String, eventId: String?, buildTask: Boolean) { override fun openRoom(context: Context, roomId: String, eventId: String?, buildTask: Boolean) {
if (sessionHolder.getSafeActiveSession()?.getRoom(roomId) == null) { if (sessionHolder.getSafeActiveSession()?.getRoom(roomId) == null) {
fatalError("Trying to open an unknown room $roomId") fatalError("Trying to open an unknown room $roomId", vectorPreferences.failFast())
return return
} }

View File

@ -24,6 +24,7 @@ import android.provider.MediaStore
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.squareup.seismic.ShakeDetector import com.squareup.seismic.ShakeDetector
import im.vector.riotx.BuildConfig
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.features.homeserver.ServerUrlsRepository import im.vector.riotx.features.homeserver.ServerUrlsRepository
import im.vector.riotx.features.themes.ThemeUtils import im.vector.riotx.features.themes.ThemeUtils
@ -268,7 +269,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
} }
fun failFast(): Boolean { fun failFast(): Boolean {
return developerMode() && defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY, false) return BuildConfig.DEBUG || (developerMode() && defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY, false))
} }
/** /**