diff --git a/changelog.d/7277.wip b/changelog.d/7277.wip new file mode 100644 index 0000000000..168d10b809 --- /dev/null +++ b/changelog.d/7277.wip @@ -0,0 +1 @@ +[Device Management] Show correct device type icons diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index e97cf437c6..1b52b79746 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -657,14 +657,7 @@ internal class RealmCryptoStore @Inject constructor( } override fun saveMyDevicesInfo(info: List) { - val entities = info.map { - MyDeviceLastSeenInfoEntity( - lastSeenTs = it.lastSeenTs, - lastSeenIp = it.lastSeenIp, - displayName = it.displayName, - deviceId = it.deviceId - ) - } + val entities = info.map { myDeviceLastSeenInfoEntityMapper.map(it) } doRealmTransactionAsync(realmConfiguration) { realm -> realm.where().findAll().deleteAllFromRealm() entities.forEach { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt index de2b74308d..9129453c8a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -36,6 +36,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo017 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo018 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo019 +import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo020 import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import org.matrix.android.sdk.internal.util.time.Clock import javax.inject.Inject @@ -50,7 +51,7 @@ internal class RealmCryptoStoreMigration @Inject constructor( private val clock: Clock, ) : MatrixRealmMigration( dbName = "Crypto", - schemaVersion = 19L, + schemaVersion = 20L, ) { /** * Forces all RealmCryptoStoreMigration instances to be equal. @@ -79,5 +80,6 @@ internal class RealmCryptoStoreMigration @Inject constructor( if (oldVersion < 17) MigrateCryptoTo017(realm).perform() if (oldVersion < 18) MigrateCryptoTo018(realm).perform() if (oldVersion < 19) MigrateCryptoTo019(realm).perform() + if (oldVersion < 20) MigrateCryptoTo020(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/MyDeviceLastSeenInfoEntityMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/MyDeviceLastSeenInfoEntityMapper.kt index 38a7569aab..b81883fb38 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/MyDeviceLastSeenInfoEntityMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/MyDeviceLastSeenInfoEntityMapper.kt @@ -27,7 +27,18 @@ internal class MyDeviceLastSeenInfoEntityMapper @Inject constructor() { deviceId = entity.deviceId, lastSeenIp = entity.lastSeenIp, lastSeenTs = entity.lastSeenTs, - displayName = entity.displayName + displayName = entity.displayName, + unstableLastSeenUserAgent = entity.lastSeenUserAgent, + ) + } + + fun map(deviceInfo: DeviceInfo): MyDeviceLastSeenInfoEntity { + return MyDeviceLastSeenInfoEntity( + deviceId = deviceInfo.deviceId, + lastSeenIp = deviceInfo.lastSeenIp, + lastSeenTs = deviceInfo.lastSeenTs, + displayName = deviceInfo.displayName, + lastSeenUserAgent = deviceInfo.getBestLastSeenUserAgent(), ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo019.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo019.kt index 9d2eb60a60..65280300ab 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo019.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo019.kt @@ -30,7 +30,7 @@ import org.matrix.android.sdk.internal.util.database.RealmMigrator * mark existing keys as safe. * This migration can take long depending on the account */ -internal class MigrateCryptoTo019(realm: DynamicRealm) : RealmMigrator(realm, 18) { +internal class MigrateCryptoTo019(realm: DynamicRealm) : RealmMigrator(realm, 19) { override fun doMigrate(realm: DynamicRealm) { realm.schema.get("CrossSigningInfoEntity") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo020.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo020.kt new file mode 100644 index 0000000000..44d07ab538 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo020.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.crypto.store.db.migration + +import io.realm.DynamicRealm +import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntityFields +import org.matrix.android.sdk.internal.util.database.RealmMigrator + +/** + * This migration adds a new field into MyDeviceLastSeenInfoEntity corresponding to the last seen user agent. + */ +internal class MigrateCryptoTo020(realm: DynamicRealm) : RealmMigrator(realm, 20) { + + override fun doMigrate(realm: DynamicRealm) { + realm.schema.get("MyDeviceLastSeenInfoEntity") + ?.addField(MyDeviceLastSeenInfoEntityFields.LAST_SEEN_USER_AGENT, String::class.java) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/MyDeviceLastSeenInfoEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/MyDeviceLastSeenInfoEntity.kt index 74a81d5b01..3e6dc2de16 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/MyDeviceLastSeenInfoEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/MyDeviceLastSeenInfoEntity.kt @@ -27,7 +27,9 @@ internal open class MyDeviceLastSeenInfoEntity( /** The last time this device has been seen. */ var lastSeenTs: Long? = null, /** The last ip address. */ - var lastSeenIp: String? = null + var lastSeenIp: String? = null, + /** The last user agent. */ + var lastSeenUserAgent: String? = null, ) : RealmObject() { companion object diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/MyDeviceLastSeenInfoEntityMapperTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/MyDeviceLastSeenInfoEntityMapperTest.kt index a27f430edc..8515427e8e 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/MyDeviceLastSeenInfoEntityMapperTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/MyDeviceLastSeenInfoEntityMapperTest.kt @@ -25,6 +25,7 @@ private const val A_DEVICE_ID = "device-id" private const val AN_IP_ADDRESS = "ip-address" private const val A_TIMESTAMP = 123L private const val A_DISPLAY_NAME = "display-name" +private const val A_USER_AGENT = "user-agent" class MyDeviceLastSeenInfoEntityMapperTest { @@ -32,21 +33,55 @@ class MyDeviceLastSeenInfoEntityMapperTest { @Test fun `given an entity when mapping to model then all fields are correctly mapped`() { + // Given val entity = MyDeviceLastSeenInfoEntity( deviceId = A_DEVICE_ID, lastSeenIp = AN_IP_ADDRESS, lastSeenTs = A_TIMESTAMP, - displayName = A_DISPLAY_NAME + displayName = A_DISPLAY_NAME, + lastSeenUserAgent = A_USER_AGENT, ) val expectedDeviceInfo = DeviceInfo( deviceId = A_DEVICE_ID, lastSeenIp = AN_IP_ADDRESS, lastSeenTs = A_TIMESTAMP, - displayName = A_DISPLAY_NAME + displayName = A_DISPLAY_NAME, + unstableLastSeenUserAgent = A_USER_AGENT, ) + // When val deviceInfo = myDeviceLastSeenInfoEntityMapper.map(entity) + // Then deviceInfo shouldBeEqualTo expectedDeviceInfo } + + @Test + fun `given a device info when mapping to entity then all fields are correctly mapped`() { + // Given + val deviceInfo = DeviceInfo( + deviceId = A_DEVICE_ID, + lastSeenIp = AN_IP_ADDRESS, + lastSeenTs = A_TIMESTAMP, + displayName = A_DISPLAY_NAME, + unstableLastSeenUserAgent = A_USER_AGENT, + ) + val expectedEntity = MyDeviceLastSeenInfoEntity( + deviceId = A_DEVICE_ID, + lastSeenIp = AN_IP_ADDRESS, + lastSeenTs = A_TIMESTAMP, + displayName = A_DISPLAY_NAME, + lastSeenUserAgent = A_USER_AGENT + ) + + // When + val entity = myDeviceLastSeenInfoEntityMapper.map(deviceInfo) + + // Then + entity.deviceId shouldBeEqualTo expectedEntity.deviceId + entity.lastSeenIp shouldBeEqualTo expectedEntity.lastSeenIp + entity.lastSeenTs shouldBeEqualTo expectedEntity.lastSeenTs + entity.displayName shouldBeEqualTo expectedEntity.displayName + entity.lastSeenUserAgent shouldBeEqualTo expectedEntity.lastSeenUserAgent + } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index 0fdbd40178..47ea96c09d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -33,6 +33,7 @@ import im.vector.app.core.dialogs.ManuallyVerifyDialog import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.DrawableProvider +import im.vector.app.core.resources.StringProvider import im.vector.app.databinding.FragmentSettingsDevicesBinding import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.verification.VerificationBottomSheet @@ -61,6 +62,8 @@ class VectorSettingsDevicesFragment : @Inject lateinit var colorProvider: ColorProvider + @Inject lateinit var stringProvider: StringProvider + private val viewModel: DevicesViewModel by fragmentViewModel() override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSettingsDevicesBinding { @@ -237,7 +240,7 @@ class VectorSettingsDevicesFragment : isCurrentSession = true, deviceFullInfo = it ) - views.deviceListCurrentSession.render(viewState, dateFormatter, drawableProvider, colorProvider) + views.deviceListCurrentSession.render(viewState, dateFormatter, drawableProvider, colorProvider, stringProvider) views.deviceListCurrentSession.debouncedClicks { currentDeviceInfo.deviceInfo.deviceId?.let { deviceId -> navigateToSessionOverview(deviceId) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt index 283e64fffe..f83f069a9f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt @@ -59,6 +59,8 @@ abstract class OtherSessionItem : VectorEpoxyModel(R.la @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var clickListener: ClickListener? = null + private val setDeviceTypeIconUseCase = SetDeviceTypeIconUseCase() + override fun bind(holder: Holder) { super.bind(holder) holder.view.onClick(clickListener) @@ -66,24 +68,7 @@ abstract class OtherSessionItem : VectorEpoxyModel(R.la holder.view.isClickable = false } - when (deviceType) { - DeviceType.MOBILE -> { - holder.otherSessionDeviceTypeImageView.setImageResource(R.drawable.ic_device_type_mobile) - holder.otherSessionDeviceTypeImageView.contentDescription = stringProvider.getString(R.string.a11y_device_manager_device_type_mobile) - } - DeviceType.WEB -> { - holder.otherSessionDeviceTypeImageView.setImageResource(R.drawable.ic_device_type_web) - holder.otherSessionDeviceTypeImageView.contentDescription = stringProvider.getString(R.string.a11y_device_manager_device_type_web) - } - DeviceType.DESKTOP -> { - holder.otherSessionDeviceTypeImageView.setImageResource(R.drawable.ic_device_type_desktop) - holder.otherSessionDeviceTypeImageView.contentDescription = stringProvider.getString(R.string.a11y_device_manager_device_type_desktop) - } - DeviceType.UNKNOWN -> { - holder.otherSessionDeviceTypeImageView.setImageResource(R.drawable.ic_device_type_unknown) - holder.otherSessionDeviceTypeImageView.contentDescription = stringProvider.getString(R.string.a11y_device_manager_device_type_unknown) - } - } + setDeviceTypeIconUseCase.execute(deviceType, holder.otherSessionDeviceTypeImageView, stringProvider) holder.otherSessionVerificationStatusImageView.render(roomEncryptionTrustLevel) holder.otherSessionNameTextView.text = sessionName holder.otherSessionDescriptionTextView.text = sessionDescription diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt index afa640fb9a..b0ba8baa1a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt @@ -64,7 +64,7 @@ class OtherSessionsController @Inject constructor( otherSessionItem { id(device.deviceInfo.deviceId) - deviceType(DeviceType.UNKNOWN) // TODO. We don't have this info yet. Update accordingly. + deviceType(device.deviceExtendedInfo.deviceType) roomEncryptionTrustLevel(device.roomEncryptionTrustLevel) sessionName(device.deviceInfo.displayName) sessionDescription(description) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt index 340a4f3c3a..6f6c5b24e2 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt @@ -28,6 +28,7 @@ import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextWithColoredPart import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.DrawableProvider +import im.vector.app.core.resources.StringProvider import im.vector.app.databinding.ViewSessionInfoBinding import im.vector.app.features.themes.ThemeUtils import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo @@ -51,13 +52,20 @@ class SessionInfoView @JvmOverloads constructor( val viewDetailsButton = views.sessionInfoViewDetailsButton val viewVerifyButton = views.sessionInfoVerifySessionButton + private val setDeviceTypeIconUseCase = SetDeviceTypeIconUseCase() + fun render( sessionInfoViewState: SessionInfoViewState, dateFormatter: VectorDateFormatter, drawableProvider: DrawableProvider, colorProvider: ColorProvider, + stringProvider: StringProvider, ) { - renderDeviceInfo(sessionInfoViewState.deviceFullInfo.deviceInfo.displayName.orEmpty()) + renderDeviceInfo( + sessionInfoViewState.deviceFullInfo.deviceInfo.displayName.orEmpty(), + sessionInfoViewState.deviceFullInfo.deviceExtendedInfo.deviceType, + stringProvider, + ) renderVerificationStatus( sessionInfoViewState.deviceFullInfo.roomEncryptionTrustLevel, sessionInfoViewState.isCurrentSession, @@ -134,10 +142,8 @@ class SessionInfoView @JvmOverloads constructor( views.sessionInfoVerifySessionButton.isVisible = isVerifyButtonVisible } - // TODO. We don't have this info yet. Update later accordingly. - private fun renderDeviceInfo(sessionName: String) { - views.sessionInfoDeviceTypeImageView.setImageResource(R.drawable.ic_device_type_mobile) - views.sessionInfoDeviceTypeImageView.contentDescription = context.getString(R.string.a11y_device_manager_device_type_mobile) + private fun renderDeviceInfo(sessionName: String, deviceType: DeviceType, stringProvider: StringProvider) { + setDeviceTypeIconUseCase.execute(deviceType, views.sessionInfoDeviceTypeImageView, stringProvider) views.sessionInfoNameTextView.text = sessionName } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SetDeviceTypeIconUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SetDeviceTypeIconUseCase.kt new file mode 100644 index 0000000000..49ff46779e --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SetDeviceTypeIconUseCase.kt @@ -0,0 +1,45 @@ +/* + * 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.list + +import android.widget.ImageView +import im.vector.app.R +import im.vector.app.core.resources.StringProvider + +class SetDeviceTypeIconUseCase { + + fun execute(deviceType: DeviceType, imageView: ImageView, stringProvider: StringProvider) { + when (deviceType) { + DeviceType.MOBILE -> { + imageView.setImageResource(R.drawable.ic_device_type_mobile) + imageView.contentDescription = stringProvider.getString(R.string.a11y_device_manager_device_type_mobile) + } + DeviceType.WEB -> { + imageView.setImageResource(R.drawable.ic_device_type_web) + imageView.contentDescription = stringProvider.getString(R.string.a11y_device_manager_device_type_web) + } + DeviceType.DESKTOP -> { + imageView.setImageResource(R.drawable.ic_device_type_desktop) + imageView.contentDescription = stringProvider.getString(R.string.a11y_device_manager_device_type_desktop) + } + DeviceType.UNKNOWN -> { + imageView.setImageResource(R.drawable.ic_device_type_unknown) + imageView.contentDescription = stringProvider.getString(R.string.a11y_device_manager_device_type_unknown) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt index 8c3b907070..58b0a13706 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt @@ -37,6 +37,7 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorMenuProvider import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.DrawableProvider +import im.vector.app.core.resources.StringProvider import im.vector.app.databinding.FragmentSessionOverviewBinding import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.crypto.recover.SetupMode @@ -64,6 +65,8 @@ class SessionOverviewFragment : @Inject lateinit var colorProvider: ColorProvider + @Inject lateinit var stringProvider: StringProvider + private val viewModel: SessionOverviewViewModel by fragmentViewModel() override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSessionOverviewBinding { @@ -205,7 +208,7 @@ class SessionOverviewFragment : isLearnMoreLinkVisible = true, isLastSeenDetailsVisible = true, ) - views.sessionOverviewInfo.render(infoViewState, dateFormatter, drawableProvider, colorProvider) + views.sessionOverviewInfo.render(infoViewState, dateFormatter, drawableProvider, colorProvider, stringProvider) views.sessionOverviewInfo.onLearnMoreClickListener = { showLearnMoreInfoVerificationStatus(deviceInfo.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted) } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/list/SetDeviceTypeIconUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/list/SetDeviceTypeIconUseCaseTest.kt new file mode 100644 index 0000000000..30456c596c --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/list/SetDeviceTypeIconUseCaseTest.kt @@ -0,0 +1,77 @@ +/* + * 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.list + +import android.widget.ImageView +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import im.vector.app.R +import im.vector.app.test.fakes.FakeStringProvider +import io.mockk.mockk +import io.mockk.verifyAll +import org.junit.Test + +private const val A_DESCRIPTION = "description" + +class SetDeviceTypeIconUseCaseTest { + + private val fakeStringProvider = FakeStringProvider() + + private val setDeviceTypeIconUseCase = SetDeviceTypeIconUseCase() + + @Test + fun `given a device type when execute then correct icon and description is set to the ImageView`() { + testType( + deviceType = DeviceType.UNKNOWN, + drawableResId = R.drawable.ic_device_type_unknown, + descriptionResId = R.string.a11y_device_manager_device_type_unknown + ) + + testType( + deviceType = DeviceType.MOBILE, + drawableResId = R.drawable.ic_device_type_mobile, + descriptionResId = R.string.a11y_device_manager_device_type_mobile + ) + + testType( + deviceType = DeviceType.WEB, + drawableResId = R.drawable.ic_device_type_web, + descriptionResId = R.string.a11y_device_manager_device_type_web + ) + + testType( + deviceType = DeviceType.DESKTOP, + drawableResId = R.drawable.ic_device_type_desktop, + descriptionResId = R.string.a11y_device_manager_device_type_desktop + ) + } + + private fun testType(deviceType: DeviceType, @DrawableRes drawableResId: Int, @StringRes descriptionResId: Int) { + // Given + val imageView = mockk(relaxUnitFun = true) + fakeStringProvider.given(descriptionResId, A_DESCRIPTION) + + // When + setDeviceTypeIconUseCase.execute(deviceType, imageView, fakeStringProvider.instance) + + // Then + verifyAll { + imageView.setImageResource(drawableResId) + imageView.contentDescription = A_DESCRIPTION + } + } +}