From a4aa38ee43e69347e6b8e1c4ff74d5c65b9d8e58 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 16 Dec 2019 17:14:26 +0100 Subject: [PATCH 01/19] Fix new issue on permalink click --- CHANGES.md | 2 +- .../im/vector/riotx/features/permalink/PermalinkHandler.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 1228ea65ca..5f83727cd7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,7 +14,7 @@ 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 Translations 🗣: - 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 { From 1c727c1ee47191b81486bf0356e383a26da03c0b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 17 Dec 2019 10:42:58 +0100 Subject: [PATCH 02/19] Fix crash reported by rageshake --- .../features/home/room/detail/RoomDetailFragment.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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() } From 42cdb1db1146a65d8a270eb23e5aac29e73edda8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 17 Dec 2019 12:26:45 +0100 Subject: [PATCH 03/19] Fix crash reported by rageshake: writeToFile may throw exceptions --- .../android/internal/session/DefaultFileService.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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) } From 1ceddd9607674bf5faa6278b04327b2618d53071 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 17 Dec 2019 14:05:58 +0100 Subject: [PATCH 04/19] Rageshake: log resumed screens and add the log verbosity ON/OFF to the rageshakes data --- .../vector/riotx/core/platform/VectorBaseActivity.kt | 3 +-- .../platform/VectorBaseBottomSheetDialogFragment.kt | 6 ++++++ .../vector/riotx/core/platform/VectorBaseFragment.kt | 2 +- .../im/vector/riotx/features/rageshake/BugReporter.kt | 11 ++++++++--- .../riotx/features/rageshake/VectorFileLogger.kt | 7 +++---- .../features/settings/VectorSettingsBaseFragment.kt | 2 +- 6 files changed, 20 insertions(+), 11 deletions(-) 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/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/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) From 65faedb06bf6c228fb36b8ece9eb933a8fca86bc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 17 Dec 2019 14:14:49 +0100 Subject: [PATCH 05/19] BugReport screen: improve UX when description is too short (reported by Matthew) --- .../riotx/features/rageshake/BugReportActivity.kt | 13 ++++++++----- vector/src/main/res/layout/activity_bug_report.xml | 5 ++++- vector/src/main/res/values/strings_riotX.xml | 1 + 3 files changed, 13 insertions(+), 6 deletions(-) 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/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"> 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 From 7fa76b9d351e99ae40179211fdfb2ad906ecb0b0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 17 Dec 2019 16:35:04 +0100 Subject: [PATCH 06/19] Prevent crash when opening unknown room, which should not happen... --- .../session/room/create/CreateRoomTask.kt | 2 +- .../java/im/vector/riotx/core/error/fatal.kt | 31 +++++++++++++++++++ .../features/navigation/DefaultNavigator.kt | 13 ++++++-- 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/error/fatal.kt 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/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/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) { From 79f11ad686d5baadf24ce556da555f31e205fccc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 17 Dec 2019 17:22:26 +0100 Subject: [PATCH 07/19] Prevent crash when mimetype is null --- .../api/session/content/ContentAttachmentData.kt | 2 +- .../api/session/room/model/message/VideoInfo.kt | 2 +- .../crypto/attachments/MXEncryptedAttachments.kt | 2 +- .../internal/session/content/FileUploader.kt | 4 ++-- .../session/room/send/LocalEchoEventFactory.kt | 4 ++-- .../features/attachments/AttachmentsMapper.kt | 15 ++++++++++----- 6 files changed, 17 insertions(+), 12 deletions(-) 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/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/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/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/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, From 08970ad8c1beb1bf33c9f125ad91af6e94593ca8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Dec 2019 09:56:58 +0100 Subject: [PATCH 08/19] Fix a crash on public room list It's maybe a workaround, as it should not happen, but at least it will not crash anymore --- .../riotx/features/roomdirectory/RoomDirectoryViewModel.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 ) } From 9d26ba3186e63ba6f7e7202bda81198f83d430dd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Dec 2019 10:57:00 +0100 Subject: [PATCH 09/19] Fix rendering issue with HTML formatted body --- CHANGES.md | 1 + vector/build.gradle | 1 + .../src/main/assets/open_source_licenses.html | 5 +++ .../vector/riotx/core/di/VectorComponent.kt | 3 ++ .../action/MessageActionsViewModel.kt | 10 ++++- .../timeline/factory/MessageItemFactory.kt | 6 ++- .../features/html/VectorHtmlCompressor.kt | 40 +++++++++++++++++++ 7 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/features/html/VectorHtmlCompressor.kt diff --git a/CHANGES.md b/CHANGES.md index 5f83727cd7..50279bcf0e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ 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 + - Fix rendering issue with HTML formatted body Translations 🗣: - 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/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/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..3c7e4624fe 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
    @@ -170,8 +172,12 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
                 EventType.MESSAGE     -> {
                     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/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt
    index 9c96f17022..30f4e94cfb 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,
    @@ -227,6 +229,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 +243,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/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 + } +} From c48a439eea00dd75f508758006283df2aa9121a0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Dec 2019 16:03:10 +0100 Subject: [PATCH 10/19] Add @JvmStatic for performance reasons. See https://github.com/airbnb/MvRx/wiki/Advanced-Concepts#mvrxviewmodel --- .../home/room/detail/timeline/action/MessageActionsViewModel.kt | 1 + .../room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt | 1 + .../room/detail/timeline/reactions/ViewReactionsViewModel.kt | 1 + .../home/room/list/actions/RoomListQuickActionsViewModel.kt | 1 + .../riotx/features/reactions/EmojiSearchResultViewModel.kt | 1 + .../vector/riotx/features/settings/push/PushGatewaysViewModel.kt | 1 + 6 files changed, 6 insertions(+) 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 3c7e4624fe..2de1fb225d 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 @@ -102,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) 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/reactions/ViewReactionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt index 9ec45b03b9..761e80dd59 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt @@ -68,6 +68,7 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted companion object : MvRxViewModelFactory { + @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/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/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) From 123ffe9f9c7c7dfb0ca65c5103d67df4b957b55a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Dec 2019 17:00:18 +0100 Subject: [PATCH 11/19] Cleanup --- .../riotx/features/home/group/GroupListViewModel.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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) } } From 7eae85a39493bf0c851dc401ccd5ac09667b092f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Dec 2019 18:41:46 +0100 Subject: [PATCH 12/19] Add a ZeroItem to avoid automatic scroll when the breadcrumbs are updated from another client --- .../im/vector/riotx/core/epoxy/ZeroItem.kt | 30 +++++++++++++++++++ .../room/breadcrumbs/BreadcrumbsController.kt | 7 ++++- vector/src/main/res/layout/item_zero.xml | 4 +++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/epoxy/ZeroItem.kt create mode 100644 vector/src/main/res/layout/item_zero.xml 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/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/res/layout/item_zero.xml b/vector/src/main/res/layout/item_zero.xml new file mode 100644 index 0000000000..ec7c4e3f98 --- /dev/null +++ b/vector/src/main/res/layout/item_zero.xml @@ -0,0 +1,4 @@ + + From 648691656a9e0dff24001126f4bb624cdb72058a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Dec 2019 19:20:44 +0100 Subject: [PATCH 13/19] Disable click on Stickers (#703) --- CHANGES.md | 1 + .../detail/timeline/factory/MessageItemFactory.kt | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 50279bcf0e..133dba8909 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ Bugfix 🐛: - 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 - Fix rendering issue with HTML formatted body + - Disable click on Stickers (#703) Translations 🗣: - 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 30f4e94cfb..c2b2b67129 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 @@ -181,10 +181,14 @@ 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) { + clickListener( + DebouncedClickListener(View.OnClickListener { view -> + callback?.onImageMessageClicked(messageContent, data, view) + })) + } + } } private fun buildVideoMessageItem(messageContent: MessageVideoContent, From b0ff2cb4bb283cf2ed594987ed0234b3d5a5d1f7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Dec 2019 19:31:10 +0100 Subject: [PATCH 14/19] cleanup --- .../vector/riotx/features/permalink/PermalinkHandlerActivity.kt | 1 - 1 file changed, 1 deletion(-) 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 From 6652965e481c22015be97a01ea6f2a79312c2912 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 19 Dec 2019 10:46:11 +0100 Subject: [PATCH 15/19] Ignore lint issue --- vector/lint.xml | 4 ++++ 1 file changed, 4 insertions(+) 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 @@ + + + + From 4b0dfa49f4429369f5f90f11e2f90b438e750efd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 19 Dec 2019 11:44:07 +0100 Subject: [PATCH 16/19] Limit sticker size in the timeline --- CHANGES.md | 1 + .../session/room/timeline/TimelineEvent.kt | 4 +-- .../timeline/factory/MessageItemFactory.kt | 4 ++- .../timeline/item/MessageImageVideoItem.kt | 4 ++- .../features/media/ImageContentRenderer.kt | 30 +++++++++++++------ 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 133dba8909..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 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/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 c2b2b67129..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 @@ -182,7 +182,9 @@ class MessageItemFactory @Inject constructor( .highlighted(highlight) .mediaData(data) .apply { - if (messageContent.type != MessageType.MSGTYPE_STICKER_LOCAL) { + if (messageContent.type == MessageType.MSGTYPE_STICKER_LOCAL) { + mode(ImageContentRenderer.Mode.STICKER) + } else { clickListener( DebouncedClickListener(View.OnClickListener { view -> callback?.onImageMessageClicked(messageContent, data, view) 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 contentUrlResolver.resolveFullSize(data.url) + Mode.FULL_SIZE, + Mode.STICKER -> contentUrlResolver.resolveFullSize(data.url) Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE) } // Fallback to base url @@ -149,18 +152,27 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: 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 From bb9510e59b7bf7abe2ddc7b7c2d92533c0282d45 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 19 Dec 2019 12:05:30 +0100 Subject: [PATCH 17/19] Create Size data class --- .../im/vector/riotx/core/ui/model/Size.kt | 20 ++++++++++++++++++ .../features/media/ImageContentRenderer.kt | 21 ++++++++++--------- 2 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/ui/model/Size.kt 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/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/riotx/features/media/ImageContentRenderer.kt index 60abd19191..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,6 +31,7 @@ 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 @@ -62,13 +63,13 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: } 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) @@ -76,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?, @@ -104,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 @@ -116,7 +117,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: val resolvedUrl = when (mode) { Mode.FULL_SIZE, Mode.STICKER -> contentUrlResolver.resolveFullSize(data.url) - Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE) + Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, size.width, size.height, ContentUrlResolver.ThumbnailMethod.SCALE) } // Fallback to base url ?: data.url @@ -147,7 +148,7 @@ 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 @@ -182,6 +183,6 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: if (finalWidth < 0) { finalWidth = maxImageWidth } - return Pair(finalWidth, finalHeight) + return Size(finalWidth, finalHeight) } } From bf69810f8f648099ca11828a428771d0cd73973f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 19 Dec 2019 12:05:47 +0100 Subject: [PATCH 18/19] Bottom sheet event preview for Sticker --- .../room/detail/timeline/action/MessageActionsViewModel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 2de1fb225d..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 @@ -170,7 +170,8 @@ 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) { val html = messageContent.formattedBody From 13439769a1cd51619dd5bde68cd0ecad3ad40958 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 19 Dec 2019 14:01:58 +0100 Subject: [PATCH 19/19] Update wording --- vector/src/main/res/values/strings_riotX.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index f315109ddf..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