From d072ab1f374dc1efd5271b327394ecd6833c5cb4 Mon Sep 17 00:00:00 2001 From: Onuray Sahin <onuray.sahin@gmail.com> Date: Tue, 23 Aug 2022 14:36:00 +0300 Subject: [PATCH 01/11] Create layout for other session list. --- .../res/layout/view_other_session_list.xml | 27 +++++++++++++++++++ vector/src/main/res/values/strings.xml | 1 + 2 files changed, 28 insertions(+) create mode 100644 vector/src/main/res/layout/view_other_session_list.xml diff --git a/vector/src/main/res/layout/view_other_session_list.xml b/vector/src/main/res/layout/view_other_session_list.xml new file mode 100644 index 0000000000..34d34437fb --- /dev/null +++ b/vector/src/main/res/layout/view_other_session_list.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + xmlns:tools="http://schemas.android.com/tools" + android:paddingStart="16dp"> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/otherSessionsRecyclerView" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <Button + android:id="@+id/otherSessionsViewAllButton" + style="@style/Widget.Vector.Button.Text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + tools:text="@string/device_manager_other_sessions_view_all" + android:padding="0dp" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/otherSessionsRecyclerView" /> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 78addf5d62..c2e8228728 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3217,5 +3217,6 @@ <string name="device_manager_verify_session">Verify Session</string> <string name="device_manager_view_details">View Details</string> <string name="device_manager_header_section_current_session">Current Session</string> + <string name="device_manager_other_sessions_view_all">View All (%$1d)</string> </resources> From 264877119619f32e33c0dc133cd48fbc81994c1e Mon Sep 17 00:00:00 2001 From: Onuray Sahin <onuray.sahin@gmail.com> Date: Tue, 23 Aug 2022 17:19:03 +0300 Subject: [PATCH 02/11] Create custom view for other sessions. --- .../devices/v2/list/OtherSessionsView.kt | 43 +++++++++++++++++++ ...ssion_list.xml => view_other_sessions.xml} | 15 ++++--- 2 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt rename vector/src/main/res/layout/{view_other_session_list.xml => view_other_sessions.xml} (77%) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt new file mode 100644 index 0000000000..6275901556 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt @@ -0,0 +1,43 @@ +/* + * 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.content.Context +import android.util.AttributeSet +import androidx.constraintlayout.widget.ConstraintLayout +import im.vector.app.R +import im.vector.app.databinding.ViewOtherSessionsBinding +import im.vector.app.features.settings.devices.DeviceFullInfo +import timber.log.Timber + +class OtherSessionsView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr) { + + private val views: ViewOtherSessionsBinding + + init { + inflate(context, R.layout.view_other_sessions, this) + views = ViewOtherSessionsBinding.bind(this) + } + + fun update(devices: List<DeviceFullInfo>) { + Timber.d("OtherSessionsView. Devices: " + devices.size) + } +} diff --git a/vector/src/main/res/layout/view_other_session_list.xml b/vector/src/main/res/layout/view_other_sessions.xml similarity index 77% rename from vector/src/main/res/layout/view_other_session_list.xml rename to vector/src/main/res/layout/view_other_sessions.xml index 34d34437fb..aacbbe8ffe 100644 --- a/vector/src/main/res/layout/view_other_session_list.xml +++ b/vector/src/main/res/layout/view_other_sessions.xml @@ -1,27 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent" xmlns:tools="http://schemas.android.com/tools" - android:paddingStart="16dp"> + android:layout_width="match_parent" + android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/otherSessionsRecyclerView" android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginStart="16dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + tools:listitem="@layout/item_other_session" /> <Button android:id="@+id/otherSessionsViewAllButton" style="@style/Widget.Vector.Button.Text" android:layout_width="wrap_content" android:layout_height="wrap_content" - tools:text="@string/device_manager_other_sessions_view_all" android:padding="0dp" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/otherSessionsRecyclerView" /> + app:layout_constraintStart_toStartOf="@id/otherSessionsRecyclerView" + app:layout_constraintTop_toBottomOf="@id/otherSessionsRecyclerView" + tools:text="@string/device_manager_other_sessions_view_all" /> </androidx.constraintlayout.widget.ConstraintLayout> From ec5d950b7bf4bff28084f21c661fb792b7d3c4ae Mon Sep 17 00:00:00 2001 From: Onuray Sahin <onuray.sahin@gmail.com> Date: Tue, 23 Aug 2022 17:20:01 +0300 Subject: [PATCH 03/11] Fix current session device name. --- .../settings/devices/v2/VectorSettingsDevicesFragment.kt | 9 +++++---- .../settings/devices/v2/list/CurrentSessionView.kt | 8 ++++---- vector/src/main/res/values/strings.xml | 1 - 3 files changed, 9 insertions(+), 9 deletions(-) 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 28a7fd513b..166d28f7d3 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 @@ -130,7 +130,7 @@ class VectorSettingsDevicesFragment @Inject constructor() : VectorBaseFragment<F } if (state.devices is Success && currentDeviceInfo != null) { - renderCurrentDevice(state) + renderCurrentDevice(state.accountCrossSigningIsTrusted, !state.hasAccountCrossSigning, currentDeviceInfo.deviceInfo.displayName ?: "") } else { hideCurrentSessionView() } @@ -143,12 +143,13 @@ class VectorSettingsDevicesFragment @Inject constructor() : VectorBaseFragment<F views.deviceListCurrentSession.isVisible = false } - private fun renderCurrentDevice(state: DevicesViewState) { + private fun renderCurrentDevice(accountCrossSigningIsTrusted: Boolean, legacyMode: Boolean, sessionName: String) { views.deviceListHeaderSectionCurrent.isVisible = true views.deviceListCurrentSession.isVisible = true views.deviceListCurrentSession.update( - accountCrossSigningIsTrusted = state.accountCrossSigningIsTrusted, - legacyMode = !state.hasAccountCrossSigning + accountCrossSigningIsTrusted = accountCrossSigningIsTrusted, + legacyMode = legacyMode, + sessionName = sessionName ) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt index baf30e35b5..6d7c96420b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt @@ -39,8 +39,8 @@ class CurrentSessionView @JvmOverloads constructor( views = ViewCurrentSessionBinding.bind(this) } - fun update(accountCrossSigningIsTrusted: Boolean, legacyMode: Boolean) { - renderDeviceType() + fun update(accountCrossSigningIsTrusted: Boolean, legacyMode: Boolean, sessionName: String) { + renderDeviceInfo(sessionName) renderVerificationStatus(accountCrossSigningIsTrusted, legacyMode) } @@ -75,9 +75,9 @@ class CurrentSessionView @JvmOverloads constructor( } // TODO. We don't have this info yet. Update later accordingly. - private fun renderDeviceType() { + private fun renderDeviceInfo(sessionName: String) { views.currentSessionDeviceTypeImageView.setImageResource(R.drawable.ic_device_type_mobile) views.currentSessionDeviceTypeImageView.contentDescription = context.getString(R.string.a11y_device_manager_device_type_mobile) - views.currentSessionDeviceTypeTextView.text = context.getString(R.string.device_manager_device_type_android) + views.currentSessionNameTextView.text = sessionName } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index c2e8228728..5e866bab50 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3209,7 +3209,6 @@ <string name="a11y_device_manager_device_type_mobile">Mobile</string> <string name="a11y_device_manager_device_type_web" tools:ignore="UnusedResources">Web</string> <string name="a11y_device_manager_device_type_desktop" tools:ignore="UnusedResources">Desktop</string> - <string name="device_manager_device_type_android">${app_name} Mobile: Android</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_detail_verified">Your current session is ready for secure messaging.</string> From 248c05f4e2fcc92a516985299656eb8a691cd304 Mon Sep 17 00:00:00 2001 From: Onuray Sahin <onuray.sahin@gmail.com> Date: Tue, 23 Aug 2022 17:20:37 +0300 Subject: [PATCH 04/11] Create other session item layout. --- .../v2/list/OtherSessionsController.kt | 28 +++++++++ .../main/res/drawable/circle_with_border.xml | 14 +++++ .../main/res/layout/item_other_session.xml | 63 +++++++++++++++++++ .../main/res/layout/view_current_session.xml | 6 +- 4 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt create mode 100644 vector/src/main/res/drawable/circle_with_border.xml create mode 100644 vector/src/main/res/layout/item_other_session.xml 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 new file mode 100644 index 0000000000..5cf85d2069 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt @@ -0,0 +1,28 @@ +/* + * 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 com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.features.settings.devices.DevicesViewState +import javax.inject.Inject + +class OtherSessionsController @Inject constructor() : TypedEpoxyController<DevicesViewState>() { + + override fun buildModels(data: DevicesViewState?) { + + } +} diff --git a/vector/src/main/res/drawable/circle_with_border.xml b/vector/src/main/res/drawable/circle_with_border.xml new file mode 100644 index 0000000000..7b9bad44f7 --- /dev/null +++ b/vector/src/main/res/drawable/circle_with_border.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="ring" + android:innerRadius="0dp" + android:thicknessRatio="2" + android:useLevel="false"> + + <solid android:color="?android:colorBackground" /> + + <stroke + android:width="1dp" + android:color="?vctr_content_quinary" /> + +</shape> diff --git a/vector/src/main/res/layout/item_other_session.xml b/vector/src/main/res/layout/item_other_session.xml new file mode 100644 index 0000000000..8805a02544 --- /dev/null +++ b/vector/src/main/res/layout/item_other_session.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageView + android:id="@+id/otherSessionDeviceTypeImageView" + android:layout_width="40dp" + android:layout_height="40dp" + android:contentDescription="@string/a11y_device_manager_device_type_mobile" + android:padding="8dp" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:background="@drawable/bg_device_type" + tools:src="@drawable/ic_device_type_mobile" /> + + <im.vector.app.core.ui.views.ShieldImageView + android:id="@+id/otherSessionVerificationStatusImageView" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="24dp" + android:layout_marginTop="24dp" + android:background="@drawable/circle_with_border" + android:importantForAccessibility="no" + android:padding="6dp" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:src="@drawable/ic_shield_trusted" /> + + <TextView + android:id="@+id/otherSessionName" + style="@style/TextAppearance.Vector.Headline.Medium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:ellipsize="end" + android:lines="1" + app:layout_constraintStart_toEndOf="@id/otherSessionDeviceTypeImageView" + app:layout_constraintTop_toTopOf="parent" + tools:text="Element Mobile: Android" /> + + <TextView + android:id="@+id/otherSessionDescriptionTextView" + style="@style/TextAppearance.Vector.Subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="2dp" + app:layout_constraintStart_toStartOf="@id/otherSessionName" + app:layout_constraintTop_toBottomOf="@id/otherSessionName" + tools:text="@string/device_manager_verification_status_verified" /> + + <View + android:layout_width="0dp" + android:layout_height="1dp" + android:layout_marginTop="16dp" + android:background="?vctr_content_quinary" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@id/otherSessionName" + app:layout_constraintTop_toBottomOf="@id/otherSessionDescriptionTextView" /> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/vector/src/main/res/layout/view_current_session.xml b/vector/src/main/res/layout/view_current_session.xml index 61641cbbe7..5709682b84 100644 --- a/vector/src/main/res/layout/view_current_session.xml +++ b/vector/src/main/res/layout/view_current_session.xml @@ -21,7 +21,7 @@ tools:src="@drawable/ic_device_type_mobile" /> <TextView - android:id="@+id/currentSessionDeviceTypeTextView" + android:id="@+id/currentSessionNameTextView" style="@style/TextAppearance.Vector.Headline.Medium" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -29,7 +29,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/currentSessionDeviceTypeImageView" - tools:text="@string/device_manager_device_type_android" /> + tools:text="Element Mobile: Android" /> <LinearLayout android:id="@+id/currentSessionVerificationStatusContainer" @@ -40,7 +40,7 @@ android:orientation="horizontal" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/currentSessionDeviceTypeTextView"> + app:layout_constraintTop_toBottomOf="@id/currentSessionNameTextView"> <im.vector.app.core.ui.views.ShieldImageView android:id="@+id/currentSessionVerificationStatusImageView" From 01bce5658b5594d55d2f37b985e408bc4f75b902 Mon Sep 17 00:00:00 2001 From: Onuray Sahin <onuray.sahin@gmail.com> Date: Fri, 26 Aug 2022 12:53:30 +0300 Subject: [PATCH 05/11] Calculate trust level for shield in viewmodel. --- .../settings/devices/DevicesViewModel.kt | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index 1840a97098..11fc6ecd64 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -57,6 +57,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction @@ -79,12 +80,13 @@ data class DevicesViewState( // TODO Replace by isLoading boolean val request: Async<Unit> = Uninitialized, val hasAccountCrossSigning: Boolean = false, - val accountCrossSigningIsTrusted: Boolean = false + val accountCrossSigningIsTrusted: Boolean = false, ) : MavericksState data class DeviceFullInfo( val deviceInfo: DeviceInfo, - val cryptoDeviceInfo: CryptoDeviceInfo? + val cryptoDeviceInfo: CryptoDeviceInfo?, + val trustLevelForShield: RoomEncryptionTrustLevel, ) class DevicesViewModel @AssistedInject constructor( @@ -108,11 +110,13 @@ class DevicesViewModel @AssistedInject constructor( private val refreshSource = PublishDataSource<Unit>() init { + val hasAccountCrossSigning = session.cryptoService().crossSigningService().isCrossSigningInitialized() + val accountCrossSigningIsTrusted = session.cryptoService().crossSigningService().isCrossSigningVerified() setState { copy( - hasAccountCrossSigning = session.cryptoService().crossSigningService().isCrossSigningInitialized(), - accountCrossSigningIsTrusted = session.cryptoService().crossSigningService().isCrossSigningVerified(), + hasAccountCrossSigning = hasAccountCrossSigning, + accountCrossSigningIsTrusted = accountCrossSigningIsTrusted, myDeviceId = session.sessionParams.deviceId ?: "" ) } @@ -125,7 +129,13 @@ class DevicesViewModel @AssistedInject constructor( .sortedByDescending { it.lastSeenTs } .map { deviceInfo -> val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId } - DeviceFullInfo(deviceInfo, cryptoDeviceInfo) + val trustLevelForShield = computeTrustLevelForShield( + currentSessionCrossTrusted = accountCrossSigningIsTrusted, + legacyMode = !hasAccountCrossSigning, + deviceTrustLevel = cryptoDeviceInfo?.trustLevel, + isCurrentDevice = deviceInfo.deviceId == session.sessionParams.deviceId + ) + DeviceFullInfo(deviceInfo, cryptoDeviceInfo, trustLevelForShield) } } .distinctUntilChanged() @@ -243,6 +253,20 @@ class DevicesViewModel @AssistedInject constructor( } } + private fun computeTrustLevelForShield( + currentSessionCrossTrusted: Boolean, + legacyMode: Boolean, + deviceTrustLevel: DeviceTrustLevel?, + isCurrentDevice: Boolean, + ): RoomEncryptionTrustLevel { + return TrustUtils.shieldForTrust( + currentDevice = isCurrentDevice, + trustMSK = currentSessionCrossTrusted, + legacyMode = legacyMode, + deviceTrustLevel = deviceTrustLevel + ) + } + private fun handleInteractiveVerification(action: DevicesAction.VerifyMyDevice) { val txID = session.cryptoService() .verificationService() From eb86a4f33c4046d850710db30ced892f883edbf2 Mon Sep 17 00:00:00 2001 From: Onuray Sahin <onuray.sahin@gmail.com> Date: Fri, 26 Aug 2022 12:57:20 +0300 Subject: [PATCH 06/11] Refactor layout files. --- .../res/drawable/ic_device_type_unknown.xml | 10 ++ .../res/layout/fragment_settings_devices.xml | 92 ++++++++++++------- .../main/res/layout/item_other_session.xml | 21 +++-- .../main/res/layout/view_current_session.xml | 2 +- vector/src/main/res/values/strings.xml | 4 +- 5 files changed, 86 insertions(+), 43 deletions(-) create mode 100644 vector/src/main/res/drawable/ic_device_type_unknown.xml diff --git a/vector/src/main/res/drawable/ic_device_type_unknown.xml b/vector/src/main/res/drawable/ic_device_type_unknown.xml new file mode 100644 index 0000000000..8368dcbd0b --- /dev/null +++ b/vector/src/main/res/drawable/ic_device_type_unknown.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="25dp" + android:viewportWidth="24" + android:viewportHeight="25"> + <path + android:pathData="M12,23.5C18.075,23.5 23,18.575 23,12.5C23,6.425 18.075,1.5 12,1.5C5.925,1.5 1,6.425 1,12.5C1,18.575 5.925,23.5 12,23.5ZM12,19.26C12.76,19.26 13.375,18.645 13.375,17.885C13.375,17.126 12.76,16.51 12,16.51C11.241,16.51 10.625,17.126 10.625,17.885C10.625,18.645 11.241,19.26 12,19.26ZM10.09,10.428C10.09,9.37 10.949,8.518 12,8.518C13.048,8.518 13.909,9.38 13.909,10.428C13.909,10.913 13.702,11.087 12.884,11.652C12.521,11.902 12.025,12.25 11.638,12.76C11.223,13.306 10.968,13.987 10.968,14.853H13.031C13.031,14.429 13.144,14.187 13.281,14.007C13.445,13.79 13.683,13.606 14.056,13.349C14.095,13.321 14.137,13.293 14.18,13.264C14.856,12.804 15.972,12.045 15.972,10.428C15.972,8.241 14.187,6.456 12,6.456C9.816,6.456 8.027,8.225 8.027,10.428H10.09Z" + android:fillColor="#737D8C" + android:fillType="evenOdd"/> +</vector> diff --git a/vector/src/main/res/layout/fragment_settings_devices.xml b/vector/src/main/res/layout/fragment_settings_devices.xml index 860e395c1f..ea28531150 100644 --- a/vector/src/main/res/layout/fragment_settings_devices.xml +++ b/vector/src/main/res/layout/fragment_settings_devices.xml @@ -1,41 +1,69 @@ <?xml version="1.0" encoding="utf-8"?> -<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> - <im.vector.app.features.settings.devices.v2.list.DevicesListHeaderView - android:id="@+id/deviceListHeaderSectionCurrent" - android:layout_width="0dp" - android:layout_height="wrap_content" - app:devicesListHeaderDescription="" - app:devicesListHeaderTitle="@string/device_manager_header_section_current_session" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> - <im.vector.app.features.settings.devices.v2.list.CurrentSessionView - android:id="@+id/deviceListCurrentSession" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginHorizontal="16dp" - android:layout_marginVertical="24dp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSectionCurrent" /> + <im.vector.app.features.settings.devices.v2.list.DevicesListHeaderView + android:id="@+id/deviceListHeaderSectionCurrent" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:devicesListHeaderDescription="" + app:devicesListHeaderTitle="@string/device_manager_header_section_current_session" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - <im.vector.app.features.settings.devices.v2.list.DevicesListHeaderView - android:id="@+id/deviceListHeaderSectionOther" - android:layout_width="0dp" - android:layout_height="wrap_content" - app:devicesListHeaderDescription="@string/settings_sessions_other_description" - app:devicesListHeaderTitle="@string/settings_sessions_other_title" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/deviceListCurrentSession" /> + <im.vector.app.features.settings.devices.v2.list.CurrentSessionView + android:id="@+id/deviceListCurrentSession" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="16dp" + android:layout_marginVertical="16dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSectionCurrent" /> - <include - android:id="@+id/waiting_view" - layout="@layout/merge_overlay_waiting_view" /> + <View + android:id="@+id/deviceListCurrentSessionDivider" + android:layout_width="0dp" + android:layout_height="1dp" + android:layout_marginTop="24dp" + android:background="@drawable/divider_horizontal" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/deviceListCurrentSession" /> -</androidx.constraintlayout.widget.ConstraintLayout> + <im.vector.app.features.settings.devices.v2.list.DevicesListHeaderView + android:id="@+id/deviceListHeaderSectionOther" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:devicesListHeaderDescription="@string/settings_sessions_other_description" + app:devicesListHeaderTitle="@string/settings_sessions_other_title" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/deviceListCurrentSessionDivider" /> + + <im.vector.app.features.settings.devices.v2.list.OtherSessionsView + android:id="@+id/deviceListOtherSessions" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSectionOther" /> + + <include + android:id="@+id/waiting_view" + layout="@layout/merge_overlay_waiting_view" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + </androidx.constraintlayout.widget.ConstraintLayout> +</ScrollView> diff --git a/vector/src/main/res/layout/item_other_session.xml b/vector/src/main/res/layout/item_other_session.xml index 8805a02544..84b813c977 100644 --- a/vector/src/main/res/layout/item_other_session.xml +++ b/vector/src/main/res/layout/item_other_session.xml @@ -3,17 +3,18 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:paddingTop="16dp"> <ImageView android:id="@+id/otherSessionDeviceTypeImageView" android:layout_width="40dp" android:layout_height="40dp" + android:background="@drawable/bg_device_type" android:contentDescription="@string/a11y_device_manager_device_type_mobile" android:padding="8dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - tools:background="@drawable/bg_device_type" tools:src="@drawable/ic_device_type_mobile" /> <im.vector.app.core.ui.views.ShieldImageView @@ -30,25 +31,27 @@ tools:src="@drawable/ic_shield_trusted" /> <TextView - android:id="@+id/otherSessionName" - style="@style/TextAppearance.Vector.Headline.Medium" - android:layout_width="wrap_content" + android:id="@+id/otherSessionNameTextView" + style="@style/TextAppearance.Vector.Subtitle.Medium.DevicesManagement" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" android:ellipsize="end" android:lines="1" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/otherSessionDeviceTypeImageView" app:layout_constraintTop_toTopOf="parent" tools:text="Element Mobile: Android" /> <TextView android:id="@+id/otherSessionDescriptionTextView" - style="@style/TextAppearance.Vector.Subtitle" + style="@style/TextAppearance.Vector.Body.DevicesManagement" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="2dp" - app:layout_constraintStart_toStartOf="@id/otherSessionName" - app:layout_constraintTop_toBottomOf="@id/otherSessionName" + app:layout_constraintStart_toStartOf="@id/otherSessionNameTextView" + app:layout_constraintTop_toBottomOf="@id/otherSessionNameTextView" tools:text="@string/device_manager_verification_status_verified" /> <View @@ -57,7 +60,7 @@ android:layout_marginTop="16dp" android:background="?vctr_content_quinary" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="@id/otherSessionName" + app:layout_constraintStart_toStartOf="@id/otherSessionNameTextView" app:layout_constraintTop_toBottomOf="@id/otherSessionDescriptionTextView" /> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/vector/src/main/res/layout/view_current_session.xml b/vector/src/main/res/layout/view_current_session.xml index 5709682b84..c6899d506b 100644 --- a/vector/src/main/res/layout/view_current_session.xml +++ b/vector/src/main/res/layout/view_current_session.xml @@ -22,7 +22,7 @@ <TextView android:id="@+id/currentSessionNameTextView" - style="@style/TextAppearance.Vector.Headline.Medium" + style="@style/TextAppearance.Vector.Subtitle.Medium.DevicesManagement" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 5e866bab50..b553549102 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3216,6 +3216,8 @@ <string name="device_manager_verify_session">Verify Session</string> <string name="device_manager_view_details">View Details</string> <string name="device_manager_header_section_current_session">Current Session</string> - <string name="device_manager_other_sessions_view_all">View All (%$1d)</string> + <string name="device_manager_other_sessions_view_all">View All (%1$d)</string> + <string name="device_manager_other_sessions_description_verified">Verified · Last activity %1$s</string> + <string name="device_manager_other_sessions_description_unverified">Unverified · Last activity %1$s</string> </resources> From eaf7da8e6e589b229c7b85a985809866ff530158 Mon Sep 17 00:00:00 2001 From: Onuray Sahin <onuray.sahin@gmail.com> Date: Fri, 26 Aug 2022 12:59:01 +0300 Subject: [PATCH 07/11] Render other sessions. --- .../v2/VectorSettingsDevicesFragment.kt | 52 ++++++++++----- .../devices/v2/list/CurrentSessionView.kt | 23 +++---- .../devices/v2/list/OtherSessionItem.kt | 65 +++++++++++++++++++ .../v2/list/OtherSessionsController.kt | 40 +++++++++++- .../devices/v2/list/OtherSessionsView.kt | 11 +++- .../devices/v2/list/SessionDeviceType.kt | 24 +++++++ .../devices/v2/list/SessionListConstants.kt | 19 ++++++ 7 files changed, 197 insertions(+), 37 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionDeviceType.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt 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 166d28f7d3..82efcc5db9 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 @@ -36,10 +36,10 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentSettingsDevicesBinding import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.verification.VerificationBottomSheet +import im.vector.app.features.settings.devices.DeviceFullInfo import im.vector.app.features.settings.devices.DevicesAction import im.vector.app.features.settings.devices.DevicesViewEvents import im.vector.app.features.settings.devices.DevicesViewModel -import im.vector.app.features.settings.devices.DevicesViewState import javax.inject.Inject /** @@ -124,35 +124,53 @@ class VectorSettingsDevicesFragment @Inject constructor() : VectorBaseFragment<F } override fun invalidate() = withState(viewModel) { state -> - val currentDeviceInfo = state.devices() - ?.firstOrNull { - it.deviceInfo.deviceId == state.myDeviceId - } + if (state.devices is Success) { + val devices = state.devices() + val currentDeviceInfo = devices?.firstOrNull { + it.deviceInfo.deviceId == state.myDeviceId + } + val otherDevices = devices?.filter { it.deviceInfo.deviceId != state.myDeviceId } - if (state.devices is Success && currentDeviceInfo != null) { - renderCurrentDevice(state.accountCrossSigningIsTrusted, !state.hasAccountCrossSigning, currentDeviceInfo.deviceInfo.displayName ?: "") + renderCurrentDevice(currentDeviceInfo) + renderOtherSessionsView(otherDevices) } else { hideCurrentSessionView() + hideOtherSessionsView() } handleRequestStatus(state.request) } + private fun renderOtherSessionsView(otherDevices: List<DeviceFullInfo>?) { + if (otherDevices.isNullOrEmpty()) { + hideOtherSessionsView() + } else { + views.deviceListHeaderSectionOther.isVisible = true + views.deviceListOtherSessions.isVisible = true + views.deviceListOtherSessions.update(otherDevices) + } + } + + private fun hideOtherSessionsView() { + views.deviceListHeaderSectionOther.isVisible = false + views.deviceListOtherSessions.isVisible = false + } + + private fun renderCurrentDevice(currentDeviceInfo: DeviceFullInfo?) { + currentDeviceInfo?.let { + views.deviceListHeaderSectionCurrent.isVisible = true + views.deviceListCurrentSession.isVisible = true + views.deviceListCurrentSession.update(it) + } ?: run { + hideCurrentSessionView() + } + } + private fun hideCurrentSessionView() { views.deviceListHeaderSectionCurrent.isVisible = false views.deviceListCurrentSession.isVisible = false } - private fun renderCurrentDevice(accountCrossSigningIsTrusted: Boolean, legacyMode: Boolean, sessionName: String) { - views.deviceListHeaderSectionCurrent.isVisible = true - views.deviceListCurrentSession.isVisible = true - views.deviceListCurrentSession.update( - accountCrossSigningIsTrusted = accountCrossSigningIsTrusted, - legacyMode = legacyMode, - sessionName = sessionName - ) - } - private fun handleRequestStatus(unIgnoreRequest: Async<Unit>) { views.waitingView.root.isVisible = when (unIgnoreRequest) { is Loading -> true diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt index 6d7c96420b..c74dd9a7fa 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt @@ -22,9 +22,9 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible import im.vector.app.R import im.vector.app.databinding.ViewCurrentSessionBinding -import im.vector.app.features.settings.devices.TrustUtils +import im.vector.app.features.settings.devices.DeviceFullInfo import im.vector.app.features.themes.ThemeUtils -import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel class CurrentSessionView @JvmOverloads constructor( context: Context, @@ -39,21 +39,14 @@ class CurrentSessionView @JvmOverloads constructor( views = ViewCurrentSessionBinding.bind(this) } - fun update(accountCrossSigningIsTrusted: Boolean, legacyMode: Boolean, sessionName: String) { - renderDeviceInfo(sessionName) - renderVerificationStatus(accountCrossSigningIsTrusted, legacyMode) + fun update(currentDeviceInfo: DeviceFullInfo) { + renderDeviceInfo(currentDeviceInfo.deviceInfo.displayName ?: "") + renderVerificationStatus(currentDeviceInfo.trustLevelForShield) } - private fun renderVerificationStatus(accountCrossSigningIsTrusted: Boolean, legacyMode: Boolean) { - val deviceTrustLevel = DeviceTrustLevel(crossSigningVerified = accountCrossSigningIsTrusted, locallyVerified = true) - val shield = TrustUtils.shieldForTrust( - currentDevice = true, - trustMSK = accountCrossSigningIsTrusted, - legacyMode = legacyMode, - deviceTrustLevel = deviceTrustLevel - ) - views.currentSessionVerificationStatusImageView.render(shield) - if (deviceTrustLevel.crossSigningVerified) { + private fun renderVerificationStatus(trustLevelForShield: RoomEncryptionTrustLevel) { + views.currentSessionVerificationStatusImageView.render(trustLevelForShield) + if (trustLevelForShield == RoomEncryptionTrustLevel.Trusted) { renderCrossSigningVerified() } else { renderCrossSigningUnverified() 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 new file mode 100644 index 0000000000..f3b44572b3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt @@ -0,0 +1,65 @@ +/* + * 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 android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.ui.views.ShieldImageView +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel + +@EpoxyModelClass +abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(R.layout.item_other_session) { + + @EpoxyAttribute + var deviceType: SessionDeviceType = SessionDeviceType.UNKNOWN + + @EpoxyAttribute + var roomEncryptionTrustLevel: RoomEncryptionTrustLevel? = null + + @EpoxyAttribute + var sessionName: String? = null + + @EpoxyAttribute + var sessionDescription: String? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.otherSessionDeviceTypeImageView.setImageResource( + when (deviceType) { + SessionDeviceType.MOBILE -> R.drawable.ic_device_type_mobile + SessionDeviceType.WEB -> R.drawable.ic_device_type_web + SessionDeviceType.DESKTOP -> R.drawable.ic_device_type_desktop + SessionDeviceType.UNKNOWN -> R.drawable.ic_device_type_unknown + } + ) + holder.otherSessionVerificationStatusImageView.render(roomEncryptionTrustLevel) + holder.otherSessionNameTextView.text = sessionName + holder.otherSessionDescriptionTextView.text = sessionDescription + } + + class Holder : VectorEpoxyHolder() { + val otherSessionDeviceTypeImageView by bind<ImageView>(R.id.otherSessionDeviceTypeImageView) + val otherSessionVerificationStatusImageView by bind<ShieldImageView>(R.id.otherSessionVerificationStatusImageView) + val otherSessionNameTextView by bind<TextView>(R.id.otherSessionNameTextView) + val otherSessionDescriptionTextView by bind<TextView>(R.id.otherSessionDescriptionTextView) + } +} 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 5cf85d2069..24b9e539d8 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 @@ -17,12 +17,46 @@ package im.vector.app.features.settings.devices.v2.list import com.airbnb.epoxy.TypedEpoxyController -import im.vector.app.features.settings.devices.DevicesViewState +import im.vector.app.R +import im.vector.app.core.date.DateFormatKind +import im.vector.app.core.date.VectorDateFormatter +import im.vector.app.core.epoxy.noResultItem +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.settings.devices.DeviceFullInfo +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import javax.inject.Inject -class OtherSessionsController @Inject constructor() : TypedEpoxyController<DevicesViewState>() { +class OtherSessionsController @Inject constructor( + private val stringProvider: StringProvider, + private val dateFormatter: VectorDateFormatter, +) : TypedEpoxyController<List<DeviceFullInfo>>() { - override fun buildModels(data: DevicesViewState?) { + override fun buildModels(data: List<DeviceFullInfo>?) { + data ?: return + val host = this + if (data.isNullOrEmpty()) { + noResultItem { + id("empty") + text(host.stringProvider.getString(R.string.no_result_placeholder)) + } + } else { + data.take(NUMBER_OF_OTHER_DEVICES_TO_RENDER).forEach { device -> + val formattedLastActivityDate = host.dateFormatter.format(device.deviceInfo.lastSeenTs, DateFormatKind.DEFAULT_DATE_AND_TIME) + val description = if (device.trustLevelForShield == RoomEncryptionTrustLevel.Trusted) { + stringProvider.getString(R.string.device_manager_other_sessions_description_verified, formattedLastActivityDate) + } else { + stringProvider.getString(R.string.device_manager_other_sessions_description_unverified, formattedLastActivityDate) + } + + otherSessionItem { + id(device.deviceInfo.deviceId) + deviceType(SessionDeviceType.UNKNOWN) // TODO. We don't have this info yet. Update accordingly. + roomEncryptionTrustLevel(device.trustLevelForShield) + sessionName(device.deviceInfo.displayName) + sessionDescription(description) + } + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt index 6275901556..3dd445beb6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt @@ -19,17 +19,22 @@ package im.vector.app.features.settings.devices.v2.list import android.content.Context import android.util.AttributeSet import androidx.constraintlayout.widget.ConstraintLayout +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R +import im.vector.app.core.extensions.configureWith import im.vector.app.databinding.ViewOtherSessionsBinding import im.vector.app.features.settings.devices.DeviceFullInfo -import timber.log.Timber +import javax.inject.Inject +@AndroidEntryPoint class OtherSessionsView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { + @Inject lateinit var otherSessionsController: OtherSessionsController + private val views: ViewOtherSessionsBinding init { @@ -38,6 +43,8 @@ class OtherSessionsView @JvmOverloads constructor( } fun update(devices: List<DeviceFullInfo>) { - Timber.d("OtherSessionsView. Devices: " + devices.size) + views.otherSessionsRecyclerView.configureWith(otherSessionsController, hasFixedSize = true) + views.otherSessionsViewAllButton.text = context.getString(R.string.device_manager_other_sessions_view_all, devices.size) + otherSessionsController.setData(devices) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionDeviceType.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionDeviceType.kt new file mode 100644 index 0000000000..62e66f72f5 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionDeviceType.kt @@ -0,0 +1,24 @@ +/* + * 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 + +enum class SessionDeviceType { + MOBILE, + WEB, + DESKTOP, + UNKNOWN, +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt new file mode 100644 index 0000000000..f4aadeb337 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt @@ -0,0 +1,19 @@ +/* + * 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 + +const val NUMBER_OF_OTHER_DEVICES_TO_RENDER = 5 From ed2c365db24ad8992685475bbd43a0a9fbbd6973 Mon Sep 17 00:00:00 2001 From: Onuray Sahin <onuray.sahin@gmail.com> Date: Fri, 26 Aug 2022 13:07:16 +0300 Subject: [PATCH 08/11] Add changelog. --- changelog.d/6945.wip | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6945.wip diff --git a/changelog.d/6945.wip b/changelog.d/6945.wip new file mode 100644 index 0000000000..6f5916a8c2 --- /dev/null +++ b/changelog.d/6945.wip @@ -0,0 +1 @@ +[Device Manager] Other Sessions Section From 66fa5ca98ebad5fac44828f26bfc59d396f15f92 Mon Sep 17 00:00:00 2001 From: Onuray Sahin <onuray.sahin@gmail.com> Date: Tue, 30 Aug 2022 15:47:00 +0300 Subject: [PATCH 09/11] Code review fixes. --- .../v2/VectorSettingsDevicesFragment.kt | 16 +++++----- .../devices/v2/list/CurrentSessionView.kt | 4 +-- .../{SessionDeviceType.kt => DeviceType.kt} | 2 +- .../devices/v2/list/OtherSessionItem.kt | 32 +++++++++++++------ .../v2/list/OtherSessionsController.kt | 4 +-- .../devices/v2/list/OtherSessionsView.kt | 2 +- .../res/layout/fragment_settings_devices.xml | 12 +++---- .../main/res/layout/view_current_session.xml | 2 +- vector/src/main/res/values/strings.xml | 5 +-- 9 files changed, 47 insertions(+), 32 deletions(-) rename vector/src/main/java/im/vector/app/features/settings/devices/v2/list/{SessionDeviceType.kt => DeviceType.kt} (95%) 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 ca98be625c..80dfe25c77 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 @@ -114,13 +114,13 @@ class VectorSettingsDevicesFragment : } private fun initLearnMoreButtons() { - views.deviceListHeaderSectionOther.onLearnMoreClickListener = { + views.deviceListHeaderOtherSessions.onLearnMoreClickListener = { Toast.makeText(context, "Learn more other", Toast.LENGTH_LONG).show() } } private fun cleanUpLearnMoreButtonsListeners() { - views.deviceListHeaderSectionOther.onLearnMoreClickListener = null + views.deviceListHeaderOtherSessions.onLearnMoreClickListener = null } override fun invalidate() = withState(viewModel) { state -> @@ -145,29 +145,29 @@ class VectorSettingsDevicesFragment : if (otherDevices.isNullOrEmpty()) { hideOtherSessionsView() } else { - views.deviceListHeaderSectionOther.isVisible = true + views.deviceListHeaderOtherSessions.isVisible = true views.deviceListOtherSessions.isVisible = true - views.deviceListOtherSessions.update(otherDevices) + views.deviceListOtherSessions.render(otherDevices) } } private fun hideOtherSessionsView() { - views.deviceListHeaderSectionOther.isVisible = false + views.deviceListHeaderOtherSessions.isVisible = false views.deviceListOtherSessions.isVisible = false } private fun renderCurrentDevice(currentDeviceInfo: DeviceFullInfo?) { currentDeviceInfo?.let { - views.deviceListHeaderSectionCurrent.isVisible = true + views.deviceListHeaderCurrentSession.isVisible = true views.deviceListCurrentSession.isVisible = true - views.deviceListCurrentSession.update(it) + views.deviceListCurrentSession.render(it) } ?: run { hideCurrentSessionView() } } private fun hideCurrentSessionView() { - views.deviceListHeaderSectionCurrent.isVisible = false + views.deviceListHeaderCurrentSession.isVisible = false views.deviceListCurrentSession.isVisible = false } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt index c74dd9a7fa..d6f81f4f79 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt @@ -39,8 +39,8 @@ class CurrentSessionView @JvmOverloads constructor( views = ViewCurrentSessionBinding.bind(this) } - fun update(currentDeviceInfo: DeviceFullInfo) { - renderDeviceInfo(currentDeviceInfo.deviceInfo.displayName ?: "") + fun render(currentDeviceInfo: DeviceFullInfo) { + renderDeviceInfo(currentDeviceInfo.deviceInfo.displayName.orEmpty()) renderVerificationStatus(currentDeviceInfo.trustLevelForShield) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionDeviceType.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/DeviceType.kt similarity index 95% rename from vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionDeviceType.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/list/DeviceType.kt index 62e66f72f5..3082901375 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionDeviceType.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/DeviceType.kt @@ -16,7 +16,7 @@ package im.vector.app.features.settings.devices.v2.list -enum class SessionDeviceType { +enum class DeviceType { MOBILE, WEB, DESKTOP, 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 f3b44572b3..2a62100994 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 @@ -23,6 +23,7 @@ import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.views.ShieldImageView import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel @@ -30,7 +31,7 @@ import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(R.layout.item_other_session) { @EpoxyAttribute - var deviceType: SessionDeviceType = SessionDeviceType.UNKNOWN + var deviceType: DeviceType = DeviceType.UNKNOWN @EpoxyAttribute var roomEncryptionTrustLevel: RoomEncryptionTrustLevel? = null @@ -41,16 +42,29 @@ abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(R.la @EpoxyAttribute var sessionDescription: String? = null + @EpoxyAttribute + lateinit var stringProvider: StringProvider + override fun bind(holder: Holder) { super.bind(holder) - holder.otherSessionDeviceTypeImageView.setImageResource( - when (deviceType) { - SessionDeviceType.MOBILE -> R.drawable.ic_device_type_mobile - SessionDeviceType.WEB -> R.drawable.ic_device_type_web - SessionDeviceType.DESKTOP -> R.drawable.ic_device_type_desktop - SessionDeviceType.UNKNOWN -> R.drawable.ic_device_type_unknown - } - ) + 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) + } + } 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 24b9e539d8..44c73e6eb7 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 @@ -32,7 +32,6 @@ class OtherSessionsController @Inject constructor( ) : TypedEpoxyController<List<DeviceFullInfo>>() { override fun buildModels(data: List<DeviceFullInfo>?) { - data ?: return val host = this if (data.isNullOrEmpty()) { @@ -51,10 +50,11 @@ class OtherSessionsController @Inject constructor( otherSessionItem { id(device.deviceInfo.deviceId) - deviceType(SessionDeviceType.UNKNOWN) // TODO. We don't have this info yet. Update accordingly. + deviceType(DeviceType.UNKNOWN) // TODO. We don't have this info yet. Update accordingly. roomEncryptionTrustLevel(device.trustLevelForShield) sessionName(device.deviceInfo.displayName) sessionDescription(description) + stringProvider(this@OtherSessionsController.stringProvider) } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt index 3dd445beb6..bcb9726b5c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt @@ -42,7 +42,7 @@ class OtherSessionsView @JvmOverloads constructor( views = ViewOtherSessionsBinding.bind(this) } - fun update(devices: List<DeviceFullInfo>) { + fun render(devices: List<DeviceFullInfo>) { views.otherSessionsRecyclerView.configureWith(otherSessionsController, hasFixedSize = true) views.otherSessionsViewAllButton.text = context.getString(R.string.device_manager_other_sessions_view_all, devices.size) otherSessionsController.setData(devices) diff --git a/vector/src/main/res/layout/fragment_settings_devices.xml b/vector/src/main/res/layout/fragment_settings_devices.xml index ea28531150..1367835d2c 100644 --- a/vector/src/main/res/layout/fragment_settings_devices.xml +++ b/vector/src/main/res/layout/fragment_settings_devices.xml @@ -9,7 +9,7 @@ android:layout_height="wrap_content"> <im.vector.app.features.settings.devices.v2.list.DevicesListHeaderView - android:id="@+id/deviceListHeaderSectionCurrent" + android:id="@+id/deviceListHeaderCurrentSession" android:layout_width="0dp" android:layout_height="wrap_content" app:devicesListHeaderDescription="" @@ -26,10 +26,10 @@ android:layout_marginVertical="16dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSectionCurrent" /> + app:layout_constraintTop_toBottomOf="@id/deviceListHeaderCurrentSession" /> <View - android:id="@+id/deviceListCurrentSessionDivider" + android:id="@+id/deviceListDividerCurrentSession" android:layout_width="0dp" android:layout_height="1dp" android:layout_marginTop="24dp" @@ -39,14 +39,14 @@ app:layout_constraintTop_toBottomOf="@id/deviceListCurrentSession" /> <im.vector.app.features.settings.devices.v2.list.DevicesListHeaderView - android:id="@+id/deviceListHeaderSectionOther" + android:id="@+id/deviceListHeaderOtherSessions" android:layout_width="0dp" android:layout_height="wrap_content" app:devicesListHeaderDescription="@string/settings_sessions_other_description" app:devicesListHeaderTitle="@string/settings_sessions_other_title" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/deviceListCurrentSessionDivider" /> + app:layout_constraintTop_toBottomOf="@id/deviceListDividerCurrentSession" /> <im.vector.app.features.settings.devices.v2.list.OtherSessionsView android:id="@+id/deviceListOtherSessions" @@ -55,7 +55,7 @@ android:layout_marginTop="16dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSectionOther" /> + app:layout_constraintTop_toBottomOf="@id/deviceListHeaderOtherSessions" /> <include android:id="@+id/waiting_view" diff --git a/vector/src/main/res/layout/view_current_session.xml b/vector/src/main/res/layout/view_current_session.xml index c6899d506b..31ad3cce2c 100644 --- a/vector/src/main/res/layout/view_current_session.xml +++ b/vector/src/main/res/layout/view_current_session.xml @@ -61,7 +61,7 @@ <TextView android:id="@+id/currentSessionVerificationStatusDetailTextView" - style="@style/TextAppearance.Vector.Caption" + style="@style/TextAppearance.Vector.Body.DevicesManagement" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginHorizontal="32dp" diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index a3e76feadb..0fc9039257 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3208,8 +3208,9 @@ <!-- Device Manager --> <string name="device_manager_settings_active_sessions_show_all">Show All Sessions (V2, WIP)</string> <string name="a11y_device_manager_device_type_mobile">Mobile</string> - <string name="a11y_device_manager_device_type_web" tools:ignore="UnusedResources">Web</string> - <string name="a11y_device_manager_device_type_desktop" tools:ignore="UnusedResources">Desktop</string> + <string name="a11y_device_manager_device_type_web">Web</string> + <string name="a11y_device_manager_device_type_desktop">Desktop</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_unverified">Unverified session</string> <string name="device_manager_verification_status_detail_verified">Your current session is ready for secure messaging.</string> From ec851992509921f9301b066c11b55ff3932b6879 Mon Sep 17 00:00:00 2001 From: Onuray Sahin <onuray.sahin@gmail.com> Date: Tue, 30 Aug 2022 15:51:53 +0300 Subject: [PATCH 10/11] Code review fixes. --- .../features/settings/devices/v2/list/SessionListConstants.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt index f4aadeb337..c1dbbdff4f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt @@ -16,4 +16,4 @@ package im.vector.app.features.settings.devices.v2.list -const val NUMBER_OF_OTHER_DEVICES_TO_RENDER = 5 +internal const val NUMBER_OF_OTHER_DEVICES_TO_RENDER = 5 From 357a859cdcea7ae44f7682792595e5f2db6d0849 Mon Sep 17 00:00:00 2001 From: Onuray Sahin <onuray.sahin@gmail.com> Date: Tue, 30 Aug 2022 16:28:02 +0300 Subject: [PATCH 11/11] Cleanup recyclerview. --- .../features/settings/devices/v2/list/OtherSessionsView.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt index bcb9726b5c..55978e61fd 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt @@ -21,6 +21,7 @@ import android.util.AttributeSet import androidx.constraintlayout.widget.ConstraintLayout import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R +import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.databinding.ViewOtherSessionsBinding import im.vector.app.features.settings.devices.DeviceFullInfo @@ -47,4 +48,9 @@ class OtherSessionsView @JvmOverloads constructor( views.otherSessionsViewAllButton.text = context.getString(R.string.device_manager_other_sessions_view_all, devices.size) otherSessionsController.setData(devices) } + + override fun onDetachedFromWindow() { + views.otherSessionsRecyclerView.cleanup() + super.onDetachedFromWindow() + } }