Merge branch 'develop' into feature/bca/rust_flavor

This commit is contained in:
valere 2022-12-12 15:26:44 +01:00
commit c52be1f5b1
43 changed files with 198 additions and 102 deletions

View file

@ -13,7 +13,7 @@ jobs:
- name: Danger
uses: danger/danger-js@11.2.0
with:
args: "--dangerfile tools/danger/dangerfile.js"
args: "--dangerfile ./tools/danger/dangerfile.js"
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
# Fallback for forks

View file

@ -68,7 +68,7 @@ jobs:
if: always()
uses: danger/danger-js@11.2.0
with:
args: "--dangerfile tools/danger/dangerfile-lint.js"
args: "--dangerfile ./tools/danger/dangerfile-lint.js"
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
# Fallback for forks

View file

@ -89,7 +89,7 @@ jobs:
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc0sUA"
PROJECT_ID: "PVT_kwDOAM0swc0sUA"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
add_product_issues:
@ -113,7 +113,7 @@ jobs:
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc4AAg6N"
PROJECT_ID: "PVT_kwDOAM0swc4AAg6N"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
delight_issues_to_board:
@ -139,7 +139,7 @@ jobs:
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc1HvQ"
PROJECT_ID: "PVT_kwDOAM0swc1HvQ"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_voice-message_issues:
@ -164,7 +164,7 @@ jobs:
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc2KCw"
PROJECT_ID: "PVT_kwDOAM0swc2KCw"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_message_bubbles_issues:
name: A-Message-Bubbles to Message bubbles board
@ -188,7 +188,7 @@ jobs:
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc3m-g"
PROJECT_ID: "PVT_kwDOAM0swc3m-g"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_ftue_issues:
@ -213,7 +213,7 @@ jobs:
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc4AAqVx"
PROJECT_ID: "PVT_kwDOAM0swc4AAqVx"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_WTF_issues:
@ -238,7 +238,7 @@ jobs:
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc4AArk0"
PROJECT_ID: "PVT_kwDOAM0swc4AArk0"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_element_x_issues:
@ -268,7 +268,7 @@ jobs:
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc4ABTXY"
PROJECT_ID: "PVT_kwDOAM0swc4ABTXY"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
ps_features1:

View file

@ -69,7 +69,7 @@ jobs:
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.pull_request.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc0sUA"
PROJECT_ID: "PVT_kwDOAM0swc0sUA"
TEAM: "design"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
@ -138,6 +138,6 @@ jobs:
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.pull_request.node_id }}
env:
PROJECT_ID: "PN_kwDOAM0swc4AAg6N"
PROJECT_ID: "PVT_kwDOAM0swc4AAg6N"
TEAM: "product"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}

1
changelog.d/7643.bugfix Normal file
View file

@ -0,0 +1 @@
[Notifications] Fixed a bug when push notification was automatically dismissed while app is on background

1
changelog.d/7691.bugfix Normal file
View file

@ -0,0 +1 @@
Rich Text Editor: improve performance when entering reply/edit/quote mode.

1
changelog.d/7699.bugfix Normal file
View file

@ -0,0 +1 @@
Fix E2EE set up failure whilst signing in using QR code

1
changelog.d/7733.bugfix Normal file
View file

@ -0,0 +1 @@
[Session manager] Sessions without encryption support should not prompt to verify

1
changelog.d/7743.bugfix Normal file
View file

@ -0,0 +1 @@
Verification request is not showing when verify session popup is displayed

1
changelog.d/7744.bugfix Normal file
View file

@ -0,0 +1 @@
Fix crash when inviting by email.

1
changelog.d/7751.bugfix Normal file
View file

@ -0,0 +1 @@
Revert usage of stable fields in live location sharing and polls

View file

@ -27,7 +27,7 @@ def jjwt = "0.11.5"
// the whole commit which set version 0.16.0-SNAPSHOT
def vanniktechEmoji = "0.16.0-SNAPSHOT"
def sentry = "6.9.0"
def fragment = "1.5.4"
def fragment = "1.5.5"
// Testing
def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819
def espresso = "3.4.0"

View file

