Select devices with basic UI for tests

This commit is contained in:
Maxime NATUREL 2022-10-19 11:40:43 +02:00
parent ab2e91ae80
commit 5b1bf8a68e
8 changed files with 93 additions and 30 deletions

View File

@ -30,4 +30,5 @@ data class DeviceFullInfo(
val isCurrentDevice: Boolean,
val deviceExtendedInfo: DeviceExtendedInfo,
val matrixClientInfo: MatrixClientInfoContent?,
val isSelected: Boolean = false,
)

View File

@ -17,10 +17,12 @@
package im.vector.app.features.settings.devices.v2.list
import android.graphics.drawable.Drawable
import android.view.View
import android.view.View.OnLongClickListener
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
@ -57,6 +59,9 @@ abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(R.la
@EpoxyAttribute
lateinit var stringProvider: StringProvider
@EpoxyAttribute
var selected: Boolean = false
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var clickListener: ClickListener? = null
@ -81,6 +86,9 @@ abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(R.la
holder.otherSessionDescriptionTextView.setTextColor(it)
}
holder.otherSessionDescriptionTextView.setCompoundDrawablesWithIntrinsicBounds(sessionDescriptionDrawable, null, null, null)
// TODO set drawable with correct color and corners
val color = if (selected) R.color.alert_default_error_background else android.R.color.transparent
holder.otherSessionItemBackgroundView.setBackgroundColor(ContextCompat.getColor(holder.view.context, color))
}
class Holder : VectorEpoxyHolder() {
@ -88,5 +96,6 @@ abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(R.la
val otherSessionVerificationStatusImageView by bind<ShieldImageView>(R.id.otherSessionVerificationStatusImageView)
val otherSessionNameTextView by bind<TextView>(R.id.otherSessionNameTextView)
val otherSessionDescriptionTextView by bind<TextView>(R.id.otherSessionDescriptionTextView)
val otherSessionItemBackgroundView by bind<View>(R.id.otherSessionItemBackground)
}
}

View File

