Select devices with basic UI for tests
This commit is contained in:
parent
ab2e91ae80
commit
5b1bf8a68e
|
@ -30,4 +30,5 @@ data class DeviceFullInfo(
|
||||||
val isCurrentDevice: Boolean,
|
val isCurrentDevice: Boolean,
|
||||||
val deviceExtendedInfo: DeviceExtendedInfo,
|
val deviceExtendedInfo: DeviceExtendedInfo,
|
||||||
val matrixClientInfo: MatrixClientInfoContent?,
|
val matrixClientInfo: MatrixClientInfoContent?,
|
||||||
|
val isSelected: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,10 +17,12 @@
|
||||||
package im.vector.app.features.settings.devices.v2.list
|
package im.vector.app.features.settings.devices.v2.list
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.view.View
|
||||||
import android.view.View.OnLongClickListener
|
import android.view.View.OnLongClickListener
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
@ -57,6 +59,9 @@ abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(R.la
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
lateinit var stringProvider: StringProvider
|
lateinit var stringProvider: StringProvider
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var selected: Boolean = false
|
||||||
|
|
||||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
var clickListener: ClickListener? = null
|
var clickListener: ClickListener? = null
|
||||||
|
|
||||||
|
@ -81,6 +86,9 @@ abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(R.la
|
||||||
holder.otherSessionDescriptionTextView.setTextColor(it)
|
holder.otherSessionDescriptionTextView.setTextColor(it)
|
||||||
}
|
}
|
||||||
holder.otherSessionDescriptionTextView.setCompoundDrawablesWithIntrinsicBounds(sessionDescriptionDrawable, null, null, null)
|
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() {
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
@ -88,5 +96,6 @@ abstract class OtherSessionItem : VectorEpoxyModel<OtherSessionItem.Holder>(R.la
|
||||||
val otherSessionVerificationStatusImageView by bind<ShieldImageView>(R.id.otherSessionVerificationStatusImageView)
|
val otherSessionVerificationStatusImageView by bind<ShieldImageView>(R.id.otherSessionVerificationStatusImageView)
|
||||||
val otherSessionNameTextView by bind<TextView>(R.id.otherSessionNameTextView)
|
val otherSessionNameTextView by bind<TextView>(R.id.otherSessionNameTextView)
|
||||||
val otherSessionDescriptionTextView by bind<TextView>(R.id.otherSessionDescriptionTextView)
|
val otherSessionDescriptionTextView by bind<TextView>(R.id.otherSessionDescriptionTextView)
|
||||||
|
val otherSessionItemBackgroundView by bind<View>(R.id.otherSessionItemBackground)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,7 @@ class OtherSessionsController @Inject constructor(
|
||||||
sessionDescriptionDrawable(descriptionDrawable)
|
sessionDescriptionDrawable(descriptionDrawable)
|
||||||
sessionDescriptionColor(descriptionColor)
|
sessionDescriptionColor(descriptionColor)
|
||||||
stringProvider(this@OtherSessionsController.stringProvider)
|
stringProvider(this@OtherSessionsController.stringProvider)
|
||||||
|
selected(device.isSelected)
|
||||||
clickListener { device.deviceInfo.deviceId?.let { host.callback?.onItemClicked(it) } }
|
clickListener { device.deviceInfo.deviceId?.let { host.callback?.onItemClicked(it) } }
|
||||||
onLongClickListener(View.OnLongClickListener {
|
onLongClickListener(View.OnLongClickListener {
|
||||||
device.deviceInfo.deviceId?.let { host.callback?.onItemLongClicked(it) }
|
device.deviceInfo.deviceId?.let { host.callback?.onItemLongClicked(it) }
|
||||||
|
|
|
@ -21,6 +21,7 @@ import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
|
||||||
|
|
||||||
sealed class OtherSessionsAction : VectorViewModelAction {
|
sealed class OtherSessionsAction : VectorViewModelAction {
|
||||||
data class FilterDevices(val filterType: DeviceManagerFilterType) : OtherSessionsAction()
|
data class FilterDevices(val filterType: DeviceManagerFilterType) : OtherSessionsAction()
|
||||||
object EnableSelectMode : OtherSessionsAction()
|
data class EnableSelectMode(val deviceId: String?) : OtherSessionsAction()
|
||||||
object DisableSelectMode : OtherSessionsAction()
|
object DisableSelectMode : OtherSessionsAction()
|
||||||
|
data class ToggleSelectionForDevice(val deviceId: String) : OtherSessionsAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,8 +89,8 @@ class OtherSessionsFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enableSelectMode(isEnabled: Boolean) {
|
private fun enableSelectMode(isEnabled: Boolean, deviceId: String? = null) {
|
||||||
val action = if (isEnabled) OtherSessionsAction.EnableSelectMode else OtherSessionsAction.DisableSelectMode
|
val action = if (isEnabled) OtherSessionsAction.EnableSelectMode(deviceId) else OtherSessionsAction.DisableSelectMode
|
||||||
viewModel.handle(action)
|
viewModel.handle(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,23 +153,25 @@ class OtherSessionsFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
updateToolbar(state.isSelectModeEnabled)
|
|
||||||
if (state.devices is Success) {
|
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()
|
invalidateOptionsMenu()
|
||||||
val title = if (isSelectModeEnabled) {
|
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 {
|
} else {
|
||||||
getString(args.titleResourceId)
|
getString(args.titleResourceId)
|
||||||
}
|
}
|
||||||
toolbar?.title = title
|
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.otherSessionsFilterBadgeImageView.isVisible = currentFilter != DeviceManagerFilterType.ALL_SESSIONS
|
||||||
views.otherSessionsSecurityRecommendationView.isVisible = currentFilter != DeviceManagerFilterType.ALL_SESSIONS
|
views.otherSessionsSecurityRecommendationView.isVisible = currentFilter != DeviceManagerFilterType.ALL_SESSIONS
|
||||||
views.deviceListHeaderOtherSessions.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.deviceListOtherSessions.isVisible = false
|
||||||
views.otherSessionsNotFoundLayout.isVisible = true
|
views.otherSessionsNotFoundLayout.isVisible = true
|
||||||
} else {
|
} else {
|
||||||
|
@ -254,15 +256,19 @@ class OtherSessionsFragment :
|
||||||
|
|
||||||
override fun onOtherSessionLongClicked(deviceId: String) = withState(viewModel) { state ->
|
override fun onOtherSessionLongClicked(deviceId: String) = withState(viewModel) { state ->
|
||||||
if (!state.isSelectModeEnabled) {
|
if (!state.isSelectModeEnabled) {
|
||||||
enableSelectMode(true)
|
enableSelectMode(true, deviceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOtherSessionClicked(deviceId: String) {
|
override fun onOtherSessionClicked(deviceId: String) = withState(viewModel) { state ->
|
||||||
viewNavigator.navigateToSessionOverview(
|
if (state.isSelectModeEnabled) {
|
||||||
context = requireActivity(),
|
viewModel.handle(OtherSessionsAction.ToggleSelectionForDevice(deviceId))
|
||||||
deviceId = deviceId
|
} else {
|
||||||
)
|
viewNavigator.navigateToSessionOverview(
|
||||||
|
context = requireActivity(),
|
||||||
|
deviceId = deviceId
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewAllOtherSessionsClicked() {
|
override fun onViewAllOtherSessionsClicked() {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.app.features.settings.devices.v2.othersessions
|
package im.vector.app.features.settings.devices.v2.othersessions
|
||||||
|
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
@ -69,7 +70,8 @@ class OtherSessionsViewModel @AssistedInject constructor(
|
||||||
when (action) {
|
when (action) {
|
||||||
is OtherSessionsAction.FilterDevices -> handleFilterDevices(action)
|
is OtherSessionsAction.FilterDevices -> handleFilterDevices(action)
|
||||||
OtherSessionsAction.DisableSelectMode -> handleDisableSelectMode()
|
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() {
|
private fun handleDisableSelectMode() {
|
||||||
|
// TODO deselect all selected sessions
|
||||||
setState { copy(isSelectModeEnabled = false) }
|
setState { copy(isSelectModeEnabled = false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEnableSelectMode() {
|
private fun handleEnableSelectMode(deviceId: String?) {
|
||||||
setState { copy(isSelectModeEnabled = true) }
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,30 +5,44 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:foreground="?selectableItemBackground"
|
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
|
<ImageView
|
||||||
android:id="@+id/otherSessionDeviceTypeImageView"
|
android:id="@+id/otherSessionDeviceTypeImageView"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="11dp"
|
||||||
android:background="@drawable/bg_device_type"
|
android:background="@drawable/bg_device_type"
|
||||||
android:contentDescription="@string/a11y_device_manager_device_type_mobile"
|
android:contentDescription="@string/a11y_device_manager_device_type_mobile"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintBottom_toBottomOf="@+id/otherSessionItemBackground"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintStart_toStartOf="@+id/otherSessionItemBackground"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/otherSessionItemBackground"
|
||||||
tools:src="@drawable/ic_device_type_mobile" />
|
tools:src="@drawable/ic_device_type_mobile" />
|
||||||
|
|
||||||
<im.vector.app.core.ui.views.ShieldImageView
|
<im.vector.app.core.ui.views.ShieldImageView
|
||||||
android:id="@+id/otherSessionVerificationStatusImageView"
|
android:id="@+id/otherSessionVerificationStatusImageView"
|
||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginStart="24dp"
|
android:layout_marginStart="26dp"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
android:background="@drawable/circle_with_border"
|
android:background="@drawable/circle_with_border"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:padding="6dp"
|
android:padding="6dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="@+id/otherSessionDeviceTypeImageView"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="@id/otherSessionDeviceTypeImageView"
|
||||||
tools:src="@drawable/ic_shield_trusted" />
|
tools:src="@drawable/ic_shield_trusted" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -37,21 +51,23 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/otherSessionDeviceTypeImageView"
|
app:layout_constraintStart_toEndOf="@id/otherSessionDeviceTypeImageView"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="@id/otherSessionDeviceTypeImageView"
|
||||||
tools:text="Element Mobile: Android" />
|
tools:text="Element Mobile: Android" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/otherSessionDescriptionTextView"
|
android:id="@+id/otherSessionDescriptionTextView"
|
||||||
style="@style/TextAppearance.Vector.Body.DevicesManagement"
|
style="@style/TextAppearance.Vector.Body.DevicesManagement"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/otherSessionDeviceTypeImageView"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/otherSessionNameTextView"
|
||||||
app:layout_constraintStart_toStartOf="@id/otherSessionNameTextView"
|
app:layout_constraintStart_toStartOf="@id/otherSessionNameTextView"
|
||||||
app:layout_constraintTop_toBottomOf="@id/otherSessionNameTextView"
|
app:layout_constraintTop_toBottomOf="@id/otherSessionNameTextView"
|
||||||
tools:text="@string/device_manager_verification_status_verified" />
|
tools:text="@string/device_manager_verification_status_verified" />
|
||||||
|
@ -59,10 +75,10 @@
|
||||||
<View
|
<View
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="8dp"
|
||||||
android:background="?vctr_content_quinary"
|
android:background="?vctr_content_quinary"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="@id/otherSessionNameTextView"
|
app:layout_constraintStart_toStartOf="@id/otherSessionNameTextView"
|
||||||
app:layout_constraintTop_toBottomOf="@id/otherSessionDescriptionTextView" />
|
app:layout_constraintTop_toBottomOf="@+id/otherSessionItemBackground" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
android:id="@+id/otherSessionsRecyclerView"
|
android:id="@+id/otherSessionsRecyclerView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
@ -21,6 +20,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="0dp"
|
android:padding="0dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
app:layout_constraintStart_toStartOf="@id/otherSessionsRecyclerView"
|
app:layout_constraintStart_toStartOf="@id/otherSessionsRecyclerView"
|
||||||
app:layout_constraintTop_toBottomOf="@id/otherSessionsRecyclerView"
|
app:layout_constraintTop_toBottomOf="@id/otherSessionsRecyclerView"
|
||||||
tools:text="@string/device_manager_other_sessions_view_all" />
|
tools:text="@string/device_manager_other_sessions_view_all" />
|
||||||
|
|
Loading…
Reference in New Issue