Merge pull request #7171 from vector-im/feature/ons/device_manager_security_sessions

[Device Manager] Unverified and inactive sessions list (PSG-698, PSG-696)
This commit is contained in:
Onuray Sahin 2022-09-23 17:22:44 +03:00 committed by GitHub
commit 6c79aae3aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 210 additions and 42 deletions

1
changelog.d/7170.wip Normal file
View file

@ -0,0 +1 @@
[Device Manager] Unverified and inactive sessions list

View file

@ -3244,6 +3244,7 @@
<string name="device_manager_other_sessions_description_verified">Verified · Last activity %1$s</string>
<!-- Examples: Unverified · Last activity Yesterday at 6PM, Unverified · Last activity Aug 31 at 5:47PM -->
<string name="device_manager_other_sessions_description_unverified">Unverified · Last activity %1$s</string>
<string name="device_manager_other_sessions_description_unverified_current_session">Unverified · Your current session</string>
<!-- Example: Inactive for 90+ days (Dec 25, 2021) -->
<plurals name="device_manager_other_sessions_description_inactive">
<item quantity="one">Inactive for %1$d+ day (%2$s)</item>

View file

@ -25,4 +25,5 @@ data class DeviceFullInfo(
val cryptoDeviceInfo: CryptoDeviceInfo?,
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel,
val isInactive: Boolean,
val isCurrentDevice: Boolean,
)

View file

@ -74,7 +74,7 @@ class DevicesViewModel @AssistedInject constructor(
.execute { async ->
if (async is Success) {
val deviceFullInfoList = async.invoke()
val unverifiedSessionsCount = deviceFullInfoList.count { !it.cryptoDeviceInfo?.isVerified.orFalse() }
val unverifiedSessionsCount = deviceFullInfoList.count { !it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse() }
val inactiveSessionsCount = deviceFullInfoList.count { it.isInactive }
copy(
devices = async,

View file

@ -71,7 +71,8 @@ class GetDeviceFullInfoListUseCase @Inject constructor(
val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }
val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo)
val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs ?: 0)
DeviceFullInfo(deviceInfo, cryptoDeviceInfo, roomEncryptionTrustLevel, isInactive)
val isCurrentDevice = currentSessionCrossSigningInfo.deviceId == cryptoDeviceInfo?.deviceId
DeviceFullInfo(deviceInfo, cryptoDeviceInfo, roomEncryptionTrustLevel, isInactive, isCurrentDevice)
}
}
}

View file

@ -37,9 +37,11 @@ import im.vector.app.core.resources.DrawableProvider
import im.vector.app.databinding.FragmentSettingsDevicesBinding
import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
import im.vector.app.features.settings.devices.v2.list.NUMBER_OF_OTHER_DEVICES_TO_RENDER
import im.vector.app.features.settings.devices.v2.list.OtherSessionsView
import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS
import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationView
import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationViewState
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
import javax.inject.Inject
@ -83,6 +85,7 @@ class VectorSettingsDevicesFragment :
initLearnMoreButtons()
initWaitingView()
initOtherSessionsView()
initSecurityRecommendationsView()
observeViewEvents()
}
@ -124,6 +127,29 @@ class VectorSettingsDevicesFragment :
views.deviceListOtherSessions.callback = this
}
private fun initSecurityRecommendationsView() {
views.deviceListUnverifiedSessionsRecommendation.callback = object : SecurityRecommendationView.Callback {
override fun onViewAllClicked() {
viewNavigator.navigateToOtherSessions(
requireActivity(),
R.string.device_manager_header_section_security_recommendations_title,
DeviceManagerFilterType.UNVERIFIED,
excludeCurrentDevice = false
)
}
}
views.deviceListInactiveSessionsRecommendation.callback = object : SecurityRecommendationView.Callback {
override fun onViewAllClicked() {
viewNavigator.navigateToOtherSessions(
requireActivity(),
R.string.device_manager_header_section_security_recommendations_title,
DeviceManagerFilterType.INACTIVE,
excludeCurrentDevice = false
)
}
}
}
override fun onDestroyView() {
cleanUpLearnMoreButtonsListeners()
super.onDestroyView()
@ -262,6 +288,11 @@ class VectorSettingsDevicesFragment :
}
override fun onViewAllOtherSessionsClicked() {
viewNavigator.navigateToOtherSessions(requireActivity())
viewNavigator.navigateToOtherSessions(
context = requireActivity(),
titleResourceId = R.string.device_manager_sessions_other_title,
defaultFilter = DeviceManagerFilterType.ALL_SESSIONS,
excludeCurrentDevice = true
)
}
}

View file

