Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/7277.wip
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[Device Management] Show correct device type icons

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be 'wip'?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have used ".wip" for all related PRs for the new device manager feature since it is under development (behind feature flag).

Original file line number Diff line number Diff line change
Expand Up @@ -624,14 +624,7 @@ internal class RealmCryptoStore @Inject constructor(
}

override fun saveMyDevicesInfo(info: List<DeviceInfo>) {
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<MyDeviceLastSeenInfoEntity>().findAll().deleteAllFromRealm()
entities.forEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this cause any issues?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully no. The target version is only used in RealmMigrator for log purpose.


override fun doMigrate(realm: DynamicRealm) {
realm.schema.get("CrossSigningInfoEntity")
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,63 @@ 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 {

private val myDeviceLastSeenInfoEntityMapper = MyDeviceLastSeenInfoEntityMapper()

@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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,31 +59,16 @@ abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(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)
if (clickListener == null) {
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand Down
Loading