@ -3315,6 +3315,7 @@
<string name="device_manager_verification_status_detail_current_session_unverified">Verify your current session for enhanced secure messaging.</string>
<string name="device_manager_verification_status_detail_other_session_unverified">Verify or sign out from this session for best security and reliability.</string>
<string name="device_manager_verification_status_detail_other_session_unknown">Verify your current session to reveal this session\'s verification status.</string>
<string name="device_manager_verification_status_detail_session_encryption_not_supported">This session doesn\'t support encryption and thus can\'t be verified.</string>
<string name="device_manager_verify_session">Verify Session</string>
<string name="device_manager_view_details">View Details</string>
<string name="device_manager_other_sessions_view_all">View All (%1$d)</string>
@ -3407,6 +3408,7 @@
<!-- TODO TO BE REMOVED -->
<string name="device_manager_learn_more_sessions_verified" tools:ignore="UnusedResources">Verified sessions have logged in with your credentials and then been verified, either using your secure passphrase or by cross-verifying.\n\nThis means they hold encryption keys for your previous messages, and confirm to other users you are communicating with that these sessions are really you.</string>
<string name="device_manager_learn_more_sessions_verified_description">Verified sessions are anywhere you are using this account after entering your passphrase or confirming your identity with another verified session.\n\nThis means that you have all the keys needed to unlock your encrypted messages and confirm to other users that you trust this session.</string>
<string name="device_manager_learn_more_sessions_encryption_not_supported">This session doesn\'t support encryption, so it can\'t be verified.\n\nYou won\'t be able to participate in rooms where encryption is enabled when using this session.\n\nFor best security and privacy, it is recommended to use Matrix clients that support encryption.</string>
<string name="device_manager_learn_more_session_rename_title">Renaming sessions</string>
<string name="device_manager_learn_more_session_rename">Other users in direct messages and rooms that you join are able to view a full list of your sessions.\n\nThis provides them with confidence that they are really speaking to you, but it also means they can see the session name you enter here.</string>
<string name="labs_enable_session_manager_title">Enable new session manager</string>

View file