@ -17,6 +17,7 @@
package im.vector.app.features.settings.devices.v2
import android.content.Context
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
import im.vector.app.features.settings.devices.v2.othersessions.OtherSessionsActivity
import im.vector.app.features.settings.devices.v2.overview.SessionOverviewActivity
import javax.inject.Inject
@ -27,7 +28,14 @@ class VectorSettingsDevicesViewNavigator @Inject constructor() {
context.startActivity(SessionOverviewActivity.newIntent(context, deviceId))
}
fun navigateToOtherSessions(context: Context) {
context.startActivity(OtherSessionsActivity.newIntent(context))
fun navigateToOtherSessions(
context: Context,
titleResourceId: Int,
defaultFilter: DeviceManagerFilterType,
excludeCurrentDevice: Boolean,
) {
context.startActivity(
OtherSessionsActivity.newIntent(context, titleResourceId, defaultFilter, excludeCurrentDevice)
)
}
}

View file

@ -31,8 +31,8 @@ class FilterDevicesUseCase @Inject constructor() {
.filter {
when (filterType) {
DeviceManagerFilterType.ALL_SESSIONS -> true
DeviceManagerFilterType.VERIFIED -> it.cryptoDeviceInfo?.isVerified.orFalse()
DeviceManagerFilterType.UNVERIFIED -> !it.cryptoDeviceInfo?.isVerified.orFalse()
DeviceManagerFilterType.VERIFIED -> it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse()
DeviceManagerFilterType.UNVERIFIED -> !it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse()
DeviceManagerFilterType.INACTIVE -> it.isInactive
}
}

View file

@ -19,6 +19,7 @@ package im.vector.app.features.settings.devices.v2.list
import android.graphics.drawable.Drawable
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
@ -45,6 +46,10 @@ abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(R.la
@EpoxyAttribute
var sessionDescription: String? = null
@EpoxyAttribute
@ColorInt
var sessionDescriptionColor: Int? = null
@EpoxyAttribute
var sessionDescriptionDrawable: Drawable? = null
@ -82,6 +87,9 @@ abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(R.la
holder.otherSessionVerificationStatusImageView.render(roomEncryptionTrustLevel)
holder.otherSessionNameTextView.text = sessionName
holder.otherSessionDescriptionTextView.text = sessionDescription
sessionDescriptionColor?.let {
holder.otherSessionDescriptionTextView.setTextColor(it)
}
holder.otherSessionDescriptionTextView.setCompoundDrawablesWithIntrinsicBounds(sessionDescriptionDrawable, null, null, null)
}

View file

@ -53,20 +53,14 @@ class OtherSessionsController @Inject constructor(
data.forEach { device ->
val dateFormatKind = if (device.isInactive) DateFormatKind.TIMELINE_DAY_DIVIDER else DateFormatKind.DEFAULT_DATE_AND_TIME
val formattedLastActivityDate = host.dateFormatter.format(device.deviceInfo.lastSeenTs, dateFormatKind)
val description = if (device.isInactive) {
stringProvider.getQuantityString(
R.plurals.device_manager_other_sessions_description_inactive,
SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
formattedLastActivityDate
)
} else if (device.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted) {
stringProvider.getString(R.string.device_manager_other_sessions_description_verified, formattedLastActivityDate)
val description = calculateDescription(device, formattedLastActivityDate)
val descriptionColor = if (device.isCurrentDevice) {
host.colorProvider.getColorFromAttribute(R.attr.colorError)
} else {
stringProvider.getString(R.string.device_manager_other_sessions_description_unverified, formattedLastActivityDate)
host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
}
val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
val descriptionDrawable = if (device.isInactive) drawableProvider.getDrawable(R.drawable.ic_inactive_sessions, drawableColor) else null
val drawableColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
val descriptionDrawable = if (device.isInactive) host.drawableProvider.getDrawable(R.drawable.ic_inactive_sessions, drawableColor) else null
otherSessionItem {
id(device.deviceInfo.deviceId)
@ -75,10 +69,33 @@ class OtherSessionsController @Inject constructor(
sessionName(device.deviceInfo.displayName)
sessionDescription(description)
sessionDescriptionDrawable(descriptionDrawable)
sessionDescriptionColor(descriptionColor)
stringProvider(this@OtherSessionsController.stringProvider)
clickListener { device.deviceInfo.deviceId?.let { host.callback?.onItemClicked(it) } }
}
}
}
}
private fun calculateDescription(device: DeviceFullInfo, formattedLastActivityDate: String): String {
return when {
device.isInactive -> {
stringProvider.getQuantityString(
R.plurals.device_manager_other_sessions_description_inactive,
SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
formattedLastActivityDate
)
}
device.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted -> {
stringProvider.getString(R.string.device_manager_other_sessions_description_verified, formattedLastActivityDate)
}
device.isCurrentDevice -> {
stringProvider.getString(R.string.device_manager_other_sessions_description_unverified_current_session)
}
else -> {
stringProvider.getString(R.string.device_manager_other_sessions_description_unverified, formattedLastActivityDate)
}
}
}
}

View file

@ -31,7 +31,12 @@ class SecurityRecommendationView @JvmOverloads constructor(
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
interface Callback {
fun onViewAllClicked()
}
private val views: ViewSecurityRecommendationBinding
var callback: Callback? = null
init {
inflate(context, R.layout.view_security_recommendation, this)
@ -47,6 +52,10 @@ class SecurityRecommendationView @JvmOverloads constructor(
setDescription(it)
setImage(it)
}
views.recommendationViewAllButton.setOnClickListener {
callback?.onViewAllClicked()
}
}
private fun setTitle(typedArray: TypedArray) {
@ -78,4 +87,9 @@ class SecurityRecommendationView @JvmOverloads constructor(
setDescription(viewState.description)
setCount(viewState.sessionsCount)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
callback = null
}
}

View file

@ -20,9 +20,12 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.annotation.StringRes
import com.airbnb.mvrx.Mavericks
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.SimpleFragmentActivity
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
@AndroidEntryPoint
class OtherSessionsActivity : SimpleFragmentActivity() {
@ -35,14 +38,23 @@ class OtherSessionsActivity : SimpleFragmentActivity() {
if (isFirstCreation()) {
addFragment(
container = views.container,
fragmentClass = OtherSessionsFragment::class.java
fragmentClass = OtherSessionsFragment::class.java,
params = intent.getParcelableExtra(Mavericks.KEY_ARG)
)
}
}
companion object {
fun newIntent(context: Context): Intent {
return Intent(context, OtherSessionsActivity::class.java)
fun newIntent(
context: Context,
@StringRes
titleResourceId: Int,
defaultFilter: DeviceManagerFilterType,
excludeCurrentDevice: Boolean,
): Intent {
return Intent(context, OtherSessionsActivity::class.java).apply {
putExtra(Mavericks.KEY_ARG, OtherSessionsArgs(titleResourceId, defaultFilter, excludeCurrentDevice))
}
}
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.devices.v2.othersessions
import android.os.Parcelable
import androidx.annotation.StringRes
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
import kotlinx.parcelize.Parcelize
@Parcelize
data class OtherSessionsArgs(
@StringRes
val titleResourceId: Int,
val defaultFilter: DeviceManagerFilterType,
val excludeCurrentDevice: Boolean,
) : Parcelable

View file

@ -22,6 +22,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint
@ -46,6 +47,7 @@ class OtherSessionsFragment :
OtherSessionsView.Callback {
private val viewModel: OtherSessionsViewModel by fragmentViewModel()
private val args: OtherSessionsArgs by args()
@Inject lateinit var colorProvider: ColorProvider
@ -57,7 +59,7 @@ class OtherSessionsFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar(views.otherSessionsToolbar).allowBack()
setupToolbar(views.otherSessionsToolbar).setTitle(args.titleResourceId).allowBack()
observeViewEvents()
initFilterView()
}
@ -85,6 +87,10 @@ class OtherSessionsFragment :
}
views.deviceListOtherSessions.callback = this
if (args.defaultFilter != DeviceManagerFilterType.ALL_SESSIONS) {
viewModel.handle(OtherSessionsAction.FilterDevices(args.defaultFilter))
}
}
override fun onBottomSheetResult(resultCode: Int, data: Any?) {

View file

@ -30,7 +30,7 @@ import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
import kotlinx.coroutines.Job
class OtherSessionsViewModel @AssistedInject constructor(
@Assisted initialState: OtherSessionsViewState,
@Assisted private val initialState: OtherSessionsViewState,
activeSessionHolder: ActiveSessionHolder,
private val getDeviceFullInfoListUseCase: GetDeviceFullInfoListUseCase,
refreshDevicesUseCase: RefreshDevicesUseCase
@ -55,7 +55,7 @@ class OtherSessionsViewModel @AssistedInject constructor(
observeDevicesJob?.cancel()
observeDevicesJob = getDeviceFullInfoListUseCase.execute(
filterType = currentFilter,
excludeCurrentDevice = true
excludeCurrentDevice = initialState.excludeCurrentDevice
)
.execute { async ->
copy(

View file

@ -25,4 +25,8 @@ import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
data class OtherSessionsViewState(
val devices: Async<List<DeviceFullInfo>> = Uninitialized,
val currentFilter: DeviceManagerFilterType = DeviceManagerFilterType.ALL_SESSIONS,
) : MavericksState
val excludeCurrentDevice: Boolean = false,
) : MavericksState {
constructor(args: OtherSessionsArgs) : this(excludeCurrentDevice = args.excludeCurrentDevice)
}

View file

@ -48,11 +48,13 @@ class GetDeviceFullInfoUseCase @Inject constructor(
val fullInfo = if (info != null && cryptoInfo != null) {
val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoInfo)
val isInactive = checkIfSessionIsInactiveUseCase.execute(info.lastSeenTs ?: 0)
val isCurrentDevice = currentSessionCrossSigningInfo.deviceId == cryptoInfo.deviceId
DeviceFullInfo(
deviceInfo = info,
cryptoDeviceInfo = cryptoInfo,
roomEncryptionTrustLevel = roomEncryptionTrustLevel,
isInactive = isInactive
isInactive = isInactive,
isCurrentDevice = isCurrentDevice,
)
} else {
null

View file

@ -16,6 +16,7 @@
package im.vector.app.features.settings.devices.v2
import android.os.SystemClock
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.test.MvRxTestRule
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
@ -30,11 +31,16 @@ import io.mockk.coVerify
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.runs
import io.mockk.unmockkAll
import io.mockk.verify
import kotlinx.coroutines.flow.flowOf
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
@ -62,6 +68,17 @@ class DevicesViewModelTest {
)
}
@Before
fun setup() {
mockkStatic(SystemClock::class)
every { SystemClock.elapsedRealtime() } returns 1234
}
@After
fun tearDown() {
unmockkAll()
}
@Test
fun `given the viewModel when initializing it then verification listener is added`() {
// Given
@ -216,21 +233,23 @@ class DevicesViewModelTest {
*/
private fun givenDeviceFullInfoList(): List<DeviceFullInfo> {
val verifiedCryptoDeviceInfo = mockk<CryptoDeviceInfo>()
every { verifiedCryptoDeviceInfo.isVerified } returns true
every { verifiedCryptoDeviceInfo.trustLevel } returns DeviceTrustLevel(crossSigningVerified = true, locallyVerified = true)
val unverifiedCryptoDeviceInfo = mockk<CryptoDeviceInfo>()
every { unverifiedCryptoDeviceInfo.isVerified } returns false
every { unverifiedCryptoDeviceInfo.trustLevel } returns DeviceTrustLevel(crossSigningVerified = false, locallyVerified = false)
val deviceFullInfo1 = DeviceFullInfo(
deviceInfo = mockk(),
cryptoDeviceInfo = verifiedCryptoDeviceInfo,
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
isInactive = false
isInactive = false,
isCurrentDevice = true
)
val deviceFullInfo2 = DeviceFullInfo(
deviceInfo = mockk(),
cryptoDeviceInfo = unverifiedCryptoDeviceInfo,
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning,
isInactive = true
isInactive = true,
isCurrentDevice = false
)
val deviceFullInfoList = listOf(deviceFullInfo1, deviceFullInfo2)
val deviceFullInfoListFlow = flowOf(deviceFullInfoList)

View file

@ -109,19 +109,22 @@ class GetDeviceFullInfoListUseCaseTest {
deviceInfo = deviceInfo1,
cryptoDeviceInfo = cryptoDeviceInfo1,
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
isInactive = true
isInactive = true,
isCurrentDevice = true
)
val expectedResult2 = DeviceFullInfo(
deviceInfo = deviceInfo2,
cryptoDeviceInfo = cryptoDeviceInfo2,
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
isInactive = false
isInactive = false,
isCurrentDevice = false
)
val expectedResult3 = DeviceFullInfo(
deviceInfo = deviceInfo3,
cryptoDeviceInfo = cryptoDeviceInfo3,
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning,
isInactive = false
isInactive = false,
isCurrentDevice = false
)
val expectedResult = listOf(expectedResult3, expectedResult2, expectedResult1)
every { filterDevicesUseCase.execute(any(), any()) } returns expectedResult
@ -163,6 +166,7 @@ class GetDeviceFullInfoListUseCaseTest {
private fun givenCurrentSessionCrossSigningInfo(): CurrentSessionCrossSigningInfo {
val currentSessionCrossSigningInfo = mockk<CurrentSessionCrossSigningInfo>()
every { getCurrentSessionCrossSigningInfoUseCase.execute() } returns flowOf(currentSessionCrossSigningInfo)
every { currentSessionCrossSigningInfo.deviceId } returns A_DEVICE_ID_1
return currentSessionCrossSigningInfo
}

View file

@ -17,6 +17,7 @@
package im.vector.app.features.settings.devices.v2
import android.content.Intent
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
import im.vector.app.features.settings.devices.v2.othersessions.OtherSessionsActivity
import im.vector.app.features.settings.devices.v2.overview.SessionOverviewActivity
import im.vector.app.test.fakes.FakeContext
@ -30,6 +31,8 @@ import org.junit.Before
import org.junit.Test
private const val A_SESSION_ID = "session_id"
private const val A_TITLE_RESOURCE_ID = 1234
private val A_DEFAULT_FILTER = DeviceManagerFilterType.INACTIVE
class VectorSettingsDevicesViewNavigatorTest {
@ -61,10 +64,10 @@ class VectorSettingsDevicesViewNavigatorTest {
@Test
fun `given an intent when navigating to other sessions list then it starts the correct activity`() {
val intent = givenIntentForOtherSessions()
val intent = givenIntentForOtherSessions(A_TITLE_RESOURCE_ID, A_DEFAULT_FILTER, true)
context.givenStartActivity(intent)
vectorSettingsDevicesViewNavigator.navigateToOtherSessions(context.instance)
vectorSettingsDevicesViewNavigator.navigateToOtherSessions(context.instance, A_TITLE_RESOURCE_ID, A_DEFAULT_FILTER, true)
verify {
context.instance.startActivity(intent)
@ -77,9 +80,9 @@ class VectorSettingsDevicesViewNavigatorTest {
return intent
}
private fun givenIntentForOtherSessions(): Intent {
private fun givenIntentForOtherSessions(titleResourceId: Int, defaultFilter: DeviceManagerFilterType, excludeCurrentDevice: Boolean): Intent {
val intent = mockk<Intent>()
every { OtherSessionsActivity.newIntent(context.instance) } returns intent
every { OtherSessionsActivity.newIntent(context.instance, titleResourceId, defaultFilter, excludeCurrentDevice) } returns intent
return intent
}
}

View file

@ -33,7 +33,8 @@ private val activeVerifiedDevice = DeviceFullInfo(
trustLevel = DeviceTrustLevel(crossSigningVerified = true, locallyVerified = true)
),
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
isInactive = false
isInactive = false,
isCurrentDevice = true
)
private val inactiveVerifiedDevice = DeviceFullInfo(
deviceInfo = DeviceInfo(deviceId = "INACTIVE_VERIFIED_DEVICE"),
@ -43,7 +44,8 @@ private val inactiveVerifiedDevice = DeviceFullInfo(
trustLevel = DeviceTrustLevel(crossSigningVerified = true, locallyVerified = true)
),
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
isInactive = true
isInactive = true,
isCurrentDevice = false
)
private val activeUnverifiedDevice = DeviceFullInfo(
deviceInfo = DeviceInfo(deviceId = "ACTIVE_UNVERIFIED_DEVICE"),
@ -53,7 +55,8 @@ private val activeUnverifiedDevice = DeviceFullInfo(
trustLevel = DeviceTrustLevel(crossSigningVerified = false, locallyVerified = false)
),
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning,
isInactive = false
isInactive = false,
isCurrentDevice = false
)
private val inactiveUnverifiedDevice = DeviceFullInfo(
deviceInfo = DeviceInfo(deviceId = "INACTIVE_UNVERIFIED_DEVICE"),
@ -63,7 +66,8 @@ private val inactiveUnverifiedDevice = DeviceFullInfo(
trustLevel = DeviceTrustLevel(crossSigningVerified = false, locallyVerified = false)
),
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning,
isInactive = true
isInactive = true,
isCurrentDevice = false
)
private val devices = listOf(

View file

@ -85,6 +85,7 @@ class GetDeviceFullInfoUseCaseTest {
fakeActiveSessionHolder.fakeSession.fakeCryptoService.cryptoDeviceInfoWithIdLiveData.givenAsFlow()
val trustLevel = givenTrustLevel(currentSessionCrossSigningInfo, cryptoDeviceInfo)
val isInactive = false
val isCurrentDevice = true
every { checkIfSessionIsInactiveUseCase.execute(any()) } returns isInactive
// When
@ -96,6 +97,7 @@ class GetDeviceFullInfoUseCaseTest {
cryptoDeviceInfo = cryptoDeviceInfo,
roomEncryptionTrustLevel = trustLevel,
isInactive = isInactive,
isCurrentDevice = isCurrentDevice
)
verify { fakeActiveSessionHolder.instance.getSafeActiveSession() }
verify { getCurrentSessionCrossSigningInfoUseCase.execute() }