Skip to content

Debounced click #3435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jun 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragment/3435.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Cleanup Epoxy items, and debounce all the clicks
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ abstract class ErrorWithRetryItem : VectorEpoxyModel<ErrorWithRetryItem.Holder>(
@EpoxyAttribute
var text: String? = null

@EpoxyAttribute
var listener: (() -> Unit)? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var listener: ClickListener? = null

override fun bind(holder: Holder) {
super.bind(holder)
holder.textView.text = text
holder.buttonView.isVisible = listener != null
holder.buttonView.setOnClickListener { listener?.invoke() }
holder.buttonView.onClick(listener)
}

class Holder : VectorEpoxyHolder() {
Expand Down
16 changes: 13 additions & 3 deletions vector/src/main/java/im/vector/app/core/epoxy/Listener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,22 @@
package im.vector.app.core.epoxy

import android.view.View
import im.vector.app.core.utils.DebouncedClickListener

/**
* Generally we do not care about the View parameter in [View.OnClickListener.onClick()], so create facility to remove it.
* View.OnClickListener lambda
*/
typealias ClickListener = () -> Unit
typealias ClickListener = (View) -> Unit

fun View.onClick(listener: ClickListener?) {
setOnClickListener { listener?.invoke() }
if (listener == null) {
setOnClickListener(null)
} else {
setOnClickListener(DebouncedClickListener(listener))
}
}

/**
* Simple Text listener lambda
*/
typealias TextListener = (String) -> Unit
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package im.vector.app.core.epoxy

import android.text.TextWatcher
import android.widget.CompoundButton
import android.widget.TextView
import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.textfield.TextInputEditText

Expand All @@ -39,3 +41,9 @@ fun VectorEpoxyHolder.setValueOnce(switchView: SwitchMaterial, switchChecked: Bo
switchView.setOnCheckedChangeListener(listener)
}
}

fun TextView.addTextChangedListenerOnce(textWatcher: TextWatcher) {
// Ensure the watcher is not added multiple times
removeTextChangedListener(textWatcher)
addTextChangedListener(textWatcher)
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ import androidx.core.widget.ImageViewCompat
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.features.themes.ThemeUtils

/**
Expand Down Expand Up @@ -69,14 +71,12 @@ abstract class BottomSheetActionItem : VectorEpoxyModel<BottomSheetActionItem.Ho
@EpoxyAttribute
var destructive = false

@EpoxyAttribute
lateinit var listener: View.OnClickListener
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
lateinit var listener: ClickListener

override fun bind(holder: Holder) {
super.bind(holder)
holder.view.setOnClickListener {
listener.onClick(it)
}
holder.view.onClick(listener)
holder.startSpace.isVisible = subMenuItem
val tintColor = if (destructive) {
ContextCompat.getColor(holder.view.context, R.color.riotx_notice)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess
Expand Down Expand Up @@ -61,14 +63,14 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
@EpoxyAttribute
var movementMethod: MovementMethod? = null

@EpoxyAttribute
var userClicked: (() -> Unit)? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var userClicked: ClickListener? = null

override fun bind(holder: Holder) {
super.bind(holder)
avatarRenderer.render(matrixItem, holder.avatar)
holder.avatar.setOnClickListener { userClicked?.invoke() }
holder.sender.setOnClickListener { userClicked?.invoke() }
holder.avatar.onClick(userClicked)
holder.sender.onClick(userClicked)
holder.sender.setTextOrHide(matrixItem.displayName)
data?.let {
imageContentRenderer?.render(it, ImageContentRenderer.Mode.THUMBNAIL, holder.imagePreview)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import im.vector.app.EmojiCompatFontProvider
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick

/**
* A quick reaction list for bottom sheet.
Expand All @@ -50,7 +51,7 @@ abstract class BottomSheetQuickReactionsItem : VectorEpoxyModel<BottomSheetQuick
textView.text = texts[index]
textView.alpha = if (selecteds[index]) 0.2f else 1f

textView.setOnClickListener {
textView.onClick {
listener?.didSelect(texts[index], !selecteds[index])
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@
*/
package im.vector.app.core.epoxy.bottomsheet

import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide

/**
Expand All @@ -47,15 +48,12 @@ abstract class BottomSheetRadioActionItem : VectorEpoxyModel<BottomSheetRadioAct
@EpoxyAttribute
var description: CharSequence? = null

@EpoxyAttribute
lateinit var listener: View.OnClickListener
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
lateinit var listener: ClickListener

override fun bind(holder: Holder) {
super.bind(holder)
holder.view.setOnClickListener {
listener.onClick(it)
}

holder.view.onClick(listener)
if (titleRes != null) {
holder.titleText.setText(titleRes!!)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ abstract class BottomSheetRoomPreviewItem : VectorEpoxyModel<BottomSheetRoomPrev
@EpoxyAttribute lateinit var stringProvider: StringProvider
@EpoxyAttribute var izLowPriority: Boolean = false
@EpoxyAttribute var izFavorite: Boolean = false
@EpoxyAttribute var settingsClickListener: ClickListener? = null
@EpoxyAttribute var lowPriorityClickListener: ClickListener? = null
@EpoxyAttribute var favoriteClickListener: ClickListener? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var settingsClickListener: ClickListener? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var lowPriorityClickListener: ClickListener? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var favoriteClickListener: ClickListener? = null

override fun bind(holder: Holder) {
super.bind(holder)
Expand All @@ -59,25 +59,25 @@ abstract class BottomSheetRoomPreviewItem : VectorEpoxyModel<BottomSheetRoomPrev
setLowPriorityState(holder, izLowPriority)
setFavoriteState(holder, izFavorite)

holder.roomLowPriority.setOnClickListener {
holder.roomLowPriority.onClick {
// Immediate echo
setLowPriorityState(holder, !izLowPriority)
if (!izLowPriority) {
// If we put the room in low priority, it will also remove the favorite tag
setFavoriteState(holder, false)
}
// And do the action
lowPriorityClickListener?.invoke()
lowPriorityClickListener?.invoke(it)
}
holder.roomFavorite.setOnClickListener {
holder.roomFavorite.onClick {
// Immediate echo
setFavoriteState(holder, !izFavorite)
if (!izFavorite) {
// If we put the room in favorite, it will also remove the low priority tag
setLowPriorityState(holder, false)
}
// And do the action
favoriteClickListener?.invoke()
favoriteClickListener?.invoke(it)
}
holder.roomSettings.apply {
onClick(settingsClickListener)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@

package im.vector.app.core.epoxy.profiles

import android.view.View
import androidx.annotation.CallSuper
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.home.AvatarRenderer
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
Expand All @@ -33,7 +34,9 @@ abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder> : VectorEpoxy

@EpoxyAttribute
var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
@EpoxyAttribute var clickListener: View.OnClickListener? = null

@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var clickListener: ClickListener? = null

@CallSuper
override fun bind(holder: T) {
Expand All @@ -43,7 +46,7 @@ abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder> : VectorEpoxy
.takeIf { it != bestName }
// Special case for ThreePid fake matrix item
.takeIf { it != "@" }
holder.view.setOnClickListener(clickListener?.takeIf { editable })
holder.view.onClick(clickListener?.takeIf { editable })
holder.titleView.text = bestName
holder.subtitleView.setTextOrHide(matrixId)
holder.editableView.isVisible = editable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ abstract class ProfileActionItem : VectorEpoxyModel<ProfileActionItem.Holder>()
@EpoxyAttribute
var destructive: Boolean = false

@EpoxyAttribute
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var listener: ClickListener? = null

override fun bind(holder: Holder) {
Expand Down
21 changes: 11 additions & 10 deletions vector/src/main/java/im/vector/app/core/platform/ButtonStateView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import android.widget.FrameLayout
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.onClick
import im.vector.app.databinding.ViewButtonStateBinding

class ButtonStateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
Expand All @@ -36,12 +38,9 @@ class ButtonStateView @JvmOverloads constructor(context: Context, attrs: Attribu
object Error : State()
}

var callback: Callback? = null

interface Callback {
fun onButtonClicked()
fun onRetryClicked()
}
var commonClicked: ClickListener? = null
var buttonClicked: ClickListener? = null
var retryClicked: ClickListener? = null

// Big or Flat button
var button: Button
Expand All @@ -54,8 +53,9 @@ class ButtonStateView @JvmOverloads constructor(context: Context, attrs: Attribu

layoutParams = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)

views.buttonStateRetry.setOnClickListener {
callback?.onRetryClicked()
views.buttonStateRetry.onClick {
commonClicked?.invoke(it)
retryClicked?.invoke(it)
}

// Read attributes
Expand All @@ -80,8 +80,9 @@ class ButtonStateView @JvmOverloads constructor(context: Context, attrs: Attribu
}
}

button.setOnClickListener {
callback?.onButtonClicked()
button.onClick {
commonClicked?.invoke(it)
buttonClicked?.invoke(it)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package im.vector.app.core.ui.bottomsheet

import android.view.View
import com.airbnb.epoxy.TypedEpoxyController

/**
Expand Down Expand Up @@ -51,7 +50,7 @@ abstract class BottomSheetGenericController<State : BottomSheetGenericState, Act
val actions = getActions(state)
actions.forEach { action ->
action.toRadioBottomSheetItem()
.listener(View.OnClickListener { listener?.didSelectAction(action) })
.listener { listener?.didSelectAction(action) }
.addTo(this)
}
}
Expand Down
24 changes: 24 additions & 0 deletions vector/src/main/java/im/vector/app/core/ui/list/Action.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2021 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.core.ui.list

import im.vector.app.core.epoxy.ClickListener

data class Action(
val title: String,
val listener: ClickListener
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
*/
package im.vector.app.core.ui.list

import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.google.android.material.button.MaterialButton
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.features.themes.ThemeUtils

/**
Expand All @@ -35,8 +36,8 @@ abstract class GenericButtonItem : VectorEpoxyModel<GenericButtonItem.Holder>()
@EpoxyAttribute
var text: String? = null

@EpoxyAttribute
var buttonClickAction: View.OnClickListener? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var buttonClickAction: ClickListener? = null

@EpoxyAttribute
@ColorInt
Expand All @@ -57,7 +58,7 @@ abstract class GenericButtonItem : VectorEpoxyModel<GenericButtonItem.Holder>()
holder.button.icon = null
}

buttonClickAction?.let { holder.button.setOnClickListener(it) }
holder.button.onClick(buttonClickAction)
}

class Holder : VectorEpoxyHolder() {
Expand Down
Loading