@ -35,7 +35,10 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_S
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.api.util.MatrixJsonParser
import org.matrix.android.sdk.api.util.awaitCallback
import timber.log.Timber
/**
@ -147,6 +150,14 @@ class Rendezvous(
val deviceKey = crypto.getMyCryptoDevice().fingerprint()
send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey))
try {
// explicitly download keys for ourself rather than racing with initial sync which might not complete in time
awaitCallback<MXUsersDevicesMap<CryptoDeviceInfo>> { crypto.downloadKeys(listOf(userId), false, it) }
} catch (e: Throwable) {
// log as warning and continue as initial sync might still complete
Timber.tag(TAG).w(e, "Failed to download keys for self")
}
// await confirmation of verification
val verificationResponse = receive()
if (verificationResponse?.outcome == Outcome.VERIFIED) {

View file

@ -46,7 +46,7 @@ internal class DefaultStartLiveLocationShareTask @Inject constructor(
isLive = true,
unstableTimestampMillis = clock.epochMillis()
).toContent()
val eventType = EventType.STATE_ROOM_BEACON_INFO.stable
val eventType = EventType.STATE_ROOM_BEACON_INFO.unstable
val sendStateTaskParams = SendStateTask.Params(
roomId = params.roomId,
stateKey = userId,

View file

@ -45,7 +45,7 @@ internal class DefaultStopLiveLocationShareTask @Inject constructor(
val sendStateTaskParams = SendStateTask.Params(
roomId = params.roomId,
stateKey = stateKey,
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
body = updatedContent
)
return try {

View file

@ -181,7 +181,7 @@ internal class LocalEchoEventFactory @Inject constructor(
originServerTs = dummyOriginServerTs(),
senderId = userId,
eventId = localId,
type = EventType.POLL_START.stable,
type = EventType.POLL_START.unstable,
content = newContent.toContent().plus(additionalContent.orEmpty())
)
}
@ -206,7 +206,7 @@ internal class LocalEchoEventFactory @Inject constructor(
originServerTs = dummyOriginServerTs(),
senderId = userId,
eventId = localId,
type = EventType.POLL_RESPONSE.stable,
type = EventType.POLL_RESPONSE.unstable,
content = content.toContent().plus(additionalContent.orEmpty()),
unsignedData = UnsignedData(age = null, transactionId = localId)
)
@ -226,7 +226,7 @@ internal class LocalEchoEventFactory @Inject constructor(
originServerTs = dummyOriginServerTs(),
senderId = userId,
eventId = localId,
type = EventType.POLL_START.stable,
type = EventType.POLL_START.unstable,
content = content.toContent().plus(additionalContent.orEmpty()),
unsignedData = UnsignedData(age = null, transactionId = localId)
)
@ -249,7 +249,7 @@ internal class LocalEchoEventFactory @Inject constructor(
originServerTs = dummyOriginServerTs(),
senderId = userId,
eventId = localId,
type = EventType.POLL_END.stable,
type = EventType.POLL_END.unstable,
content = content.toContent().plus(additionalContent.orEmpty()),
unsignedData = UnsignedData(age = null, transactionId = localId)
)
@ -300,7 +300,7 @@ internal class LocalEchoEventFactory @Inject constructor(
originServerTs = dummyOriginServerTs(),
senderId = userId,
eventId = localId,
type = EventType.BEACON_LOCATION_DATA.stable,
type = EventType.BEACON_LOCATION_DATA.unstable,
content = content.toContent().plus(additionalContent.orEmpty()),
unsignedData = UnsignedData(age = null, transactionId = localId)
)

View file

@ -87,7 +87,7 @@ object PollEventsTestData {
)
internal val A_POLL_START_EVENT = Event(
type = EventType.POLL_START.stable,
type = EventType.POLL_START.unstable,
eventId = AN_EVENT_ID,
originServerTs = 1652435922563,
senderId = A_USER_ID_1,
@ -96,7 +96,7 @@ object PollEventsTestData {
)
internal val A_POLL_RESPONSE_EVENT = Event(
type = EventType.POLL_RESPONSE.stable,
type = EventType.POLL_RESPONSE.unstable,
eventId = AN_EVENT_ID,
originServerTs = 1652435922563,
senderId = A_USER_ID_1,
@ -105,7 +105,7 @@ object PollEventsTestData {
)
internal val A_POLL_END_EVENT = Event(
type = EventType.POLL_END.stable,
type = EventType.POLL_END.unstable,
eventId = AN_EVENT_ID,
originServerTs = 1652435922563,
senderId = A_USER_ID_1,

View file

@ -69,7 +69,7 @@ class DefaultGetActiveBeaconInfoForUserTaskTest {
result shouldBeEqualTo currentStateEvent
fakeStateEventDataSource.verifyGetStateEvent(
roomId = params.roomId,
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
stateKey = QueryStringValue.Equals(A_USER_ID)
)
}

View file

@ -75,7 +75,7 @@ internal class DefaultStartLiveLocationShareTaskTest {
val expectedParams = SendStateTask.Params(
roomId = params.roomId,
stateKey = A_USER_ID,
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
body = expectedBeaconContent
)
fakeSendStateTask.verifyExecuteRetry(

View file

@ -79,7 +79,7 @@ class DefaultStopLiveLocationShareTaskTest {
val expectedSendParams = SendStateTask.Params(
roomId = params.roomId,
stateKey = A_USER_ID,
eventType = EventType.STATE_ROOM_BEACON_INFO.stable,
eventType = EventType.STATE_ROOM_BEACON_INFO.unstable,
body = expectedBeaconContent
)
fakeSendStateTask.verifyExecuteRetry(

View file

@ -79,7 +79,7 @@ class LiveLocationShareRedactionEventProcessorTest {
@Test
fun `given a redacted live location share event when processing it then related summaries are deleted from database`() = runTest {
val event = Event(eventId = AN_EVENT_ID, redacts = A_REDACTED_EVENT_ID)
val redactedEventEntity = EventEntity(eventId = A_REDACTED_EVENT_ID, type = EventType.STATE_ROOM_BEACON_INFO.stable)
val redactedEventEntity = EventEntity(eventId = A_REDACTED_EVENT_ID, type = EventType.STATE_ROOM_BEACON_INFO.unstable)
fakeRealm.givenWhere<EventEntity>()
.givenEqualTo(EventEntityFields.EVENT_ID, A_REDACTED_EVENT_ID)
.givenFindFirst(redactedEventEntity)

View file

@ -40,20 +40,26 @@ class ShieldImageView @JvmOverloads constructor(
/**
* Renders device shield with the support of unknown shields instead of black shields which is used for rooms.
* @param roomEncryptionTrustLevel trust level that is usally calculated with [im.vector.app.features.settings.devices.TrustUtils.shieldForTrust]
* @param roomEncryptionTrustLevel trust level that is usually calculated with [im.vector.app.features.settings.devices.TrustUtils.shieldForTrust]
* @param borderLess if true then the shield icon with border around is used
*/
fun renderDeviceShield(roomEncryptionTrustLevel: RoomEncryptionTrustLevel?, borderLess: Boolean = false) {
isVisible = roomEncryptionTrustLevel != null
if (roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Default) {
contentDescription = context.getString(R.string.a11y_trust_level_default)
setImageResource(
if (borderLess) R.drawable.ic_shield_unknown_no_border
else R.drawable.ic_shield_unknown
)
} else {
render(roomEncryptionTrustLevel, borderLess)
when (roomEncryptionTrustLevel) {
null -> {
contentDescription = context.getString(R.string.a11y_trust_level_warning)
setImageResource(
if (borderLess) R.drawable.ic_shield_warning_no_border
else R.drawable.ic_shield_warning
)
}
RoomEncryptionTrustLevel.Default -> {
contentDescription = context.getString(R.string.a11y_trust_level_default)
setImageResource(
if (borderLess) R.drawable.ic_shield_unknown_no_border
else R.drawable.ic_shield_unknown
)
}
else -> render(roomEncryptionTrustLevel, borderLess)
}
}

View file

@ -155,12 +155,9 @@ class IncomingVerificationRequestHandler @Inject constructor(
// For incoming request we should prompt (if not in activity where this request apply)
if (pr.isIncoming) {
// if it's a self verification for my devices, we can discard the review login alert
// if not this request will be underneath and not visible by the user...
// if not, this request will be underneath and not visible by the user...
// it will re-appear later
if (pr.otherUserId == session?.myUserId) {
// XXX this is a bit hard coded :/
popupAlertManager.cancelAlert("review_login")
}
cancelAnyVerifySessionAlerts(pr)
val user = session.getUserOrDefault(pr.otherUserId).toMatrixItem()
val name = user.getBestName()
val description = if (name == pr.otherUserId) {
@ -170,21 +167,23 @@ class IncomingVerificationRequestHandler @Inject constructor(
}
val alert = VerificationVectorAlert(
uniqueIdForVerificationRequest(pr),
context.getString(R.string.sas_incoming_request_notif_title),
description,
R.drawable.ic_shield_black,
uid = uniqueIdForVerificationRequest(pr),
title = context.getString(R.string.sas_incoming_request_notif_title),
description = description,
iconId = R.drawable.ic_shield_black,
priority = PopupAlertManager.INCOMING_VERIFICATION_REQUEST_PRIORITY,
shouldBeDisplayedIn = { activity ->
if (activity is RoomDetailActivity) {
activity.intent?.extras?.getParcelableCompat<TimelineArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
it.roomId != pr.roomId
} ?: true
} else true
}
},
)
.apply {
viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer.get())
contentAction = Runnable {
cancelAnyVerifySessionAlerts(pr)
(weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let {
val roomId = pr.roomId
if (roomId.isNullOrBlank()) {
@ -215,6 +214,13 @@ class IncomingVerificationRequestHandler @Inject constructor(
}
}
private fun cancelAnyVerifySessionAlerts(pr: PendingVerificationRequest) {
if (pr.otherUserId == session?.myUserId) {
popupAlertManager.cancelAlert(PopupAlertManager.REVIEW_LOGIN_UID)
popupAlertManager.cancelAlert(PopupAlertManager.VERIFY_SESSION_UID)
}
}
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
// If an incoming request is readied (by another device?) we should discard the alert
if (pr.isIncoming && (pr.state == EVerificationState.HandledByOtherSession ||

View file

@ -441,9 +441,10 @@ class HomeActivity :
private fun handleAskPasswordToInitCrossSigning(events: HomeActivityViewEvents.AskPasswordToInitCrossSigning) {
// We need to ask
promptSecurityEvent(
events.userItem,
R.string.upgrade_security,
R.string.security_prompt_text
uid = PopupAlertManager.UPGRADE_SECURITY_UID,
userItem = events.userItem,
titleRes = R.string.upgrade_security,
descRes = R.string.security_prompt_text,
) {
it.navigator.upgradeSessionSecurity(it, true)
}
@ -452,9 +453,10 @@ class HomeActivity :
private fun handleCrossSigningInvalidated(event: HomeActivityViewEvents.OnCrossSignedInvalidated) {
// We need to ask
promptSecurityEvent(
event.userItem,
R.string.crosssigning_verify_this_session,
R.string.confirm_your_identity
uid = PopupAlertManager.VERIFY_SESSION_UID,
userItem = event.userItem,
titleRes = R.string.crosssigning_verify_this_session,
descRes = R.string.confirm_your_identity,
) {
// check first if it's not an outdated request?
activeSessionHolder.getSafeActiveSession()?.let { session ->
@ -472,9 +474,10 @@ class HomeActivity :
private fun handleOnNewSession(event: HomeActivityViewEvents.CurrentSessionNotVerified) {
// We need to ask
promptSecurityEvent(
event.userItem,
R.string.crosssigning_verify_this_session,
R.string.confirm_your_identity
uid = PopupAlertManager.VERIFY_SESSION_UID,
userItem = event.userItem,
titleRes = R.string.crosssigning_verify_this_session,
descRes = R.string.confirm_your_identity,
) {
// navigator.openSettings(this, SettingsActivityPayload.SecurityPrivacy)
// if (event.waitForIncomingRequest) {
@ -488,9 +491,10 @@ class HomeActivity :
private fun handleCantVerify(event: HomeActivityViewEvents.CurrentSessionCannotBeVerified) {
// We need to ask
promptSecurityEvent(
event.userItem,
R.string.crosssigning_cannot_verify_this_session,
R.string.crosssigning_cannot_verify_this_session_desc
uid = PopupAlertManager.UPGRADE_SECURITY_UID,
userItem = event.userItem,
titleRes = R.string.crosssigning_cannot_verify_this_session,
descRes = R.string.crosssigning_cannot_verify_this_session_desc,
) {
it.navigator.open4SSetup(it, SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET)
}
@ -499,7 +503,7 @@ class HomeActivity :
private fun handlePromptToEnablePush() {
popupAlertManager.postVectorAlert(
DefaultVectorAlert(
uid = "enablePush",
uid = PopupAlertManager.ENABLE_PUSH_UID,
title = getString(R.string.alert_push_are_disabled_title),
description = getString(R.string.alert_push_are_disabled_description),
iconId = R.drawable.ic_room_actions_notifications_mutes,
@ -532,10 +536,16 @@ class HomeActivity :
)
}
private fun promptSecurityEvent(userItem: MatrixItem.UserItem, titleRes: Int, descRes: Int, action: ((VectorBaseActivity<*>) -> Unit)) {
private fun promptSecurityEvent(
uid: String,
userItem: MatrixItem.UserItem,
titleRes: Int,
descRes: Int,
action: ((VectorBaseActivity<*>) -> Unit),
) {
popupAlertManager.postVectorAlert(
VerificationVectorAlert(
uid = "upgradeSecurity",
uid = uid,
title = getString(titleRes),
description = getString(descRes),
iconId = R.drawable.ic_shield_warning

View file

@ -156,7 +156,7 @@ class HomeDetailFragment :
unknownDeviceDetectorSharedViewModel.onEach { state ->
state.unknownSessions.invoke()?.let { unknownDevices ->
if (unknownDevices.firstOrNull()?.currentSessionTrust == true) {
val uid = "review_login"
val uid = PopupAlertManager.REVIEW_LOGIN_UID
alertManager.cancelAlert(uid)
val olderUnverified = unknownDevices.filter { !it.isNew }
val newest = unknownDevices.firstOrNull { it.isNew }?.deviceInfo

View file

@ -160,7 +160,7 @@ class NewHomeDetailFragment :
unknownDeviceDetectorSharedViewModel.onEach { state ->
state.unknownSessions.invoke()?.let { unknownDevices ->
if (unknownDevices.firstOrNull()?.currentSessionTrust == true) {
val uid = "review_login"
val uid = PopupAlertManager.REVIEW_LOGIN_UID
alertManager.cancelAlert(uid)
val olderUnverified = unknownDevices.filter { !it.isNew }
val newest = unknownDevices.firstOrNull { it.isNew }?.deviceInfo

View file

@ -105,7 +105,7 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(
// Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}")
infoList
.filter { info ->
// filter verified session, by checking the crypto device info
// filter out verified sessions or those which do not support encryption (i.e. without crypto info)
cryptoList.firstOrNull { info.deviceId == it.deviceId }?.isVerified?.not().orFalse()
}
// filter out ignored devices

View file

@ -975,6 +975,7 @@ class TimelineFragment :
notificationDrawerManager.setCurrentThread(timelineArgs.threadTimelineArgs?.rootThreadEventId)
roomDetailPendingActionStore.data?.let { handlePendingAction(it) }
roomDetailPendingActionStore.data = null
views.timelineRecyclerView.adapter = timelineEventController.adapter
}
private fun handlePendingAction(roomDetailPendingAction: RoomDetailPendingAction) {
@ -993,6 +994,7 @@ class TimelineFragment :
super.onPause()
notificationDrawerManager.setCurrentRoom(null)
notificationDrawerManager.setCurrentThread(null)
views.timelineRecyclerView.adapter = null
}
private val emojiActivityResultLauncher = registerStartForActivityResult { activityResult ->
@ -1058,7 +1060,6 @@ class TimelineFragment :
it.dispatchTo(scrollOnHighlightedEventCallback)
}
timelineEventController.addModelBuildListener(modelBuildListener)
views.timelineRecyclerView.adapter = timelineEventController.adapter
if (vectorPreferences.swipeToReplyIsEnabled()) {
val quickReplyHandler = object : RoomMessageTouchHelperCallback.QuickReplayHandler {

View file

@ -285,7 +285,7 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
else -> return
}
(composer as? RichTextComposerLayout)?.setFullScreen(setFullScreen)
(composer as? RichTextComposerLayout)?.setFullScreen(setFullScreen, true)
messageComposerViewModel.handle(MessageComposerAction.SetFullScreen(setFullScreen))
}

View file

@ -23,6 +23,6 @@ sealed interface MessageComposerMode {
sealed class Special(open val event: TimelineEvent, open val defaultContent: CharSequence) : MessageComposerMode
data class Edit(override val event: TimelineEvent, override val defaultContent: CharSequence) : Special(event, defaultContent)
class Quote(override val event: TimelineEvent, override val defaultContent: CharSequence) : Special(event, defaultContent)
class Reply(override val event: TimelineEvent, override val defaultContent: CharSequence) : Special(event, defaultContent)
data class Quote(override val event: TimelineEvent, override val defaultContent: CharSequence) : Special(event, defaultContent)
data class Reply(override val event: TimelineEvent, override val defaultContent: CharSequence) : Special(event, defaultContent)
}

View file

@ -66,6 +66,7 @@ internal class RichTextComposerLayout @JvmOverloads constructor(
// There is no need to persist these values since they're always updated by the parent fragment
private var isFullScreen = false
private var hasRelatedMessage = false
private var composerMode: MessageComposerMode? = null
var isTextFormattingEnabled = true
set(value) {
@ -114,9 +115,15 @@ internal class RichTextComposerLayout @JvmOverloads constructor(
private val dimensionConverter = DimensionConverter(resources)
fun setFullScreen(isFullScreen: Boolean) {
fun setFullScreen(isFullScreen: Boolean, animated: Boolean) {
if (!animated && views.composerLayout.layoutParams != null) {
views.composerLayout.updateLayoutParams<ViewGroup.LayoutParams> {
height =
if (isFullScreen) ViewGroup.LayoutParams.MATCH_PARENT else ViewGroup.LayoutParams.WRAP_CONTENT
}
}
editText.updateLayoutParams<ViewGroup.LayoutParams> {
height = if (isFullScreen) 0 else ViewGroup.LayoutParams.WRAP_CONTENT
height = if (isFullScreen) ViewGroup.LayoutParams.MATCH_PARENT else ViewGroup.LayoutParams.WRAP_CONTENT
}
updateTextFieldBorder(isFullScreen)
@ -371,7 +378,11 @@ internal class RichTextComposerLayout @JvmOverloads constructor(
override fun renderComposerMode(mode: MessageComposerMode) {
if (mode is MessageComposerMode.Special) {
views.composerModeGroup.isVisible = true
replaceFormattedContent(mode.defaultContent)
if (isTextFormattingEnabled) {
replaceFormattedContent(mode.defaultContent)
} else {
views.plainTextComposerEditText.setText(mode.defaultContent)
}
hasRelatedMessage = true
editText.showKeyboard(andRequestFocus = true)
} else {
@ -383,10 +394,14 @@ internal class RichTextComposerLayout @JvmOverloads constructor(
views.plainTextComposerEditText.setText(text)
}
}
views.sendButton.contentDescription = resources.getString(R.string.action_send)
hasRelatedMessage = false
}
updateTextFieldBorder(isFullScreen)
if (this.composerMode == mode) return
this.composerMode = mode
views.sendButton.apply {
if (mode is MessageComposerMode.Edit) {
contentDescription = resources.getString(R.string.action_save)
@ -397,8 +412,6 @@ internal class RichTextComposerLayout @JvmOverloads constructor(
}
}
updateTextFieldBorder(isFullScreen)
when (mode) {
is MessageComposerMode.Edit -> {
views.composerModeTitleView.setText(R.string.editing)

View file

@ -50,6 +50,12 @@ class PopupAlertManager @Inject constructor(
companion object {
const val INCOMING_CALL_PRIORITY = Int.MAX_VALUE
const val INCOMING_VERIFICATION_REQUEST_PRIORITY = 1
const val DEFAULT_PRIORITY = 0
const val REVIEW_LOGIN_UID = "review_login"
const val UPGRADE_SECURITY_UID = "upgrade_security"
const val VERIFY_SESSION_UID = "verify_session"
const val ENABLE_PUSH_UID = "enable_push"
}
private var weakCurrentActivity: WeakReference<Activity>? = null
@ -145,7 +151,7 @@ class PopupAlertManager @Inject constructor(
private fun displayNextIfPossible() {
val currentActivity = weakCurrentActivity?.get()
if (Alerter.isShowing || currentActivity == null || currentActivity.isDestroyed) {
if (currentActivity == null || currentActivity.isDestroyed) {
// will retry later
return
}

View file

@ -98,7 +98,7 @@ open class DefaultVectorAlert(
override val dismissOnClick: Boolean = true
override val priority: Int = 0
override val priority: Int = PopupAlertManager.DEFAULT_PRIORITY
override val isLight: Boolean = false

View file

@ -30,6 +30,7 @@ class VerificationVectorAlert(
title: String,
override val description: String,
@DrawableRes override val iconId: Int?,
override val priority: Int = PopupAlertManager.DEFAULT_PRIORITY,
/**
* Alert are displayed by default, but let this lambda return false to prevent displaying.
*/

View file

@ -83,7 +83,7 @@ data class DevicesViewState(
data class DeviceFullInfo(
val deviceInfo: DeviceInfo,
val cryptoDeviceInfo: CryptoDeviceInfo?,
val trustLevelForShield: RoomEncryptionTrustLevel,
val trustLevelForShield: RoomEncryptionTrustLevel?,
val isInactive: Boolean,
)

View file

@ -25,7 +25,7 @@ import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
data class DeviceFullInfo(
val deviceInfo: DeviceInfo,
val cryptoDeviceInfo: CryptoDeviceInfo?,
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel,
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel?,
val isInactive: Boolean,
val isCurrentDevice: Boolean,
val deviceExtendedInfo: DeviceExtendedInfo,

View file

@ -85,13 +85,14 @@ class SessionInfoView @JvmOverloads constructor(
}
private fun renderVerificationStatus(
encryptionTrustLevel: RoomEncryptionTrustLevel,
encryptionTrustLevel: RoomEncryptionTrustLevel?,
isCurrentSession: Boolean,
hasLearnMoreLink: Boolean,
isVerifyButtonVisible: Boolean,
) {
views.sessionInfoVerificationStatusImageView.renderDeviceShield(encryptionTrustLevel)
when {
encryptionTrustLevel == null -> renderCrossSigningEncryptionNotSupported()
encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted -> renderCrossSigningVerified(isCurrentSession)
encryptionTrustLevel == RoomEncryptionTrustLevel.Default && !isCurrentSession -> renderCrossSigningUnknown()
else -> renderCrossSigningUnverified(isCurrentSession, isVerifyButtonVisible)
@ -149,6 +150,14 @@ class SessionInfoView @JvmOverloads constructor(
views.sessionInfoVerifySessionButton.isVisible = false
}
private fun renderCrossSigningEncryptionNotSupported() {
views.sessionInfoVerificationStatusTextView.text = context.getString(R.string.device_manager_verification_status_unverified)
views.sessionInfoVerificationStatusTextView.setTextColor(ThemeUtils.getColor(context, R.attr.colorError))
views.sessionInfoVerificationStatusDetailTextView.text =
context.getString(R.string.device_manager_verification_status_detail_session_encryption_not_supported)
views.sessionInfoVerifySessionButton.isVisible = false
}
private fun renderDeviceInfo(sessionName: String, deviceType: DeviceType, stringProvider: StringProvider) {
setDeviceTypeIconUseCase.execute(deviceType, views.sessionInfoDeviceTypeImageView, stringProvider)
views.sessionInfoNameTextView.text = sessionName

View file

@ -229,7 +229,7 @@ class SessionOverviewFragment :
)
views.sessionOverviewInfo.render(infoViewState, dateFormatter, drawableProvider, colorProvider, stringProvider)
views.sessionOverviewInfo.onLearnMoreClickListener = {
showLearnMoreInfoVerificationStatus(deviceInfo.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted)
showLearnMoreInfoVerificationStatus(deviceInfo.roomEncryptionTrustLevel)
}
} else {
views.sessionOverviewInfo.isVisible = false
@ -293,21 +293,28 @@ class SessionOverviewFragment :
}
}
private fun showLearnMoreInfoVerificationStatus(isVerified: Boolean) {
val titleResId = if (isVerified) {
R.string.device_manager_verification_status_verified
} else {
R.string.device_manager_verification_status_unverified
private fun showLearnMoreInfoVerificationStatus(roomEncryptionTrustLevel: RoomEncryptionTrustLevel?) {
val args = when (roomEncryptionTrustLevel) {
null -> {
// encryption not supported
SessionLearnMoreBottomSheet.Args(
title = getString(R.string.device_manager_verification_status_unverified),
description = getString(R.string.device_manager_learn_more_sessions_encryption_not_supported),
)
}
RoomEncryptionTrustLevel.Trusted -> {
SessionLearnMoreBottomSheet.Args(
title = getString(R.string.device_manager_verification_status_verified),
description = getString(R.string.device_manager_learn_more_sessions_verified_description),
)
}
else -> {
SessionLearnMoreBottomSheet.Args(
title = getString(R.string.device_manager_verification_status_unverified),
description = getString(R.string.device_manager_learn_more_sessions_unverified),
)
}
}
val descriptionResId = if (isVerified) {
R.string.device_manager_learn_more_sessions_verified_description
} else {
R.string.device_manager_learn_more_sessions_unverified
}
val args = SessionLearnMoreBottomSheet.Args(
title = getString(titleResId),
description = getString(descriptionResId),
)
SessionLearnMoreBottomSheet.show(childFragmentManager, args)
}
}

View file

@ -25,11 +25,15 @@ class GetEncryptionTrustLevelForDeviceUseCase @Inject constructor(
private val getEncryptionTrustLevelForOtherDeviceUseCase: GetEncryptionTrustLevelForOtherDeviceUseCase,
) {
fun execute(currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo, cryptoDeviceInfo: CryptoDeviceInfo?): RoomEncryptionTrustLevel {
fun execute(currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo, cryptoDeviceInfo: CryptoDeviceInfo?): RoomEncryptionTrustLevel? {
if (cryptoDeviceInfo == null) {
return null
}
val legacyMode = !currentSessionCrossSigningInfo.isCrossSigningInitialized
val trustMSK = currentSessionCrossSigningInfo.isCrossSigningVerified
val isCurrentDevice = !cryptoDeviceInfo?.deviceId.isNullOrEmpty() && cryptoDeviceInfo?.deviceId == currentSessionCrossSigningInfo.deviceId
val deviceTrustLevel = cryptoDeviceInfo?.trustLevel
val isCurrentDevice = !cryptoDeviceInfo.deviceId.isNullOrEmpty() && cryptoDeviceInfo.deviceId == currentSessionCrossSigningInfo.deviceId
val deviceTrustLevel = cryptoDeviceInfo.trustLevel
return when {
isCurrentDevice -> getEncryptionTrustLevelForCurrentDeviceUseCase.execute(trustMSK, legacyMode)

View file

@ -25,12 +25,10 @@ import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.features.home.AvatarRenderer
@EpoxyModelClass
abstract class InviteByEmailItem : VectorEpoxyModel<InviteByEmailItem.Holder>(R.layout.item_invite_by_mail) {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute lateinit var foundItem: ThreePidUser
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var clickListener: ClickListener? = null
@EpoxyAttribute var selected: Boolean = false

View file

@ -19,6 +19,7 @@ package im.vector.app.features.settings.devices.v2.verification
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.amshove.kluent.shouldBe
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
@ -89,6 +90,20 @@ class GetEncryptionTrustLevelForDeviceUseCaseTest {
}
}
@Test
fun `given no crypto device info when computing trust level then result is null`() {
val currentSessionCrossSigningInfo = givenCurrentSessionCrossSigningInfo(
deviceId = A_DEVICE_ID,
isCrossSigningInitialized = true,
isCrossSigningVerified = false
)
val cryptoDeviceInfo = null
val result = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo)
result shouldBe null
}
private fun givenCurrentSessionCrossSigningInfo(
deviceId: String,
isCrossSigningInitialized: Boolean,

View file

@ -63,7 +63,7 @@ object FakeCreatePollViewStates {
)
private val A_POLL_START_EVENT = Event(
type = EventType.POLL_START.stable,
type = EventType.POLL_START.unstable,
eventId = A_FAKE_EVENT_ID,
originServerTs = 1652435922563,
senderId = A_FAKE_USER_ID,