List devices.
This commit is contained in:
parent
643f99b8e0
commit
ab4ebc7f11
|
@ -19,6 +19,8 @@ package im.vector.app.features.settings.devices.v2
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MavericksState
|
import com.airbnb.mvrx.MavericksState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
|
|
||||||
data class DevicesViewState(
|
data class DevicesViewState(
|
||||||
val currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo = CurrentSessionCrossSigningInfo(),
|
val currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo = CurrentSessionCrossSigningInfo(),
|
||||||
|
@ -26,4 +28,17 @@ data class DevicesViewState(
|
||||||
val unverifiedSessionsCount: Int = 0,
|
val unverifiedSessionsCount: Int = 0,
|
||||||
val inactiveSessionsCount: Int = 0,
|
val inactiveSessionsCount: Int = 0,
|
||||||
val isLoading: Boolean = false,
|
val isLoading: Boolean = false,
|
||||||
) : MavericksState
|
val currentFilter: DeviceManagerFilterType = DeviceManagerFilterType.ALL_SESSIONS,
|
||||||
|
) : MavericksState {
|
||||||
|
|
||||||
|
fun List<DeviceFullInfo>?.filteredDevices(): List<DeviceFullInfo>? {
|
||||||
|
return this?.filter {
|
||||||
|
when (currentFilter) {
|
||||||
|
DeviceManagerFilterType.ALL_SESSIONS -> true
|
||||||
|
DeviceManagerFilterType.VERIFIED -> it.cryptoDeviceInfo?.isVerified.orFalse()
|
||||||
|
DeviceManagerFilterType.UNVERIFIED -> !it.cryptoDeviceInfo?.isVerified.orFalse()
|
||||||
|
DeviceManagerFilterType.INACTIVE -> it.isInactive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -37,10 +37,11 @@ import im.vector.app.core.resources.DrawableProvider
|
||||||
import im.vector.app.databinding.FragmentSettingsDevicesBinding
|
import im.vector.app.databinding.FragmentSettingsDevicesBinding
|
||||||
import im.vector.app.features.crypto.recover.SetupMode
|
import im.vector.app.features.crypto.recover.SetupMode
|
||||||
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
||||||
|
import im.vector.app.features.settings.devices.v2.list.NUMBER_OF_OTHER_DEVICES_TO_RENDER
|
||||||
|
import im.vector.app.features.settings.devices.v2.list.OtherSessionsView
|
||||||
import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS
|
import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS
|
||||||
import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationViewState
|
import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationViewState
|
||||||
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
|
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
|
||||||
import im.vector.app.features.settings.devices.v2.list.OtherSessionsView
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -198,7 +199,7 @@ class VectorSettingsDevicesFragment :
|
||||||
} else {
|
} else {
|
||||||
views.deviceListHeaderOtherSessions.isVisible = true
|
views.deviceListHeaderOtherSessions.isVisible = true
|
||||||
views.deviceListOtherSessions.isVisible = true
|
views.deviceListOtherSessions.isVisible = true
|
||||||
views.deviceListOtherSessions.render(otherDevices)
|
views.deviceListOtherSessions.render(otherDevices.take(NUMBER_OF_OTHER_DEVICES_TO_RENDER), otherDevices.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ class OtherSessionsController @Inject constructor(
|
||||||
text(host.stringProvider.getString(R.string.no_result_placeholder))
|
text(host.stringProvider.getString(R.string.no_result_placeholder))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
data.take(NUMBER_OF_OTHER_DEVICES_TO_RENDER).forEach { device ->
|
data.forEach { device ->
|
||||||
val dateFormatKind = if (device.isInactive) DateFormatKind.TIMELINE_DAY_DIVIDER else DateFormatKind.DEFAULT_DATE_AND_TIME
|
val dateFormatKind = if (device.isInactive) DateFormatKind.TIMELINE_DAY_DIVIDER else DateFormatKind.DEFAULT_DATE_AND_TIME
|
||||||
val formattedLastActivityDate = host.dateFormatter.format(device.deviceInfo.lastSeenTs, dateFormatKind)
|
val formattedLastActivityDate = host.dateFormatter.format(device.deviceInfo.lastSeenTs, dateFormatKind)
|
||||||
val description = if (device.isInactive) {
|
val description = if (device.isInactive) {
|
||||||
|
|
|
@ -55,9 +55,9 @@ class OtherSessionsView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(devices: List<DeviceFullInfo>) {
|
fun render(devices: List<DeviceFullInfo>, totalNumberOfDevices: Int) {
|
||||||
views.otherSessionsRecyclerView.configureWith(otherSessionsController, hasFixedSize = true)
|
views.otherSessionsRecyclerView.configureWith(otherSessionsController, hasFixedSize = true)
|
||||||
views.otherSessionsViewAllButton.text = context.getString(R.string.device_manager_other_sessions_view_all, devices.size)
|
views.otherSessionsViewAllButton.text = context.getString(R.string.device_manager_other_sessions_view_all, totalNumberOfDevices)
|
||||||
otherSessionsController.setData(devices)
|
otherSessionsController.setData(devices)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,13 @@ class SessionsListHeaderView @JvmOverloads constructor(
|
||||||
|
|
||||||
private fun setTitle(typedArray: TypedArray) {
|
private fun setTitle(typedArray: TypedArray) {
|
||||||
val title = typedArray.getString(R.styleable.SessionsListHeaderView_devicesListHeaderTitle)
|
val title = typedArray.getString(R.styleable.SessionsListHeaderView_devicesListHeaderTitle)
|
||||||
|
if (title.isNullOrEmpty()) {
|
||||||
|
binding.sessionsListHeaderTitle.isVisible = false
|
||||||
|
} else {
|
||||||
|
binding.sessionsListHeaderTitle.isVisible = true
|
||||||
binding.sessionsListHeaderTitle.text = title
|
binding.sessionsListHeaderTitle.text = title
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setDescription(typedArray: TypedArray) {
|
private fun setDescription(typedArray: TypedArray) {
|
||||||
val description = typedArray.getString(R.styleable.SessionsListHeaderView_devicesListHeaderDescription)
|
val description = typedArray.getString(R.styleable.SessionsListHeaderView_devicesListHeaderDescription)
|
||||||
|
|
|
@ -21,16 +21,25 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
import com.airbnb.mvrx.withState
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment.ResultListener.Companion.RESULT_OK
|
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment.ResultListener.Companion.RESULT_OK
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.databinding.FragmentOtherSessionsBinding
|
import im.vector.app.databinding.FragmentOtherSessionsBinding
|
||||||
|
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
||||||
|
import im.vector.app.features.settings.devices.v2.DevicesViewModel
|
||||||
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterBottomSheet
|
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterBottomSheet
|
||||||
|
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class OtherSessionsFragment : VectorBaseFragment<FragmentOtherSessionsBinding>(), VectorBaseBottomSheetDialogFragment.ResultListener {
|
class OtherSessionsFragment : VectorBaseFragment<FragmentOtherSessionsBinding>(), VectorBaseBottomSheetDialogFragment.ResultListener {
|
||||||
|
|
||||||
|
private val viewModel: DevicesViewModel by fragmentViewModel()
|
||||||
|
|
||||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentOtherSessionsBinding {
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentOtherSessionsBinding {
|
||||||
return FragmentOtherSessionsBinding.inflate(layoutInflater, container, false)
|
return FragmentOtherSessionsBinding.inflate(layoutInflater, container, false)
|
||||||
}
|
}
|
||||||
|
@ -54,4 +63,25 @@ class OtherSessionsFragment : VectorBaseFragment<FragmentOtherSessionsBinding>()
|
||||||
Toast.makeText(requireContext(), data.toString(), Toast.LENGTH_LONG).show()
|
Toast.makeText(requireContext(), data.toString(), Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
|
if (state.devices is Success) {
|
||||||
|
with(state) {
|
||||||
|
val devices = state.devices()
|
||||||
|
?.filter { it.deviceInfo.deviceId != state.currentSessionCrossSigningInfo.deviceId }
|
||||||
|
?.filteredDevices()
|
||||||
|
renderDevices(devices, state.currentFilter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderDevices(devices: List<DeviceFullInfo>?, currentFilter: DeviceManagerFilterType) {
|
||||||
|
views.otherSessionsFilterBadgeImageView.isVisible = currentFilter != DeviceManagerFilterType.ALL_SESSIONS
|
||||||
|
|
||||||
|
if (devices.isNullOrEmpty()) {
|
||||||
|
// TODO. Render empty state
|
||||||
|
} else {
|
||||||
|
views.deviceListOtherSessions.render(devices, devices.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,4 +46,25 @@
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<im.vector.app.features.settings.devices.v2.list.SessionsListHeaderView
|
||||||
|
android:id="@+id/deviceListHeaderOtherSessions"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:devicesListHeaderDescription="@string/settings_sessions_other_description"
|
||||||
|
app:devicesListHeaderTitle=""
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/appBarLayout"/>
|
||||||
|
|
||||||
|
<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" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -23,10 +23,10 @@
|
||||||
style="@style/TextAppearance.Vector.Body.DevicesManagement"
|
style="@style/TextAppearance.Vector.Body.DevicesManagement"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/layout_horizontal_margin"
|
||||||
android:layout_marginTop="18.5dp"
|
android:layout_marginTop="18.5dp"
|
||||||
android:layout_marginEnd="40dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="@id/sessions_list_header_title"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/sessions_list_header_title"
|
app:layout_constraintTop_toBottomOf="@id/sessions_list_header_title"
|
||||||
tools:text="For best security, verify your sessions and sign out from any session that you don’t recognize or use anymore. Learn More." />
|
tools:text="For best security, verify your sessions and sign out from any session that you don’t recognize or use anymore. Learn More." />
|
||||||
</merge>
|
</merge>
|
||||||
|
|
Loading…
Reference in New Issue