diff --git a/CHANGES.md b/CHANGES.md index fbefb75e06..a776f483e6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,20 @@ +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) ======================================= diff --git a/fastlane/metadata/android/en-US/changelogs/40103110.txt b/fastlane/metadata/android/en-US/changelogs/40103110.txt new file mode 100644 index 0000000000..c28b303a35 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40103110.txt @@ -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 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cf2b23094e..fa58fc5aae 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=b75392c5625a88bccd58a574552a5a323edca82dab5942d2d41097f809c6bcce -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-all.zip +distributionSha256Sum=dd54e87b4d7aa8ff3c6afb0f7805aa121d4b70bca55b8c9b1b896eb103184582 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 477f971e04..d0dee31e9d 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -31,7 +31,7 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' - buildConfigField "String", "SDK_VERSION", "\"1.3.10\"" + buildConfigField "String", "SDK_VERSION", "\"1.3.11\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision", "\"${gitRevision()}\"" diff --git a/tools/release/sign_apk.sh b/tools/release/sign_apk.sh index aae9e1a378..de5a22dd34 100755 --- a/tools/release/sign_apk.sh +++ b/tools/release/sign_apk.sh @@ -17,7 +17,7 @@ PARAM_KEYSTORE_PATH=$1 PARAM_APK=$2 # Other params -BUILD_TOOLS_VERSION="31.0.0-rc5" +BUILD_TOOLS_VERSION="31.0.0" MIN_SDK_VERSION=21 echo "Signing APK with build-tools version ${BUILD_TOOLS_VERSION} for min SDK version ${MIN_SDK_VERSION}..." diff --git a/tools/release/sign_apk_unsafe.sh b/tools/release/sign_apk_unsafe.sh index 5d209a4a2b..a7536616e9 100755 --- a/tools/release/sign_apk_unsafe.sh +++ b/tools/release/sign_apk_unsafe.sh @@ -23,7 +23,7 @@ PARAM_KS_PASS=$3 PARAM_KEY_PASS=$4 # Other params -BUILD_TOOLS_VERSION="31.0.0-rc5" +BUILD_TOOLS_VERSION="31.0.0" MIN_SDK_VERSION=21 echo "Signing APK with build-tools version ${BUILD_TOOLS_VERSION} for min SDK version ${MIN_SDK_VERSION}..." diff --git a/vector/build.gradle b/vector/build.gradle index a578fdb52f..490057192e 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -15,7 +15,7 @@ kapt { // Note: 2 digits max for each value ext.versionMajor = 1 ext.versionMinor = 3 -ext.versionPatch = 10 +ext.versionPatch = 11 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' diff --git a/vector/src/androidTest/java/im/vector/app/features/html/SpanUtilsTest.kt b/vector/src/androidTest/java/im/vector/app/features/html/SpanUtilsTest.kt index d33fd4948a..a42a6f0212 100644 --- a/vector/src/androidTest/java/im/vector/app/features/html/SpanUtilsTest.kt +++ b/vector/src/androidTest/java/im/vector/app/features/html/SpanUtilsTest.kt @@ -24,6 +24,7 @@ import android.text.Spanned import android.text.style.ForegroundColorSpan import android.text.style.StrikethroughSpan import android.text.style.UnderlineSpan +import androidx.emoji2.text.EmojiCompat import im.vector.app.InstrumentedTest import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeTrue @@ -32,12 +33,18 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.junit.runners.MethodSorters +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) 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 { return getBindingOptions(message).canUseTextFuture @@ -122,4 +129,17 @@ class SpanUtilsTest : InstrumentedTest { } 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) + } } diff --git a/vector/src/main/java/im/vector/app/EmojiCompatWrapper.kt b/vector/src/main/java/im/vector/app/EmojiCompatWrapper.kt index 995e2e8049..47133a61b4 100644 --- a/vector/src/main/java/im/vector/app/EmojiCompatWrapper.kt +++ b/vector/src/main/java/im/vector/app/EmojiCompatWrapper.kt @@ -23,8 +23,12 @@ import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton +fun interface EmojiSpanify { + fun spanify(sequence: CharSequence): CharSequence +} + @Singleton -class EmojiCompatWrapper @Inject constructor(private val context: Context) { +class EmojiCompatWrapper @Inject constructor(private val context: Context) : EmojiSpanify { 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) { try { return EmojiCompat.get().process(sequence) ?: sequence diff --git a/vector/src/main/java/im/vector/app/core/di/SingletonModule.kt b/vector/src/main/java/im/vector/app/core/di/SingletonModule.kt index 14ed17d0bb..d83bb5cb57 100644 --- a/vector/src/main/java/im/vector/app/core/di/SingletonModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/SingletonModule.kt @@ -26,6 +26,8 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn 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.error.DefaultErrorFormatter import im.vector.app.core.error.ErrorFormatter @@ -76,6 +78,9 @@ abstract class VectorBindModule { @Binds abstract fun bindDefaultClock(clock: DefaultClock): Clock + + @Binds + abstract fun bindEmojiSpanify(emojiCompatWrapper: EmojiCompatWrapper): EmojiSpanify } @InstallIn(SingletonComponent::class) diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt index eab1101064..64443139f1 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt @@ -151,6 +151,7 @@ abstract class VectorBaseFragment : Fragment(), MavericksView override fun onDestroyView() { Timber.i("onDestroyView Fragment ${javaClass.simpleName}") _binding = null + dismissLoadingDialog() super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/app/features/call/CallProximityManager.kt b/vector/src/main/java/im/vector/app/features/call/CallProximityManager.kt index 74e6c40783..3aeeac15af 100644 --- a/vector/src/main/java/im/vector/app/features/call/CallProximityManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/CallProximityManager.kt @@ -93,7 +93,9 @@ class CallProximityManager @Inject constructor( if (wakeLock == null) { wakeLock = powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, generateWakeLockTag()) } - wakeLock?.acquire(WAKE_LOCK_TIMEOUT_MILLIS) + wakeLock + ?.takeIf { !it.isHeld } + ?.acquire(WAKE_LOCK_TIMEOUT_MILLIS) } private fun onProximityFar() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 22d282d567..28b2d98909 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -613,7 +613,7 @@ class MessageItemFactory @Inject constructor( val formattedBody = SpannableStringBuilder() formattedBody.append("* ${informationData.memberName} ") formattedBody.append(messageContent.getHtmlBody()) - + val bindingOptions = spanUtils.getBindingOptions(formattedBody) val message = formattedBody.linkify(callback) return MessageTextItem_() @@ -625,6 +625,7 @@ class MessageItemFactory @Inject constructor( message(message) } } + .bindingOptions(bindingOptions) .leftGuideline(avatarSizeProvider.leftGuideline) .previewUrlRetriever(callback?.getPreviewUrlRetriever()) .imageContentRenderer(imageContentRenderer) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt index 3616367e7d..3892bfff85 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home.room.detail.timeline.format import dagger.Lazy -import im.vector.app.EmojiCompatWrapper +import im.vector.app.EmojiSpanify import im.vector.app.R import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider @@ -39,7 +39,7 @@ import javax.inject.Inject class DisplayableEventFormatter @Inject constructor( private val stringProvider: StringProvider, private val colorProvider: ColorProvider, - private val emojiCompatWrapper: EmojiCompatWrapper, + private val emojiSpanify: EmojiSpanify, private val noticeEventFormatter: NoticeEventFormatter, private val htmlRenderer: Lazy ) { @@ -100,7 +100,7 @@ class DisplayableEventFormatter @Inject constructor( } EventType.REACTION -> { timelineEvent.root.getClearContent().toModel()?.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) } ?: span { } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsEpoxyController.kt index 0b3d381619..0031cf9feb 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsEpoxyController.kt @@ -20,7 +20,7 @@ import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Success -import im.vector.app.EmojiCompatWrapper +import im.vector.app.EmojiSpanify import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem @@ -32,8 +32,8 @@ import javax.inject.Inject */ class ViewReactionsEpoxyController @Inject constructor( private val stringProvider: StringProvider, - private val emojiCompatWrapper: EmojiCompatWrapper) : - TypedEpoxyController() { + private val emojiSpanify: EmojiSpanify) : + TypedEpoxyController() { var listener: Listener? = null @@ -56,7 +56,7 @@ class ViewReactionsEpoxyController @Inject constructor( reactionInfoSimpleItem { id(reactionInfo.eventId) timeStamp(reactionInfo.timestamp) - reactionKey(host.emojiCompatWrapper.safeEmojiSpanify(reactionInfo.reactionKey)) + reactionKey(host.emojiSpanify.spanify(reactionInfo.reactionKey)) authorDisplayName(reactionInfo.authorName ?: reactionInfo.authorId) userClicked { host.listener?.didSelectUser(reactionInfo.authorId) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/tools/EventRenderingTools.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/tools/EventRenderingTools.kt index 1e8a81ff61..3226b56c24 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/tools/EventRenderingTools.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/tools/EventRenderingTools.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.tools +import android.text.SpannableStringBuilder import android.text.style.ClickableSpan import android.view.MotionEvent import android.widget.TextView @@ -44,7 +45,8 @@ fun CharSequence.findPillsAndProcess(scope: CoroutineScope, processBlock: (PillI fun CharSequence.linkify(callback: TimelineEventController.UrlClickCallback?): CharSequence { val text = this.toString() - val spannable = toSpannable() + // SpannableStringBuilder is used to avoid Epoxy throwing ImmutableModelException + val spannable = SpannableStringBuilder(this) MatrixLinkify.addLinks(spannable, object : MatrixPermalinkSpan.Callback { override fun onUrlClicked(url: String) { callback?.onUrlClicked(url, text) diff --git a/vector/src/main/java/im/vector/app/features/html/SpanUtils.kt b/vector/src/main/java/im/vector/app/features/html/SpanUtils.kt index 27c966e33e..e668f29a6a 100644 --- a/vector/src/main/java/im/vector/app/features/html/SpanUtils.kt +++ b/vector/src/main/java/im/vector/app/features/html/SpanUtils.kt @@ -21,13 +21,15 @@ import android.text.Spanned import android.text.style.MetricAffectingSpan import android.text.style.StrikethroughSpan 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 javax.inject.Inject -class SpanUtils @Inject constructor() { +class SpanUtils @Inject constructor( + private val emojiSpanify: EmojiSpanify +) { fun getBindingOptions(charSequence: CharSequence): BindingOptions { - val emojiCharSequence = EmojiCompat.get().process(charSequence) + val emojiCharSequence = emojiSpanify.spanify(charSequence) if (emojiCharSequence !is Spanned) { return BindingOptions() diff --git a/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt b/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt index 2b4e9ee5ab..67095b974a 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt @@ -24,7 +24,7 @@ import android.widget.LinearLayout import androidx.core.content.ContextCompat import androidx.core.content.withStyledAttributes import dagger.hilt.android.AndroidEntryPoint -import im.vector.app.EmojiCompatWrapper +import im.vector.app.EmojiSpanify import im.vector.app.R import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.TextUtils @@ -39,9 +39,9 @@ import javax.inject.Inject class ReactionButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, 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 @@ -57,7 +57,7 @@ class ReactionButton @JvmOverloads constructor(context: Context, set(value) { field = value // maybe cache this for performances? - val emojiSpanned = emojiCompatWrapper.safeEmojiSpanify(value) + val emojiSpanned = emojiSpanify.spanify(value) views.reactionText.text = emojiSpanned } diff --git a/vector/src/main/res/layout/view_current_calls.xml b/vector/src/main/res/layout/view_current_calls.xml index 892f097e01..ab4ed8a3b8 100644 --- a/vector/src/main/res/layout/view_current_calls.xml +++ b/vector/src/main/res/layout/view_current_calls.xml @@ -1,25 +1,20 @@ + android:textColor="?colorOnPrimary" + tools:text="@string/call_only_active" /> diff --git a/vector/src/main/res/layout/view_stub_room_member_profile_header.xml b/vector/src/main/res/layout/view_stub_room_member_profile_header.xml index 6df1010196..40f4bd8515 100644 --- a/vector/src/main/res/layout/view_stub_room_member_profile_header.xml +++ b/vector/src/main/res/layout/view_stub_room_member_profile_header.xml @@ -12,8 +12,8 @@ android:id="@+id/memberProfileInfoContainer" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingEnd="16dp" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" app:layout_constraintBottom_toTopOf="@id/memberProfileNameView" app:layout_constraintTop_toTopOf="@id/memberProfileNameView"> @@ -23,7 +23,7 @@ android:layout_height="128dp" android:layout_marginBottom="16dp" android:contentDescription="@string/avatar" - app:layout_constraintBottom_toTopOf="@id/memberProfileNameView" + app:layout_constraintBottom_toTopOf="@id/memberProfileLinearLayout" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" @@ -45,30 +45,36 @@ tools:src="@drawable/ic_presence_offline" tools:visibility="visible" /> - - - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/memberProfileAvatarView"> + + + + + +