diff --git a/CHANGES.md b/CHANGES.md index 1228ea65ca..eef8808393 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ Features ✨: Improvements 🙌: - Handle navigation to room via room alias (#201) - Open matrix.to link in RiotX (#57) + - Limit sticker size in the timeline Other changes: - Use same default room colors than Riot-Web @@ -14,7 +15,9 @@ Other changes: Bugfix 🐛: - Scroll breadcrumbs to top when opened - Render default room name when it starts with an emoji (#477) - - Do not display " (IRC)") in display names https://github.com/vector-im/riot-android/issues/444 + - Do not display " (IRC)" in display names https://github.com/vector-im/riot-android/issues/444 + - Fix rendering issue with HTML formatted body + - Disable click on Stickers (#703) Translations 🗣: - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt index 933657b2fb..0d8ef2c52b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt @@ -30,7 +30,7 @@ data class ContentAttachmentData( val exifOrientation: Int = ExifInterface.ORIENTATION_UNDEFINED, val name: String? = null, val path: String, - val mimeType: String, + val mimeType: String?, val type: Type ) : Parcelable { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/VideoInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/VideoInfo.kt index a7d4708d33..da247810cb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/VideoInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/VideoInfo.kt @@ -25,7 +25,7 @@ data class VideoInfo( /** * The mimetype of the video e.g. "video/mp4". */ - @Json(name = "mimetype") val mimeType: String, + @Json(name = "mimetype") val mimeType: String?, /** * The width of the video in pixels. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt index ed7f49aa46..caa64a85f8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt @@ -104,7 +104,7 @@ fun TimelineEvent.getLastMessageContent(): MessageContent? { root.getClearContent().toModel() } else { annotations?.editSummary?.aggregatedContent?.toModel() - ?: root.getClearContent().toModel() + ?: root.getClearContent().toModel() } } @@ -116,7 +116,7 @@ fun TimelineEvent.getLastMessageBody(): String? { if (lastMessageContent != null) { return lastMessageContent.newContent?.toModel()?.body - ?: lastMessageContent.body + ?: lastMessageContent.body } return null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/attachments/MXEncryptedAttachments.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/attachments/MXEncryptedAttachments.kt index 5f90b636ac..503bf8e4ff 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/attachments/MXEncryptedAttachments.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/attachments/MXEncryptedAttachments.kt @@ -49,7 +49,7 @@ object MXEncryptedAttachments { * @param mimetype the mime type * @return the encryption file info */ - fun encryptAttachment(attachmentStream: InputStream, mimetype: String): EncryptionResult { + fun encryptAttachment(attachmentStream: InputStream, mimetype: String?): EncryptionResult { val t0 = System.currentTimeMillis() val secureRandom = SecureRandom() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt index 868d63665a..c160ac9b31 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt @@ -83,15 +83,13 @@ internal class DefaultFileService @Inject constructor(private val context: Conte if (elementToDecrypt != null) { Timber.v("## decrypt file") - MXEncryptedAttachments.decryptAttachment(inputStream, elementToDecrypt) ?: throw IllegalStateException("Decryption error") - } else { - inputStream + MXEncryptedAttachments.decryptAttachment(inputStream, elementToDecrypt) + ?: throw IllegalStateException("Decryption error") } + + writeToFile(inputStream, destFile) + destFile } - .map { inputStream -> - writeToFile(inputStream, destFile) - destFile - } } else { Try.just(destFile) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/FileUploader.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/FileUploader.kt index 209f03ad9d..2f4e991e62 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/FileUploader.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/FileUploader.kt @@ -43,9 +43,9 @@ internal class FileUploader @Inject constructor(@Authenticated suspend fun uploadFile(file: File, filename: String?, - mimeType: String, + mimeType: String?, progressListener: ProgressRequestBody.Listener? = null): ContentUploadResponse { - val uploadBody = file.asRequestBody(mimeType.toMediaTypeOrNull()) + val uploadBody = file.asRequestBody(mimeType?.toMediaTypeOrNull()) return upload(uploadBody, filename, progressListener) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt index ca86765cd9..9af8434b7c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt @@ -52,7 +52,7 @@ internal class DefaultCreateRoomTask @Inject constructor(private val roomAPI: Ro apiCall = roomAPI.createRoom(params) } val roomId = createRoomResponse.roomId!! - // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) + // Wait for room to come back from the sync (but it can maybe be in the DB if the sync response is received before) val rql = RealmQueryLatch(realmConfiguration) { realm -> realm.where(RoomEntity::class.java) .equalTo(RoomEntityFields.ROOM_ID, roomId) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index 0fed1ca6f5..7a935783cf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -251,7 +251,7 @@ internal class LocalEchoEventFactory @Inject constructor( type = MessageType.MSGTYPE_AUDIO, body = attachment.name ?: "audio", audioInfo = AudioInfo( - mimeType = attachment.mimeType.takeIf { it.isNotBlank() } ?: "audio/mpeg", + mimeType = attachment.mimeType?.takeIf { it.isNotBlank() } ?: "audio/mpeg", size = attachment.size ), url = attachment.path @@ -264,7 +264,7 @@ internal class LocalEchoEventFactory @Inject constructor( type = MessageType.MSGTYPE_FILE, body = attachment.name ?: "file", info = FileInfo( - mimeType = attachment.mimeType.takeIf { it.isNotBlank() } + mimeType = attachment.mimeType?.takeIf { it.isNotBlank() } ?: "application/octet-stream", size = attachment.size ), diff --git a/vector/build.gradle b/vector/build.gradle index 8a2df7c120..de15a67fbd 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -293,6 +293,7 @@ dependencies { implementation 'me.gujun.android:span:1.7' implementation "io.noties.markwon:core:$markwon_version" implementation "io.noties.markwon:html:$markwon_version" + implementation 'com.googlecode.htmlcompressor:htmlcompressor:1.4' implementation 'me.saket:better-link-movement-method:2.2.0' implementation 'com.google.android:flexbox:1.1.1' implementation "androidx.autofill:autofill:$autofill_version" diff --git a/vector/lint.xml b/vector/lint.xml index b6da88aedd..6a9b0634a7 100644 --- a/vector/lint.xml +++ b/vector/lint.xml @@ -31,4 +31,8 @@ + + + + diff --git a/vector/src/main/assets/open_source_licenses.html b/vector/src/main/assets/open_source_licenses.html index 3cd500138a..2b08270c89 100755 --- a/vector/src/main/assets/open_source_licenses.html +++ b/vector/src/main/assets/open_source_licenses.html @@ -359,6 +359,11 @@ SOFTWARE.
Copyright 2018 Kumar Bibek +
  • + htmlcompressor +
    + Copyright 2017 Sergiy Kovalchuk +
  •  Apache License
    diff --git a/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt
    index c8c5d697f6..b78e291506 100644
    --- a/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt
    +++ b/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt
    @@ -38,6 +38,7 @@ import im.vector.riotx.features.home.AvatarRenderer
     import im.vector.riotx.features.home.HomeRoomListDataSource
     import im.vector.riotx.features.home.group.SelectedGroupDataSource
     import im.vector.riotx.features.html.EventHtmlRenderer
    +import im.vector.riotx.features.html.VectorHtmlCompressor
     import im.vector.riotx.features.navigation.Navigator
     import im.vector.riotx.features.notifications.*
     import im.vector.riotx.features.rageshake.BugReporter
    @@ -87,6 +88,8 @@ interface VectorComponent {
     
         fun eventHtmlRenderer(): EventHtmlRenderer
     
    +    fun vectorHtmlCompressor(): VectorHtmlCompressor
    +
         fun navigator(): Navigator
     
         fun errorFormatter(): ErrorFormatter
    diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/ZeroItem.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/ZeroItem.kt
    new file mode 100644
    index 0000000000..b64abdcc6c
    --- /dev/null
    +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/ZeroItem.kt
    @@ -0,0 +1,30 @@
    +/*
    + * Copyright 2019 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.riotx.core.epoxy
    +
    +import com.airbnb.epoxy.EpoxyModelClass
    +import im.vector.riotx.R
    +
    +/**
    + * Item of size (0, 0).
    + * It can be useful to avoid automatic scroll of RecyclerView with Epoxy controller, when the first valuable item changes.
    + */
    +@EpoxyModelClass(layout = R.layout.item_zero)
    +abstract class ZeroItem : VectorEpoxyModel() {
    +
    +    class Holder : VectorEpoxyHolder()
    +}
    diff --git a/vector/src/main/java/im/vector/riotx/core/error/fatal.kt b/vector/src/main/java/im/vector/riotx/core/error/fatal.kt
    new file mode 100644
    index 0000000000..800e1ea7ad
    --- /dev/null
    +++ b/vector/src/main/java/im/vector/riotx/core/error/fatal.kt
    @@ -0,0 +1,31 @@
    +/*
    + * Copyright 2019 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.riotx.core.error
    +
    +import im.vector.riotx.BuildConfig
    +import timber.log.Timber
    +
    +/**
    + * throw in debug, only log in production. As this method does not always throw, next statement should be a return
    + */
    +fun fatalError(message: String) {
    +    if (BuildConfig.DEBUG) {
    +        error(message)
    +    } else {
    +        Timber.e(message)
    +    }
    +}
    diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt
    index 02e28e079c..f70aed9393 100644
    --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt
    +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt
    @@ -222,8 +222,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
     
         override fun onResume() {
             super.onResume()
    -
    -        Timber.v("onResume Activity ${this.javaClass.simpleName}")
    +        Timber.i("onResume Activity ${this.javaClass.simpleName}")
     
             configurationViewModel.onActivityResumed()
     
    diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt
    index 70311e2f57..b3a56f48ee 100644
    --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt
    @@ -32,6 +32,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
     import im.vector.riotx.core.di.DaggerScreenComponent
     import im.vector.riotx.core.di.ScreenComponent
     import im.vector.riotx.core.utils.DimensionConverter
    +import timber.log.Timber
     
     /**
      * Add MvRx capabilities to bottomsheetdialog (like BaseMvRxFragment)
    @@ -80,6 +81,11 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment()
             super.onCreate(savedInstanceState)
         }
     
    +    override fun onResume() {
    +        super.onResume()
    +        Timber.i("onResume BottomSheet ${this.javaClass.simpleName}")
    +    }
    +
         override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
             return super.onCreateDialog(savedInstanceState).apply {
                 val dialog = this as? BottomSheetDialog
    diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt
    index efcbdfff39..8e1ad72150 100644
    --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt
    @@ -104,7 +104,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
         @CallSuper
         override fun onResume() {
             super.onResume()
    -        Timber.v("onResume Fragment ${this.javaClass.simpleName}")
    +        Timber.i("onResume Fragment ${this.javaClass.simpleName}")
         }
     
         @CallSuper
    diff --git a/vector/src/main/java/im/vector/riotx/core/ui/model/Size.kt b/vector/src/main/java/im/vector/riotx/core/ui/model/Size.kt
    new file mode 100644
    index 0000000000..65ab0ad2b2
    --- /dev/null
    +++ b/vector/src/main/java/im/vector/riotx/core/ui/model/Size.kt
    @@ -0,0 +1,20 @@
    +/*
    + * Copyright 2019 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.riotx.core.ui.model
    +
    +// android.util.Size in API 21+
    +data class Size(val width: Int, val height: Int)
    diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt
    index 5e843fcdfd..4b51c548a7 100644
    --- a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt
    @@ -18,6 +18,7 @@ package im.vector.riotx.features.attachments
     
     import com.kbeanie.multipicker.api.entity.*
     import im.vector.matrix.android.api.session.content.ContentAttachmentData
    +import timber.log.Timber
     
     fun ChosenContact.toContactAttachment(): ContactAttachment {
         return ContactAttachment(
    @@ -29,6 +30,7 @@ fun ChosenContact.toContactAttachment(): ContactAttachment {
     }
     
     fun ChosenFile.toContentAttachmentData(): ContentAttachmentData {
    +    if (mimeType == null) Timber.w("No mimeType")
         return ContentAttachmentData(
                 path = originalPath,
                 mimeType = mimeType,
    @@ -40,6 +42,7 @@ fun ChosenFile.toContentAttachmentData(): ContentAttachmentData {
     }
     
     fun ChosenAudio.toContentAttachmentData(): ContentAttachmentData {
    +    if (mimeType == null) Timber.w("No mimeType")
         return ContentAttachmentData(
                 path = originalPath,
                 mimeType = mimeType,
    @@ -51,16 +54,17 @@ fun ChosenAudio.toContentAttachmentData(): ContentAttachmentData {
         )
     }
     
    -fun ChosenFile.mapType(): ContentAttachmentData.Type {
    +private fun ChosenFile.mapType(): ContentAttachmentData.Type {
         return when {
    -        mimeType.startsWith("image/") -> ContentAttachmentData.Type.IMAGE
    -        mimeType.startsWith("video/") -> ContentAttachmentData.Type.VIDEO
    -        mimeType.startsWith("audio/") -> ContentAttachmentData.Type.AUDIO
    -        else                          -> ContentAttachmentData.Type.FILE
    +        mimeType?.startsWith("image/") == true -> ContentAttachmentData.Type.IMAGE
    +        mimeType?.startsWith("video/") == true -> ContentAttachmentData.Type.VIDEO
    +        mimeType?.startsWith("audio/") == true -> ContentAttachmentData.Type.AUDIO
    +        else                                   -> ContentAttachmentData.Type.FILE
         }
     }
     
     fun ChosenImage.toContentAttachmentData(): ContentAttachmentData {
    +    if (mimeType == null) Timber.w("No mimeType")
         return ContentAttachmentData(
                 path = originalPath,
                 mimeType = mimeType,
    @@ -75,6 +79,7 @@ fun ChosenImage.toContentAttachmentData(): ContentAttachmentData {
     }
     
     fun ChosenVideo.toContentAttachmentData(): ContentAttachmentData {
    +    if (mimeType == null) Timber.w("No mimeType")
         return ContentAttachmentData(
                 path = originalPath,
                 mimeType = mimeType,
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt
    index bbeda127fc..24318bc508 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt
    @@ -68,14 +68,14 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
         }
     
         private fun observeSelectionState() {
    -        selectSubscribe(GroupListViewState::selectedGroup) {
    -            if (it != null) {
    +        selectSubscribe(GroupListViewState::selectedGroup) { groupSummary ->
    +            if (groupSummary != null) {
                     val selectedGroup = _openGroupLiveData.value?.peekContent()
    -                // We only wan to open group if the updated selectedGroup is a different one.
    -                if (selectedGroup?.groupId != it.groupId) {
    -                    _openGroupLiveData.postLiveEvent(it)
    +                // We only want to open group if the updated selectedGroup is a different one.
    +                if (selectedGroup?.groupId != groupSummary.groupId) {
    +                    _openGroupLiveData.postLiveEvent(groupSummary)
                     }
    -                val optionGroup = Option.fromNullable(it)
    +                val optionGroup = Option.just(groupSummary)
                     selectedGroupStore.post(optionGroup)
                 }
             }
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsController.kt
    index bfc91bf5a1..3b77835917 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsController.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsController.kt
    @@ -19,6 +19,7 @@ package im.vector.riotx.features.home.room.breadcrumbs
     import android.view.View
     import com.airbnb.epoxy.EpoxyController
     import im.vector.matrix.android.api.util.toMatrixItem
    +import im.vector.riotx.core.epoxy.zeroItem
     import im.vector.riotx.core.utils.DebouncedClickListener
     import im.vector.riotx.features.home.AvatarRenderer
     import javax.inject.Inject
    @@ -45,9 +46,13 @@ class BreadcrumbsController @Inject constructor(
         override fun buildModels() {
             val safeViewState = viewState ?: return
     
    +        // Add a ZeroItem to avoid automatic scroll when the breadcrumbs are updated from another client
    +        zeroItem {
    +            id("top")
    +        }
    +
             // An empty breadcrumbs list can only be temporary because when entering in a room,
             // this one is added to the breadcrumbs
    -
             safeViewState.asyncBreadcrumbs.invoke()
                     ?.forEach {
                         breadcrumbsItem {
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    index 8887b94f92..49f23f7f2c 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
    @@ -86,8 +86,6 @@ import im.vector.riotx.features.autocomplete.command.CommandAutocompletePolicy
     import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter
     import im.vector.riotx.features.command.Command
     import im.vector.riotx.features.home.AvatarRenderer
    -import im.vector.riotx.features.permalink.NavigateToRoomInterceptor
    -import im.vector.riotx.features.permalink.PermalinkHandler
     import im.vector.riotx.features.home.getColorFromUserId
     import im.vector.riotx.features.home.room.detail.composer.TextComposerAction
     import im.vector.riotx.features.home.room.detail.composer.TextComposerView
    @@ -109,6 +107,8 @@ import im.vector.riotx.features.media.ImageMediaViewerActivity
     import im.vector.riotx.features.media.VideoContentRenderer
     import im.vector.riotx.features.media.VideoMediaViewerActivity
     import im.vector.riotx.features.notifications.NotificationDrawerManager
    +import im.vector.riotx.features.permalink.NavigateToRoomInterceptor
    +import im.vector.riotx.features.permalink.PermalinkHandler
     import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
     import im.vector.riotx.features.settings.VectorPreferences
     import im.vector.riotx.features.share.SharedData
    @@ -416,8 +416,10 @@ class RoomDetailFragment @Inject constructor(
                     composerLayout.composerRelatedMessageAvatar
             )
             composerLayout.expand {
    -            // need to do it here also when not using quick reply
    -            focusComposerAndShowKeyboard()
    +            if (isAdded) {
    +                // need to do it here also when not using quick reply
    +                focusComposerAndShowKeyboard()
    +            }
             }
             focusComposerAndShowKeyboard()
         }
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
    index 102412948b..1303c3aad9 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
    @@ -41,6 +41,7 @@ import im.vector.riotx.core.resources.StringProvider
     import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventFormatter
     import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
     import im.vector.riotx.features.html.EventHtmlRenderer
    +import im.vector.riotx.features.html.VectorHtmlCompressor
     import java.text.SimpleDateFormat
     import java.util.*
     
    @@ -82,6 +83,7 @@ data class MessageActionState(
     class MessageActionsViewModel @AssistedInject constructor(@Assisted
                                                               initialState: MessageActionState,
                                                               private val eventHtmlRenderer: Lazy,
    +                                                          private val htmlCompressor: VectorHtmlCompressor,
                                                               private val session: Session,
                                                               private val noticeEventFormatter: NoticeEventFormatter,
                                                               private val stringProvider: StringProvider
    @@ -100,6 +102,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
     
             val quickEmojis = listOf("👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀")
     
    +        @JvmStatic
             override fun create(viewModelContext: ViewModelContext, state: MessageActionState): MessageActionsViewModel? {
                 val fragment: MessageActionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
                 return fragment.messageActionViewModelFactory.create(state)
    @@ -167,11 +170,16 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
     
         private fun computeMessageBody(timelineEvent: Async): CharSequence? {
             return when (timelineEvent()?.root?.getClearType()) {
    -            EventType.MESSAGE     -> {
    +            EventType.MESSAGE,
    +            EventType.STICKER     -> {
                     val messageContent: MessageContent? = timelineEvent()?.getLastMessageContent()
                     if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
    -                    eventHtmlRenderer.get().render(messageContent.formattedBody
    -                            ?: messageContent.body)
    +                    val html = messageContent.formattedBody
    +                            ?.takeIf { it.isNotBlank() }
    +                            ?.let { htmlCompressor.compress(it) }
    +                            ?: messageContent.body
    +
    +                    eventHtmlRenderer.get().render(html)
                     } else {
                         messageContent?.body
                     }
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
    index c1cccbef7a..64d8950420 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt
    @@ -61,6 +61,7 @@ class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
     
         companion object : MvRxViewModelFactory {
     
    +        @JvmStatic
             override fun create(viewModelContext: ViewModelContext, state: ViewEditHistoryViewState): ViewEditHistoryViewModel? {
                 val fragment: ViewEditHistoryBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
                 return fragment.viewEditHistoryViewModelFactory.create(state)
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt
    index 9c96f17022..9e05cdcc18 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt
    @@ -46,6 +46,7 @@ import im.vector.riotx.features.home.room.detail.timeline.tools.createLinkMoveme
     import im.vector.riotx.features.home.room.detail.timeline.tools.linkify
     import im.vector.riotx.features.html.CodeVisitor
     import im.vector.riotx.features.html.EventHtmlRenderer
    +import im.vector.riotx.features.html.VectorHtmlCompressor
     import im.vector.riotx.features.media.ImageContentRenderer
     import im.vector.riotx.features.media.VideoContentRenderer
     import me.gujun.android.span.span
    @@ -57,6 +58,7 @@ class MessageItemFactory @Inject constructor(
             private val dimensionConverter: DimensionConverter,
             private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
             private val htmlRenderer: Lazy,
    +        private val htmlCompressor: VectorHtmlCompressor,
             private val stringProvider: StringProvider,
             private val imageContentRenderer: ImageContentRenderer,
             private val messageInformationDataFactory: MessageInformationDataFactory,
    @@ -179,10 +181,16 @@ class MessageItemFactory @Inject constructor(
                     .playable(messageContent.info?.mimeType == "image/gif")
                     .highlighted(highlight)
                     .mediaData(data)
    -                .clickListener(
    -                        DebouncedClickListener(View.OnClickListener { view ->
    -                            callback?.onImageMessageClicked(messageContent, data, view)
    -                        }))
    +                .apply {
    +                    if (messageContent.type == MessageType.MSGTYPE_STICKER_LOCAL) {
    +                        mode(ImageContentRenderer.Mode.STICKER)
    +                    } else {
    +                        clickListener(
    +                                DebouncedClickListener(View.OnClickListener { view ->
    +                                    callback?.onImageMessageClicked(messageContent, data, view)
    +                                }))
    +                    }
    +                }
         }
     
         private fun buildVideoMessageItem(messageContent: MessageVideoContent,
    @@ -227,6 +235,7 @@ class MessageItemFactory @Inject constructor(
                                             attributes: AbsMessageItem.Attributes): VectorEpoxyModel<*>? {
             val isFormatted = messageContent.formattedBody.isNullOrBlank().not()
             return if (isFormatted) {
    +            // First detect if the message contains some code block(s) or inline code
                 val localFormattedBody = htmlRenderer.get().parse(messageContent.body) as Document
                 val codeVisitor = CodeVisitor()
                 codeVisitor.visit(localFormattedBody)
    @@ -240,7 +249,8 @@ class MessageItemFactory @Inject constructor(
                         buildMessageTextItem(codeFormatted, false, informationData, highlight, callback, attributes)
                     }
                     CodeVisitor.Kind.NONE   -> {
    -                    val formattedBody = htmlRenderer.get().render(messageContent.formattedBody!!)
    +                    val compressed = htmlCompressor.compress(messageContent.formattedBody!!)
    +                    val formattedBody = htmlRenderer.get().render(compressed)
                         buildMessageTextItem(formattedBody, true, informationData, highlight, callback, attributes)
                     }
                 }
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
    index 457f30cbf4..2fd46ddf12 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
    @@ -36,6 +36,8 @@ abstract class MessageImageVideoItem : AbsMessageItem {
     
    +        @JvmStatic
             override fun create(viewModelContext: ViewModelContext, state: DisplayReactionsViewState): ViewReactionsViewModel? {
                 val fragment: ViewReactionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
                 return fragment.viewReactionsViewModelFactory.create(state)
    diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsViewModel.kt
    index 7f7a1f41c4..1c4d414f18 100644
    --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsViewModel.kt
    @@ -37,6 +37,7 @@ class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initia
     
         companion object : MvRxViewModelFactory {
     
    +        @JvmStatic
             override fun create(viewModelContext: ViewModelContext, state: RoomListQuickActionsState): RoomListQuickActionsViewModel? {
                 val fragment: RoomListQuickActionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
                 return fragment.roomListActionsViewModelFactory.create(state)
    diff --git a/vector/src/main/java/im/vector/riotx/features/html/VectorHtmlCompressor.kt b/vector/src/main/java/im/vector/riotx/features/html/VectorHtmlCompressor.kt
    new file mode 100644
    index 0000000000..9f3cf96a7e
    --- /dev/null
    +++ b/vector/src/main/java/im/vector/riotx/features/html/VectorHtmlCompressor.kt
    @@ -0,0 +1,40 @@
    +/*
    + * Copyright 2019 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.riotx.features.html
    +
    +import com.googlecode.htmlcompressor.compressor.Compressor
    +import com.googlecode.htmlcompressor.compressor.HtmlCompressor
    +import javax.inject.Inject
    +import javax.inject.Singleton
    +
    +@Singleton
    +class VectorHtmlCompressor @Inject constructor() {
    +
    +    // All default options are suitable so far
    +    private val htmlCompressor: Compressor = HtmlCompressor()
    +
    +    fun compress(html: String): String {
    +        var result = htmlCompressor.compress(html)
    +
    +        // Trim space after 
    and

    , unfortunately the method setRemoveSurroundingSpaces() from the doc does not exist + result = result.replace("
    ", "
    ") + result = result.replace("
    ", "
    ") + result = result.replace("

    ", "

    ") + + return result + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt index df638b462b..909fd5b8eb 100644 --- a/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt +++ b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt @@ -31,11 +31,13 @@ import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.core.glide.GlideRequest +import im.vector.riotx.core.ui.model.Size import im.vector.riotx.core.utils.DimensionConverter import im.vector.riotx.core.utils.isLocalFile import kotlinx.android.parcel.Parcelize import timber.log.Timber import javax.inject.Inject +import kotlin.math.min class ImageContentRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, private val dimensionConverter: DimensionConverter) { @@ -56,17 +58,18 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: enum class Mode { FULL_SIZE, - THUMBNAIL + THUMBNAIL, + STICKER } fun render(data: Data, mode: Mode, imageView: ImageView) { - val (width, height) = processSize(data, mode) - imageView.layoutParams.height = height - imageView.layoutParams.width = width + val size = processSize(data, mode) + imageView.layoutParams.width = size.width + imageView.layoutParams.height = size.height // a11y imageView.contentDescription = data.filename - createGlideRequest(data, mode, imageView, width, height) + createGlideRequest(data, mode, imageView, size) .dontAnimate() .transform(RoundedCorners(dimensionConverter.dpToPx(8))) .thumbnail(0.3f) @@ -74,12 +77,12 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: } fun renderFitTarget(data: Data, mode: Mode, imageView: ImageView, callback: ((Boolean) -> Unit)? = null) { - val (width, height) = processSize(data, mode) + val size = processSize(data, mode) // a11y imageView.contentDescription = data.filename - createGlideRequest(data, mode, imageView, width, height) + createGlideRequest(data, mode, imageView, size) .listener(object : RequestListener { override fun onLoadFailed(e: GlideException?, model: Any?, @@ -102,7 +105,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: .into(imageView) } - private fun createGlideRequest(data: Data, mode: Mode, imageView: ImageView, width: Int, height: Int): GlideRequest { + private fun createGlideRequest(data: Data, mode: Mode, imageView: ImageView, size: Size): GlideRequest { return if (data.elementToDecrypt != null) { // Encrypted image GlideApp @@ -112,8 +115,9 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: // Clear image val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver() val resolvedUrl = when (mode) { - Mode.FULL_SIZE -> contentUrlResolver.resolveFullSize(data.url) - Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE) + Mode.FULL_SIZE, + Mode.STICKER -> contentUrlResolver.resolveFullSize(data.url) + Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, size.width, size.height, ContentUrlResolver.ThumbnailMethod.SCALE) } // Fallback to base url ?: data.url @@ -144,23 +148,32 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: ) } - private fun processSize(data: Data, mode: Mode): Pair { + private fun processSize(data: Data, mode: Mode): Size { val maxImageWidth = data.maxWidth val maxImageHeight = data.maxHeight val width = data.width ?: maxImageWidth val height = data.height ?: maxImageHeight - var finalHeight = -1 var finalWidth = -1 + var finalHeight = -1 // if the image size is known // compute the expected height if (width > 0 && height > 0) { - if (mode == Mode.FULL_SIZE) { - finalHeight = height - finalWidth = width - } else { - finalHeight = Math.min(maxImageWidth * height / width, maxImageHeight) - finalWidth = finalHeight * width / height + when (mode) { + Mode.FULL_SIZE -> { + finalHeight = height + finalWidth = width + } + Mode.THUMBNAIL -> { + finalHeight = min(maxImageWidth * height / width, maxImageHeight) + finalWidth = finalHeight * width / height + } + Mode.STICKER -> { + // limit on width + val maxWidthDp = min(dimensionConverter.dpToPx(120), maxImageWidth / 2) + finalWidth = min(dimensionConverter.dpToPx(width), maxWidthDp) + finalHeight = finalWidth * height / width + } } } // ensure that some values are properly initialized @@ -170,6 +183,6 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: if (finalWidth < 0) { finalWidth = maxImageWidth } - return Pair(finalWidth, finalHeight) + return Size(finalWidth, finalHeight) } } diff --git a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt index 19f6aece58..08ff11217d 100644 --- a/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/riotx/features/navigation/DefaultNavigator.kt @@ -19,8 +19,11 @@ package im.vector.riotx.features.navigation import android.app.Activity import android.content.Context import android.content.Intent +import androidx.core.app.TaskStackBuilder import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom import im.vector.riotx.R +import im.vector.riotx.core.di.ActiveSessionHolder +import im.vector.riotx.core.error.fatalError import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.utils.toast import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupManageActivity @@ -38,12 +41,18 @@ import im.vector.riotx.features.share.SharedData import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton -import androidx.core.app.TaskStackBuilder @Singleton -class DefaultNavigator @Inject constructor() : Navigator { +class DefaultNavigator @Inject constructor( + private val sessionHolder: ActiveSessionHolder +) : Navigator { override fun openRoom(context: Context, roomId: String, eventId: String?, buildTask: Boolean) { + if (sessionHolder.getSafeActiveSession()?.getRoom(roomId) == null) { + fatalError("Trying to open an unknown room $roomId") + return + } + val args = RoomDetailArgs(roomId, eventId) val intent = RoomDetailActivity.newIntent(context, args) if (buildTask) { diff --git a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt index c849166738..e46adc53fc 100644 --- a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandler.kt @@ -57,7 +57,7 @@ class PermalinkHandler @Inject constructor(private val session: Session, .observeOn(AndroidSchedulers.mainThread()) .map { val roomId = it.getOrNull() - if (navigateToRoomInterceptor?.navToRoom(roomId) != true) { + if (navigateToRoomInterceptor?.navToRoom(roomId, permalinkData.eventId) != true) { openRoom(context, roomId, permalinkData.eventId, buildTask) } true @@ -87,9 +87,9 @@ class PermalinkHandler @Inject constructor(private val session: Session, } /** - * Open room either joined, or not unknown + * Open room either joined, or not */ - private fun openRoom(context: Context, roomId: String?, eventId: String? = null, buildTask: Boolean) { + private fun openRoom(context: Context, roomId: String?, eventId: String?, buildTask: Boolean) { return if (roomId != null && session.getRoom(roomId) != null) { navigator.openRoom(context, roomId, eventId, buildTask) } else { diff --git a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandlerActivity.kt b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandlerActivity.kt index 08e09fa48d..5339a2c6f9 100644 --- a/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandlerActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/permalink/PermalinkHandlerActivity.kt @@ -27,7 +27,6 @@ import im.vector.riotx.core.utils.toast import im.vector.riotx.features.home.LoadingFragment import im.vector.riotx.features.login.LoginActivity import io.reactivex.android.schedulers.AndroidSchedulers -import kotlinx.android.synthetic.debug.activity_test_material_theme.* import java.util.concurrent.TimeUnit import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/riotx/features/rageshake/BugReportActivity.kt b/vector/src/main/java/im/vector/riotx/features/rageshake/BugReportActivity.kt index 187566f660..5c1428cb54 100755 --- a/vector/src/main/java/im/vector/riotx/features/rageshake/BugReportActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/rageshake/BugReportActivity.kt @@ -77,8 +77,7 @@ class BugReportActivity : VectorBaseActivity() { override fun onPrepareOptionsMenu(menu: Menu): Boolean { menu.findItem(R.id.ic_action_send_bug_report)?.let { - val isValid = bug_report_edit_text.text.toString().trim().length > 10 - && !bug_report_mask_view.isVisible + val isValid = !bug_report_mask_view.isVisible it.isEnabled = isValid it.icon.alpha = if (isValid) 255 else 100 @@ -90,7 +89,11 @@ class BugReportActivity : VectorBaseActivity() { override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.ic_action_send_bug_report -> { - sendBugReport() + if (bug_report_edit_text.text.toString().trim().length >= 10) { + sendBugReport() + } else { + bug_report_text_input_layout.error = getString(R.string.bug_report_error_too_short) + } return true } } @@ -150,7 +153,7 @@ class BugReportActivity : VectorBaseActivity() { val myProgress = progress.coerceIn(0, 100) bug_report_progress_view.progress = myProgress - bug_report_progress_text_view.text = getString(R.string.send_bug_report_progress, "$myProgress") + bug_report_progress_text_view.text = getString(R.string.send_bug_report_progress, myProgress.toString()) } override fun onUploadSucceed() { @@ -179,7 +182,7 @@ class BugReportActivity : VectorBaseActivity() { @OnTextChanged(R.id.bug_report_edit_text) internal fun textChanged() { - invalidateOptionsMenu() + bug_report_text_input_layout.error = null } @OnCheckedChanged(R.id.bug_report_button_include_screenshot) diff --git a/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt index b96542a8ce..dc353363d5 100755 --- a/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt +++ b/vector/src/main/java/im/vector/riotx/features/rageshake/BugReporter.kt @@ -33,6 +33,7 @@ import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.extensions.toOnOff import im.vector.riotx.core.utils.getDeviceLocale import im.vector.riotx.features.settings.VectorLocale +import im.vector.riotx.features.settings.VectorPreferences import im.vector.riotx.features.themes.ThemeUtils import im.vector.riotx.features.version.VersionProvider import okhttp3.Call @@ -44,12 +45,15 @@ import okhttp3.Response import org.json.JSONException import org.json.JSONObject import timber.log.Timber -import java.io.* +import java.io.File +import java.io.IOException +import java.io.OutputStreamWriter import java.net.HttpURLConnection -import java.util.Locale +import java.util.* import java.util.zip.GZIPOutputStream import javax.inject.Inject import javax.inject.Singleton +import kotlin.collections.ArrayList /** * BugReporter creates and sends the bug reports. @@ -57,6 +61,7 @@ import javax.inject.Singleton @Singleton class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, private val versionProvider: VersionProvider, + private val vectorPreferences: VectorPreferences, private val vectorFileLogger: VectorFileLogger) { var inMultiWindowMode = false @@ -230,7 +235,7 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes .addFormDataPart("matrix_sdk_version", Matrix.getSdkVersion()) .addFormDataPart("olm_version", olmVersion) .addFormDataPart("device", Build.MODEL.trim()) - .addFormDataPart("lazy_loading", true.toOnOff()) + .addFormDataPart("verbose_log", vectorPreferences.labAllowedExtendedLogging().toOnOff()) .addFormDataPart("multi_window", inMultiWindowMode.toOnOff()) .addFormDataPart("os", Build.VERSION.RELEASE + " (API " + Build.VERSION.SDK_INT + ") " + Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME) diff --git a/vector/src/main/java/im/vector/riotx/features/rageshake/VectorFileLogger.kt b/vector/src/main/java/im/vector/riotx/features/rageshake/VectorFileLogger.kt index 95053790c8..6049db6180 100644 --- a/vector/src/main/java/im/vector/riotx/features/rageshake/VectorFileLogger.kt +++ b/vector/src/main/java/im/vector/riotx/features/rageshake/VectorFileLogger.kt @@ -24,9 +24,7 @@ import java.io.File import java.io.PrintWriter import java.io.StringWriter import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale -import java.util.TimeZone +import java.util.* import java.util.logging.* import java.util.logging.Formatter import javax.inject.Inject @@ -83,7 +81,8 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec return if (vectorPreferences.labAllowedExtendedLogging()) { false } else { - priority < Log.ERROR + // Exclude debug and verbose logs + priority <= Log.DEBUG } } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt index 057c5d8159..01debac5ed 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt @@ -42,6 +42,7 @@ class EmojiSearchResultViewModel @AssistedInject constructor( companion object : MvRxViewModelFactory { + @JvmStatic override fun create(viewModelContext: ViewModelContext, state: EmojiSearchResultViewState): EmojiSearchResultViewModel? { val activity: EmojiReactionPickerActivity = (viewModelContext as ActivityViewModelContext).activity() return activity.emojiSearchResultViewModelFactory.create(state) diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt index d89f0e2b99..dcd64c6a46 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/RoomDirectoryViewModel.kt @@ -178,7 +178,9 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: copy( asyncPublicRoomsRequest = Success(data.chunk!!), // It's ok to append at the end of the list, so I use publicRooms.size() - publicRooms = publicRooms.appendAt(data.chunk!!, publicRooms.size), + publicRooms = publicRooms.appendAt(data.chunk!!, publicRooms.size) + // Rageshake #8206 tells that we can have several times the same room + .distinctBy { it.roomId }, hasMore = since != null ) } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsBaseFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsBaseFragment.kt index 666f1610b0..e32cc98123 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsBaseFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsBaseFragment.kt @@ -65,7 +65,7 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), HasScree override fun onResume() { super.onResume() - Timber.v("onResume Fragment ${this.javaClass.simpleName}") + Timber.i("onResume Fragment ${this.javaClass.simpleName}") vectorActivity.supportActionBar?.setTitle(titleRes) // find the view from parent activity mLoadingView = vectorActivity.findViewById(R.id.vector_settings_spinner_views) diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysViewModel.kt index dd773f4c22..db4586dff5 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysViewModel.kt @@ -40,6 +40,7 @@ class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: companion object : MvRxViewModelFactory { + @JvmStatic override fun create(viewModelContext: ViewModelContext, state: PushGatewayViewState): PushGatewaysViewModel? { val fragment: PushGatewaysFragment = (viewModelContext as FragmentViewModelContext).fragment() return fragment.pushGatewaysViewModelFactory.create(state) diff --git a/vector/src/main/res/layout/activity_bug_report.xml b/vector/src/main/res/layout/activity_bug_report.xml index bbe819769a..7156f91314 100644 --- a/vector/src/main/res/layout/activity_bug_report.xml +++ b/vector/src/main/res/layout/activity_bug_report.xml @@ -1,5 +1,6 @@ + android:textColorHint="?attr/vctr_default_text_hint_color" + app:counterEnabled="true" + app:counterMaxLength="500"> + diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index c9180c3878..753a15274a 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -114,7 +114,7 @@ Sign up to %1$s - Username + Username or email Password Next That username is taken @@ -162,4 +162,5 @@ The current session is for user %1$s and you provide credentials for user %2$s. This is not supported by RiotX.\nPlease first clear data, then sign in again on another account. Your matrix.to link was malformed + The description is too short