@ -73,6 +73,7 @@ class OtherSessionsController @Inject constructor(
sessionDescriptionDrawable(descriptionDrawable)
sessionDescriptionColor(descriptionColor)
stringProvider(this@OtherSessionsController.stringProvider)
selected(device.isSelected)
clickListener { device.deviceInfo.deviceId?.let { host.callback?.onItemClicked(it) } }
onLongClickListener(View.OnLongClickListener {
device.deviceInfo.deviceId?.let { host.callback?.onItemLongClicked(it) }

View File

@ -21,6 +21,7 @@ import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
sealed class OtherSessionsAction : VectorViewModelAction {
data class FilterDevices(val filterType: DeviceManagerFilterType) : OtherSessionsAction()
object EnableSelectMode : OtherSessionsAction()
data class EnableSelectMode(val deviceId: String?) : OtherSessionsAction()
object DisableSelectMode : OtherSessionsAction()
data class ToggleSelectionForDevice(val deviceId: String) : OtherSessionsAction()
}

View File

@ -89,8 +89,8 @@ class OtherSessionsFragment :
}
}
private fun enableSelectMode(isEnabled: Boolean) {
val action = if (isEnabled) OtherSessionsAction.EnableSelectMode else OtherSessionsAction.DisableSelectMode
private fun enableSelectMode(isEnabled: Boolean, deviceId: String? = null) {
val action = if (isEnabled) OtherSessionsAction.EnableSelectMode(deviceId) else OtherSessionsAction.DisableSelectMode
viewModel.handle(action)
}
@ -153,23 +153,25 @@ class OtherSessionsFragment :
}
override fun invalidate() = withState(viewModel) { state ->
updateToolbar(state.isSelectModeEnabled)
if (state.devices is Success) {
renderDevices(state.devices(), state.currentFilter)
val devices = state.devices().orEmpty()
renderDevices(devices, state.currentFilter)
updateToolbar(devices, state.isSelectModeEnabled)
}
}
private fun updateToolbar(isSelectModeEnabled: Boolean) {
private fun updateToolbar(devices: List<DeviceFullInfo>, isSelectModeEnabled: Boolean) {
invalidateOptionsMenu()
val title = if (isSelectModeEnabled) {
stringProvider.getQuantityString(R.plurals.device_manager_other_sessions_selected, 0, 0)
val selection = devices.count { it.isSelected }
stringProvider.getQuantityString(R.plurals.device_manager_other_sessions_selected, selection, selection)
} else {
getString(args.titleResourceId)
}
toolbar?.title = title
}
private fun renderDevices(devices: List<DeviceFullInfo>?, currentFilter: DeviceManagerFilterType) {
private fun renderDevices(devices: List<DeviceFullInfo>, currentFilter: DeviceManagerFilterType) {
views.otherSessionsFilterBadgeImageView.isVisible = currentFilter != DeviceManagerFilterType.ALL_SESSIONS
views.otherSessionsSecurityRecommendationView.isVisible = currentFilter != DeviceManagerFilterType.ALL_SESSIONS
views.deviceListHeaderOtherSessions.isVisible = currentFilter == DeviceManagerFilterType.ALL_SESSIONS
@ -222,7 +224,7 @@ class OtherSessionsFragment :
}
}
if (devices.isNullOrEmpty()) {
if (devices.isEmpty()) {
views.deviceListOtherSessions.isVisible = false
views.otherSessionsNotFoundLayout.isVisible = true
} else {
@ -254,15 +256,19 @@ class OtherSessionsFragment :
override fun onOtherSessionLongClicked(deviceId: String) = withState(viewModel) { state ->
if (!state.isSelectModeEnabled) {
enableSelectMode(true)
enableSelectMode(true, deviceId)
}
}
override fun onOtherSessionClicked(deviceId: String) {
viewNavigator.navigateToSessionOverview(
context = requireActivity(),
deviceId = deviceId
)
override fun onOtherSessionClicked(deviceId: String) = withState(viewModel) { state ->
if (state.isSelectModeEnabled) {
viewModel.handle(OtherSessionsAction.ToggleSelectionForDevice(deviceId))
} else {
viewNavigator.navigateToSessionOverview(
context = requireActivity(),
deviceId = deviceId
)
}
}
override fun onViewAllOtherSessionsClicked() {

View File

@ -17,6 +17,7 @@
package im.vector.app.features.settings.devices.v2.othersessions
import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.Success
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -69,7 +70,8 @@ class OtherSessionsViewModel @AssistedInject constructor(
when (action) {
is OtherSessionsAction.FilterDevices -> handleFilterDevices(action)
OtherSessionsAction.DisableSelectMode -> handleDisableSelectMode()
OtherSessionsAction.EnableSelectMode -> handleEnableSelectMode()
is OtherSessionsAction.EnableSelectMode -> handleEnableSelectMode(action.deviceId)
is OtherSessionsAction.ToggleSelectionForDevice -> handleToggleSelectionForDevice(action.deviceId)
}
}
@ -83,10 +85,37 @@ class OtherSessionsViewModel @AssistedInject constructor(
}
private fun handleDisableSelectMode() {
// TODO deselect all selected sessions
setState { copy(isSelectModeEnabled = false) }
}
private fun handleEnableSelectMode() {
setState { copy(isSelectModeEnabled = true) }
private fun handleEnableSelectMode(deviceId: String?) {
toggleSelectionForDevice(deviceId, true)
}
private fun handleToggleSelectionForDevice(deviceId: String) = withState { state ->
toggleSelectionForDevice(deviceId, state.isSelectModeEnabled)
}
private fun toggleSelectionForDevice(deviceId: String?, enableSelectMode: Boolean) = withState { state ->
val updatedDevices = if (state.devices is Success) {
val devices = state.devices.invoke().toMutableList()
val indexToUpdate = devices.indexOfFirst { it.deviceInfo.deviceId == deviceId }
if (indexToUpdate >= 0) {
val currentInfo = devices[indexToUpdate]
val updatedInfo = currentInfo.copy(isSelected = !currentInfo.isSelected)
devices[indexToUpdate] = updatedInfo
}
Success(devices)
} else {
state.devices
}
setState {
copy(
devices = updatedDevices,
isSelectModeEnabled = enableSelectMode
)
}
}
}

View File

@ -5,30 +5,44 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?selectableItemBackground"
android:paddingTop="16dp">
android:paddingTop="8dp"
android:paddingHorizontal="8dp">
<View
android:id="@+id/otherSessionItemBackground"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@id/otherSessionVerificationStatusImageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/otherSessionDeviceTypeImageView"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="11dp"
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"
app:layout_constraintBottom_toBottomOf="@+id/otherSessionItemBackground"
app:layout_constraintStart_toStartOf="@+id/otherSessionItemBackground"
app:layout_constraintTop_toTopOf="@+id/otherSessionItemBackground"
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_marginStart="26dp"
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"
app:layout_constraintStart_toStartOf="@+id/otherSessionDeviceTypeImageView"
app:layout_constraintTop_toTopOf="@id/otherSessionDeviceTypeImageView"
tools:src="@drawable/ic_shield_trusted" />
<TextView
@ -37,21 +51,23 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:lines="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/otherSessionDeviceTypeImageView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toTopOf="@id/otherSessionDeviceTypeImageView"
tools:text="Element Mobile: Android" />
<TextView
android:id="@+id/otherSessionDescriptionTextView"
style="@style/TextAppearance.Vector.Body.DevicesManagement"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:drawablePadding="8dp"
app:layout_constraintBottom_toBottomOf="@id/otherSessionDeviceTypeImageView"
app:layout_constraintEnd_toEndOf="@id/otherSessionNameTextView"
app:layout_constraintStart_toStartOf="@id/otherSessionNameTextView"
app:layout_constraintTop_toBottomOf="@id/otherSessionNameTextView"
tools:text="@string/device_manager_verification_status_verified" />
@ -59,10 +75,10 @@
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginTop="16dp"
android:layout_marginTop="8dp"
android:background="?vctr_content_quinary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/otherSessionNameTextView"
app:layout_constraintTop_toBottomOf="@id/otherSessionDescriptionTextView" />
app:layout_constraintTop_toBottomOf="@+id/otherSessionItemBackground" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -9,7 +9,6 @@
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"
@ -21,6 +20,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="0dp"
android:layout_marginStart="16dp"
app:layout_constraintStart_toStartOf="@id/otherSessionsRecyclerView"
app:layout_constraintTop_toBottomOf="@id/otherSessionsRecyclerView"
tools:text="@string/device_manager_other_sessions_view_all" />