Compare commits
1 Commits
develop
...
feature/bm
Author | SHA1 | Date |
---|---|---|
Benoit Marty | 0d2fb8e3d0 |
|
@ -1 +0,0 @@
|
|||
[Session manager] Missing info when a session does not support encryption
|
|
@ -1 +0,0 @@
|
|||
"[Rich text editor] Add list formatting buttons to the rich text editor"
|
|
@ -101,7 +101,7 @@ ext.libs = [
|
|||
],
|
||||
element : [
|
||||
'opusencoder' : "io.element.android:opusencoder:1.1.0",
|
||||
'wysiwyg' : "io.element.android:wysiwyg:0.13.0"
|
||||
'wysiwyg' : "io.element.android:wysiwyg:0.10.0"
|
||||
],
|
||||
squareup : [
|
||||
'moshi' : "com.squareup.moshi:moshi:$moshi",
|
||||
|
|
|
@ -3415,7 +3415,7 @@
|
|||
<string name="labs_enable_session_manager_summary">Have greater visibility and control over all your sessions.</string>
|
||||
<string name="labs_enable_client_info_recording_title">Enable client info recording</string>
|
||||
<string name="labs_enable_client_info_recording_summary">Record the client name, version, and url to recognise sessions more easily in session manager.</string>
|
||||
<string name="labs_enable_voice_broadcast_title">Enable voice broadcast</string>
|
||||
<string name="labs_enable_voice_broadcast_title">Enable voice broadcast (under active development)</string>
|
||||
<string name="labs_enable_voice_broadcast_summary">Be able to record and send voice broadcast in room timeline.</string>
|
||||
|
||||
<!-- Note to translators: %s will be replaces with selected space name -->
|
||||
|
@ -3485,8 +3485,6 @@
|
|||
<string name="rich_text_editor_format_strikethrough">Apply strikethrough format</string>
|
||||
<string name="rich_text_editor_format_underline">Apply underline format</string>
|
||||
<string name="rich_text_editor_link">Set link</string>
|
||||
<string name="rich_text_editor_numbered_list">Toggle numbered list</string>
|
||||
<string name="rich_text_editor_bullet_list">Toggle bullet list</string>
|
||||
<string name="rich_text_editor_full_screen_toggle">Toggle full screen mode</string>
|
||||
|
||||
<string name="set_link_text">Text</string>
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.di
|
||||
|
||||
import androidx.annotation.Nullable
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonQualifier
|
||||
import com.squareup.moshi.Moshi
|
||||
|
@ -28,7 +27,6 @@ import java.lang.reflect.Type
|
|||
internal annotation class SerializeNulls {
|
||||
companion object {
|
||||
val JSON_ADAPTER_FACTORY: JsonAdapter.Factory = object : JsonAdapter.Factory {
|
||||
@Nullable
|
||||
override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? {
|
||||
val nextAnnotations = Types.nextAnnotations(annotations, SerializeNulls::class.java)
|
||||
?: return null
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.network.interceptors
|
||||
|
||||
import androidx.annotation.NonNull
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
|
@ -38,7 +37,7 @@ internal class FormattedJsonHttpLogger(
|
|||
* @param message
|
||||
*/
|
||||
@Synchronized
|
||||
override fun log(@NonNull message: String) {
|
||||
override fun log(message: String) {
|
||||
Timber.v(message)
|
||||
|
||||
// Try to log formatted Json only if there is a chance that [message] contains Json.
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.network.parsing
|
||||
|
||||
import androidx.annotation.Nullable
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonReader
|
||||
import com.squareup.moshi.JsonWriter
|
||||
|
@ -32,14 +31,12 @@ internal interface CheckNumberType {
|
|||
|
||||
companion object {
|
||||
val JSON_ADAPTER_FACTORY = object : JsonAdapter.Factory {
|
||||
@Nullable
|
||||
override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? {
|
||||
if (type !== Any::class.java) {
|
||||
return null
|
||||
}
|
||||
val delegate: JsonAdapter<Any> = moshi.nextAdapter(this, Any::class.java, emptySet())
|
||||
return object : JsonAdapter<Any?>() {
|
||||
@Nullable
|
||||
@Throws(IOException::class)
|
||||
override fun fromJson(reader: JsonReader): Any? {
|
||||
return if (reader.peek() !== JsonReader.Token.NUMBER) {
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
<issue id="UseValueOf" severity="error" />
|
||||
<issue id="ObsoleteSdkInt" severity="error" />
|
||||
<issue id="Recycle" severity="error" />
|
||||
<issue id="KotlinNullnessAnnotation" severity="error" />
|
||||
<issue id="KotlinPropertyAccess" severity="error" />
|
||||
<issue id="DefaultLocale" severity="error" />
|
||||
<issue id="CheckResult" severity="error" />
|
||||
|
|
|
@ -18,7 +18,6 @@ package im.vector.app.core.resources
|
|||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.ArrayRes
|
||||
import androidx.annotation.NonNull
|
||||
import javax.inject.Inject
|
||||
|
||||
class StringArrayProvider @Inject constructor(private val resources: Resources) {
|
||||
|
@ -31,7 +30,6 @@ class StringArrayProvider @Inject constructor(private val resources: Resources)
|
|||
* @return The string array associated with the resource, stripped of styled
|
||||
* text information.
|
||||
*/
|
||||
@NonNull
|
||||
fun getStringArray(@ArrayRes resId: Int): Array<String> {
|
||||
return resources.getStringArray(resId)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package im.vector.app.core.resources
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.NonNull
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.annotation.StringRes
|
||||
import javax.inject.Inject
|
||||
|
@ -32,7 +31,6 @@ class StringProvider @Inject constructor(private val resources: Resources) {
|
|||
* @return The string data associated with the resource, stripped of styled
|
||||
* text information.
|
||||
*/
|
||||
@NonNull
|
||||
fun getString(@StringRes resId: Int): String {
|
||||
return resources.getString(resId)
|
||||
}
|
||||
|
@ -48,12 +46,10 @@ class StringProvider @Inject constructor(private val resources: Resources) {
|
|||
* @return The string data associated with the resource, formatted and
|
||||
* stripped of styled text information.
|
||||
*/
|
||||
@NonNull
|
||||
fun getString(@StringRes resId: Int, vararg formatArgs: Any?): String {
|
||||
return resources.getString(resId, *formatArgs)
|
||||
}
|
||||
|
||||
@NonNull
|
||||
fun getQuantityString(@PluralsRes resId: Int, quantity: Int, vararg formatArgs: Any?): String {
|
||||
return resources.getQuantityString(resId, quantity, *formatArgs)
|
||||
}
|
||||
|
|
|
@ -240,12 +240,6 @@ internal class RichTextComposerLayout @JvmOverloads constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
addRichTextMenuItem(R.drawable.ic_composer_bullet_list, R.string.rich_text_editor_bullet_list, ComposerAction.UNORDERED_LIST) {
|
||||
views.richTextComposerEditText.toggleList(ordered = false)
|
||||
}
|
||||
addRichTextMenuItem(R.drawable.ic_composer_numbered_list, R.string.rich_text_editor_numbered_list, ComposerAction.ORDERED_LIST) {
|
||||
views.richTextComposerEditText.toggleList(ordered = true)
|
||||
}
|
||||
}
|
||||
|
||||
fun setLink(link: String?) =
|
||||
|
|
|
@ -142,7 +142,7 @@ class DevicesViewModel @AssistedInject constructor(
|
|||
.map { deviceInfo ->
|
||||
val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }
|
||||
val trustLevelForShield = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo)
|
||||
val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs)
|
||||
val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs ?: 0)
|
||||
DeviceFullInfo(deviceInfo, cryptoDeviceInfo, trustLevelForShield, isInactive)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.app.features.settings.devices.v2
|
|||
|
||||
import android.content.SharedPreferences
|
||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
@ -31,10 +32,10 @@ import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsReAuthN
|
|||
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase
|
||||
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
|
||||
import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -102,27 +103,27 @@ class DevicesViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun observeDevices() {
|
||||
val allSessionsFlow = getDeviceFullInfoListUseCase.execute(
|
||||
getDeviceFullInfoListUseCase.execute(
|
||||
filterType = DeviceManagerFilterType.ALL_SESSIONS,
|
||||
excludeCurrentDevice = false,
|
||||
)
|
||||
val unverifiedSessionsFlow = getDeviceFullInfoListUseCase.execute(
|
||||
filterType = DeviceManagerFilterType.UNVERIFIED,
|
||||
excludeCurrentDevice = true,
|
||||
)
|
||||
val inactiveSessionsFlow = getDeviceFullInfoListUseCase.execute(
|
||||
filterType = DeviceManagerFilterType.INACTIVE,
|
||||
excludeCurrentDevice = true,
|
||||
excludeCurrentDevice = false
|
||||
)
|
||||
.execute { async ->
|
||||
if (async is Success) {
|
||||
val deviceFullInfoList = async.invoke()
|
||||
val unverifiedSessionsCount = deviceFullInfoList.count { !it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse() }
|
||||
val inactiveSessionsCount = deviceFullInfoList.count { it.isInactive }
|
||||
|
||||
combine(allSessionsFlow, unverifiedSessionsFlow, inactiveSessionsFlow) { allSessions, unverifiedSessions, inactiveSessions ->
|
||||
DeviceFullInfoList(
|
||||
allSessions = allSessions,
|
||||
unverifiedSessionsCount = unverifiedSessions.size,
|
||||
inactiveSessionsCount = inactiveSessions.size,
|
||||
)
|
||||
}
|
||||
.execute { async -> copy(devices = async) }
|
||||
copy(
|
||||
devices = async,
|
||||
unverifiedSessionsCount = unverifiedSessionsCount,
|
||||
inactiveSessionsCount = inactiveSessionsCount,
|
||||
)
|
||||
} else {
|
||||
copy(
|
||||
devices = async
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshDevicesOnCryptoDevicesChange() {
|
||||
|
@ -184,7 +185,6 @@ class DevicesViewModel @AssistedInject constructor(
|
|||
private fun getDeviceIdsOfOtherSessions(state: DevicesViewState): List<String> {
|
||||
val currentDeviceId = state.currentSessionCrossSigningInfo.deviceId
|
||||
return state.devices()
|
||||
?.allSessions
|
||||
?.mapNotNull { fullInfo -> fullInfo.deviceInfo.deviceId.takeUnless { it == currentDeviceId } }
|
||||
.orEmpty()
|
||||
}
|
||||
|
|
|
@ -23,13 +23,9 @@ import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCro
|
|||
|
||||
data class DevicesViewState(
|
||||
val currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo = CurrentSessionCrossSigningInfo(),
|
||||
val devices: Async<DeviceFullInfoList> = Uninitialized,
|
||||
val devices: Async<List<DeviceFullInfo>> = Uninitialized,
|
||||
val unverifiedSessionsCount: Int = 0,
|
||||
val inactiveSessionsCount: Int = 0,
|
||||
val isLoading: Boolean = false,
|
||||
val isShowingIpAddress: Boolean = false,
|
||||
) : MavericksState
|
||||
|
||||
data class DeviceFullInfoList(
|
||||
val allSessions: List<DeviceFullInfo>,
|
||||
val unverifiedSessionsCount: Int,
|
||||
val inactiveSessionsCount: Int,
|
||||
)
|
||||
|
|
|
@ -75,7 +75,7 @@ class GetDeviceFullInfoListUseCase @Inject constructor(
|
|||
.map { deviceInfo ->
|
||||
val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }
|
||||
val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo)
|
||||
val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs)
|
||||
val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs ?: 0)
|
||||
val isCurrentDevice = currentSessionCrossSigningInfo.deviceId == cryptoDeviceInfo?.deviceId
|
||||
val deviceExtendedInfo = parseDeviceUserAgentUseCase.execute(deviceInfo.getBestLastSeenUserAgent())
|
||||
val matrixClientInfo = deviceInfo.deviceId
|
||||
|
|
|
@ -55,6 +55,7 @@ import im.vector.app.features.settings.devices.v2.signout.BuildConfirmSignoutDia
|
|||
import im.vector.app.features.workers.signout.SignOutUiWorker
|
||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
@ -281,15 +282,13 @@ class VectorSettingsDevicesFragment :
|
|||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
if (state.devices is Success) {
|
||||
val deviceFullInfoList = state.devices()
|
||||
val devices = deviceFullInfoList?.allSessions
|
||||
val devices = state.devices()
|
||||
val currentDeviceId = state.currentSessionCrossSigningInfo.deviceId
|
||||
val currentDeviceInfo = devices?.firstOrNull { it.deviceInfo.deviceId == currentDeviceId }
|
||||
val isCurrentSessionVerified = currentDeviceInfo?.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted
|
||||
val otherDevices = devices?.filter { it.deviceInfo.deviceId != currentDeviceId }
|
||||
val inactiveSessionsCount = deviceFullInfoList?.inactiveSessionsCount ?: 0
|
||||
val unverifiedSessionsCount = deviceFullInfoList?.unverifiedSessionsCount ?: 0
|
||||
|
||||
renderSecurityRecommendations(inactiveSessionsCount, unverifiedSessionsCount)
|
||||
renderSecurityRecommendations(state.inactiveSessionsCount, state.unverifiedSessionsCount, isCurrentSessionVerified)
|
||||
renderCurrentSessionView(currentDeviceInfo, hasOtherDevices = otherDevices?.isNotEmpty().orFalse())
|
||||
renderOtherSessionsView(otherDevices, state.isShowingIpAddress)
|
||||
} else {
|
||||
|
@ -304,8 +303,9 @@ class VectorSettingsDevicesFragment :
|
|||
private fun renderSecurityRecommendations(
|
||||
inactiveSessionsCount: Int,
|
||||
unverifiedSessionsCount: Int,
|
||||
isCurrentSessionVerified: Boolean,
|
||||
) {
|
||||
val isUnverifiedSectionVisible = unverifiedSessionsCount > 0
|
||||
val isUnverifiedSectionVisible = unverifiedSessionsCount > 0 && isCurrentSessionVerified
|
||||
val isInactiveSectionVisible = inactiveSessionsCount > 0
|
||||
if (isUnverifiedSectionVisible.not() && isInactiveSectionVisible.not()) {
|
||||
hideSecurityRecommendations()
|
||||
|
|
|
@ -37,9 +37,7 @@ class FilterDevicesUseCase @Inject constructor() {
|
|||
// when current session is not verified, other session status cannot be trusted
|
||||
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()) ||
|
||||
it.cryptoDeviceInfo == null
|
||||
DeviceManagerFilterType.UNVERIFIED -> isCurrentSessionVerified && !it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse()
|
||||
DeviceManagerFilterType.INACTIVE -> it.isInactive
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,13 +24,11 @@ class CheckIfSessionIsInactiveUseCase @Inject constructor(
|
|||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
fun execute(lastSeenTsMillis: Long?): Boolean {
|
||||
return if (lastSeenTsMillis == null || lastSeenTsMillis <= 0) {
|
||||
// in these situations we cannot say anything about the inactivity of the session
|
||||
false
|
||||
} else {
|
||||
val diffMilliseconds = clock.epochMillis() - lastSeenTsMillis
|
||||
diffMilliseconds >= TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong())
|
||||
}
|
||||
fun execute(lastSeenTs: Long): Boolean {
|
||||
// In case of the server doesn't send the last seen date.
|
||||
if (lastSeenTs == 0L) return true
|
||||
|
||||
val diffMilliseconds = clock.epochMillis() - lastSeenTs
|
||||
return diffMilliseconds >= TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,13 +63,12 @@ class OtherSessionsController @Inject constructor(
|
|||
}
|
||||
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
|
||||
val sessionName = device.deviceInfo.displayName ?: device.deviceInfo.deviceId
|
||||
|
||||
otherSessionItem {
|
||||
id(device.deviceInfo.deviceId)
|
||||
deviceType(device.deviceExtendedInfo.deviceType)
|
||||
roomEncryptionTrustLevel(device.roomEncryptionTrustLevel)
|
||||
sessionName(sessionName)
|
||||
sessionName(device.deviceInfo.displayName)
|
||||
sessionDescription(description)
|
||||
sessionDescriptionDrawable(descriptionDrawable)
|
||||
sessionDescriptionColor(descriptionColor)
|
||||
|
|
|
@ -62,10 +62,9 @@ class SessionInfoView @JvmOverloads constructor(
|
|||
stringProvider: StringProvider,
|
||||
) {
|
||||
renderDeviceInfo(
|
||||
sessionName = sessionInfoViewState.deviceFullInfo.deviceInfo.displayName
|
||||
?: sessionInfoViewState.deviceFullInfo.deviceInfo.deviceId.orEmpty(),
|
||||
deviceType = sessionInfoViewState.deviceFullInfo.deviceExtendedInfo.deviceType,
|
||||
stringProvider = stringProvider,
|
||||
sessionInfoViewState.deviceFullInfo.deviceInfo.displayName.orEmpty(),
|
||||
sessionInfoViewState.deviceFullInfo.deviceExtendedInfo.deviceType,
|
||||
stringProvider,
|
||||
)
|
||||
renderVerificationStatus(
|
||||
sessionInfoViewState.deviceFullInfo.roomEncryptionTrustLevel,
|
||||
|
|
|
@ -49,10 +49,10 @@ class GetDeviceFullInfoUseCase @Inject constructor(
|
|||
) { currentSessionCrossSigningInfo, deviceInfo, cryptoDeviceInfo ->
|
||||
val info = deviceInfo.getOrNull()
|
||||
val cryptoInfo = cryptoDeviceInfo.getOrNull()
|
||||
val fullInfo = if (info != null) {
|
||||
val fullInfo = if (info != null && cryptoInfo != null) {
|
||||
val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoInfo)
|
||||
val isInactive = checkIfSessionIsInactiveUseCase.execute(info.lastSeenTs)
|
||||
val isCurrentDevice = currentSessionCrossSigningInfo.deviceId == info.deviceId
|
||||
val isInactive = checkIfSessionIsInactiveUseCase.execute(info.lastSeenTs ?: 0)
|
||||
val isCurrentDevice = currentSessionCrossSigningInfo.deviceId == cryptoInfo.deviceId
|
||||
val deviceUserAgent = parseDeviceUserAgentUseCase.execute(info.getBestLastSeenUserAgent())
|
||||
val matrixClientInfo = info.deviceId
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="44dp"
|
||||
android:height="44dp"
|
||||
android:viewportWidth="44"
|
||||
android:viewportHeight="44">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M10,10h24v24h-24z"/>
|
||||
<path
|
||||
android:pathData="M14,20.5C13.17,20.5 12.5,21.17 12.5,22C12.5,22.83 13.17,23.5 14,23.5C14.83,23.5 15.5,22.83 15.5,22C15.5,21.17 14.83,20.5 14,20.5ZM14,14.5C13.17,14.5 12.5,15.17 12.5,16C12.5,16.83 13.17,17.5 14,17.5C14.83,17.5 15.5,16.83 15.5,16C15.5,15.17 14.83,14.5 14,14.5ZM14,26.5C13.17,26.5 12.5,27.18 12.5,28C12.5,28.82 13.18,29.5 14,29.5C14.82,29.5 15.5,28.82 15.5,28C15.5,27.18 14.83,26.5 14,26.5ZM18,29H30C30.55,29 31,28.55 31,28C31,27.45 30.55,27 30,27H18C17.45,27 17,27.45 17,28C17,28.55 17.45,29 18,29ZM18,23H30C30.55,23 31,22.55 31,22C31,21.45 30.55,21 30,21H18C17.45,21 17,21.45 17,22C17,22.55 17.45,23 18,23ZM17,16C17,16.55 17.45,17 18,17H30C30.55,17 31,16.55 31,16C31,15.45 30.55,15 30,15H18C17.45,15 17,15.45 17,16Z"
|
||||
android:fillColor="#8D97A5"/>
|
||||
</group>
|
||||
</vector>
|
|
@ -1,24 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="44dp"
|
||||
android:height="44dp"
|
||||
android:viewportWidth="44"
|
||||
android:viewportHeight="44">
|
||||
<path
|
||||
android:pathData="m14.5,20h-2c-0.28,0 -0.5,0.22 -0.5,0.5 0,0.28 0.22,0.5 0.5,0.5h1.3l-1.68,1.96C12.04,23.05 12,23.17 12,23.28v0.22c0,0.28 0.22,0.5 0.5,0.5h2C14.78,24 15,23.78 15,23.5 15,23.22 14.78,23 14.5,23h-1.3l1.68,-1.96C14.96,20.95 15,20.83 15,20.72V20.5C15,20.22 14.78,20 14.5,20Z"
|
||||
android:fillColor="#8d97a5"/>
|
||||
<path
|
||||
android:pathData="M12.5,15H13v2.5c0,0.28 0.22,0.5 0.5,0.5 0.28,0 0.5,-0.22 0.5,-0.5v-3C14,14.22 13.78,14 13.5,14h-1c-0.28,0 -0.5,0.22 -0.5,0.5 0,0.28 0.22,0.5 0.5,0.5z"
|
||||
android:fillColor="#8d97a5"/>
|
||||
<path
|
||||
android:pathData="m14.5,26h-2c-0.28,0 -0.5,0.22 -0.5,0.5 0,0.28 0.22,0.5 0.5,0.5H14v0.5h-0.5c-0.28,0 -0.5,0.22 -0.5,0.5 0,0.28 0.22,0.5 0.5,0.5H14V29h-1.5c-0.28,0 -0.5,0.22 -0.5,0.5 0,0.28 0.22,0.5 0.5,0.5h2c0.28,0 0.5,-0.22 0.5,-0.5v-3C15,26.22 14.78,26 14.5,26Z"
|
||||
android:fillColor="#8d97a5"/>
|
||||
<path
|
||||
android:pathData="M30,21H18c-0.55,0 -1,0.45 -1,1 0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1 0,-0.55 -0.45,-1 -1,-1z"
|
||||
android:fillColor="#8d97a5"/>
|
||||
<path
|
||||
android:pathData="M30,27H18c-0.55,0 -1,0.45 -1,1 0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1 0,-0.55 -0.45,-1 -1,-1z"
|
||||
android:fillColor="#8d97a5"/>
|
||||
<path
|
||||
android:pathData="m18,17h12c0.55,0 1,-0.45 1,-1 0,-0.55 -0.45,-1 -1,-1H18c-0.55,0 -1,0.45 -1,1 0,0.55 0.45,1 1,1z"
|
||||
android:fillColor="#8d97a5"/>
|
||||
</vector>
|
|
@ -180,8 +180,6 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="52dp"
|
||||
android:requiresFadingEdge="horizontal"
|
||||
android:fadingEdgeLength="28dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/composerEditTextOuterBorder"
|
||||
app:layout_constraintStart_toEndOf="@id/attachmentButton"
|
||||
app:layout_constraintEnd_toStartOf="@id/sendButton"
|
||||
|
|
|
@ -21,7 +21,6 @@ import com.airbnb.mvrx.Success
|
|||
import com.airbnb.mvrx.test.MavericksTestRule
|
||||
import im.vector.app.core.session.clientinfo.MatrixClientInfoContent
|
||||
import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo
|
||||
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
|
||||
import im.vector.app.features.settings.devices.v2.list.DeviceType
|
||||
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
|
||||
import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
|
||||
|
@ -177,7 +176,10 @@ class DevicesViewModelTest {
|
|||
val viewModelTest = createViewModel().test()
|
||||
|
||||
// Then
|
||||
viewModelTest.assertLatestState { it.devices is Success && it.devices.invoke() == deviceFullInfoList }
|
||||
viewModelTest.assertLatestState {
|
||||
it.devices is Success && it.devices.invoke() == deviceFullInfoList &&
|
||||
it.inactiveSessionsCount == 1 && it.unverifiedSessionsCount == 1
|
||||
}
|
||||
viewModelTest.finish()
|
||||
}
|
||||
|
||||
|
@ -401,7 +403,7 @@ class DevicesViewModelTest {
|
|||
/**
|
||||
* Generate mocked deviceFullInfo list with 1 unverified and inactive + 1 verified and active.
|
||||
*/
|
||||
private fun givenDeviceFullInfoList(deviceId1: String, deviceId2: String): DeviceFullInfoList {
|
||||
private fun givenDeviceFullInfoList(deviceId1: String, deviceId2: String): List<DeviceFullInfo> {
|
||||
val verifiedCryptoDeviceInfo = mockk<CryptoDeviceInfo>()
|
||||
every { verifiedCryptoDeviceInfo.trustLevel } returns DeviceTrustLevel(crossSigningVerified = true, locallyVerified = true)
|
||||
val unverifiedCryptoDeviceInfo = mockk<CryptoDeviceInfo>()
|
||||
|
@ -430,15 +432,10 @@ class DevicesViewModelTest {
|
|||
deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
|
||||
matrixClientInfo = MatrixClientInfoContent(),
|
||||
)
|
||||
val devices = listOf(deviceFullInfo1, deviceFullInfo2)
|
||||
every { getDeviceFullInfoListUseCase.execute(DeviceManagerFilterType.ALL_SESSIONS, any()) } returns flowOf(devices)
|
||||
every { getDeviceFullInfoListUseCase.execute(DeviceManagerFilterType.UNVERIFIED, any()) } returns flowOf(listOf(deviceFullInfo2))
|
||||
every { getDeviceFullInfoListUseCase.execute(DeviceManagerFilterType.INACTIVE, any()) } returns flowOf(listOf(deviceFullInfo1))
|
||||
return DeviceFullInfoList(
|
||||
allSessions = devices,
|
||||
unverifiedSessionsCount = 1,
|
||||
inactiveSessionsCount = 1,
|
||||
)
|
||||
val deviceFullInfoList = listOf(deviceFullInfo1, deviceFullInfo2)
|
||||
val deviceFullInfoListFlow = flowOf(deviceFullInfoList)
|
||||
every { getDeviceFullInfoListUseCase.execute(any(), any()) } returns deviceFullInfoListFlow
|
||||
return deviceFullInfoList
|
||||
}
|
||||
|
||||
private fun givenInitialViewState(deviceId1: String, deviceId2: String): DevicesViewState {
|
||||
|
@ -447,6 +444,8 @@ class DevicesViewModelTest {
|
|||
return DevicesViewState(
|
||||
currentSessionCrossSigningInfo = currentSessionCrossSigningInfo,
|
||||
devices = Success(deviceFullInfoList),
|
||||
unverifiedSessionsCount = 1,
|
||||
inactiveSessionsCount = 1,
|
||||
isLoading = false,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtende
|
|||
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.shouldContain
|
||||
import org.amshove.kluent.shouldContainAll
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
||||
|
@ -83,22 +82,11 @@ private val inactiveUnverifiedDevice = DeviceFullInfo(
|
|||
matrixClientInfo = MatrixClientInfoContent(),
|
||||
)
|
||||
|
||||
private val deviceWithoutEncryptionSupport = DeviceFullInfo(
|
||||
deviceInfo = DeviceInfo(deviceId = "DEVICE_WITHOUT_ENCRYPTION_SUPPORT"),
|
||||
cryptoDeviceInfo = null,
|
||||
roomEncryptionTrustLevel = null,
|
||||
isInactive = false,
|
||||
isCurrentDevice = false,
|
||||
deviceExtendedInfo = DeviceExtendedInfo(DeviceType.UNKNOWN),
|
||||
matrixClientInfo = MatrixClientInfoContent(),
|
||||
)
|
||||
|
||||
private val devices = listOf(
|
||||
activeVerifiedDevice,
|
||||
inactiveVerifiedDevice,
|
||||
activeUnverifiedDevice,
|
||||
inactiveUnverifiedDevice,
|
||||
deviceWithoutEncryptionSupport,
|
||||
)
|
||||
|
||||
class FilterDevicesUseCaseTest {
|
||||
|
@ -135,8 +123,8 @@ class FilterDevicesUseCaseTest {
|
|||
val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true)
|
||||
val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.UNVERIFIED, emptyList())
|
||||
|
||||
filteredDeviceList.size shouldBeEqualTo 3
|
||||
filteredDeviceList shouldContainAll listOf(activeUnverifiedDevice, inactiveUnverifiedDevice, deviceWithoutEncryptionSupport)
|
||||
filteredDeviceList.size shouldBeEqualTo 2
|
||||
filteredDeviceList shouldContainAll listOf(activeUnverifiedDevice, inactiveUnverifiedDevice)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -144,8 +132,7 @@ class FilterDevicesUseCaseTest {
|
|||
val currentSessionCrossSigningInfo = givenCurrentSessionVerified(false)
|
||||
val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.UNVERIFIED, emptyList())
|
||||
|
||||
filteredDeviceList.size shouldBeEqualTo 1
|
||||
filteredDeviceList shouldContain deviceWithoutEncryptionSupport
|
||||
filteredDeviceList.size shouldBeEqualTo 0
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -17,69 +17,43 @@
|
|||
package im.vector.app.features.settings.devices.v2.list
|
||||
|
||||
import im.vector.app.test.fakes.FakeClock
|
||||
import org.amshove.kluent.shouldBeFalse
|
||||
import org.amshove.kluent.shouldBeTrue
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private const val A_TIMESTAMP_MILLIS = 1654689143000L
|
||||
private const val A_TIMESTAMP = 1654689143L
|
||||
|
||||
class CheckIfSessionIsInactiveUseCaseTest {
|
||||
|
||||
private val clock = FakeClock().apply { givenEpoch(A_TIMESTAMP_MILLIS) }
|
||||
private val clock = FakeClock().apply { givenEpoch(A_TIMESTAMP) }
|
||||
private val checkIfSessionIsInactiveUseCase = CheckIfSessionIsInactiveUseCase(clock)
|
||||
|
||||
@Test
|
||||
fun `given an old last seen date then session is inactive`() {
|
||||
val lastSeenDate = A_TIMESTAMP_MILLIS - TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong()) - 1
|
||||
val lastSeenDate = A_TIMESTAMP - TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong()) - 1
|
||||
|
||||
val result = checkIfSessionIsInactiveUseCase.execute(lastSeenDate)
|
||||
|
||||
result.shouldBeTrue()
|
||||
checkIfSessionIsInactiveUseCase.execute(lastSeenDate) shouldBeEqualTo true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a last seen date equal to the threshold then session is inactive`() {
|
||||
val lastSeenDate = A_TIMESTAMP_MILLIS - TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong())
|
||||
val lastSeenDate = A_TIMESTAMP - TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong())
|
||||
|
||||
val result = checkIfSessionIsInactiveUseCase.execute(lastSeenDate)
|
||||
|
||||
result.shouldBeTrue()
|
||||
checkIfSessionIsInactiveUseCase.execute(lastSeenDate) shouldBeEqualTo true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a recent last seen date then session is active`() {
|
||||
val lastSeenDate = A_TIMESTAMP_MILLIS - TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong()) + 1
|
||||
val lastSeenDate = A_TIMESTAMP - TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong()) + 1
|
||||
|
||||
val result = checkIfSessionIsInactiveUseCase.execute(lastSeenDate)
|
||||
|
||||
result.shouldBeFalse()
|
||||
checkIfSessionIsInactiveUseCase.execute(lastSeenDate) shouldBeEqualTo false
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a last seen date as zero then session is not inactive`() {
|
||||
fun `given a last seen date as zero then session is inactive`() {
|
||||
// In case of the server doesn't send the last seen date.
|
||||
val lastSeenDate = 0L
|
||||
|
||||
val result = checkIfSessionIsInactiveUseCase.execute(lastSeenDate)
|
||||
|
||||
result.shouldBeFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a last seen date as null then session is not inactive`() {
|
||||
val lastSeenDate = null
|
||||
|
||||
val result = checkIfSessionIsInactiveUseCase.execute(lastSeenDate)
|
||||
|
||||
result.shouldBeFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a last seen date as negative then session is not inactive`() {
|
||||
val lastSeenDate = -3L
|
||||
|
||||
val result = checkIfSessionIsInactiveUseCase.execute(lastSeenDate)
|
||||
|
||||
result.shouldBeFalse()
|
||||
checkIfSessionIsInactiveUseCase.execute(lastSeenDate) shouldBeEqualTo true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,45 +117,6 @@ class GetDeviceFullInfoUseCaseTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given current session and no crypto info for device when getting device info then the result is correct`() = runTest {
|
||||
// Given
|
||||
val currentSessionCrossSigningInfo = givenCurrentSessionCrossSigningInfo()
|
||||
val deviceInfo = givenADeviceInfo()
|
||||
val cryptoDeviceInfo = null
|
||||
val trustLevel = givenTrustLevel(currentSessionCrossSigningInfo, cryptoDeviceInfo)
|
||||
val isInactive = false
|
||||
val isCurrentDevice = true
|
||||
every { checkIfSessionIsInactiveUseCase.execute(any()) } returns isInactive
|
||||
every { parseDeviceUserAgentUseCase.execute(any()) } returns DeviceExtendedInfo(DeviceType.MOBILE)
|
||||
val matrixClientInfo = givenAMatrixClientInfo()
|
||||
fakeActiveSessionHolder.fakeSession.fakeCryptoService.cryptoDeviceInfoWithIdLiveData = MutableLiveData(Optional(null))
|
||||
fakeActiveSessionHolder.fakeSession.fakeCryptoService.cryptoDeviceInfoWithIdLiveData.givenAsFlow()
|
||||
|
||||
// When
|
||||
val deviceFullInfo = getDeviceFullInfoUseCase.execute(A_DEVICE_ID).firstOrNull()
|
||||
|
||||
// Then
|
||||
deviceFullInfo shouldBeEqualTo DeviceFullInfo(
|
||||
deviceInfo = deviceInfo,
|
||||
cryptoDeviceInfo = cryptoDeviceInfo,
|
||||
roomEncryptionTrustLevel = trustLevel,
|
||||
isInactive = isInactive,
|
||||
isCurrentDevice = isCurrentDevice,
|
||||
deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
|
||||
matrixClientInfo = matrixClientInfo,
|
||||
)
|
||||
verify {
|
||||
fakeActiveSessionHolder.instance.getSafeActiveSession()
|
||||
getCurrentSessionCrossSigningInfoUseCase.execute()
|
||||
getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo)
|
||||
fakeActiveSessionHolder.fakeSession.fakeCryptoService.getMyDevicesInfoLive(A_DEVICE_ID).asFlow()
|
||||
fakeActiveSessionHolder.fakeSession.fakeCryptoService.getLiveCryptoDeviceInfoWithId(A_DEVICE_ID).asFlow()
|
||||
checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP)
|
||||
getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given current session and no info for device when getting device info then the result is empty`() = runTest {
|
||||
// Given
|
||||
|
@ -170,11 +131,9 @@ class GetDeviceFullInfoUseCaseTest {
|
|||
|
||||
// Then
|
||||
deviceFullInfo.shouldBeNull()
|
||||
verify {
|
||||
fakeActiveSessionHolder.instance.getSafeActiveSession()
|
||||
fakeActiveSessionHolder.fakeSession.fakeCryptoService.getMyDevicesInfoLive(A_DEVICE_ID).asFlow()
|
||||
fakeActiveSessionHolder.fakeSession.fakeCryptoService.getLiveCryptoDeviceInfoWithId(A_DEVICE_ID).asFlow()
|
||||
}
|
||||
verify { fakeActiveSessionHolder.instance.getSafeActiveSession() }
|
||||
verify { fakeActiveSessionHolder.fakeSession.fakeCryptoService.getMyDevicesInfoLive(A_DEVICE_ID).asFlow() }
|
||||
verify { fakeActiveSessionHolder.fakeSession.fakeCryptoService.getLiveCryptoDeviceInfoWithId(A_DEVICE_ID).asFlow() }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue