Merge pull request #7361 from vector-im/feature/mna/device-manager-unknown-verification-status
[Device management] Update the unknown verification status icon (PSG-824)
This commit is contained in:
commit
612d2e51e5
|
@ -0,0 +1 @@
|
||||||
|
[Device management] Update the unknown verification status icon
|
|
@ -3254,10 +3254,12 @@
|
||||||
<string name="a11y_device_manager_device_type_unknown">Unknown device type</string>
|
<string name="a11y_device_manager_device_type_unknown">Unknown device type</string>
|
||||||
<string name="device_manager_verification_status_verified">Verified session</string>
|
<string name="device_manager_verification_status_verified">Verified session</string>
|
||||||
<string name="device_manager_verification_status_unverified">Unverified session</string>
|
<string name="device_manager_verification_status_unverified">Unverified session</string>
|
||||||
|
<string name="device_manager_verification_status_unknown">Unknown verification status</string>
|
||||||
<string name="device_manager_verification_status_detail_current_session_verified">Your current session is ready for secure messaging.</string>
|
<string name="device_manager_verification_status_detail_current_session_verified">Your current session is ready for secure messaging.</string>
|
||||||
<string name="device_manager_verification_status_detail_other_session_verified">This session is ready for secure messaging.</string>
|
<string name="device_manager_verification_status_detail_other_session_verified">This session is ready for secure messaging.</string>
|
||||||
<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_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_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_verify_session">Verify Session</string>
|
<string name="device_manager_verify_session">Verify Session</string>
|
||||||
<string name="device_manager_view_details">View Details</string>
|
<string name="device_manager_view_details">View Details</string>
|
||||||
<string name="device_manager_other_sessions_view_all">View All (%1$d)</string>
|
<string name="device_manager_other_sessions_view_all">View All (%1$d)</string>
|
||||||
|
|
|
@ -146,6 +146,7 @@
|
||||||
<color name="shield_color_gray">#91A1C0</color>
|
<color name="shield_color_gray">#91A1C0</color>
|
||||||
<color name="shield_color_warning">#FF4B55</color>
|
<color name="shield_color_warning">#FF4B55</color>
|
||||||
<color name="shield_color_warning_background">#0FFF4B55</color>
|
<color name="shield_color_warning_background">#0FFF4B55</color>
|
||||||
|
<color name="shield_color_unknown">@color/palette_gray_200</color>
|
||||||
|
|
||||||
<!-- Badge Colors -->
|
<!-- Badge Colors -->
|
||||||
<attr name="vctr_badge_color_border" format="color" />
|
<attr name="vctr_badge_color_border" format="color" />
|
||||||
|
|
|
@ -53,6 +53,8 @@ android {
|
||||||
// "pm clear" command after each test invocation. This command ensures
|
// "pm clear" command after each test invocation. This command ensures
|
||||||
// that the app's state is completely cleared between tests.
|
// that the app's state is completely cleared between tests.
|
||||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||||
|
|
||||||
|
vectorDrawables.useSupportLibrary = true
|
||||||
}
|
}
|
||||||
|
|
||||||
testOptions {
|
testOptions {
|
||||||
|
|
|
@ -45,8 +45,8 @@ class ShieldImageView @JvmOverloads constructor(
|
||||||
RoomEncryptionTrustLevel.Default -> {
|
RoomEncryptionTrustLevel.Default -> {
|
||||||
contentDescription = context.getString(R.string.a11y_trust_level_default)
|
contentDescription = context.getString(R.string.a11y_trust_level_default)
|
||||||
setImageResource(
|
setImageResource(
|
||||||
if (borderLess) R.drawable.ic_shield_black_no_border
|
if (borderLess) R.drawable.ic_shield_unknown_no_border
|
||||||
else R.drawable.ic_shield_black
|
else R.drawable.ic_shield_unknown
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
RoomEncryptionTrustLevel.Warning -> {
|
RoomEncryptionTrustLevel.Warning -> {
|
||||||
|
@ -137,7 +137,7 @@ class ShieldImageView @JvmOverloads constructor(
|
||||||
@DrawableRes
|
@DrawableRes
|
||||||
fun RoomEncryptionTrustLevel.toDrawableRes(): Int {
|
fun RoomEncryptionTrustLevel.toDrawableRes(): Int {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_black
|
RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_unknown
|
||||||
RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning
|
RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning
|
||||||
RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted
|
RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted
|
||||||
RoomEncryptionTrustLevel.E2EWithUnsupportedAlgorithm -> R.drawable.ic_warning_badge
|
RoomEncryptionTrustLevel.E2EWithUnsupportedAlgorithm -> R.drawable.ic_warning_badge
|
||||||
|
|
|
@ -57,7 +57,7 @@ class GetDeviceFullInfoListUseCase @Inject constructor(
|
||||||
} else {
|
} else {
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
filterDevicesUseCase.execute(deviceFullInfoList, filterType, excludedDeviceIds)
|
filterDevicesUseCase.execute(currentSessionCrossSigningInfo, deviceFullInfoList, filterType, excludedDeviceIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceFullInfoFlow.distinctUntilChanged()
|
deviceFullInfoFlow.distinctUntilChanged()
|
||||||
|
|
|
@ -44,6 +44,7 @@ import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INAC
|
||||||
import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationView
|
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.SecurityRecommendationViewState
|
||||||
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
|
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,12 +165,11 @@ class VectorSettingsDevicesFragment :
|
||||||
if (state.devices is Success) {
|
if (state.devices is Success) {
|
||||||
val devices = state.devices()
|
val devices = state.devices()
|
||||||
val currentDeviceId = state.currentSessionCrossSigningInfo.deviceId
|
val currentDeviceId = state.currentSessionCrossSigningInfo.deviceId
|
||||||
val currentDeviceInfo = devices?.firstOrNull {
|
val currentDeviceInfo = devices?.firstOrNull { it.deviceInfo.deviceId == currentDeviceId }
|
||||||
it.deviceInfo.deviceId == currentDeviceId
|
val isCurrentSessionVerified = currentDeviceInfo?.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted
|
||||||
}
|
|
||||||
val otherDevices = devices?.filter { it.deviceInfo.deviceId != currentDeviceId }
|
val otherDevices = devices?.filter { it.deviceInfo.deviceId != currentDeviceId }
|
||||||
|
|
||||||
renderSecurityRecommendations(state.inactiveSessionsCount, state.unverifiedSessionsCount)
|
renderSecurityRecommendations(state.inactiveSessionsCount, state.unverifiedSessionsCount, isCurrentSessionVerified)
|
||||||
renderCurrentDevice(currentDeviceInfo)
|
renderCurrentDevice(currentDeviceInfo)
|
||||||
renderOtherSessionsView(otherDevices)
|
renderOtherSessionsView(otherDevices)
|
||||||
} else {
|
} else {
|
||||||
|
@ -181,14 +181,21 @@ class VectorSettingsDevicesFragment :
|
||||||
handleLoadingStatus(state.isLoading)
|
handleLoadingStatus(state.isLoading)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderSecurityRecommendations(inactiveSessionsCount: Int, unverifiedSessionsCount: Int) {
|
private fun renderSecurityRecommendations(
|
||||||
if (unverifiedSessionsCount == 0 && inactiveSessionsCount == 0) {
|
inactiveSessionsCount: Int,
|
||||||
|
unverifiedSessionsCount: Int,
|
||||||
|
isCurrentSessionVerified: Boolean,
|
||||||
|
) {
|
||||||
|
val isUnverifiedSectionVisible = unverifiedSessionsCount > 0 && isCurrentSessionVerified
|
||||||
|
val isInactiveSectionVisible = inactiveSessionsCount > 0
|
||||||
|
if (isUnverifiedSectionVisible.not() && isInactiveSectionVisible.not()) {
|
||||||
hideSecurityRecommendations()
|
hideSecurityRecommendations()
|
||||||
} else {
|
} else {
|
||||||
views.deviceListHeaderSectionSecurityRecommendations.isVisible = true
|
views.deviceListHeaderSectionSecurityRecommendations.isVisible = true
|
||||||
views.deviceListSecurityRecommendationsDivider.isVisible = true
|
views.deviceListSecurityRecommendationsDivider.isVisible = true
|
||||||
views.deviceListUnverifiedSessionsRecommendation.isVisible = unverifiedSessionsCount > 0
|
|
||||||
views.deviceListInactiveSessionsRecommendation.isVisible = inactiveSessionsCount > 0
|
views.deviceListUnverifiedSessionsRecommendation.isVisible = isUnverifiedSectionVisible
|
||||||
|
views.deviceListInactiveSessionsRecommendation.isVisible = isInactiveSectionVisible
|
||||||
val unverifiedSessionsViewState = SecurityRecommendationViewState(
|
val unverifiedSessionsViewState = SecurityRecommendationViewState(
|
||||||
description = getString(R.string.device_manager_unverified_sessions_description),
|
description = getString(R.string.device_manager_unverified_sessions_description),
|
||||||
sessionsCount = unverifiedSessionsCount,
|
sessionsCount = unverifiedSessionsCount,
|
||||||
|
@ -206,11 +213,19 @@ class VectorSettingsDevicesFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun hideUnverifiedSessionsRecommendation() {
|
||||||
|
views.deviceListUnverifiedSessionsRecommendation.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideInactiveSessionsRecommendation() {
|
||||||
|
views.deviceListInactiveSessionsRecommendation.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
private fun hideSecurityRecommendations() {
|
private fun hideSecurityRecommendations() {
|
||||||
views.deviceListHeaderSectionSecurityRecommendations.isVisible = false
|
views.deviceListHeaderSectionSecurityRecommendations.isVisible = false
|
||||||
views.deviceListUnverifiedSessionsRecommendation.isVisible = false
|
|
||||||
views.deviceListInactiveSessionsRecommendation.isVisible = false
|
|
||||||
views.deviceListSecurityRecommendationsDivider.isVisible = false
|
views.deviceListSecurityRecommendationsDivider.isVisible = false
|
||||||
|
hideUnverifiedSessionsRecommendation()
|
||||||
|
hideInactiveSessionsRecommendation()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderOtherSessionsView(otherDevices: List<DeviceFullInfo>?) {
|
private fun renderOtherSessionsView(otherDevices: List<DeviceFullInfo>?) {
|
||||||
|
|
|
@ -17,22 +17,27 @@
|
||||||
package im.vector.app.features.settings.devices.v2.filter
|
package im.vector.app.features.settings.devices.v2.filter
|
||||||
|
|
||||||
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
||||||
|
import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class FilterDevicesUseCase @Inject constructor() {
|
class FilterDevicesUseCase @Inject constructor() {
|
||||||
|
|
||||||
fun execute(
|
fun execute(
|
||||||
|
currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo,
|
||||||
devices: List<DeviceFullInfo>,
|
devices: List<DeviceFullInfo>,
|
||||||
filterType: DeviceManagerFilterType,
|
filterType: DeviceManagerFilterType,
|
||||||
excludedDeviceIds: List<String> = emptyList(),
|
excludedDeviceIds: List<String> = emptyList(),
|
||||||
): List<DeviceFullInfo> {
|
): List<DeviceFullInfo> {
|
||||||
|
val isCurrentSessionVerified = currentSessionCrossSigningInfo.isCrossSigningVerified.orFalse()
|
||||||
return devices
|
return devices
|
||||||
.filter {
|
.filter {
|
||||||
when (filterType) {
|
when (filterType) {
|
||||||
DeviceManagerFilterType.ALL_SESSIONS -> true
|
DeviceManagerFilterType.ALL_SESSIONS -> true
|
||||||
DeviceManagerFilterType.VERIFIED -> it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse()
|
// when current session is not verified, other session status cannot be trusted
|
||||||
DeviceManagerFilterType.UNVERIFIED -> !it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse()
|
DeviceManagerFilterType.VERIFIED -> isCurrentSessionVerified && it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse()
|
||||||
|
// when current session is not verified, other session status cannot be trusted
|
||||||
|
DeviceManagerFilterType.UNVERIFIED -> isCurrentSessionVerified && !it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse()
|
||||||
DeviceManagerFilterType.INACTIVE -> it.isInactive
|
DeviceManagerFilterType.INACTIVE -> it.isInactive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ class OtherSessionsController @Inject constructor(
|
||||||
data.forEach { device ->
|
data.forEach { device ->
|
||||||
val dateFormatKind = if (device.isInactive) DateFormatKind.TIMELINE_DAY_DIVIDER else DateFormatKind.DEFAULT_DATE_AND_TIME
|
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 formattedLastActivityDate = host.dateFormatter.format(device.deviceInfo.lastSeenTs, dateFormatKind)
|
||||||
val description = calculateDescription(device, formattedLastActivityDate)
|
val description = buildDescription(device, formattedLastActivityDate)
|
||||||
val descriptionColor = if (device.isCurrentDevice) {
|
val descriptionColor = if (device.isCurrentDevice) {
|
||||||
host.colorProvider.getColorFromAttribute(R.attr.colorError)
|
host.colorProvider.getColorFromAttribute(R.attr.colorError)
|
||||||
} else {
|
} else {
|
||||||
|
@ -77,7 +77,7 @@ class OtherSessionsController @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateDescription(device: DeviceFullInfo, formattedLastActivityDate: String): String {
|
private fun buildDescription(device: DeviceFullInfo, formattedLastActivityDate: String): String {
|
||||||
return when {
|
return when {
|
||||||
device.isInactive -> {
|
device.isInactive -> {
|
||||||
stringProvider.getQuantityString(
|
stringProvider.getQuantityString(
|
||||||
|
@ -93,6 +93,9 @@ class OtherSessionsController @Inject constructor(
|
||||||
device.isCurrentDevice -> {
|
device.isCurrentDevice -> {
|
||||||
stringProvider.getString(R.string.device_manager_other_sessions_description_unverified_current_session)
|
stringProvider.getString(R.string.device_manager_other_sessions_description_unverified_current_session)
|
||||||
}
|
}
|
||||||
|
device.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Default -> {
|
||||||
|
stringProvider.getString(R.string.device_manager_session_last_activity, formattedLastActivityDate)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
stringProvider.getString(R.string.device_manager_other_sessions_description_unverified, formattedLastActivityDate)
|
stringProvider.getString(R.string.device_manager_other_sessions_description_unverified, formattedLastActivityDate)
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,10 +90,10 @@ class SessionInfoView @JvmOverloads constructor(
|
||||||
isVerifyButtonVisible: Boolean,
|
isVerifyButtonVisible: Boolean,
|
||||||
) {
|
) {
|
||||||
views.sessionInfoVerificationStatusImageView.render(encryptionTrustLevel)
|
views.sessionInfoVerificationStatusImageView.render(encryptionTrustLevel)
|
||||||
if (encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted) {
|
when {
|
||||||
renderCrossSigningVerified(isCurrentSession)
|
encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted -> renderCrossSigningVerified(isCurrentSession)
|
||||||
} else {
|
encryptionTrustLevel == RoomEncryptionTrustLevel.Default && !isCurrentSession -> renderCrossSigningUnknown()
|
||||||
renderCrossSigningUnverified(isCurrentSession, isVerifyButtonVisible)
|
else -> renderCrossSigningUnverified(isCurrentSession, isVerifyButtonVisible)
|
||||||
}
|
}
|
||||||
if (hasLearnMoreLink) {
|
if (hasLearnMoreLink) {
|
||||||
appendLearnMoreToVerificationStatus()
|
appendLearnMoreToVerificationStatus()
|
||||||
|
@ -142,6 +142,12 @@ class SessionInfoView @JvmOverloads constructor(
|
||||||
views.sessionInfoVerifySessionButton.isVisible = isVerifyButtonVisible
|
views.sessionInfoVerifySessionButton.isVisible = isVerifyButtonVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun renderCrossSigningUnknown() {
|
||||||
|
views.sessionInfoVerificationStatusTextView.text = context.getString(R.string.device_manager_verification_status_unknown)
|
||||||
|
views.sessionInfoVerificationStatusDetailTextView.text = context.getString(R.string.device_manager_verification_status_detail_other_session_unknown)
|
||||||
|
views.sessionInfoVerifySessionButton.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
private fun renderDeviceInfo(sessionName: String, deviceType: DeviceType, stringProvider: StringProvider) {
|
private fun renderDeviceInfo(sessionName: String, deviceType: DeviceType, stringProvider: StringProvider) {
|
||||||
setDeviceTypeIconUseCase.execute(deviceType, views.sessionInfoDeviceTypeImageView, stringProvider)
|
setDeviceTypeIconUseCase.execute(deviceType, views.sessionInfoDeviceTypeImageView, stringProvider)
|
||||||
views.sessionInfoNameTextView.text = sessionName
|
views.sessionInfoNameTextView.text = sessionName
|
||||||
|
@ -155,34 +161,31 @@ class SessionInfoView @JvmOverloads constructor(
|
||||||
drawableProvider: DrawableProvider,
|
drawableProvider: DrawableProvider,
|
||||||
colorProvider: ColorProvider,
|
colorProvider: ColorProvider,
|
||||||
) {
|
) {
|
||||||
deviceInfo.lastSeenTs
|
if (deviceInfo.lastSeenTs != null && isLastSeenDetailsVisible) {
|
||||||
?.takeIf { isLastSeenDetailsVisible }
|
val timestamp = deviceInfo.lastSeenTs
|
||||||
?.let { timestamp ->
|
views.sessionInfoLastActivityTextView.isVisible = true
|
||||||
views.sessionInfoLastActivityTextView.isVisible = true
|
views.sessionInfoLastActivityTextView.text = if (isInactive) {
|
||||||
views.sessionInfoLastActivityTextView.text = if (isInactive) {
|
val formattedTs = dateFormatter.format(timestamp, DateFormatKind.TIMELINE_DAY_DIVIDER)
|
||||||
val formattedTs = dateFormatter.format(timestamp, DateFormatKind.TIMELINE_DAY_DIVIDER)
|
context.resources.getQuantityString(
|
||||||
context.resources.getQuantityString(
|
R.plurals.device_manager_other_sessions_description_inactive,
|
||||||
R.plurals.device_manager_other_sessions_description_inactive,
|
SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
|
||||||
SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
|
SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
|
||||||
SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
|
formattedTs
|
||||||
formattedTs
|
)
|
||||||
)
|
} else {
|
||||||
} else {
|
val formattedTs = dateFormatter.format(timestamp, DateFormatKind.DEFAULT_DATE_AND_TIME)
|
||||||
val formattedTs = dateFormatter.format(timestamp, DateFormatKind.DEFAULT_DATE_AND_TIME)
|
context.getString(R.string.device_manager_session_last_activity, formattedTs)
|
||||||
context.getString(R.string.device_manager_session_last_activity, formattedTs)
|
}
|
||||||
}
|
val drawable = if (isInactive) {
|
||||||
val drawable = if (isInactive) {
|
val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
|
||||||
val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
|
drawableProvider.getDrawable(R.drawable.ic_inactive_sessions, drawableColor)
|
||||||
drawableProvider.getDrawable(R.drawable.ic_inactive_sessions, drawableColor)
|
} else {
|
||||||
} else {
|
null
|
||||||
null
|
}
|
||||||
}
|
views.sessionInfoLastActivityTextView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null)
|
||||||
views.sessionInfoLastActivityTextView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null)
|
} else {
|
||||||
}
|
views.sessionInfoLastActivityTextView.isGone = true
|
||||||
?: run {
|
}
|
||||||
views.sessionInfoLastActivityTextView.isGone = true
|
|
||||||
}
|
|
||||||
|
|
||||||
views.sessionInfoLastIPAddressTextView.setTextOrHide(deviceInfo.lastSeenIp?.takeIf { isLastSeenDetailsVisible })
|
views.sessionInfoLastIPAddressTextView.setTextOrHide(deviceInfo.lastSeenIp?.takeIf { isLastSeenDetailsVisible })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.isGone
|
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
@ -42,7 +41,6 @@ import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.databinding.FragmentSessionOverviewBinding
|
import im.vector.app.databinding.FragmentSessionOverviewBinding
|
||||||
import im.vector.app.features.auth.ReAuthActivity
|
import im.vector.app.features.auth.ReAuthActivity
|
||||||
import im.vector.app.features.crypto.recover.SetupMode
|
import im.vector.app.features.crypto.recover.SetupMode
|
||||||
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
|
||||||
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
|
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
|
||||||
import im.vector.app.features.settings.devices.v2.more.SessionLearnMoreBottomSheet
|
import im.vector.app.features.settings.devices.v2.more.SessionLearnMoreBottomSheet
|
||||||
import im.vector.app.features.workers.signout.SignOutUiWorker
|
import im.vector.app.features.workers.signout.SignOutUiWorker
|
||||||
|
@ -181,11 +179,6 @@ class SessionOverviewFragment :
|
||||||
updateSessionInfo(state)
|
updateSessionInfo(state)
|
||||||
updateLoading(state.isLoading)
|
updateLoading(state.isLoading)
|
||||||
updatePushNotificationToggle(state.deviceId, state.pushers.invoke().orEmpty())
|
updatePushNotificationToggle(state.deviceId, state.pushers.invoke().orEmpty())
|
||||||
if (state.deviceInfo is Success) {
|
|
||||||
renderSessionInfo(state.isCurrentSessionTrusted, state.deviceInfo.invoke())
|
|
||||||
} else {
|
|
||||||
hideSessionInfo()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateToolbar(viewState: SessionOverviewViewState) {
|
private fun updateToolbar(viewState: SessionOverviewViewState) {
|
||||||
|
@ -214,7 +207,7 @@ class SessionOverviewFragment :
|
||||||
deviceFullInfo = deviceInfo,
|
deviceFullInfo = deviceInfo,
|
||||||
isVerifyButtonVisible = isCurrentSession || viewState.isCurrentSessionTrusted,
|
isVerifyButtonVisible = isCurrentSession || viewState.isCurrentSessionTrusted,
|
||||||
isDetailsButtonVisible = false,
|
isDetailsButtonVisible = false,
|
||||||
isLearnMoreLinkVisible = true,
|
isLearnMoreLinkVisible = deviceInfo.roomEncryptionTrustLevel != RoomEncryptionTrustLevel.Default,
|
||||||
isLastSeenDetailsVisible = !isCurrentSession,
|
isLastSeenDetailsVisible = !isCurrentSession,
|
||||||
)
|
)
|
||||||
views.sessionOverviewInfo.render(infoViewState, dateFormatter, drawableProvider, colorProvider, stringProvider)
|
views.sessionOverviewInfo.render(infoViewState, dateFormatter, drawableProvider, colorProvider, stringProvider)
|
||||||
|
@ -243,18 +236,6 @@ class SessionOverviewFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderSessionInfo(isCurrentSession: Boolean, deviceFullInfo: DeviceFullInfo) {
|
|
||||||
views.sessionOverviewInfo.isVisible = true
|
|
||||||
val viewState = SessionInfoViewState(
|
|
||||||
isCurrentSession = isCurrentSession,
|
|
||||||
deviceFullInfo = deviceFullInfo,
|
|
||||||
isDetailsButtonVisible = false,
|
|
||||||
isLearnMoreLinkVisible = true,
|
|
||||||
isLastSeenDetailsVisible = true,
|
|
||||||
)
|
|
||||||
views.sessionOverviewInfo.render(viewState, dateFormatter, drawableProvider, colorProvider, stringProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateLoading(isLoading: Boolean) {
|
private fun updateLoading(isLoading: Boolean) {
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
showLoading(null)
|
showLoading(null)
|
||||||
|
@ -313,8 +294,4 @@ class SessionOverviewFragment :
|
||||||
)
|
)
|
||||||
SessionLearnMoreBottomSheet.show(childFragmentManager, args)
|
SessionLearnMoreBottomSheet.show(childFragmentManager, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hideSessionInfo() {
|
|
||||||
views.sessionOverviewInfo.isGone = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/shield_color_unknown"
|
||||||
|
android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#ffffff" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/shield_color_unknown"
|
||||||
|
android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#ffffff" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M12.003,16.5C12.577,16.5 13.043,16.035 13.043,15.46C13.043,14.887 12.577,14.421 12.003,14.421C11.429,14.421 10.964,14.887 10.964,15.46C10.964,16.035 11.429,16.5 12.003,16.5ZM10.559,9.823C10.559,9.023 11.208,8.379 12.003,8.379C12.795,8.379 13.447,9.031 13.447,9.823C13.447,10.19 13.29,10.321 12.671,10.748C12.398,10.937 12.022,11.2 11.729,11.586C11.416,11.999 11.223,12.513 11.223,13.168H12.783C12.783,12.847 12.868,12.665 12.971,12.528C13.096,12.364 13.276,12.226 13.558,12.031C13.587,12.01 13.618,11.989 13.651,11.967C14.162,11.619 15.006,11.045 15.006,9.823C15.006,8.17 13.656,6.82 12.003,6.82C10.352,6.82 9,8.157 9,9.823H10.559Z" />
|
||||||
|
</vector>
|
|
@ -0,0 +1,18 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/shield_color_unknown"
|
||||||
|
android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#ffffff" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/shield_color_unknown"
|
||||||
|
android:pathData="M1.5,12.405V3.075L12,0L22.5,3.075V12.405C22.5,21.945 12,24 12,24C12,24 1.5,21.945 1.5,12.405Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M12.003,16.5C12.577,16.5 13.043,16.035 13.043,15.46C13.043,14.887 12.577,14.421 12.003,14.421C11.429,14.421 10.964,14.887 10.964,15.46C10.964,16.035 11.429,16.5 12.003,16.5ZM10.559,9.823C10.559,9.023 11.208,8.379 12.003,8.379C12.795,8.379 13.447,9.031 13.447,9.823C13.447,10.19 13.29,10.321 12.671,10.748C12.398,10.937 12.022,11.2 11.729,11.586C11.416,11.999 11.223,12.513 11.223,13.168H12.783C12.783,12.847 12.868,12.665 12.971,12.528C13.096,12.364 13.276,12.226 13.558,12.031C13.587,12.01 13.618,11.989 13.651,11.967C14.162,11.619 15.006,11.045 15.006,9.823C15.006,8.17 13.656,6.82 12.003,6.82C10.352,6.82 9,8.157 9,9.823H10.559Z" />
|
||||||
|
</vector>
|
|
@ -144,10 +144,11 @@ class GetDeviceFullInfoListUseCaseTest {
|
||||||
matrixClientInfo = matrixClientInfo3,
|
matrixClientInfo = matrixClientInfo3,
|
||||||
)
|
)
|
||||||
val expectedResult = listOf(expectedResult3, expectedResult2, expectedResult1)
|
val expectedResult = listOf(expectedResult3, expectedResult2, expectedResult1)
|
||||||
every { filterDevicesUseCase.execute(any(), any()) } returns expectedResult
|
every { filterDevicesUseCase.execute(any(), any(), any()) } returns expectedResult
|
||||||
|
val filterType = DeviceManagerFilterType.ALL_SESSIONS
|
||||||
|
|
||||||
// When
|
// When
|
||||||
val result = getDeviceFullInfoListUseCase.execute(DeviceManagerFilterType.ALL_SESSIONS, excludeCurrentDevice = false)
|
val result = getDeviceFullInfoListUseCase.execute(filterType, excludeCurrentDevice = false)
|
||||||
.test(this)
|
.test(this)
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
|
@ -166,6 +167,7 @@ class GetDeviceFullInfoListUseCaseTest {
|
||||||
getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_1)
|
getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_1)
|
||||||
getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_2)
|
getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_2)
|
||||||
getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_3)
|
getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_3)
|
||||||
|
filterDevicesUseCase.execute(currentSessionCrossSigningInfo, expectedResult, filterType, emptyList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import im.vector.app.core.session.clientinfo.MatrixClientInfoContent
|
||||||
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
||||||
import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo
|
import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo
|
||||||
import im.vector.app.features.settings.devices.v2.list.DeviceType
|
import im.vector.app.features.settings.devices.v2.list.DeviceType
|
||||||
|
import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
import org.amshove.kluent.shouldContainAll
|
import org.amshove.kluent.shouldContainAll
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -94,32 +95,58 @@ class FilterDevicesUseCaseTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given a device list when filter type is ALL_SESSIONS then returns the same list`() {
|
fun `given a device list when filter type is ALL_SESSIONS then returns the same list`() {
|
||||||
val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.ALL_SESSIONS, emptyList())
|
val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true)
|
||||||
|
val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.ALL_SESSIONS, emptyList())
|
||||||
|
|
||||||
filteredDeviceList.size shouldBeEqualTo devices.size
|
filteredDeviceList.size shouldBeEqualTo devices.size
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given a device list when filter type is VERIFIED then returns only verified devices`() {
|
fun `given a device list and current session is verified when filter type is VERIFIED then returns only verified devices`() {
|
||||||
val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.VERIFIED, emptyList())
|
val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true)
|
||||||
|
val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.VERIFIED, emptyList())
|
||||||
|
|
||||||
filteredDeviceList.size shouldBeEqualTo 2
|
filteredDeviceList.size shouldBeEqualTo 2
|
||||||
filteredDeviceList shouldContainAll listOf(activeVerifiedDevice, inactiveVerifiedDevice)
|
filteredDeviceList shouldContainAll listOf(activeVerifiedDevice, inactiveVerifiedDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given a device list when filter type is UNVERIFIED then returns only unverified devices`() {
|
fun `given a device list and current session is unverified when filter type is VERIFIED then returns empty list of devices`() {
|
||||||
val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.UNVERIFIED, emptyList())
|
val currentSessionCrossSigningInfo = givenCurrentSessionVerified(false)
|
||||||
|
val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.VERIFIED, emptyList())
|
||||||
|
|
||||||
|
filteredDeviceList.size shouldBeEqualTo 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a device list and current session is verified when filter type is UNVERIFIED then returns only unverified devices`() {
|
||||||
|
val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true)
|
||||||
|
val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.UNVERIFIED, emptyList())
|
||||||
|
|
||||||
filteredDeviceList.size shouldBeEqualTo 2
|
filteredDeviceList.size shouldBeEqualTo 2
|
||||||
filteredDeviceList shouldContainAll listOf(activeUnverifiedDevice, inactiveUnverifiedDevice)
|
filteredDeviceList shouldContainAll listOf(activeUnverifiedDevice, inactiveUnverifiedDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a device list and current session is unverified when filter type is UNVERIFIED then returns empty list of devices`() {
|
||||||
|
val currentSessionCrossSigningInfo = givenCurrentSessionVerified(false)
|
||||||
|
val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.UNVERIFIED, emptyList())
|
||||||
|
|
||||||
|
filteredDeviceList.size shouldBeEqualTo 0
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given a device list when filter type is INACTIVE then returns only inactive devices`() {
|
fun `given a device list when filter type is INACTIVE then returns only inactive devices`() {
|
||||||
val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.INACTIVE, emptyList())
|
val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true)
|
||||||
|
val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.INACTIVE, emptyList())
|
||||||
|
|
||||||
filteredDeviceList.size shouldBeEqualTo 2
|
filteredDeviceList.size shouldBeEqualTo 2
|
||||||
filteredDeviceList shouldContainAll listOf(inactiveVerifiedDevice, inactiveUnverifiedDevice)
|
filteredDeviceList shouldContainAll listOf(inactiveVerifiedDevice, inactiveUnverifiedDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun givenCurrentSessionVerified(isVerified: Boolean): CurrentSessionCrossSigningInfo = CurrentSessionCrossSigningInfo(
|
||||||
|
isCrossSigningVerified = isVerified,
|
||||||
|
isCrossSigningInitialized = true,
|
||||||
|
deviceId = ""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue