mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-18 20:29:10 +03:00
Merge pull request #6946 from vector-im/feature/ons/device_manager_other_session_list
[Device Manager] Render other sessions (PSG-668)
This commit is contained in:
commit
6341cf92a3
16 changed files with 507 additions and 81 deletions
1
changelog.d/6945.wip
Normal file
1
changelog.d/6945.wip
Normal file
|
@ -0,0 +1 @@
|
|||
[Device Manager] Other Sessions Section
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
* Display the list of the user's devices and sessions.
|
||||
|
@ -114,42 +114,61 @@ 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 ->
|
||||
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)
|
||||
renderCurrentDevice(currentDeviceInfo)
|
||||
renderOtherSessionsView(otherDevices)
|
||||
} else {
|
||||
hideCurrentSessionView()
|
||||
hideOtherSessionsView()
|
||||
}
|
||||
|
||||
handleRequestStatus(state.request)
|
||||
}
|
||||
|
||||
private fun hideCurrentSessionView() {
|
||||
views.deviceListHeaderSectionCurrent.isVisible = false
|
||||
views.deviceListCurrentSession.isVisible = false
|
||||
private fun renderOtherSessionsView(otherDevices: List<DeviceFullInfo>?) {
|
||||
if (otherDevices.isNullOrEmpty()) {
|
||||
hideOtherSessionsView()
|
||||
} else {
|
||||
views.deviceListHeaderOtherSessions.isVisible = true
|
||||
views.deviceListOtherSessions.isVisible = true
|
||||
views.deviceListOtherSessions.render(otherDevices)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderCurrentDevice(state: DevicesViewState) {
|
||||
views.deviceListHeaderSectionCurrent.isVisible = true
|
||||
views.deviceListCurrentSession.isVisible = true
|
||||
views.deviceListCurrentSession.update(
|
||||
accountCrossSigningIsTrusted = state.accountCrossSigningIsTrusted,
|
||||
legacyMode = !state.hasAccountCrossSigning
|
||||
)
|
||||
private fun hideOtherSessionsView() {
|
||||
views.deviceListHeaderOtherSessions.isVisible = false
|
||||
views.deviceListOtherSessions.isVisible = false
|
||||
}
|
||||
|
||||
private fun renderCurrentDevice(currentDeviceInfo: DeviceFullInfo?) {
|
||||
currentDeviceInfo?.let {
|
||||
views.deviceListHeaderCurrentSession.isVisible = true
|
||||
views.deviceListCurrentSession.isVisible = true
|
||||
views.deviceListCurrentSession.render(it)
|
||||
} ?: run {
|
||||
hideCurrentSessionView()
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideCurrentSessionView() {
|
||||
views.deviceListHeaderCurrentSession.isVisible = false
|
||||
views.deviceListCurrentSession.isVisible = false
|
||||
}
|
||||
|
||||
private fun handleRequestStatus(unIgnoreRequest: Async<Unit>) {
|
||||
|
|
|
@ -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) {
|
||||
renderDeviceType()
|
||||
renderVerificationStatus(accountCrossSigningIsTrusted, legacyMode)
|
||||
fun render(currentDeviceInfo: DeviceFullInfo) {
|
||||
renderDeviceInfo(currentDeviceInfo.deviceInfo.displayName.orEmpty())
|
||||
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()
|
||||
|
@ -75,9 +68,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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 DeviceType {
|
||||
MOBILE,
|
||||
WEB,
|
||||
DESKTOP,
|
||||
UNKNOWN,
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.resources.StringProvider
|
||||
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: DeviceType = DeviceType.UNKNOWN
|
||||
|
||||
@EpoxyAttribute
|
||||
var roomEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var sessionName: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var sessionDescription: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var stringProvider: StringProvider
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.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(
|
||||
private val stringProvider: StringProvider,
|
||||
private val dateFormatter: VectorDateFormatter,
|
||||
) : TypedEpoxyController<List<DeviceFullInfo>>() {
|
||||
|
||||
override fun buildModels(data: List<DeviceFullInfo>?) {
|
||||
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(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 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
|
||||
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 {
|
||||
inflate(context, R.layout.view_other_sessions, this)
|
||||
views = ViewOtherSessionsBinding.bind(this)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
views.otherSessionsRecyclerView.cleanup()
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
internal const val NUMBER_OF_OTHER_DEVICES_TO_RENDER = 5
|
14
vector/src/main/res/drawable/circle_with_border.xml
Normal file
14
vector/src/main/res/drawable/circle_with_border.xml
Normal file
|
@ -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>
|
10
vector/src/main/res/drawable/ic_device_type_unknown.xml
Normal file
10
vector/src/main/res/drawable/ic_device_type_unknown.xml
Normal file
|
@ -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>
|
|
@ -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/deviceListHeaderCurrentSession"
|
||||
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/deviceListHeaderCurrentSession" />
|
||||
|
||||
<include
|
||||
android:id="@+id/waiting_view"
|
||||
layout="@layout/merge_overlay_waiting_view" />
|
||||
<View
|
||||
android:id="@+id/deviceListDividerCurrentSession"
|
||||
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/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/deviceListDividerCurrentSession" />
|
||||
|
||||
<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/deviceListHeaderOtherSessions" />
|
||||
|
||||
<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>
|
||||
|
|
66
vector/src/main/res/layout/item_other_session.xml
Normal file
66
vector/src/main/res/layout/item_other_session.xml
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?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"
|
||||
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: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/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.Body.DevicesManagement"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
app:layout_constraintStart_toStartOf="@id/otherSessionNameTextView"
|
||||
app:layout_constraintTop_toBottomOf="@id/otherSessionNameTextView"
|
||||
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/otherSessionNameTextView"
|
||||
app:layout_constraintTop_toBottomOf="@id/otherSessionDescriptionTextView" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -21,15 +21,15 @@
|
|||
tools:src="@drawable/ic_device_type_mobile" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/currentSessionDeviceTypeTextView"
|
||||
style="@style/TextAppearance.Vector.Headline.Medium"
|
||||
android:id="@+id/currentSessionNameTextView"
|
||||
style="@style/TextAppearance.Vector.Subtitle.Medium.DevicesManagement"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
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"
|
||||
|
@ -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"
|
||||
|
|
28
vector/src/main/res/layout/view_other_sessions.xml
Normal file
28
vector/src/main/res/layout/view_other_sessions.xml
Normal file
|
@ -0,0 +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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
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"
|
||||
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"
|
||||
android:padding="0dp"
|
||||
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>
|
|
@ -3209,9 +3209,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="device_manager_device_type_android">${app_name} Mobile: Android</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>
|
||||
|
@ -3219,5 +3219,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 (%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>
|
||||
|
|
Loading…
Add table
Reference in a new issue