Merge branch 'develop' into feature/aris/threads

This commit is contained in:
ariskotsomitopoulos 2021-12-21 13:24:03 +02:00
commit 7048080ee0
19 changed files with 116 additions and 55 deletions

View File

@ -1,3 +1,28 @@
Changes in Element v1.3.12 (2021-12-20)
=======================================
Bugfixes 🐛
----------
- Fixing emoji related crashes on android 8.1.1 and below ([#4769](https://github.com/vector-im/element-android/issues/4769))
Changes in Element v1.3.11 (2021-12-17)
=======================================
Bugfixes 🐛
----------
- Fixing proximity sensor still being active after a call ([#2467](https://github.com/vector-im/element-android/issues/2467))
- Fix name and shield are truncated in the room detail screen ([#4700](https://github.com/vector-im/element-android/issues/4700))
- Call banner: center text vertically ([#4710](https://github.com/vector-im/element-android/issues/4710))
- Fixes unable to render messages by allowing them to render whilst the emoji library is initialising ([#4733](https://github.com/vector-im/element-android/issues/4733))
- Fix app crash uppon long press on a reply event ([#4742](https://github.com/vector-im/element-android/issues/4742))
- Fixes crash when launching rooms which contain emojis in the emote content on android 12+ ([#4743](https://github.com/vector-im/element-android/issues/4743))
Other changes
-------------
- Avoids leaking the activity windows when loading dialogs are displaying ([#4713](https://github.com/vector-im/element-android/issues/4713))
Changes in Element v1.3.10 (2021-12-14) Changes in Element v1.3.10 (2021-12-14)
======================================= =======================================

View File

@ -1 +0,0 @@
Fixing proximity sensor still being active after a call

View File

@ -1 +0,0 @@
Call banner: center text vertically

View File

@ -1 +0,0 @@
Avoids leaking the activity windows when loading dialogs are displaying

View File

@ -1 +0,0 @@
Fix app crash uppon long press on a reply event

View File

@ -0,0 +1,2 @@
Main changes in this version: Bug fixes!
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.11

View File

@ -0,0 +1,2 @@
Main changes in this version: Bug fixes!
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.12

View File

@ -31,7 +31,7 @@ android {
// that the app's state is completely cleared between tests. // that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true' testInstrumentationRunnerArguments clearPackageData: 'true'
buildConfigField "String", "SDK_VERSION", "\"1.3.11\"" buildConfigField "String", "SDK_VERSION", "\"1.3.13\""
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
resValue "string", "git_sdk_revision", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision", "\"${gitRevision()}\""

View File

@ -15,7 +15,7 @@ kapt {
// Note: 2 digits max for each value // Note: 2 digits max for each value
ext.versionMajor = 1 ext.versionMajor = 1
ext.versionMinor = 3 ext.versionMinor = 3
ext.versionPatch = 11 ext.versionPatch = 13
static def getGitTimestamp() { static def getGitTimestamp() {
def cmd = 'git show -s --format=%ct' def cmd = 'git show -s --format=%ct'

View File

@ -24,6 +24,7 @@ import android.text.Spanned
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.text.style.StrikethroughSpan import android.text.style.StrikethroughSpan
import android.text.style.UnderlineSpan import android.text.style.UnderlineSpan
import androidx.emoji2.text.EmojiCompat
import im.vector.app.InstrumentedTest import im.vector.app.InstrumentedTest
import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeEqualTo
import org.amshove.kluent.shouldBeTrue import org.amshove.kluent.shouldBeTrue
@ -32,12 +33,18 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.JUnit4 import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters import org.junit.runners.MethodSorters
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@RunWith(JUnit4::class) @RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM) @FixMethodOrder(MethodSorters.JVM)
class SpanUtilsTest : InstrumentedTest { class SpanUtilsTest : InstrumentedTest {
private val spanUtils = SpanUtils() private val spanUtils = SpanUtils {
val emojiCompat = EmojiCompat.get()
emojiCompat.waitForInit()
emojiCompat.process(it) ?: it
}
private fun SpanUtils.canUseTextFuture(message: CharSequence): Boolean { private fun SpanUtils.canUseTextFuture(message: CharSequence): Boolean {
return getBindingOptions(message).canUseTextFuture return getBindingOptions(message).canUseTextFuture
@ -122,4 +129,17 @@ class SpanUtilsTest : InstrumentedTest {
} }
private fun trueIfAlwaysAllowed() = Build.VERSION.SDK_INT < Build.VERSION_CODES.P private fun trueIfAlwaysAllowed() = Build.VERSION.SDK_INT < Build.VERSION_CODES.P
private fun EmojiCompat.waitForInit() {
val latch = CountDownLatch(1)
registerInitCallback(object : EmojiCompat.InitCallback() {
override fun onInitialized() = latch.countDown()
override fun onFailed(throwable: Throwable?) {
latch.countDown()
throw RuntimeException(throwable)
}
})
EmojiCompat.init(context())
latch.await(30, TimeUnit.SECONDS)
}
} }

View File

@ -23,8 +23,12 @@ import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
fun interface EmojiSpanify {
fun spanify(sequence: CharSequence): CharSequence
}
@Singleton @Singleton
class EmojiCompatWrapper @Inject constructor(private val context: Context) { class EmojiCompatWrapper @Inject constructor(private val context: Context) : EmojiSpanify {
private var initialized = false private var initialized = false
@ -49,7 +53,7 @@ class EmojiCompatWrapper @Inject constructor(private val context: Context) {
}) })
} }
fun safeEmojiSpanify(sequence: CharSequence): CharSequence { override fun spanify(sequence: CharSequence): CharSequence {
if (initialized) { if (initialized) {
try { try {
return EmojiCompat.get().process(sequence) ?: sequence return EmojiCompat.get().process(sequence) ?: sequence

View File

@ -26,6 +26,8 @@ import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import im.vector.app.EmojiCompatWrapper
import im.vector.app.EmojiSpanify
import im.vector.app.core.dispatchers.CoroutineDispatchers import im.vector.app.core.dispatchers.CoroutineDispatchers
import im.vector.app.core.error.DefaultErrorFormatter import im.vector.app.core.error.DefaultErrorFormatter
import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.error.ErrorFormatter
@ -76,6 +78,9 @@ abstract class VectorBindModule {
@Binds @Binds
abstract fun bindDefaultClock(clock: DefaultClock): Clock abstract fun bindDefaultClock(clock: DefaultClock): Clock
@Binds
abstract fun bindEmojiSpanify(emojiCompatWrapper: EmojiCompatWrapper): EmojiSpanify
} }
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)

View File

@ -620,7 +620,7 @@ class MessageItemFactory @Inject constructor(
val formattedBody = SpannableStringBuilder() val formattedBody = SpannableStringBuilder()
formattedBody.append("* ${informationData.memberName} ") formattedBody.append("* ${informationData.memberName} ")
formattedBody.append(messageContent.getHtmlBody()) formattedBody.append(messageContent.getHtmlBody())
val bindingOptions = spanUtils.getBindingOptions(formattedBody)
val message = formattedBody.linkify(callback) val message = formattedBody.linkify(callback)
return MessageTextItem_() return MessageTextItem_()
@ -632,6 +632,7 @@ class MessageItemFactory @Inject constructor(
message(message) message(message)
} }
} }
.bindingOptions(bindingOptions)
.leftGuideline(avatarSizeProvider.leftGuideline) .leftGuideline(avatarSizeProvider.leftGuideline)
.previewUrlRetriever(callback?.getPreviewUrlRetriever()) .previewUrlRetriever(callback?.getPreviewUrlRetriever())
.imageContentRenderer(imageContentRenderer) .imageContentRenderer(imageContentRenderer)

View File

@ -17,7 +17,7 @@
package im.vector.app.features.home.room.detail.timeline.format package im.vector.app.features.home.room.detail.timeline.format
import dagger.Lazy import dagger.Lazy
import im.vector.app.EmojiCompatWrapper import im.vector.app.EmojiSpanify
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -39,7 +39,7 @@ import javax.inject.Inject
class DisplayableEventFormatter @Inject constructor( class DisplayableEventFormatter @Inject constructor(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val colorProvider: ColorProvider, private val colorProvider: ColorProvider,
private val emojiCompatWrapper: EmojiCompatWrapper, private val emojiSpanify: EmojiSpanify,
private val noticeEventFormatter: NoticeEventFormatter, private val noticeEventFormatter: NoticeEventFormatter,
private val htmlRenderer: Lazy<EventHtmlRenderer> private val htmlRenderer: Lazy<EventHtmlRenderer>
) { ) {
@ -100,7 +100,7 @@ class DisplayableEventFormatter @Inject constructor(
} }
EventType.REACTION -> { EventType.REACTION -> {
timelineEvent.root.getClearContent().toModel<ReactionContent>()?.relatesTo?.let { timelineEvent.root.getClearContent().toModel<ReactionContent>()?.relatesTo?.let {
val emojiSpanned = emojiCompatWrapper.safeEmojiSpanify(stringProvider.getString(R.string.sent_a_reaction, it.key)) val emojiSpanned = emojiSpanify.spanify(stringProvider.getString(R.string.sent_a_reaction, it.key))
simpleFormat(senderName, emojiSpanned, appendAuthor) simpleFormat(senderName, emojiSpanned, appendAuthor)
} ?: span { } } ?: span { }
} }

View File

@ -20,7 +20,7 @@ import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import im.vector.app.EmojiCompatWrapper import im.vector.app.EmojiSpanify
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericFooterItem
@ -32,8 +32,8 @@ import javax.inject.Inject
*/ */
class ViewReactionsEpoxyController @Inject constructor( class ViewReactionsEpoxyController @Inject constructor(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val emojiCompatWrapper: EmojiCompatWrapper) : private val emojiSpanify: EmojiSpanify) :
TypedEpoxyController<DisplayReactionsViewState>() { TypedEpoxyController<DisplayReactionsViewState>() {
var listener: Listener? = null var listener: Listener? = null
@ -56,7 +56,7 @@ class ViewReactionsEpoxyController @Inject constructor(
reactionInfoSimpleItem { reactionInfoSimpleItem {
id(reactionInfo.eventId) id(reactionInfo.eventId)
timeStamp(reactionInfo.timestamp) timeStamp(reactionInfo.timestamp)
reactionKey(host.emojiCompatWrapper.safeEmojiSpanify(reactionInfo.reactionKey)) reactionKey(host.emojiSpanify.spanify(reactionInfo.reactionKey))
authorDisplayName(reactionInfo.authorName ?: reactionInfo.authorId) authorDisplayName(reactionInfo.authorName ?: reactionInfo.authorId)
userClicked { host.listener?.didSelectUser(reactionInfo.authorId) } userClicked { host.listener?.didSelectUser(reactionInfo.authorId) }
} }

View File

@ -45,6 +45,7 @@ fun CharSequence.findPillsAndProcess(scope: CoroutineScope, processBlock: (PillI
fun CharSequence.linkify(callback: TimelineEventController.UrlClickCallback?): CharSequence { fun CharSequence.linkify(callback: TimelineEventController.UrlClickCallback?): CharSequence {
val text = this.toString() val text = this.toString()
// SpannableStringBuilder is used to avoid Epoxy throwing ImmutableModelException
val spannable = SpannableStringBuilder(this) val spannable = SpannableStringBuilder(this)
MatrixLinkify.addLinks(spannable, object : MatrixPermalinkSpan.Callback { MatrixLinkify.addLinks(spannable, object : MatrixPermalinkSpan.Callback {
override fun onUrlClicked(url: String) { override fun onUrlClicked(url: String) {

View File

@ -16,18 +16,19 @@
package im.vector.app.features.html package im.vector.app.features.html
import android.os.Build
import android.text.Spanned import android.text.Spanned
import android.text.style.MetricAffectingSpan import android.text.style.MetricAffectingSpan
import android.text.style.StrikethroughSpan import android.text.style.StrikethroughSpan
import android.text.style.UnderlineSpan import android.text.style.UnderlineSpan
import androidx.emoji2.text.EmojiCompat import im.vector.app.EmojiSpanify
import im.vector.app.features.home.room.detail.timeline.item.BindingOptions import im.vector.app.features.home.room.detail.timeline.item.BindingOptions
import javax.inject.Inject import javax.inject.Inject
class SpanUtils @Inject constructor() { class SpanUtils @Inject constructor(
private val emojiSpanify: EmojiSpanify
) {
fun getBindingOptions(charSequence: CharSequence): BindingOptions { fun getBindingOptions(charSequence: CharSequence): BindingOptions {
val emojiCharSequence = EmojiCompat.get().process(charSequence) val emojiCharSequence = emojiSpanify.spanify(charSequence)
if (emojiCharSequence !is Spanned) { if (emojiCharSequence !is Spanned) {
return BindingOptions() return BindingOptions()
@ -39,13 +40,11 @@ class SpanUtils @Inject constructor() {
) )
} }
// Workaround for https://issuetracker.google.com/issues/188454876 /**
* TextFutures do not support StrikethroughSpan, UnderlineSpan or MetricAffectingSpan
* Workaround for https://issuetracker.google.com/issues/188454876
*/
private fun canUseTextFuture(spanned: Spanned): Boolean { private fun canUseTextFuture(spanned: Spanned): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
// On old devices, it works correctly
return true
}
return spanned return spanned
.getSpans(0, spanned.length, Any::class.java) .getSpans(0, spanned.length, Any::class.java)
.all { it !is StrikethroughSpan && it !is UnderlineSpan && it !is MetricAffectingSpan } .all { it !is StrikethroughSpan && it !is UnderlineSpan && it !is MetricAffectingSpan }

View File

@ -24,7 +24,7 @@ import android.widget.LinearLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.withStyledAttributes import androidx.core.content.withStyledAttributes
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.EmojiCompatWrapper import im.vector.app.EmojiSpanify
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.DimensionConverter
import im.vector.app.core.utils.TextUtils import im.vector.app.core.utils.TextUtils
@ -39,9 +39,9 @@ import javax.inject.Inject
class ReactionButton @JvmOverloads constructor(context: Context, class ReactionButton @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
defStyleAttr: Int = 0) : defStyleAttr: Int = 0) :
LinearLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener { LinearLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener {
@Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper @Inject lateinit var emojiSpanify: EmojiSpanify
private val views: ReactionButtonBinding private val views: ReactionButtonBinding
@ -57,7 +57,7 @@ class ReactionButton @JvmOverloads constructor(context: Context,
set(value) { set(value) {
field = value field = value
// maybe cache this for performances? // maybe cache this for performances?
val emojiSpanned = emojiCompatWrapper.safeEmojiSpanify(value) val emojiSpanned = emojiSpanify.spanify(value)
views.reactionText.text = emojiSpanned views.reactionText.text = emojiSpanned
} }

View File

@ -12,8 +12,8 @@
android:id="@+id/memberProfileInfoContainer" android:id="@+id/memberProfileInfoContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="16dp" android:layout_marginStart="16dp"
android:paddingEnd="16dp" android:layout_marginEnd="16dp"
app:layout_constraintBottom_toTopOf="@id/memberProfileNameView" app:layout_constraintBottom_toTopOf="@id/memberProfileNameView"
app:layout_constraintTop_toTopOf="@id/memberProfileNameView"> app:layout_constraintTop_toTopOf="@id/memberProfileNameView">
@ -23,7 +23,7 @@
android:layout_height="128dp" android:layout_height="128dp"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:contentDescription="@string/avatar" android:contentDescription="@string/avatar"
app:layout_constraintBottom_toTopOf="@id/memberProfileNameView" app:layout_constraintBottom_toTopOf="@id/memberProfileLinearLayout"
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"
@ -45,30 +45,36 @@
tools:src="@drawable/ic_presence_offline" tools:src="@drawable/ic_presence_offline"
tools:visibility="visible" /> tools:visibility="visible" />
<im.vector.app.core.ui.views.ShieldImageView <LinearLayout
android:id="@+id/memberProfileDecorationImageView" android:id="@+id/memberProfileLinearLayout"
android:layout_width="30dp" android:layout_width="0dp"
android:layout_height="30dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="@id/memberProfileNameView"
app:layout_constraintEnd_toStartOf="@id/memberProfileNameView"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/memberProfileNameView" />
<TextView
android:id="@+id/memberProfileNameView"
style="@style/Widget.Vector.TextView.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:textStyle="bold" android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@id/memberProfileIdView" app:layout_constraintBottom_toTopOf="@id/memberProfileIdView"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/memberProfileDecorationImageView" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/memberProfileAvatarView" app:layout_constraintTop_toBottomOf="@id/memberProfileAvatarView">
tools:text="@sample/users.json/data/displayName" />
<im.vector.app.core.ui.views.ShieldImageView
android:id="@+id/memberProfileDecorationImageView"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="8dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="8dp" />
<TextView
android:id="@+id/memberProfileNameView"
style="@style/Widget.Vector.TextView.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:gravity="center"
android:textStyle="bold"
tools:text="@sample/users.json/data/displayName" />
</LinearLayout>
<TextView <TextView
android:id="@+id/memberProfileIdView" android:id="@+id/memberProfileIdView"
@ -82,7 +88,7 @@
app:layout_constraintBottom_toTopOf="@id/memberProfilePowerLevelView" app:layout_constraintBottom_toTopOf="@id/memberProfilePowerLevelView"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/memberProfileNameView" app:layout_constraintTop_toBottomOf="@id/memberProfileLinearLayout"
tools:text="@sample/users.json/data/id" /> tools:text="@sample/users.json/data/id" />
<TextView <TextView