Merge pull request #4008 from vector-im/feature/fga/small_timeline_optimisation

Feature/fga/small timeline optimisation
This commit is contained in:
Benoit Marty 2021-09-17 18:06:58 +02:00 committed by GitHub
commit b5f7351564
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 34 additions and 253 deletions

View file

@ -239,7 +239,7 @@ data class Event(
fun Event.isTextMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_TEXT,
MessageType.MSGTYPE_EMOTE,
MessageType.MSGTYPE_NOTICE -> true
@ -249,7 +249,7 @@ fun Event.isTextMessage(): Boolean {
fun Event.isImageMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_IMAGE -> true
else -> false
}
@ -257,7 +257,7 @@ fun Event.isImageMessage(): Boolean {
fun Event.isVideoMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_VIDEO -> true
else -> false
}
@ -265,7 +265,7 @@ fun Event.isVideoMessage(): Boolean {
fun Event.isAudioMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_AUDIO -> true
else -> false
}
@ -273,7 +273,7 @@ fun Event.isAudioMessage(): Boolean {
fun Event.isFileMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_FILE -> true
else -> false
}
@ -281,7 +281,7 @@ fun Event.isFileMessage(): Boolean {
fun Event.isAttachmentMessage(): Boolean {
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
&& when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) {
MessageType.MSGTYPE_IMAGE,
MessageType.MSGTYPE_AUDIO,
MessageType.MSGTYPE_VIDEO,

View file

@ -28,7 +28,7 @@ data class MessageAudioContent(
/**
* Required. Must be 'm.audio'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. A description of the audio e.g. 'Bee Gees - Stayin' Alive', or some kind of content description for accessibility e.g. 'audio attachment'.

View file

@ -20,6 +20,11 @@ import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
interface MessageContent {
companion object {
const val MSG_TYPE_JSON_KEY = "msgtype"
}
val msgType: String
val body: String
val relatesTo: RelationDefaultContent?

View file

@ -23,7 +23,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon
@JsonClass(generateAdapter = true)
data class MessageDefaultContent(
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
@Json(name = "body") override val body: String,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null

View file

@ -26,7 +26,7 @@ data class MessageEmoteContent(
/**
* Required. Must be 'm.emote'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. The emote action to perform.

View file

@ -28,7 +28,7 @@ data class MessageFileContent(
/**
* Required. Must be 'm.file'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. A human-readable description of the file. This is recommended to be the filename of the original upload.

View file

@ -27,7 +27,7 @@ data class MessageImageContent(
/**
* Required. Must be 'm.image'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. A textual representation of the image. This could be the alt text of the image, the filename of the image,

View file

@ -26,7 +26,7 @@ data class MessageLocationContent(
/**
* Required. Must be 'm.location'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. A description of the location e.g. 'Big Ben, London, UK', or some kind

View file

@ -26,7 +26,7 @@ data class MessageNoticeContent(
/**
* Required. Must be 'm.notice'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. The notice text to send.

View file

@ -30,7 +30,7 @@ const val OPTION_TYPE_BUTTONS = "org.matrix.buttons"
*/
@JsonClass(generateAdapter = true)
data class MessageOptionsContent(
@Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_OPTIONS,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_OPTIONS,
@Json(name = "type") val optionType: String? = null,
@Json(name = "body") override val body: String,
@Json(name = "label") val label: String?,

View file

@ -26,7 +26,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon
*/
@JsonClass(generateAdapter = true)
data class MessagePollResponseContent(
@Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_RESPONSE,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_RESPONSE,
@Json(name = "body") override val body: String,
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
@Json(name = "m.new_content") override val newContent: Content? = null

View file

@ -26,7 +26,7 @@ data class MessageTextContent(
/**
* Required. Must be 'm.text'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
/**
* Required. The body of the message.

View file

@ -24,7 +24,7 @@ import org.matrix.android.sdk.internal.crypto.verification.VerificationInfoReque
@JsonClass(generateAdapter = true)
data class MessageVerificationRequestContent(
@Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_VERIFICATION_REQUEST,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY)override val msgType: String = MessageType.MSGTYPE_VERIFICATION_REQUEST,
@Json(name = "body") override val body: String,
@Json(name = "from_device") override val fromDevice: String?,
@Json(name = "methods") override val methods: List<String>,

View file

@ -27,7 +27,7 @@ data class MessageVideoContent(
/**
* Required. Must be 'm.video'.
*/
@Json(name = "msgtype") override val msgType: String,
@Json(name = MessageContent.MSG_TYPE_JSON_KEY)override val msgType: String,
/**
* Required. A description of the video e.g. 'Gangnam style', or some kind of content description for accessibility e.g. 'video attachment'.

View file

@ -15,27 +15,18 @@
*/
package im.vector.app.features.reactions.widget
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.content.res.TypedArray
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.Gravity
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.view.animation.OvershootInterpolator
import android.widget.ImageView
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.constraintlayout.widget.ConstraintLayout
import android.widget.LinearLayout
import androidx.core.content.ContextCompat
import androidx.core.content.withStyledAttributes
import im.vector.app.EmojiCompatWrapper
import im.vector.app.R
import im.vector.app.core.di.HasScreenInjector
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.core.utils.TextUtils
import im.vector.app.databinding.ReactionButtonBinding
import javax.inject.Inject
@ -47,7 +38,7 @@ import javax.inject.Inject
class ReactionButton @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0)
: ConstraintLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener {
: LinearLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener {
init {
if (context is HasScreenInjector) {
@ -55,21 +46,11 @@ class ReactionButton @JvmOverloads constructor(context: Context,
}
}
companion object {
private val DECCELERATE_INTERPOLATOR = DecelerateInterpolator()
private val ACCELERATE_DECELERATE_INTERPOLATOR = AccelerateDecelerateInterpolator()
private val OVERSHOOT_INTERPOLATOR = OvershootInterpolator(4f)
}
@Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper
private val views: ReactionButtonBinding
var reactedListener: ReactedListener? = null
private var dotPrimaryColor: Int = 0
private var dotSecondaryColor: Int = 0
private var circleStartColor: Int = 0
private var circleEndColor: Int = 0
var reactionCount = 11
set(value) {
@ -85,50 +66,24 @@ class ReactionButton @JvmOverloads constructor(context: Context,
views.reactionText.text = emojiSpanned
}
private var animationScaleFactor: Float = 0.toFloat()
private var isChecked: Boolean = false
private var animatorSet: AnimatorSet? = null
private var onDrawable: Drawable? = null
private var offDrawable: Drawable? = null
init {
inflate(context, R.layout.reaction_button, this)
orientation = HORIZONTAL
minimumHeight = DimensionConverter(context.resources).dpToPx(30)
gravity = Gravity.CENTER
views = ReactionButtonBinding.bind(this)
views.reactionCount.text = TextUtils.formatCountToShortDecimal(reactionCount)
// emojiView?.typeface = this.emojiTypeFace ?: Typeface.DEFAULT
context.withStyledAttributes(attrs, R.styleable.ReactionButton, defStyleAttr) {
onDrawable = ContextCompat.getDrawable(context, R.drawable.reaction_rounded_rect_shape)
offDrawable = ContextCompat.getDrawable(context, R.drawable.reaction_rounded_rect_shape_off)
circleStartColor = getColor(R.styleable.ReactionButton_circle_start_color, 0)
if (circleStartColor != 0) {
views.circle.startColor = circleStartColor
}
circleEndColor = getColor(R.styleable.ReactionButton_circle_end_color, 0)
if (circleEndColor != 0) {
views.circle.endColor = circleEndColor
}
dotPrimaryColor = getColor(R.styleable.ReactionButton_dots_primary_color, 0)
dotSecondaryColor = getColor(R.styleable.ReactionButton_dots_secondary_color, 0)
if (dotPrimaryColor != 0 && dotSecondaryColor != 0) {
views.dots.setColors(dotPrimaryColor, dotSecondaryColor)
}
getString(R.styleable.ReactionButton_emoji)?.let {
reactionString = it
}
reactionCount = getInt(R.styleable.ReactionButton_reaction_count, 0)
val status = getBoolean(R.styleable.ReactionButton_toggled, false)
setChecked(status)
}
@ -137,12 +92,6 @@ class ReactionButton @JvmOverloads constructor(context: Context,
setOnLongClickListener(this)
}
private fun getDrawableFromResource(array: TypedArray, styleableIndexId: Int): Drawable? {
val id = array.getResourceId(styleableIndexId, -1)
return if (-1 != id) ContextCompat.getDrawable(context, id) else null
}
/**
* This triggers the entire functionality of the button such as icon changes,
* animations, listeners etc.
@ -153,164 +102,25 @@ class ReactionButton @JvmOverloads constructor(context: Context,
if (!isEnabled) {
return
}
isChecked = !isChecked
// icon!!.setImageDrawable(if (isChecked) likeDrawable else unLikeDrawable)
background = if (isChecked) onDrawable else offDrawable
if (isChecked) {
reactedListener?.onReacted(this)
} else {
reactedListener?.onUnReacted(this)
}
if (animatorSet != null) {
animatorSet!!.cancel()
}
if (isChecked) {
views.reactionText.animate().cancel()
views.reactionText.scaleX = 0f
views.reactionText.scaleY = 0f
views.circle.innerCircleRadiusProgress = 0f
views.circle.outerCircleRadiusProgress = 0f
views.dots.currentProgress = 0f
animatorSet = AnimatorSet()
val outerCircleAnimator = ObjectAnimator.ofFloat(views.circle, CircleView.OUTER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f)
outerCircleAnimator.duration = 250
outerCircleAnimator.interpolator = DECCELERATE_INTERPOLATOR
val innerCircleAnimator = ObjectAnimator.ofFloat(views.circle, CircleView.INNER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f)
innerCircleAnimator.duration = 200
innerCircleAnimator.startDelay = 200
innerCircleAnimator.interpolator = DECCELERATE_INTERPOLATOR
val starScaleYAnimator = ObjectAnimator.ofFloat(views.reactionText, ImageView.SCALE_Y, 0.2f, 1f)
starScaleYAnimator.duration = 350
starScaleYAnimator.startDelay = 250
starScaleYAnimator.interpolator = OVERSHOOT_INTERPOLATOR
val starScaleXAnimator = ObjectAnimator.ofFloat(views.reactionText, ImageView.SCALE_X, 0.2f, 1f)
starScaleXAnimator.duration = 350
starScaleXAnimator.startDelay = 250
starScaleXAnimator.interpolator = OVERSHOOT_INTERPOLATOR
val dotsAnimator = ObjectAnimator.ofFloat(views.dots, DotsView.DOTS_PROGRESS, 0f, 1f)
// .ofFloat<DotsView>(views.dots, DotsView.DOTS_PROGRESS, 0, 1f)
dotsAnimator.duration = 900
dotsAnimator.startDelay = 50
dotsAnimator.interpolator = ACCELERATE_DECELERATE_INTERPOLATOR
animatorSet!!.playTogether(
outerCircleAnimator,
innerCircleAnimator,
starScaleYAnimator,
starScaleXAnimator,
dotsAnimator
)
animatorSet!!.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationCancel(animation: Animator) {
views.circle.innerCircleRadiusProgress = 0f
views.circle.outerCircleRadiusProgress = 0f
views.dots.currentProgress = 0f
views.reactionText.scaleX = 1f
views.reactionText.scaleY = 1f
}
override fun onAnimationEnd(animation: Animator) {
// if (animationEndListener != null) {
// // animationEndListener!!.onAnimationEnd(this@ReactionButton)
// }
}
})
animatorSet!!.start()
} else {
reactedListener?.onUnReacted(this)
}
}
/**
* Used to trigger the scale animation that takes places on the
* icon when the button is touched.
*
* @param event
* @return
*/
// override fun onTouchEvent(event: MotionEvent): Boolean {
// if (!isEnabled)
// return true
//
// when (event.action) {
// MotionEvent.ACTION_DOWN ->
// /*
// Commented out this line and moved the animation effect to the action up event due to
// conflicts that were occurring when library is used in sliding type views.
//
// icon.animate().scaleX(0.7f).scaleY(0.7f).setDuration(150).setInterpolator(DECCELERATE_INTERPOLATOR);
// */
// isPressed = true
//
// MotionEvent.ACTION_MOVE -> {
// val x = event.x
// val y = event.y
// val isInside = x > 0 && x < width && y > 0 && y < height
// if (isPressed != isInside) {
// isPressed = isInside
// }
// }
//
// MotionEvent.ACTION_UP -> {
// views.reactionText!!.animate().scaleX(0.7f).scaleY(0.7f).setDuration(150).interpolator = DECCELERATE_INTERPOLATOR
// views.reactionText!!.animate().scaleX(1f).scaleY(1f).interpolator = DECCELERATE_INTERPOLATOR
// if (isPressed) {
// performClick()
// isPressed = false
// }
// }
// MotionEvent.ACTION_CANCEL -> isPressed = false
// }
// return true
// }
override fun onLongClick(v: View?): Boolean {
reactedListener?.onLongClick(this)
return reactedListener != null
}
/**
* This set sets the colours that are used for the little dots
* that will be exploding once the like button is clicked.
*
* @param primaryColor
* @param secondaryColor
*/
fun setExplodingDotColorsRes(@ColorRes primaryColor: Int, @ColorRes secondaryColor: Int) {
views.dots.setColors(ContextCompat.getColor(context, primaryColor), ContextCompat.getColor(context, secondaryColor))
}
fun setExplodingDotColorsInt(@ColorInt primaryColor: Int, @ColorInt secondaryColor: Int) {
views.dots.setColors(primaryColor, secondaryColor)
}
fun setCircleStartColorRes(@ColorRes circleStartColor: Int) {
this.circleStartColor = ContextCompat.getColor(context, circleStartColor)
views.circle.startColor = this.circleStartColor
}
fun setCircleStartColorInt(@ColorInt circleStartColor: Int) {
this.circleStartColor = circleStartColor
views.circle.startColor = circleStartColor
}
fun setCircleEndColorRes(@ColorRes circleEndColor: Int) {
this.circleEndColor = ContextCompat.getColor(context, circleEndColor)
views.circle.endColor = this.circleEndColor
}
/**
* Sets the initial state of the button to liked
* or unliked.
@ -327,13 +137,6 @@ class ReactionButton @JvmOverloads constructor(context: Context,
}
}
/**
* Sets the factor by which the dots should be sized.
*/
fun setAnimationScaleFactor(animationScaleFactor: Float) {
this.animationScaleFactor = animationScaleFactor
}
interface ReactedListener {
fun onReacted(reactionButton: ReactionButton)
fun onUnReacted(reactionButton: ReactionButton)

View file

@ -7,7 +7,8 @@
android:background="@drawable/reaction_rounded_rect_shape"
android:clipChildren="false"
android:minWidth="44dp"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
android:gravity="center"
tools:parentTag="android.widget.LinearLayout">
<!--<View-->
<!--android:id="@+id/reactionSelector"-->
@ -15,25 +16,6 @@
<!--android:layout_height="match_parent"-->
<!--android:background="@drawable/rounded_rect_shape" />-->
<im.vector.app.features.reactions.widget.DotsView
android:id="@+id/dots"
android:layout_width="30dp"
android:layout_height="30dp"
android:clipChildren="false"
app:layout_constraintBottom_toBottomOf="@+id/reactionText"
app:layout_constraintEnd_toEndOf="@+id/reactionText"
app:layout_constraintStart_toStartOf="@+id/reactionText"
app:layout_constraintTop_toTopOf="@+id/reactionText" />
<im.vector.app.features.reactions.widget.CircleView
android:id="@+id/circle"
android:layout_width="14dp"
android:layout_height="14dp"
app:layout_constraintBottom_toBottomOf="@+id/reactionText"
app:layout_constraintEnd_toEndOf="@+id/reactionText"
app:layout_constraintStart_toStartOf="@+id/reactionText"
app:layout_constraintTop_toTopOf="@+id/reactionText" />
<TextView
android:id="@+id/reactionText"
style="@style/Widget.Vector.TextView.Caption"
@ -46,11 +28,6 @@
android:minWidth="20dp"
android:singleLine="true"
android:textColor="@color/emoji_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/reactionCount"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="* Party Parrot Again * 👀" />
<TextView
@ -67,10 +44,6 @@
app:autoSizeMaxTextSize="14sp"
app:autoSizeMinTextSize="8sp"
app:autoSizeTextType="uniform"
app:layout_constraintBaseline_toBaselineOf="@id/reactionText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/reactionText"
tools:text="13450" />
</merge>