From 23623b8895958acfe9b8d6e8b5b674e7d012fe33 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 4 Jan 2021 17:12:49 +0300 Subject: [PATCH 01/16] Migrate to Android 11, API 30. --- CHANGES.md | 4 +- attachment-viewer/build.gradle | 4 +- .../AttachmentViewerActivity.kt | 69 ++- .../src/main/res/values/colors.xml | 6 + matrix-sdk-android-rx/build.gradle | 4 +- matrix-sdk-android/build.gradle | 4 +- .../session/content/ThumbnailExtractor.kt | 34 +- multipicker/build.gradle | 4 +- .../im/vector/lib/multipicker/AudioPicker.kt | 2 +- .../im/vector/lib/multipicker/VideoPicker.kt | 8 +- vector/build.gradle | 4 +- .../app/core/platform/VectorBaseActivity.kt | 22 +- .../preview/AttachmentsPreviewFragment.kt | 7 +- .../app/features/call/VectorCallActivity.kt | 41 +- .../crypto/recover/BootstrapBottomSheet.kt | 8 +- .../app/features/login/LoginWebFragment.kt | 9 +- .../app/features/popup/PopupAlertManager.kt | 9 +- .../app/features/rageshake/BugReporter.kt | 492 +++++++++--------- .../uploads/media/RoomUploadsMediaFragment.kt | 8 +- .../features/webview/VectorWebViewActivity.kt | 4 +- .../features/widgets/webview/WidgetWebView.kt | 3 +- 21 files changed, 424 insertions(+), 322 deletions(-) create mode 100644 attachment-viewer/src/main/res/values/colors.xml diff --git a/CHANGES.md b/CHANGES.md index 373c3aa985..f02c05672d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,10 +15,10 @@ Translations 🗣: - SDK API changes ⚠️: - - + - Increase targetSdkVersion to 30 (#2600) Build 🧱: - - + - Compile with Android SDK 30 (Android 11) Test: - diff --git a/attachment-viewer/build.gradle b/attachment-viewer/build.gradle index d8cd7d0c98..5ce9f1eff6 100644 --- a/attachment-viewer/build.gradle +++ b/attachment-viewer/build.gradle @@ -32,11 +32,11 @@ buildscript { } android { - compileSdkVersion 29 + compileSdkVersion 30 defaultConfig { minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 versionCode 1 versionName "1.0" } diff --git a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt index ae095be41a..1d09e1ef0e 100644 --- a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt +++ b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt @@ -24,9 +24,12 @@ import android.view.MotionEvent import android.view.ScaleGestureDetector import android.view.View import android.view.ViewGroup +import android.view.WindowInsets +import android.view.WindowInsetsController import android.view.WindowManager import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.core.view.GestureDetectorCompat import androidx.core.view.ViewCompat import androidx.core.view.isVisible @@ -89,14 +92,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // This is important for the dispatchTouchEvent, if not we must correct - // the touch coordinates - window.decorView.systemUiVisibility = ( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - or View.SYSTEM_UI_FLAG_IMMERSIVE) - window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) - window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) + setDecorViewFullScreen() views = ActivityAttachmentViewerBinding.inflate(layoutInflater) setContentView(views.root) @@ -132,6 +128,25 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi } } + @Suppress("DEPRECATION") + private fun setDecorViewFullScreen() { + // This is important for the dispatchTouchEvent, if not we must correct + // the touch coordinates + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE // New API instead of SYSTEM_UI_FLAG_IMMERSIVE + window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // New API instead of FLAG_TRANSLUCENT_STATUS + window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // new API instead of FLAG_TRANSLUCENT_NAVIGATION + } else { + window.decorView.systemUiVisibility = ( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_IMMERSIVE) + window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) + window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) + } + } + fun onSelectedPositionChanged(position: Int) { attachmentsAdapter.recyclerView?.findViewHolderForAdapterPosition(currentPosition)?.let { (it as? BaseViewHolder)?.onSelected(false) @@ -311,28 +326,42 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi ?.handleCommand(commands) } + @Suppress("DEPRECATION") private fun hideSystemUI() { systemUiVisibility = false // Enables regular immersive mode. // For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE. // Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY - window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE - // Set the content to appear under the system bars so that the - // content doesn't resize when the system bars hide and show. - or View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - // Hide the nav bar and status bar - or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_FULLSCREEN) + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars()) // new API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION + window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE // New API instead of SYSTEM_UI_FLAG_IMMERSIVE + window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // New API instead of FLAG_TRANSLUCENT_STATUS + window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // new API instead of FLAG_TRANSLUCENT_NAVIGATION + } else { + window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE + // Set the content to appear under the system bars so that the + // content doesn't resize when the system bars hide and show. + or View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + // Hide the nav bar and status bar + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_FULLSCREEN) + } } // Shows the system bars by removing all the flags // except for the ones that make the content appear under the system bars. + @Suppress("DEPRECATION") private fun showSystemUI() { systemUiVisibility = true - window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + } else { + window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) + } } } diff --git a/attachment-viewer/src/main/res/values/colors.xml b/attachment-viewer/src/main/res/values/colors.xml new file mode 100644 index 0000000000..7ceef40881 --- /dev/null +++ b/attachment-viewer/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + + #80000000 + + \ No newline at end of file diff --git a/matrix-sdk-android-rx/build.gradle b/matrix-sdk-android-rx/build.gradle index a99b5856ba..0e899e21ff 100644 --- a/matrix-sdk-android-rx/build.gradle +++ b/matrix-sdk-android-rx/build.gradle @@ -3,11 +3,11 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' android { - compileSdkVersion 29 + compileSdkVersion 30 defaultConfig { minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 versionCode 1 versionName "1.0" diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index d72e5bda41..ad177db95c 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -14,12 +14,12 @@ buildscript { } android { - compileSdkVersion 29 + compileSdkVersion 30 testOptions.unitTests.includeAndroidResources = true defaultConfig { minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 versionCode 1 versionName "0.0.1" // Multidex is useful for tests diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt index 4b31db59b1..c28668a53e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ThumbnailExtractor.kt @@ -47,22 +47,24 @@ internal object ThumbnailExtractor { val mediaMetadataRetriever = MediaMetadataRetriever() try { mediaMetadataRetriever.setDataSource(context, attachment.queryUri) - val thumbnail = mediaMetadataRetriever.frameAtTime - - val outputStream = ByteArrayOutputStream() - thumbnail.compress(Bitmap.CompressFormat.JPEG, 100, outputStream) - val thumbnailWidth = thumbnail.width - val thumbnailHeight = thumbnail.height - val thumbnailSize = outputStream.size() - thumbnailData = ThumbnailData( - width = thumbnailWidth, - height = thumbnailHeight, - size = thumbnailSize.toLong(), - bytes = outputStream.toByteArray(), - mimeType = MimeTypes.Jpeg - ) - thumbnail.recycle() - outputStream.reset() + mediaMetadataRetriever.frameAtTime?.let { thumbnail -> + val outputStream = ByteArrayOutputStream() + thumbnail.compress(Bitmap.CompressFormat.JPEG, 100, outputStream) + val thumbnailWidth = thumbnail.width + val thumbnailHeight = thumbnail.height + val thumbnailSize = outputStream.size() + thumbnailData = ThumbnailData( + width = thumbnailWidth, + height = thumbnailHeight, + size = thumbnailSize.toLong(), + bytes = outputStream.toByteArray(), + mimeType = MimeTypes.Jpeg + ) + thumbnail.recycle() + outputStream.reset() + } ?: run { + Timber.e("Cannot extract video thumbnail at %s", attachment.queryUri.toString()) + } } catch (e: Exception) { Timber.e(e, "Cannot extract video thumbnail") } finally { diff --git a/multipicker/build.gradle b/multipicker/build.gradle index c58c4586b2..10dc18e488 100644 --- a/multipicker/build.gradle +++ b/multipicker/build.gradle @@ -19,11 +19,11 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-parcelize' android { - compileSdkVersion 29 + compileSdkVersion 30 defaultConfig { minSdkVersion 19 - targetSdkVersion 29 + targetSdkVersion 30 versionCode 1 versionName "1.0" diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt index 516022100d..e8970d72ef 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt @@ -58,7 +58,7 @@ class AudioPicker : Picker() { context.contentResolver.openFileDescriptor(selectedUri, "r")?.use { pfd -> val mediaMetadataRetriever = MediaMetadataRetriever() mediaMetadataRetriever.setDataSource(pfd.fileDescriptor) - duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toLong() + duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L } audioList.add( diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt index c7c06f795f..dada9ac5bd 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt @@ -61,10 +61,10 @@ class VideoPicker : Picker() { context.contentResolver.openFileDescriptor(selectedUri, "r")?.use { pfd -> val mediaMetadataRetriever = MediaMetadataRetriever() mediaMetadataRetriever.setDataSource(pfd.fileDescriptor) - duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toLong() - width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH).toInt() - height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT).toInt() - orientation = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION).toInt() + duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L + width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toInt() ?: 0 + height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toInt() ?: 0 + orientation = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)?.toInt() ?: 0 } videoList.add( diff --git a/vector/build.gradle b/vector/build.gradle index f6ba5d6e27..d75bf6c1fe 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -101,7 +101,7 @@ ext.abiVersionCodes = ["armeabi-v7a": 1, "arm64-v8a": 2, "x86": 3, "x86_64": 4]. def buildNumber = System.env.BUILDKITE_BUILD_NUMBER as Integer ?: 0 android { - compileSdkVersion 29 + compileSdkVersion 30 // Due to a bug introduced in Android gradle plugin 3.6.0, we have to specify the ndk version to use // Ref: https://issuetracker.google.com/issues/144111441 @@ -111,7 +111,7 @@ android { applicationId "im.vector.app" // Set to API 21: see #405 minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 multiDexEnabled true // `develop` branch will have version code from timestamp, to ensure each build from CI has a incremented versionCode. diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt index a585e8ea77..3ea995c418 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt @@ -23,6 +23,7 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.view.View +import android.view.WindowInsetsController import android.view.WindowManager import android.widget.TextView import androidx.annotation.AttrRes @@ -33,6 +34,7 @@ import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentFactory @@ -410,13 +412,21 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScr /** * Force to render the activity in fullscreen */ + @Suppress("DEPRECATION") private fun setFullScreen() { - window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_FULLSCREEN - or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE // New API instead of SYSTEM_UI_FLAG_IMMERSIVE + window.statusBarColor = ContextCompat.getColor(this, im.vector.lib.attachmentviewer.R.color.half_transparent_status_bar) // New API instead of FLAG_TRANSLUCENT_STATUS + window.navigationBarColor = ContextCompat.getColor(this, im.vector.lib.attachmentviewer.R.color.half_transparent_status_bar) // new API instead of FLAG_TRANSLUCENT_NAVIGATION + } else { + window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_FULLSCREEN + or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) + } } /* ========================================================================================== diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt index 407b51666b..147492fc46 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -153,8 +153,13 @@ class AttachmentsPreviewFragment @Inject constructor( ) } + @Suppress("DEPRECATION") private fun applyInsets() { - view?.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + activity?.window?.setDecorFitsSystemWindows(false) + } else { + view?.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + } ViewCompat.setOnApplyWindowInsetsListener(views.attachmentPreviewerBottomContainer) { v, insets -> v.updatePadding(bottom = insets.systemWindowInsetBottom) insets diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 41bf7bbeaf..ef96bc810a 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -25,8 +25,11 @@ import android.os.Bundle import android.os.Parcelable import android.view.View import android.view.Window +import android.view.WindowInsets +import android.view.WindowInsetsController import android.view.WindowManager import androidx.appcompat.app.AlertDialog +import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import androidx.core.view.ViewCompat import androidx.core.view.isInvisible @@ -102,29 +105,43 @@ class VectorCallActivity : VectorBaseActivity(), CallContro setContentView(R.layout.activity_call) } + @Suppress("DEPRECATION") private fun hideSystemUI() { systemUiVisibility = false // Enables regular immersive mode. // For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE. // Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY - window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE - // Set the content to appear under the system bars so that the - // content doesn't resize when the system bars hide and show. - or View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - // Hide the nav bar and status bar - or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_FULLSCREEN) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars()) // new API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION + window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE // New API instead of SYSTEM_UI_FLAG_IMMERSIVE + window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // New API instead of FLAG_TRANSLUCENT_STATUS + window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // new API instead of FLAG_TRANSLUCENT_NAVIGATION + } else { + window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE + // Set the content to appear under the system bars so that the + // content doesn't resize when the system bars hide and show. + or View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + // Hide the nav bar and status bar + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_FULLSCREEN) + } } // Shows the system bars by removing all the flags // except for the ones that make the content appear under the system bars. + @Suppress("DEPRECATION") private fun showSystemUI() { systemUiVisibility = true - window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + } else { + window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) + } } private fun toggleUiSystemVisibility() { diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt index f1ea50c9bf..149bd629e1 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt @@ -17,6 +17,7 @@ package im.vector.app.features.crypto.recover import android.app.Dialog +import android.os.Build import android.os.Bundle import android.os.Parcelable import android.view.KeyEvent @@ -102,7 +103,12 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment= Build.VERSION_CODES.R) { + dialog?.window?.setDecorFitsSystemWindows(false) + } else { + @Suppress("DEPRECATION") + dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) + } return rootView } diff --git a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt index 4b03c93321..9ea42308eb 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt @@ -155,18 +155,15 @@ class LoginWebFragment @Inject constructor( // avoid infinite onPageFinished call if (url.startsWith("http")) { // Generic method to make a bridge between JS and the UIWebView - val mxcJavascriptSendObjectMessage = assetReader.readAssetFile("sendObject.js") - view.loadUrl(mxcJavascriptSendObjectMessage) + assetReader.readAssetFile("sendObject.js")?.let { view.loadUrl(it) } if (state.signMode == SignMode.SignIn) { // The function the fallback page calls when the login is complete - val mxcJavascriptOnLogin = assetReader.readAssetFile("onLogin.js") - view.loadUrl(mxcJavascriptOnLogin) + assetReader.readAssetFile("onLogin.js")?.let { view.loadUrl(it) } } else { // MODE_REGISTER // The function the fallback page calls when the registration is complete - val mxcJavascriptOnRegistered = assetReader.readAssetFile("onRegistered.js") - view.loadUrl(mxcJavascriptOnRegistered) + assetReader.readAssetFile("onRegistered.js")?.let { view.loadUrl(it) } } } } diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt index 24fb6159f8..28b2a8b4d5 100644 --- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt +++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt @@ -21,6 +21,7 @@ import android.os.Build import android.os.Handler import android.os.Looper import android.view.View +import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS import android.widget.ImageView import com.tapadoo.alerter.Alerter import com.tapadoo.alerter.OnHideAlertListener @@ -165,9 +166,7 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy - var flags = view.systemUiVisibility - flags = flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() - view.systemUiVisibility = flags + view.windowInsetsController?.setSystemBarsAppearance(0, APPEARANCE_LIGHT_STATUS_BARS) } } @@ -179,9 +178,7 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy - var flags = view.systemUiVisibility - flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR - view.systemUiVisibility = flags + view.windowInsetsController?.setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS) } } diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt index 7be7624a48..fa037a5435 100755 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt @@ -21,7 +21,6 @@ import android.content.Context import android.content.Intent import android.graphics.Bitmap import android.graphics.Canvas -import android.os.AsyncTask import android.os.Build import android.view.View import androidx.fragment.app.DialogFragment @@ -37,6 +36,11 @@ import im.vector.app.features.settings.devtools.GossipingEventsSerializer import im.vector.app.features.settings.locale.SystemLocaleProvider import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.version.VersionProvider +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import okhttp3.Call import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient @@ -98,6 +102,8 @@ class BugReporter @Inject constructor( var screenshot: Bitmap? = null private set + private val coroutineScope = CoroutineScope(SupervisorJob()) + private val LOGCAT_CMD_ERROR = arrayOf("logcat", // /< Run 'logcat' command "-d", // /< Dump the log rather than continue outputting it "-v", // formatting @@ -160,286 +166,287 @@ class BugReporter @Inject constructor( withScreenshot: Boolean, theBugDescription: String, listener: IMXBugReportListener?) { - object : AsyncTask() { - // enumerate files to delete - val mBugReportFiles: MutableList = ArrayList() + // enumerate files to delete + val mBugReportFiles: MutableList = ArrayList() - override fun doInBackground(vararg voids: Void?): String? { - var bugDescription = theBugDescription - var serverError: String? = null - val crashCallStack = getCrashDescription(context) + coroutineScope.executeAsyncTask( + onPreExecute = { + // NOOP + }, + doInBackground = { publishProgress: suspend (progress: Int) -> Unit -> + var bugDescription = theBugDescription + var serverError: String? = null + val crashCallStack = getCrashDescription(context) - if (null != crashCallStack) { - bugDescription += "\n\n\n\n--------------------------------- crash call stack ---------------------------------\n" - bugDescription += crashCallStack - } - - val gzippedFiles = ArrayList() - - if (withDevicesLogs) { - val files = vectorFileLogger.getLogFiles() - files.mapNotNullTo(gzippedFiles) { f -> - if (!mIsCancelled) { - compressFile(f) - } else { - null - } + if (null != crashCallStack) { + bugDescription += "\n\n\n\n--------------------------------- crash call stack ---------------------------------\n" + bugDescription += crashCallStack } - } - if (!mIsCancelled && (withCrashLogs || withDevicesLogs)) { - val gzippedLogcat = saveLogCat(context, false) + val gzippedFiles = ArrayList() - if (null != gzippedLogcat) { - if (gzippedFiles.size == 0) { - gzippedFiles.add(gzippedLogcat) - } else { - gzippedFiles.add(0, gzippedLogcat) + if (withDevicesLogs) { + val files = vectorFileLogger.getLogFiles() + files.mapNotNullTo(gzippedFiles) { f -> + if (!mIsCancelled) { + compressFile(f) + } else { + null + } } } - val crashDescription = getCrashFile(context) - if (crashDescription.exists()) { - val compressedCrashDescription = compressFile(crashDescription) + if (!mIsCancelled && (withCrashLogs || withDevicesLogs)) { + val gzippedLogcat = saveLogCat(context, false) - if (null != compressedCrashDescription) { + if (null != gzippedLogcat) { if (gzippedFiles.size == 0) { - gzippedFiles.add(compressedCrashDescription) + gzippedFiles.add(gzippedLogcat) } else { - gzippedFiles.add(0, compressedCrashDescription) + gzippedFiles.add(0, gzippedLogcat) + } + } + + val crashDescription = getCrashFile(context) + if (crashDescription.exists()) { + val compressedCrashDescription = compressFile(crashDescription) + + if (null != compressedCrashDescription) { + if (gzippedFiles.size == 0) { + gzippedFiles.add(compressedCrashDescription) + } else { + gzippedFiles.add(0, compressedCrashDescription) + } } } } - } - activeSessionHolder.getSafeActiveSession() - ?.takeIf { !mIsCancelled && withKeyRequestHistory } - ?.cryptoService() - ?.getGossipingEvents() - ?.let { GossipingEventsSerializer().serialize(it) } - ?.toByteArray() - ?.let { rawByteArray -> - File(context.cacheDir.absolutePath, KEY_REQUESTS_FILENAME) - .also { - it.outputStream() - .use { os -> os.write(rawByteArray) } - } - } - ?.let { compressFile(it) } - ?.let { gzippedFiles.add(it) } - - var deviceId = "undefined" - var userId = "undefined" - var olmVersion = "undefined" - - activeSessionHolder.getSafeActiveSession()?.let { session -> - userId = session.myUserId - deviceId = session.sessionParams.deviceId ?: "undefined" - olmVersion = session.cryptoService().getCryptoVersion(context, true) - } - - if (!mIsCancelled) { - val text = "[Element] " + - if (forSuggestion) { - "[Suggestion] " - } else { - "" - } + - bugDescription - - // build the multi part request - val builder = BugReporterMultipartBody.Builder() - .addFormDataPart("text", text) - .addFormDataPart("app", "riot-android") - .addFormDataPart("user_agent", Matrix.getInstance(context).getUserAgent()) - .addFormDataPart("user_id", userId) - .addFormDataPart("device_id", deviceId) - .addFormDataPart("version", versionProvider.getVersion(longFormat = true, useBuildNumber = false)) - .addFormDataPart("branch_name", context.getString(R.string.git_branch_name)) - .addFormDataPart("matrix_sdk_version", Matrix.getSdkVersion()) - .addFormDataPart("olm_version", olmVersion) - .addFormDataPart("device", Build.MODEL.trim()) - .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) - .addFormDataPart("locale", Locale.getDefault().toString()) - .addFormDataPart("app_language", VectorLocale.applicationLocale.toString()) - .addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString()) - .addFormDataPart("theme", ThemeUtils.getApplicationTheme(context)) - - val buildNumber = context.getString(R.string.build_number) - if (buildNumber.isNotEmpty() && buildNumber != "0") { - builder.addFormDataPart("build_number", buildNumber) - } - - // add the gzipped files - for (file in gzippedFiles) { - builder.addFormDataPart("compressed-log", file.name, file.asRequestBody(MimeTypes.OctetStream.toMediaTypeOrNull())) - } - - mBugReportFiles.addAll(gzippedFiles) - - if (withScreenshot) { - val bitmap = screenshot - - if (null != bitmap) { - val logCatScreenshotFile = File(context.cacheDir.absolutePath, LOG_CAT_SCREENSHOT_FILENAME) - - if (logCatScreenshotFile.exists()) { - logCatScreenshotFile.delete() + activeSessionHolder.getSafeActiveSession() + ?.takeIf { !mIsCancelled && withKeyRequestHistory } + ?.cryptoService() + ?.getGossipingEvents() + ?.let { GossipingEventsSerializer().serialize(it) } + ?.toByteArray() + ?.let { rawByteArray -> + File(context.cacheDir.absolutePath, KEY_REQUESTS_FILENAME) + .also { + it.outputStream() + .use { os -> os.write(rawByteArray) } + } } + ?.let { compressFile(it) } + ?.let { gzippedFiles.add(it) } - try { - logCatScreenshotFile.outputStream().use { - bitmap.compress(Bitmap.CompressFormat.PNG, 100, it) + var deviceId = "undefined" + var userId = "undefined" + var olmVersion = "undefined" + + activeSessionHolder.getSafeActiveSession()?.let { session -> + userId = session.myUserId + deviceId = session.sessionParams.deviceId ?: "undefined" + olmVersion = session.cryptoService().getCryptoVersion(context, true) + } + + if (!mIsCancelled) { + val text = "[Element] " + + if (forSuggestion) { + "[Suggestion] " + } else { + "" + } + + bugDescription + + // build the multi part request + val builder = BugReporterMultipartBody.Builder() + .addFormDataPart("text", text) + .addFormDataPart("app", "riot-android") + .addFormDataPart("user_agent", Matrix.getInstance(context).getUserAgent()) + .addFormDataPart("user_id", userId) + .addFormDataPart("device_id", deviceId) + .addFormDataPart("version", versionProvider.getVersion(longFormat = true, useBuildNumber = false)) + .addFormDataPart("branch_name", context.getString(R.string.git_branch_name)) + .addFormDataPart("matrix_sdk_version", Matrix.getSdkVersion()) + .addFormDataPart("olm_version", olmVersion) + .addFormDataPart("device", Build.MODEL.trim()) + .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) + .addFormDataPart("locale", Locale.getDefault().toString()) + .addFormDataPart("app_language", VectorLocale.applicationLocale.toString()) + .addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString()) + .addFormDataPart("theme", ThemeUtils.getApplicationTheme(context)) + + val buildNumber = context.getString(R.string.build_number) + if (buildNumber.isNotEmpty() && buildNumber != "0") { + builder.addFormDataPart("build_number", buildNumber) + } + + // add the gzipped files + for (file in gzippedFiles) { + builder.addFormDataPart("compressed-log", file.name, file.asRequestBody(MimeTypes.OctetStream.toMediaTypeOrNull())) + } + + mBugReportFiles.addAll(gzippedFiles) + + if (withScreenshot) { + val bitmap = screenshot + + if (null != bitmap) { + val logCatScreenshotFile = File(context.cacheDir.absolutePath, LOG_CAT_SCREENSHOT_FILENAME) + + if (logCatScreenshotFile.exists()) { + logCatScreenshotFile.delete() } - builder.addFormDataPart("file", - logCatScreenshotFile.name, logCatScreenshotFile.asRequestBody(MimeTypes.OctetStream.toMediaTypeOrNull())) - } catch (e: Exception) { - Timber.e(e, "## sendBugReport() : fail to write screenshot$e") + try { + logCatScreenshotFile.outputStream().use { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, it) + } + + builder.addFormDataPart("file", + logCatScreenshotFile.name, logCatScreenshotFile.asRequestBody(MimeTypes.OctetStream.toMediaTypeOrNull())) + } catch (e: Exception) { + Timber.e(e, "## sendBugReport() : fail to write screenshot$e") + } } } - } - screenshot = null + screenshot = null - // add some github labels - builder.addFormDataPart("label", BuildConfig.VERSION_NAME) - builder.addFormDataPart("label", BuildConfig.FLAVOR_DESCRIPTION) - builder.addFormDataPart("label", context.getString(R.string.git_branch_name)) + // add some github labels + builder.addFormDataPart("label", BuildConfig.VERSION_NAME) + builder.addFormDataPart("label", BuildConfig.FLAVOR_DESCRIPTION) + builder.addFormDataPart("label", context.getString(R.string.git_branch_name)) - // Special for RiotX - builder.addFormDataPart("label", "[Element]") + // Special for RiotX + builder.addFormDataPart("label", "[Element]") - // Suggestion - if (forSuggestion) { - builder.addFormDataPart("label", "[Suggestion]") - } + // Suggestion + if (forSuggestion) { + builder.addFormDataPart("label", "[Suggestion]") + } - if (getCrashFile(context).exists()) { - builder.addFormDataPart("label", "crash") - deleteCrashFile(context) - } + if (getCrashFile(context).exists()) { + builder.addFormDataPart("label", "crash") + deleteCrashFile(context) + } - val requestBody = builder.build() + val requestBody = builder.build() - // add a progress listener - requestBody.setWriteListener { totalWritten, contentLength -> - val percentage = if (-1L != contentLength) { - if (totalWritten > contentLength) { - 100 + // add a progress listener + requestBody.setWriteListener { totalWritten, contentLength -> + val percentage = if (-1L != contentLength) { + if (totalWritten > contentLength) { + 100 + } else { + (totalWritten * 100 / contentLength).toInt() + } } else { - (totalWritten * 100 / contentLength).toInt() + 0 } - } else { - 0 + + if (mIsCancelled && null != mBugReportCall) { + mBugReportCall!!.cancel() + } + + Timber.v("## onWrite() : $percentage%") + suspend { publishProgress(percentage) } } - if (mIsCancelled && null != mBugReportCall) { - mBugReportCall!!.cancel() + // build the request + val request = Request.Builder() + .url(context.getString(R.string.bug_report_url)) + .post(requestBody) + .build() + + var responseCode = HttpURLConnection.HTTP_INTERNAL_ERROR + var response: Response? = null + var errorMessage: String? = null + + // trigger the request + try { + mBugReportCall = mOkHttpClient.newCall(request) + response = mBugReportCall!!.execute() + responseCode = response.code + } catch (e: Exception) { + Timber.e(e, "response") + errorMessage = e.localizedMessage } - Timber.v("## onWrite() : $percentage%") - publishProgress(percentage) - } + // if the upload failed, try to retrieve the reason + if (responseCode != HttpURLConnection.HTTP_OK) { + if (null != errorMessage) { + serverError = "Failed with error $errorMessage" + } else if (null == response || null == response.body) { + serverError = "Failed with error $responseCode" + } else { + try { + val inputStream = response.body!!.byteStream() - // build the request - val request = Request.Builder() - .url(context.getString(R.string.bug_report_url)) - .post(requestBody) - .build() - - var responseCode = HttpURLConnection.HTTP_INTERNAL_ERROR - var response: Response? = null - var errorMessage: String? = null - - // trigger the request - try { - mBugReportCall = mOkHttpClient.newCall(request) - response = mBugReportCall!!.execute() - responseCode = response.code - } catch (e: Exception) { - Timber.e(e, "response") - errorMessage = e.localizedMessage - } - - // if the upload failed, try to retrieve the reason - if (responseCode != HttpURLConnection.HTTP_OK) { - if (null != errorMessage) { - serverError = "Failed with error $errorMessage" - } else if (null == response || null == response.body) { - serverError = "Failed with error $responseCode" - } else { - try { - val inputStream = response.body!!.byteStream() - - serverError = inputStream.use { - buildString { - var ch = it.read() - while (ch != -1) { - append(ch.toChar()) - ch = it.read() + serverError = inputStream.use { + buildString { + var ch = it.read() + while (ch != -1) { + append(ch.toChar()) + ch = it.read() + } } } - } - // check if the error message - try { - val responseJSON = JSONObject(serverError) - serverError = responseJSON.getString("error") - } catch (e: JSONException) { - Timber.e(e, "doInBackground ; Json conversion failed") - } + // check if the error message + try { + val responseJSON = JSONObject(serverError) + serverError = responseJSON.getString("error") + } catch (e: JSONException) { + Timber.e(e, "doInBackground ; Json conversion failed") + } - // should never happen - if (null == serverError) { - serverError = "Failed with error $responseCode" + // should never happen + if (null == serverError) { + serverError = "Failed with error $responseCode" + } + } catch (e: Exception) { + Timber.e(e, "## sendBugReport() : failed to parse error") } - } catch (e: Exception) { - Timber.e(e, "## sendBugReport() : failed to parse error") } } } - } - return serverError - } - - override fun onProgressUpdate(vararg progress: Int?) { - if (null != listener) { - try { - listener.onProgress(progress[0] ?: 0) - } catch (e: Exception) { - Timber.e(e, "## onProgress() : failed") - } - } - } - - override fun onPostExecute(reason: String?) { - mBugReportCall = null - - // delete when the bug report has been successfully sent - for (file in mBugReportFiles) { - file.delete() - } - - if (null != listener) { - try { - if (mIsCancelled) { - listener.onUploadCancelled() - } else if (null == reason) { - listener.onUploadSucceed() - } else { - listener.onUploadFailed(reason) + serverError + }, + onProgressUpdate = { progress -> + if (null != listener) { + try { + listener.onProgress(progress) + } catch (e: Exception) { + Timber.e(e, "## onProgress() : failed") + } + } + }, + onPostExecute = { reason: String? -> + mBugReportCall = null + + // delete when the bug report has been successfully sent + for (file in mBugReportFiles) { + file.delete() + } + + if (null != listener) { + try { + if (mIsCancelled) { + listener.onUploadCancelled() + } else if (null == reason) { + listener.onUploadSucceed() + } else { + listener.onUploadFailed(reason) + } + } catch (e: Exception) { + Timber.e(e, "## onPostExecute() : failed") } - } catch (e: Exception) { - Timber.e(e, "## onPostExecute() : failed") } } - } - }.execute() + ) } /** @@ -696,4 +703,21 @@ class BugReporter @Inject constructor( return null } + + fun CoroutineScope.executeAsyncTask( + onPreExecute: () -> Unit, + doInBackground: suspend (suspend (P) -> Unit) -> R, + onPostExecute: (R) -> Unit, + onProgressUpdate: (P) -> Unit + ) = launch { + onPreExecute() + + val result = withContext(Dispatchers.IO) { + doInBackground { + withContext(Dispatchers.Main) { onProgressUpdate(it) } + } + } + + onPostExecute(result) + } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt index 84a419c6c6..fe6dc86165 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt @@ -16,6 +16,7 @@ package im.vector.app.features.roomprofile.uploads.media +import android.os.Build import android.os.Bundle import android.util.DisplayMetrics import android.view.LayoutInflater @@ -78,9 +79,14 @@ class RoomUploadsMediaFragment @Inject constructor( controller.listener = this } + @Suppress("DEPRECATION") private fun getNumberOfColumns(): Int { val displayMetrics = DisplayMetrics() - requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + requireContext().display?.getMetrics(displayMetrics) + } else { + requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics) + } return dimensionConverter.pxToDp(displayMetrics.widthPixels) / IMAGE_SIZE_DP } diff --git a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt index 68c13c300e..e4d2571333 100644 --- a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt @@ -64,7 +64,9 @@ class VectorWebViewActivity : VectorBaseActivity() // Allow use of Local Storage domStorageEnabled = true + @Suppress("DEPRECATION") allowFileAccessFromFileURLs = true + @Suppress("DEPRECATION") allowUniversalAccessFromFileURLs = true displayZoomControls = false @@ -73,7 +75,7 @@ class VectorWebViewActivity : VectorBaseActivity() val cookieManager = android.webkit.CookieManager.getInstance() cookieManager.setAcceptThirdPartyCookies(views.simpleWebview, true) - val url = intent.extras?.getString(EXTRA_URL) + val url = intent.extras?.getString(EXTRA_URL) ?: return val title = intent.extras?.getString(EXTRA_TITLE, USE_TITLE_FROM_WEB_PAGE) if (title != USE_TITLE_FROM_WEB_PAGE) { setTitle(title) diff --git a/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt b/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt index 446bc1663f..d30baef55a 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/webview/WidgetWebView.kt @@ -54,7 +54,9 @@ fun WebView.setupForWidget(webViewEventListener: WebViewEventListener) { // Allow use of Local Storage settings.domStorageEnabled = true + @Suppress("DEPRECATION") settings.allowFileAccessFromFileURLs = true + @Suppress("DEPRECATION") settings.allowUniversalAccessFromFileURLs = true settings.displayZoomControls = false @@ -75,7 +77,6 @@ fun WebView.clearAfterWidget() { // Make sure you remove the WebView from its parent view before doing anything. (parent as? ViewGroup)?.removeAllViews() webChromeClient = null - webViewClient = null clearHistory() // NOTE: clears RAM cache, if you pass true, it will also clear the disk cache. clearCache(true) From fa311f4ce28ba9e0a944d3dce02ec5910338eafd Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 5 Jan 2021 14:44:02 +0300 Subject: [PATCH 02/16] Fix bug reporter progress. --- .../app/features/rageshake/BugReporter.kt | 516 +++++++++--------- 1 file changed, 246 insertions(+), 270 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt index fa037a5435..1a03fc6c47 100755 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt @@ -102,7 +102,7 @@ class BugReporter @Inject constructor( var screenshot: Bitmap? = null private set - private val coroutineScope = CoroutineScope(SupervisorJob()) + private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) private val LOGCAT_CMD_ERROR = arrayOf("logcat", // /< Run 'logcat' command "-d", // /< Dump the log rather than continue outputting it @@ -169,284 +169,277 @@ class BugReporter @Inject constructor( // enumerate files to delete val mBugReportFiles: MutableList = ArrayList() - coroutineScope.executeAsyncTask( - onPreExecute = { - // NOOP - }, - doInBackground = { publishProgress: suspend (progress: Int) -> Unit -> - var bugDescription = theBugDescription - var serverError: String? = null - val crashCallStack = getCrashDescription(context) + coroutineScope.launch { + var serverError: String? = null + withContext(Dispatchers.IO) { + var bugDescription = theBugDescription + val crashCallStack = getCrashDescription(context) - if (null != crashCallStack) { - bugDescription += "\n\n\n\n--------------------------------- crash call stack ---------------------------------\n" - bugDescription += crashCallStack + if (null != crashCallStack) { + bugDescription += "\n\n\n\n--------------------------------- crash call stack ---------------------------------\n" + bugDescription += crashCallStack + } + + val gzippedFiles = ArrayList() + + if (withDevicesLogs) { + val files = vectorFileLogger.getLogFiles() + files.mapNotNullTo(gzippedFiles) { f -> + if (!mIsCancelled) { + compressFile(f) + } else { + null + } } + } - val gzippedFiles = ArrayList() + if (!mIsCancelled && (withCrashLogs || withDevicesLogs)) { + val gzippedLogcat = saveLogCat(context, false) - if (withDevicesLogs) { - val files = vectorFileLogger.getLogFiles() - files.mapNotNullTo(gzippedFiles) { f -> - if (!mIsCancelled) { - compressFile(f) - } else { - null - } + if (null != gzippedLogcat) { + if (gzippedFiles.size == 0) { + gzippedFiles.add(gzippedLogcat) + } else { + gzippedFiles.add(0, gzippedLogcat) } } - if (!mIsCancelled && (withCrashLogs || withDevicesLogs)) { - val gzippedLogcat = saveLogCat(context, false) + val crashDescription = getCrashFile(context) + if (crashDescription.exists()) { + val compressedCrashDescription = compressFile(crashDescription) - if (null != gzippedLogcat) { + if (null != compressedCrashDescription) { if (gzippedFiles.size == 0) { - gzippedFiles.add(gzippedLogcat) + gzippedFiles.add(compressedCrashDescription) } else { - gzippedFiles.add(0, gzippedLogcat) - } - } - - val crashDescription = getCrashFile(context) - if (crashDescription.exists()) { - val compressedCrashDescription = compressFile(crashDescription) - - if (null != compressedCrashDescription) { - if (gzippedFiles.size == 0) { - gzippedFiles.add(compressedCrashDescription) - } else { - gzippedFiles.add(0, compressedCrashDescription) - } + gzippedFiles.add(0, compressedCrashDescription) } } } + } - activeSessionHolder.getSafeActiveSession() - ?.takeIf { !mIsCancelled && withKeyRequestHistory } - ?.cryptoService() - ?.getGossipingEvents() - ?.let { GossipingEventsSerializer().serialize(it) } - ?.toByteArray() - ?.let { rawByteArray -> - File(context.cacheDir.absolutePath, KEY_REQUESTS_FILENAME) - .also { - it.outputStream() - .use { os -> os.write(rawByteArray) } - } - } - ?.let { compressFile(it) } - ?.let { gzippedFiles.add(it) } - - var deviceId = "undefined" - var userId = "undefined" - var olmVersion = "undefined" - - activeSessionHolder.getSafeActiveSession()?.let { session -> - userId = session.myUserId - deviceId = session.sessionParams.deviceId ?: "undefined" - olmVersion = session.cryptoService().getCryptoVersion(context, true) - } - - if (!mIsCancelled) { - val text = "[Element] " + - if (forSuggestion) { - "[Suggestion] " - } else { - "" - } + - bugDescription - - // build the multi part request - val builder = BugReporterMultipartBody.Builder() - .addFormDataPart("text", text) - .addFormDataPart("app", "riot-android") - .addFormDataPart("user_agent", Matrix.getInstance(context).getUserAgent()) - .addFormDataPart("user_id", userId) - .addFormDataPart("device_id", deviceId) - .addFormDataPart("version", versionProvider.getVersion(longFormat = true, useBuildNumber = false)) - .addFormDataPart("branch_name", context.getString(R.string.git_branch_name)) - .addFormDataPart("matrix_sdk_version", Matrix.getSdkVersion()) - .addFormDataPart("olm_version", olmVersion) - .addFormDataPart("device", Build.MODEL.trim()) - .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) - .addFormDataPart("locale", Locale.getDefault().toString()) - .addFormDataPart("app_language", VectorLocale.applicationLocale.toString()) - .addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString()) - .addFormDataPart("theme", ThemeUtils.getApplicationTheme(context)) - - val buildNumber = context.getString(R.string.build_number) - if (buildNumber.isNotEmpty() && buildNumber != "0") { - builder.addFormDataPart("build_number", buildNumber) - } - - // add the gzipped files - for (file in gzippedFiles) { - builder.addFormDataPart("compressed-log", file.name, file.asRequestBody(MimeTypes.OctetStream.toMediaTypeOrNull())) - } - - mBugReportFiles.addAll(gzippedFiles) - - if (withScreenshot) { - val bitmap = screenshot - - if (null != bitmap) { - val logCatScreenshotFile = File(context.cacheDir.absolutePath, LOG_CAT_SCREENSHOT_FILENAME) - - if (logCatScreenshotFile.exists()) { - logCatScreenshotFile.delete() - } - - try { - logCatScreenshotFile.outputStream().use { - bitmap.compress(Bitmap.CompressFormat.PNG, 100, it) + activeSessionHolder.getSafeActiveSession() + ?.takeIf { !mIsCancelled && withKeyRequestHistory } + ?.cryptoService() + ?.getGossipingEvents() + ?.let { GossipingEventsSerializer().serialize(it) } + ?.toByteArray() + ?.let { rawByteArray -> + File(context.cacheDir.absolutePath, KEY_REQUESTS_FILENAME) + .also { + it.outputStream() + .use { os -> os.write(rawByteArray) } } - - builder.addFormDataPart("file", - logCatScreenshotFile.name, logCatScreenshotFile.asRequestBody(MimeTypes.OctetStream.toMediaTypeOrNull())) - } catch (e: Exception) { - Timber.e(e, "## sendBugReport() : fail to write screenshot$e") - } - } } + ?.let { compressFile(it) } + ?.let { gzippedFiles.add(it) } - screenshot = null + var deviceId = "undefined" + var userId = "undefined" + var olmVersion = "undefined" - // add some github labels - builder.addFormDataPart("label", BuildConfig.VERSION_NAME) - builder.addFormDataPart("label", BuildConfig.FLAVOR_DESCRIPTION) - builder.addFormDataPart("label", context.getString(R.string.git_branch_name)) + activeSessionHolder.getSafeActiveSession()?.let { session -> + userId = session.myUserId + deviceId = session.sessionParams.deviceId ?: "undefined" + olmVersion = session.cryptoService().getCryptoVersion(context, true) + } - // Special for RiotX - builder.addFormDataPart("label", "[Element]") - - // Suggestion - if (forSuggestion) { - builder.addFormDataPart("label", "[Suggestion]") - } - - if (getCrashFile(context).exists()) { - builder.addFormDataPart("label", "crash") - deleteCrashFile(context) - } - - val requestBody = builder.build() - - // add a progress listener - requestBody.setWriteListener { totalWritten, contentLength -> - val percentage = if (-1L != contentLength) { - if (totalWritten > contentLength) { - 100 - } else { - (totalWritten * 100 / contentLength).toInt() - } + if (!mIsCancelled) { + val text = "[Element] " + + if (forSuggestion) { + "[Suggestion] " } else { - 0 + "" + } + + bugDescription + + // build the multi part request + val builder = BugReporterMultipartBody.Builder() + .addFormDataPart("text", text) + .addFormDataPart("app", "riot-android") + .addFormDataPart("user_agent", Matrix.getInstance(context).getUserAgent()) + .addFormDataPart("user_id", userId) + .addFormDataPart("device_id", deviceId) + .addFormDataPart("version", versionProvider.getVersion(longFormat = true, useBuildNumber = false)) + .addFormDataPart("branch_name", context.getString(R.string.git_branch_name)) + .addFormDataPart("matrix_sdk_version", Matrix.getSdkVersion()) + .addFormDataPart("olm_version", olmVersion) + .addFormDataPart("device", Build.MODEL.trim()) + .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) + .addFormDataPart("locale", Locale.getDefault().toString()) + .addFormDataPart("app_language", VectorLocale.applicationLocale.toString()) + .addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString()) + .addFormDataPart("theme", ThemeUtils.getApplicationTheme(context)) + + val buildNumber = context.getString(R.string.build_number) + if (buildNumber.isNotEmpty() && buildNumber != "0") { + builder.addFormDataPart("build_number", buildNumber) + } + + // add the gzipped files + for (file in gzippedFiles) { + builder.addFormDataPart("compressed-log", file.name, file.asRequestBody(MimeTypes.OctetStream.toMediaTypeOrNull())) + } + + mBugReportFiles.addAll(gzippedFiles) + + if (withScreenshot) { + val bitmap = screenshot + + if (null != bitmap) { + val logCatScreenshotFile = File(context.cacheDir.absolutePath, LOG_CAT_SCREENSHOT_FILENAME) + + if (logCatScreenshotFile.exists()) { + logCatScreenshotFile.delete() } - if (mIsCancelled && null != mBugReportCall) { - mBugReportCall!!.cancel() - } + try { + logCatScreenshotFile.outputStream().use { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, it) + } - Timber.v("## onWrite() : $percentage%") - suspend { publishProgress(percentage) } + builder.addFormDataPart("file", + logCatScreenshotFile.name, logCatScreenshotFile.asRequestBody(MimeTypes.OctetStream.toMediaTypeOrNull())) + } catch (e: Exception) { + Timber.e(e, "## sendBugReport() : fail to write screenshot$e") + } + } + } + + screenshot = null + + // add some github labels + builder.addFormDataPart("label", BuildConfig.VERSION_NAME) + builder.addFormDataPart("label", BuildConfig.FLAVOR_DESCRIPTION) + builder.addFormDataPart("label", context.getString(R.string.git_branch_name)) + + // Special for RiotX + builder.addFormDataPart("label", "[Element]") + + // Suggestion + if (forSuggestion) { + builder.addFormDataPart("label", "[Suggestion]") + } + + if (getCrashFile(context).exists()) { + builder.addFormDataPart("label", "crash") + deleteCrashFile(context) + } + + val requestBody = builder.build() + + // add a progress listener + requestBody.setWriteListener { totalWritten, contentLength -> + val percentage = if (-1L != contentLength) { + if (totalWritten > contentLength) { + 100 + } else { + (totalWritten * 100 / contentLength).toInt() + } + } else { + 0 } - // build the request - val request = Request.Builder() - .url(context.getString(R.string.bug_report_url)) - .post(requestBody) - .build() + if (mIsCancelled && null != mBugReportCall) { + mBugReportCall!!.cancel() + } - var responseCode = HttpURLConnection.HTTP_INTERNAL_ERROR - var response: Response? = null - var errorMessage: String? = null - - // trigger the request + Timber.v("## onWrite() : $percentage%") try { - mBugReportCall = mOkHttpClient.newCall(request) - response = mBugReportCall!!.execute() - responseCode = response.code - } catch (e: Exception) { - Timber.e(e, "response") - errorMessage = e.localizedMessage - } - - // if the upload failed, try to retrieve the reason - if (responseCode != HttpURLConnection.HTTP_OK) { - if (null != errorMessage) { - serverError = "Failed with error $errorMessage" - } else if (null == response || null == response.body) { - serverError = "Failed with error $responseCode" - } else { - try { - val inputStream = response.body!!.byteStream() - - serverError = inputStream.use { - buildString { - var ch = it.read() - while (ch != -1) { - append(ch.toChar()) - ch = it.read() - } - } - } - - // check if the error message - try { - val responseJSON = JSONObject(serverError) - serverError = responseJSON.getString("error") - } catch (e: JSONException) { - Timber.e(e, "doInBackground ; Json conversion failed") - } - - // should never happen - if (null == serverError) { - serverError = "Failed with error $responseCode" - } - } catch (e: Exception) { - Timber.e(e, "## sendBugReport() : failed to parse error") - } - } - } - } - - serverError - }, - onProgressUpdate = { progress -> - if (null != listener) { - try { - listener.onProgress(progress) + listener?.onProgress(percentage) } catch (e: Exception) { Timber.e(e, "## onProgress() : failed") } } - }, - onPostExecute = { reason: String? -> - mBugReportCall = null - // delete when the bug report has been successfully sent - for (file in mBugReportFiles) { - file.delete() + // build the request + val request = Request.Builder() + .url(context.getString(R.string.bug_report_url)) + .post(requestBody) + .build() + + var responseCode = HttpURLConnection.HTTP_INTERNAL_ERROR + var response: Response? = null + var errorMessage: String? = null + + // trigger the request + try { + mBugReportCall = mOkHttpClient.newCall(request) + response = mBugReportCall!!.execute() + responseCode = response.code + } catch (e: Exception) { + Timber.e(e, "response") + errorMessage = e.localizedMessage } - if (null != listener) { - try { - if (mIsCancelled) { - listener.onUploadCancelled() - } else if (null == reason) { - listener.onUploadSucceed() - } else { - listener.onUploadFailed(reason) + // if the upload failed, try to retrieve the reason + if (responseCode != HttpURLConnection.HTTP_OK) { + if (null != errorMessage) { + serverError = "Failed with error $errorMessage" + } else if (null == response || null == response.body) { + serverError = "Failed with error $responseCode" + } else { + try { + val inputStream = response.body!!.byteStream() + + serverError = inputStream.use { + buildString { + var ch = it.read() + while (ch != -1) { + append(ch.toChar()) + ch = it.read() + } + } + } + + // check if the error message + serverError?.let { + try { + val responseJSON = JSONObject(it) + serverError = responseJSON.getString("error") + } catch (e: JSONException) { + Timber.e(e, "doInBackground ; Json conversion failed") + } + } + + // should never happen + if (null == serverError) { + serverError = "Failed with error $responseCode" + } + } catch (e: Exception) { + Timber.e(e, "## sendBugReport() : failed to parse error") } - } catch (e: Exception) { - Timber.e(e, "## onPostExecute() : failed") } } } - ) + } + + withContext(Dispatchers.Main) { + mBugReportCall = null + + // delete when the bug report has been successfully sent + for (file in mBugReportFiles) { + file.delete() + } + + if (null != listener) { + try { + if (mIsCancelled) { + listener.onUploadCancelled() + } else if (null == serverError) { + listener.onUploadSucceed() + } else { + listener.onUploadFailed(serverError) + } + } catch (e: Exception) { + Timber.e(e, "## onPostExecute() : failed") + } + } + } + } } /** @@ -464,9 +457,9 @@ class BugReporter @Inject constructor( activity.startActivity(intent) } - // ============================================================================================================== - // crash report management - // ============================================================================================================== +// ============================================================================================================== +// crash report management +// ============================================================================================================== /** * Provides the crash file @@ -536,9 +529,9 @@ class BugReporter @Inject constructor( return null } - // ============================================================================================================== - // Screenshot management - // ============================================================================================================== +// ============================================================================================================== +// Screenshot management +// ============================================================================================================== /** * Take a screenshot of the display. @@ -605,9 +598,9 @@ class BugReporter @Inject constructor( } } - // ============================================================================================================== - // Logcat management - // ============================================================================================================== +// ============================================================================================================== +// Logcat management +// ============================================================================================================== /** * Save the logcat @@ -667,9 +660,9 @@ class BugReporter @Inject constructor( } } - // ============================================================================================================== - // File compression management - // ============================================================================================================== +// ============================================================================================================== +// File compression management +// ============================================================================================================== /** * GZip a file @@ -703,21 +696,4 @@ class BugReporter @Inject constructor( return null } - - fun CoroutineScope.executeAsyncTask( - onPreExecute: () -> Unit, - doInBackground: suspend (suspend (P) -> Unit) -> R, - onPostExecute: (R) -> Unit, - onProgressUpdate: (P) -> Unit - ) = launch { - onPreExecute() - - val result = withContext(Dispatchers.IO) { - doInBackground { - withContext(Dispatchers.Main) { onProgressUpdate(it) } - } - } - - onPostExecute(result) - } } From 869eb262f33ee87cdfeffcf128c166d4586e87df Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 6 Jan 2021 12:49:11 +0300 Subject: [PATCH 03/16] Lint fixes. --- .../AttachmentViewerActivity.kt | 37 ++++++++++++------- .../app/core/platform/VectorBaseActivity.kt | 15 +++++--- .../preview/AttachmentsPreviewFragment.kt | 3 +- .../app/features/call/VectorCallActivity.kt | 18 ++++++--- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt index 1d09e1ef0e..88109e7ea0 100644 --- a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt +++ b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt @@ -18,6 +18,7 @@ package im.vector.lib.attachmentviewer import android.graphics.Color +import android.os.Build import android.os.Bundle import android.view.GestureDetector import android.view.MotionEvent @@ -132,11 +133,15 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi private fun setDecorViewFullScreen() { // This is important for the dispatchTouchEvent, if not we must correct // the touch coordinates - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { - window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE // New API instead of SYSTEM_UI_FLAG_IMMERSIVE - window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // New API instead of FLAG_TRANSLUCENT_STATUS - window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // new API instead of FLAG_TRANSLUCENT_NAVIGATION + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.setDecorFitsSystemWindows(false) + // New API instead of SYSTEM_UI_FLAG_IMMERSIVE + window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE + // New API instead of FLAG_TRANSLUCENT_STATUS + window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) + // new API instead of FLAG_TRANSLUCENT_NAVIGATION + window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) } else { window.decorView.systemUiVisibility = ( View.SYSTEM_UI_FLAG_LAYOUT_STABLE @@ -332,12 +337,17 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi // Enables regular immersive mode. // For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE. // Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { - window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars()) // new API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION - window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE // New API instead of SYSTEM_UI_FLAG_IMMERSIVE - window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // New API instead of FLAG_TRANSLUCENT_STATUS - window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // new API instead of FLAG_TRANSLUCENT_NAVIGATION + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.setDecorFitsSystemWindows(false) + // new API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION + window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars()) + // New API instead of SYSTEM_UI_FLAG_IMMERSIVE + window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE + // New API instead of FLAG_TRANSLUCENT_STATUS + window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) + // New API instead of FLAG_TRANSLUCENT_NAVIGATION + window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) } else { window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE // Set the content to appear under the system bars so that the @@ -356,8 +366,9 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi @Suppress("DEPRECATION") private fun showSystemUI() { systemUiVisibility = true - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { - window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.setDecorFitsSystemWindows(false) } else { window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt index 3ea995c418..d8b61f3cba 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt @@ -19,6 +19,7 @@ package im.vector.app.core.platform import android.app.Activity import android.content.Context import android.content.res.Configuration +import android.os.Build import android.os.Bundle import android.view.Menu import android.view.MenuItem @@ -414,11 +415,15 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScr */ @Suppress("DEPRECATION") private fun setFullScreen() { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { - window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE // New API instead of SYSTEM_UI_FLAG_IMMERSIVE - window.statusBarColor = ContextCompat.getColor(this, im.vector.lib.attachmentviewer.R.color.half_transparent_status_bar) // New API instead of FLAG_TRANSLUCENT_STATUS - window.navigationBarColor = ContextCompat.getColor(this, im.vector.lib.attachmentviewer.R.color.half_transparent_status_bar) // new API instead of FLAG_TRANSLUCENT_NAVIGATION + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.setDecorFitsSystemWindows(false) + // New API instead of SYSTEM_UI_FLAG_IMMERSIVE + window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE + // New API instead of FLAG_TRANSLUCENT_STATUS + window.statusBarColor = ContextCompat.getColor(this, im.vector.lib.attachmentviewer.R.color.half_transparent_status_bar) + // New API instead of FLAG_TRANSLUCENT_NAVIGATION + window.navigationBarColor = ContextCompat.getColor(this, im.vector.lib.attachmentviewer.R.color.half_transparent_status_bar) } else { window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt index 147492fc46..cdb015e4da 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -19,6 +19,7 @@ package im.vector.app.features.attachments.preview import android.app.Activity.RESULT_CANCELED import android.app.Activity.RESULT_OK +import android.os.Build import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater @@ -155,7 +156,7 @@ class AttachmentsPreviewFragment @Inject constructor( @Suppress("DEPRECATION") private fun applyInsets() { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { activity?.window?.setDecorFitsSystemWindows(false) } else { view?.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index ef96bc810a..6c49d4d3e2 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -112,11 +112,16 @@ class VectorCallActivity : VectorBaseActivity(), CallContro // For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE. // Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars()) // new API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION - window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE // New API instead of SYSTEM_UI_FLAG_IMMERSIVE - window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // New API instead of FLAG_TRANSLUCENT_STATUS - window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) // new API instead of FLAG_TRANSLUCENT_NAVIGATION + // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.setDecorFitsSystemWindows(false) + // New API instead of SYSTEM_UI_FLAG_HIDE_NAVIGATION + window.decorView.windowInsetsController?.hide(WindowInsets.Type.navigationBars()) + // New API instead of SYSTEM_UI_FLAG_IMMERSIVE + window.decorView.windowInsetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE + // New API instead of FLAG_TRANSLUCENT_STATUS + window.statusBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) + // New API instead of FLAG_TRANSLUCENT_NAVIGATION + window.navigationBarColor = ContextCompat.getColor(this, R.color.half_transparent_status_bar) } else { window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE // Set the content to appear under the system bars so that the @@ -136,7 +141,8 @@ class VectorCallActivity : VectorBaseActivity(), CallContro private fun showSystemUI() { systemUiVisibility = true if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - window.setDecorFitsSystemWindows(false) // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + // New API instead of SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.setDecorFitsSystemWindows(false) } else { window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION From e948e9d85ad3f4ba84b85bfe82337762c02bec88 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 6 Jan 2021 16:28:45 +0300 Subject: [PATCH 04/16] Lint fixes. --- .../main/java/im/vector/app/features/call/CallAudioManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/call/CallAudioManager.kt b/vector/src/main/java/im/vector/app/features/call/CallAudioManager.kt index 3a24cf6d48..82bbaf1d54 100644 --- a/vector/src/main/java/im/vector/app/features/call/CallAudioManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/CallAudioManager.kt @@ -48,7 +48,7 @@ class CallAudioManager( private var savedIsSpeakerPhoneOn = false private var savedIsMicrophoneMute = false - private var savedAudioMode = AudioManager.MODE_INVALID + private var savedAudioMode = AudioManager.MODE_NORMAL private var connectedBlueToothHeadset: BluetoothProfile? = null private var wantsBluetoothConnection = false From 36b1a1471aa788dd973cff699e0322a92df353bf Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 13 Jan 2021 14:23:02 +0100 Subject: [PATCH 05/16] Fix Dendrite sync response support --- .../room/timeline/EventContextResponse.kt | 12 ++++++++---- .../session/room/timeline/PaginationResponse.kt | 4 ++-- .../session/room/timeline/TokenChunkEvent.kt | 4 ++-- .../room/timeline/TokenChunkEventPersistor.kt | 16 ++++++++-------- .../session/room/uploads/GetUploadsTask.kt | 2 +- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt index bce03354d7..d76ba35280 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt @@ -24,13 +24,17 @@ import org.matrix.android.sdk.api.session.events.model.Event data class EventContextResponse( @Json(name = "event") val event: Event, @Json(name = "start") override val start: String? = null, - @Json(name = "events_before") val eventsBefore: List = emptyList(), - @Json(name = "events_after") val eventsAfter: List = emptyList(), + @Json(name = "events_before") val eventsBefore: List? = emptyList(), + @Json(name = "events_after") val eventsAfter: List? = emptyList(), @Json(name = "end") override val end: String? = null, - @Json(name = "state") override val stateEvents: List = emptyList() + @Json(name = "state") override val stateEvents: List? = emptyList() ) : TokenChunkEvent { override val events: List by lazy { - eventsAfter.reversed() + listOf(event) + eventsBefore + mutableListOf().apply { + eventsAfter?.let { addAll(it.reversed()) } + add(event) + eventsBefore?.let { addAll(it) } + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt index ed384d3b3c..ff0c7fbbde 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt @@ -24,6 +24,6 @@ import org.matrix.android.sdk.api.session.events.model.Event internal data class PaginationResponse( @Json(name = "start") override val start: String? = null, @Json(name = "end") override val end: String? = null, - @Json(name = "chunk") override val events: List = emptyList(), - @Json(name = "state") override val stateEvents: List = emptyList() + @Json(name = "chunk") override val events: List? = emptyList(), + @Json(name = "state") override val stateEvents: List? = emptyList() ) : TokenChunkEvent diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt index 08b20f1701..50cc50beb2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt @@ -21,8 +21,8 @@ import org.matrix.android.sdk.api.session.events.model.Event internal interface TokenChunkEvent { val start: String? val end: String? - val events: List - val stateEvents: List + val events: List? + val stateEvents: List? fun hasMore() = start != end } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt index 2a532c6bf5..fb636a9314 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -124,7 +124,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri direction: PaginationDirection): Result { monarchy .awaitTransaction { realm -> - Timber.v("Start persisting ${receivedChunk.events.size} events in $roomId towards $direction") + Timber.v("Start persisting ${receivedChunk.events?.size} events in $roomId towards $direction") val nextToken: String? val prevToken: String? @@ -149,13 +149,13 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri } ?: ChunkEntity.create(realm, prevToken, nextToken) - if (receivedChunk.events.isEmpty() && !receivedChunk.hasMore()) { + if (receivedChunk.events.isNullOrEmpty() && !receivedChunk.hasMore()) { handleReachEnd(realm, roomId, direction, currentChunk) } else { handlePagination(realm, roomId, direction, receivedChunk, currentChunk) } } - return if (receivedChunk.events.isEmpty()) { + return if (receivedChunk.events.isNullOrEmpty()) { if (receivedChunk.start != receivedChunk.end) { Result.SHOULD_FETCH_MORE } else { @@ -189,14 +189,14 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri receivedChunk: TokenChunkEvent, currentChunk: ChunkEntity ) { - Timber.v("Add ${receivedChunk.events.size} events in chunk(${currentChunk.nextToken} | ${currentChunk.prevToken}") + Timber.v("Add ${receivedChunk.events?.size} events in chunk(${currentChunk.nextToken} | ${currentChunk.prevToken}") val roomMemberContentsByUser = HashMap() val eventList = receivedChunk.events val stateEvents = receivedChunk.stateEvents val now = System.currentTimeMillis() - for (stateEvent in stateEvents) { + stateEvents?.forEach { stateEvent -> val ageLocalTs = stateEvent.unsignedData?.age?.let { now - it } val stateEventEntity = stateEvent.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION) currentChunk.addStateEvent(roomId, stateEventEntity, direction) @@ -204,10 +204,10 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri roomMemberContentsByUser[stateEvent.stateKey] = stateEvent.content.toModel() } } - val eventIds = ArrayList(eventList.size) - for (event in eventList) { + val eventIds = ArrayList(eventList?.size ?: 0) + eventList?.forEach { event -> if (event.eventId == null || event.senderId == null) { - continue + return@forEach } val ageLocalTs = event.unsignedData?.age?.let { now - it } eventIds.add(event.eventId) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt index 0c0e6a8ed0..9d66474a73 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt @@ -95,7 +95,7 @@ internal class DefaultGetUploadsTask @Inject constructor( nextToken = chunk.end ?: "", hasMore = chunk.hasMore() ) - events = chunk.events + events = chunk.events ?: emptyList() } var uploadEvents = listOf() From daf019b28807452df54b3d8efe59f95cff2d96c9 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Tue, 5 Jan 2021 00:58:55 -0500 Subject: [PATCH 06/16] Identity: Recompute hashes after M_INVALID_PEPPER When a new pepper is retrieved after an M_INVALID_PEPPER response, recompute hashes with that pepper, and send those new hashes in the next lookup attempt instead of reusing the original hashes. Signed-off-by: Andrew Ferrazzutti --- CHANGES.md | 1 + .../identity/IdentityBulkLookupTask.kt | 48 ++++++++++--------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b2483df78d..a8aabb213d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,7 @@ Improvements 🙌: Bugfix 🐛: - Fix clear cache issue: sometimes, after a clear cache, there is still a token, so the init sync service is not started. - Sidebar too large in horizontal orientation or tablets (#475) + - When receiving a new pepper from identity server, use it on the next hash lookup (#2708) Translations 🗣: - diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt index a03bef9501..773d1066b5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt @@ -46,6 +46,17 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( @UserId private val userId: String ) : IdentityBulkLookupTask { + private fun getHashedAddresses(threePids: List, pepper: String): List { + return withOlmUtility { olmUtility -> + threePids.map { threePid -> + base64ToBase64Url( + olmUtility.sha256(threePid.value.toLowerCase(Locale.ROOT) + + " " + threePid.toMedium() + " " + pepper) + ) + } + } + } + override suspend fun execute(params: IdentityBulkLookupTask.Params): List { val identityAPI = getIdentityApiAndEnsureTerms(identityApiProvider, userId) val identityData = identityStore.getIdentityData() ?: throw IdentityServiceError.NoIdentityServerConfigured @@ -63,33 +74,26 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( throw IdentityServiceError.BulkLookupSha256NotSupported } - val hashedAddresses = withOlmUtility { olmUtility -> - params.threePids.map { threePid -> - base64ToBase64Url( - olmUtility.sha256(threePid.value.toLowerCase(Locale.ROOT) - + " " + threePid.toMedium() + " " + hashDetailResponse.pepper) - ) - } - } - - val identityLookUpV2Response = lookUpInternal(identityAPI, hashedAddresses, hashDetailResponse, true) + val lookupResult = lookUpInternal(identityAPI, params.threePids, hashDetailResponse, true) // Convert back to List - return handleSuccess(params.threePids, hashedAddresses, identityLookUpV2Response) + return handleSuccess(params.threePids, lookupResult.first, lookupResult.second) } private suspend fun lookUpInternal(identityAPI: IdentityAPI, - hashedAddresses: List, + threePids: List, hashDetailResponse: IdentityHashDetailResponse, - canRetry: Boolean): IdentityLookUpResponse { + canRetry: Boolean): Pair, IdentityLookUpResponse> { + val hashedAddresses = getHashedAddresses(threePids, hashDetailResponse.pepper) return try { - executeRequest(null) { - apiCall = identityAPI.lookup(IdentityLookUpParams( - hashedAddresses, - IdentityHashDetailResponse.ALGORITHM_SHA256, - hashDetailResponse.pepper - )) - } + Pair(hashedAddresses, + executeRequest(null) { + apiCall = identityAPI.lookup(IdentityLookUpParams( + hashedAddresses, + IdentityHashDetailResponse.ALGORITHM_SHA256, + hashDetailResponse.pepper + )) + }) } catch (failure: Throwable) { // Catch invalid hash pepper and retry if (canRetry && failure is Failure.ServerError && failure.error.code == MatrixError.M_INVALID_PEPPER) { @@ -98,7 +102,7 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( // Store it and use it right now hashDetailResponse.copy(pepper = failure.error.newLookupPepper) .also { identityStore.setHashDetails(it) } - .let { lookUpInternal(identityAPI, hashedAddresses, it, false /* Avoid infinite loop */) } + .let { lookUpInternal(identityAPI, threePids, it, false /* Avoid infinite loop */) } } else { // Retrieve the new hash details val newHashDetailResponse = fetchAndStoreHashDetails(identityAPI) @@ -109,7 +113,7 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( throw IdentityServiceError.BulkLookupSha256NotSupported } - lookUpInternal(identityAPI, hashedAddresses, newHashDetailResponse, false /* Avoid infinite loop */) + lookUpInternal(identityAPI, threePids, newHashDetailResponse, false /* Avoid infinite loop */) } } else { // Other error From 883a7cecf0ab17a03d7b876995cc20b7c3013a07 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 22 Jan 2021 15:34:40 +0300 Subject: [PATCH 07/16] Fix viewbinding npe crashes. --- CHANGES.md | 1 + .../java/im/vector/app/ui/UiAllScreensSanityTest.kt | 2 ++ .../app/features/home/room/detail/RoomDetailFragment.kt | 5 ++++- .../app/features/home/room/detail/search/SearchFragment.kt | 4 ++-- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b2483df78d..d6e3a9b21a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,7 @@ Improvements 🙌: Bugfix 🐛: - Fix clear cache issue: sometimes, after a clear cache, there is still a token, so the init sync service is not started. - Sidebar too large in horizontal orientation or tablets (#475) + - Crashes reported by PlayStore (new in 1.0.14) (#2707) Translations 🗣: - diff --git a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt index 58b596b05f..2d0077fc55 100644 --- a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt +++ b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt @@ -196,6 +196,8 @@ class UiAllScreensSanityTest { pressBack() clickMenu(R.id.video_call) pressBack() + clickMenu(R.id.search) + pressBack() pressBack() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index e134230c61..2d2059377c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -297,6 +297,8 @@ class RoomDetailFragment @Inject constructor( private var lockSendButton = false private val activeCallViewHolder = ActiveCallViewHolder() + private lateinit var emojiPopup: EmojiPopup + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) @@ -512,7 +514,7 @@ class RoomDetailFragment @Inject constructor( } private fun setupEmojiPopup() { - val emojiPopup = EmojiPopup + emojiPopup = EmojiPopup .Builder .fromRootView(views.rootConstraintLayout) .setKeyboardAnimationStyle(R.style.emoji_fade_animation_style) @@ -591,6 +593,7 @@ class RoomDetailFragment @Inject constructor( autoCompleter.clear() debouncer.cancelAll() views.timelineRecyclerView.cleanup() + emojiPopup.dismiss() super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index 8a8701e45f..9a57d9480c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -76,10 +76,10 @@ class SearchFragment @Inject constructor( controller.listener = this } - override fun onDestroy() { - super.onDestroy() + override fun onDestroyView() { views.searchResultRecycler.cleanup() controller.listener = null + super.onDestroyView() } override fun invalidate() = withState(searchViewModel) { state -> From 25dbb3e9eac4d738afa5459f9f7100c62e793ffd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 Jan 2021 17:26:10 +0100 Subject: [PATCH 08/16] Fix bad copy/paste --- .../sdk/internal/session/identity/IdentityBulkLookupTask.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt index 773d1066b5..2d2e5f823d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt @@ -107,9 +107,8 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( // Retrieve the new hash details val newHashDetailResponse = fetchAndStoreHashDetails(identityAPI) - if (hashDetailResponse.algorithms.contains(IdentityHashDetailResponse.ALGORITHM_SHA256).not()) { + if (newHashDetailResponse.algorithms.contains(IdentityHashDetailResponse.ALGORITHM_SHA256).not()) { // TODO We should ask the user if he is ok to send their 3Pid in clear, but for the moment we do not do it - // Also, what we have in cache is maybe outdated, the identity server maybe now support sha256 throw IdentityServiceError.BulkLookupSha256NotSupported } From 07ffd3ded37b4756f2d580ab6377c1daf2914287 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 Jan 2021 17:32:00 +0100 Subject: [PATCH 09/16] Improve code --- .../session/identity/IdentityBulkLookupTask.kt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt index 2d2e5f823d..ed206d237b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt @@ -98,22 +98,19 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( // Catch invalid hash pepper and retry if (canRetry && failure is Failure.ServerError && failure.error.code == MatrixError.M_INVALID_PEPPER) { // This is not documented, but the error can contain the new pepper! - if (!failure.error.newLookupPepper.isNullOrEmpty()) { + val newHashDetailResponse = if (!failure.error.newLookupPepper.isNullOrEmpty()) { // Store it and use it right now hashDetailResponse.copy(pepper = failure.error.newLookupPepper) .also { identityStore.setHashDetails(it) } - .let { lookUpInternal(identityAPI, threePids, it, false /* Avoid infinite loop */) } } else { // Retrieve the new hash details - val newHashDetailResponse = fetchAndStoreHashDetails(identityAPI) - - if (newHashDetailResponse.algorithms.contains(IdentityHashDetailResponse.ALGORITHM_SHA256).not()) { - // TODO We should ask the user if he is ok to send their 3Pid in clear, but for the moment we do not do it - throw IdentityServiceError.BulkLookupSha256NotSupported - } - - lookUpInternal(identityAPI, threePids, newHashDetailResponse, false /* Avoid infinite loop */) + fetchAndStoreHashDetails(identityAPI) } + if (newHashDetailResponse.algorithms.contains(IdentityHashDetailResponse.ALGORITHM_SHA256).not()) { + // TODO We should ask the user if he is ok to send their 3Pid in clear, but for the moment we do not do it + throw IdentityServiceError.BulkLookupSha256NotSupported + } + lookUpInternal(identityAPI, threePids, newHashDetailResponse, false /* Avoid infinite loop */) } else { // Other error throw failure From 887da0a3d61aa8c6b7175981b376501b01f53c3f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 Jan 2021 17:37:25 +0100 Subject: [PATCH 10/16] Improve code #2 --- .../session/identity/IdentityBulkLookupTask.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt index ed206d237b..c064b81e4a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt @@ -63,7 +63,8 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( val pepper = identityData.hashLookupPepper val hashDetailResponse = if (pepper == null) { // We need to fetch the hash details first - fetchAndStoreHashDetails(identityAPI) + fetchHashDetails(identityAPI) + .also { identityStore.setHashDetails(it) } } else { IdentityHashDetailResponse(pepper, identityData.hashLookupAlgorithm) } @@ -101,11 +102,11 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( val newHashDetailResponse = if (!failure.error.newLookupPepper.isNullOrEmpty()) { // Store it and use it right now hashDetailResponse.copy(pepper = failure.error.newLookupPepper) - .also { identityStore.setHashDetails(it) } } else { // Retrieve the new hash details - fetchAndStoreHashDetails(identityAPI) + fetchHashDetails(identityAPI) } + .also { identityStore.setHashDetails(it) } if (newHashDetailResponse.algorithms.contains(IdentityHashDetailResponse.ALGORITHM_SHA256).not()) { // TODO We should ask the user if he is ok to send their 3Pid in clear, but for the moment we do not do it throw IdentityServiceError.BulkLookupSha256NotSupported @@ -118,11 +119,10 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( } } - private suspend fun fetchAndStoreHashDetails(identityAPI: IdentityAPI): IdentityHashDetailResponse { - return executeRequest(null) { + private suspend fun fetchHashDetails(identityAPI: IdentityAPI): IdentityHashDetailResponse { + return executeRequest(null) { apiCall = identityAPI.hashDetails() } - .also { identityStore.setHashDetails(it) } } private fun handleSuccess(threePids: List, hashedAddresses: List, identityLookUpResponse: IdentityLookUpResponse): List { From 267ae457eeaee7a0f36ec3d33ebece01105e6ce2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 Jan 2021 17:38:17 +0100 Subject: [PATCH 11/16] Use const --- .../sdk/internal/session/identity/IdentityBulkLookupTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt index c064b81e4a..e429289a24 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt @@ -69,7 +69,7 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( IdentityHashDetailResponse(pepper, identityData.hashLookupAlgorithm) } - if (hashDetailResponse.algorithms.contains("sha256").not()) { + if (hashDetailResponse.algorithms.contains(IdentityHashDetailResponse.ALGORITHM_SHA256).not()) { // TODO We should ask the user if he is ok to send their 3Pid in clear, but for the moment we do not do it // Also, what we have in cache could be outdated, the identity server maybe now supports sha256 throw IdentityServiceError.BulkLookupSha256NotSupported From a44d00a31c84ecfb9fb0b7a6b84ef7b08eb1ea48 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 Jan 2021 17:44:24 +0100 Subject: [PATCH 12/16] Create data class. --- .../identity/IdentityBulkLookupTask.kt | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt index e429289a24..ccee5f7d47 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt @@ -78,16 +78,21 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( val lookupResult = lookUpInternal(identityAPI, params.threePids, hashDetailResponse, true) // Convert back to List - return handleSuccess(params.threePids, lookupResult.first, lookupResult.second) + return handleSuccess(params.threePids, lookupResult) } + data class LookUpData( + val hashedAddresses: List, + val identityLookUpResponse: IdentityLookUpResponse + ) + private suspend fun lookUpInternal(identityAPI: IdentityAPI, threePids: List, hashDetailResponse: IdentityHashDetailResponse, - canRetry: Boolean): Pair, IdentityLookUpResponse> { + canRetry: Boolean): LookUpData { val hashedAddresses = getHashedAddresses(threePids, hashDetailResponse.pepper) return try { - Pair(hashedAddresses, + LookUpData(hashedAddresses, executeRequest(null) { apiCall = identityAPI.lookup(IdentityLookUpParams( hashedAddresses, @@ -125,9 +130,12 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( } } - private fun handleSuccess(threePids: List, hashedAddresses: List, identityLookUpResponse: IdentityLookUpResponse): List { - return identityLookUpResponse.mappings.keys.map { hashedAddress -> - FoundThreePid(threePids[hashedAddresses.indexOf(hashedAddress)], identityLookUpResponse.mappings[hashedAddress] ?: error("")) + private fun handleSuccess(threePids: List, lookupData: LookUpData): List { + return lookupData.identityLookUpResponse.mappings.keys.map { hashedAddress -> + FoundThreePid( + threePids[lookupData.hashedAddresses.indexOf(hashedAddress)], + lookupData.identityLookUpResponse.mappings[hashedAddress] ?: error("") + ) } } } From 401b5e2b7a38cd67924d24a633195c7d6a0ab783 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 Jan 2021 17:48:13 +0100 Subject: [PATCH 13/16] Move private fun --- .../identity/IdentityBulkLookupTask.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt index ccee5f7d47..61d44a7a65 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt @@ -46,17 +46,6 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( @UserId private val userId: String ) : IdentityBulkLookupTask { - private fun getHashedAddresses(threePids: List, pepper: String): List { - return withOlmUtility { olmUtility -> - threePids.map { threePid -> - base64ToBase64Url( - olmUtility.sha256(threePid.value.toLowerCase(Locale.ROOT) - + " " + threePid.toMedium() + " " + pepper) - ) - } - } - } - override suspend fun execute(params: IdentityBulkLookupTask.Params): List { val identityAPI = getIdentityApiAndEnsureTerms(identityApiProvider, userId) val identityData = identityStore.getIdentityData() ?: throw IdentityServiceError.NoIdentityServerConfigured @@ -124,6 +113,17 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( } } + private fun getHashedAddresses(threePids: List, pepper: String): List { + return withOlmUtility { olmUtility -> + threePids.map { threePid -> + base64ToBase64Url( + olmUtility.sha256(threePid.value.toLowerCase(Locale.ROOT) + + " " + threePid.toMedium() + " " + pepper) + ) + } + } + } + private suspend fun fetchHashDetails(identityAPI: IdentityAPI): IdentityHashDetailResponse { return executeRequest(null) { apiCall = identityAPI.hashDetails() From b65fc4f46b19fe3399e1e555044efbf33eecae00 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 Jan 2021 17:59:56 +0100 Subject: [PATCH 14/16] rename val --- .../sdk/internal/session/identity/IdentityBulkLookupTask.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt index 61d44a7a65..67f3b2aa56 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt @@ -64,10 +64,10 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( throw IdentityServiceError.BulkLookupSha256NotSupported } - val lookupResult = lookUpInternal(identityAPI, params.threePids, hashDetailResponse, true) + val lookUpData = lookUpInternal(identityAPI, params.threePids, hashDetailResponse, true) // Convert back to List - return handleSuccess(params.threePids, lookupResult) + return handleSuccess(params.threePids, lookUpData) } data class LookUpData( From 89cf1f32376e83f3f250cedd577570cd4cd6b521 Mon Sep 17 00:00:00 2001 From: gradle-update-robot Date: Sat, 23 Jan 2021 01:32:49 +0000 Subject: [PATCH 15/16] Update Gradle Wrapper from 6.8 to 6.8.1. Signed-off-by: gradle-update-robot --- gradle/wrapper/gradle-wrapper.properties | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 247e2b90ad..517ae0d4ce 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,6 @@ -#Fri Jan 15 11:30:47 CET 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=a7ca23b3ccf265680f2bfd35f1f00b1424f4466292c7337c85d46c9641b3f053 +distributionSha256Sum=3db89524a3981819ff28c3f979236c1274a726e146ced0c8a2020417f9bc0782 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip From 5a0d62db6f28c58110e0774a53fcadf2bd9e0810 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 25 Jan 2021 12:42:39 +0100 Subject: [PATCH 16/16] Cleanup (PR review) Also add some doc and add missing `internal` keyword --- .../room/timeline/EventContextResponse.kt | 32 +++++++++++++------ .../room/timeline/PaginationResponse.kt | 26 +++++++++++++-- .../session/room/timeline/TokenChunkEvent.kt | 2 +- .../room/timeline/TokenChunkEventPersistor.kt | 14 ++++---- .../session/room/uploads/GetUploadsTask.kt | 6 ++-- 5 files changed, 57 insertions(+), 23 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt index d76ba35280..654cf0fb74 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/EventContextResponse.kt @@ -21,20 +21,34 @@ import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.session.events.model.Event @JsonClass(generateAdapter = true) -data class EventContextResponse( +internal data class EventContextResponse( + /** + * Details of the requested event. + */ @Json(name = "event") val event: Event, + /** + * A token that can be used to paginate backwards with. + */ @Json(name = "start") override val start: String? = null, - @Json(name = "events_before") val eventsBefore: List? = emptyList(), - @Json(name = "events_after") val eventsAfter: List? = emptyList(), + /** + * A list of room events that happened just before the requested event, in reverse-chronological order. + */ + @Json(name = "events_before") val eventsBefore: List? = null, + /** + * A list of room events that happened just after the requested event, in chronological order. + */ + @Json(name = "events_after") val eventsAfter: List? = null, + /** + * A token that can be used to paginate forwards with. + */ @Json(name = "end") override val end: String? = null, - @Json(name = "state") override val stateEvents: List? = emptyList() + /** + * The state of the room at the last event returned. + */ + @Json(name = "state") override val stateEvents: List? = null ) : TokenChunkEvent { override val events: List by lazy { - mutableListOf().apply { - eventsAfter?.let { addAll(it.reversed()) } - add(event) - eventsBefore?.let { addAll(it) } - } + eventsAfter.orEmpty().reversed() + event + eventsBefore.orEmpty() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt index ff0c7fbbde..2f61b1cce8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/PaginationResponse.kt @@ -22,8 +22,28 @@ import org.matrix.android.sdk.api.session.events.model.Event @JsonClass(generateAdapter = true) internal data class PaginationResponse( + /** + * The token the pagination starts from. If dir=b this will be the token supplied in from. + */ @Json(name = "start") override val start: String? = null, + /** + * The token the pagination ends at. If dir=b this token should be used again to request even earlier events. + */ @Json(name = "end") override val end: String? = null, - @Json(name = "chunk") override val events: List? = emptyList(), - @Json(name = "state") override val stateEvents: List? = emptyList() -) : TokenChunkEvent + /** + * A list of room events. The order depends on the dir parameter. For dir=b events will be in + * reverse-chronological order, for dir=f in chronological order, so that events start at the from point. + */ + @Json(name = "chunk") val chunk: List? = null, + /** + * A list of state events relevant to showing the chunk. For example, if lazy_load_members is enabled + * in the filter then this may contain the membership events for the senders of events in the chunk. + * + * Unless include_redundant_members is true, the server may remove membership events which would have + * already been sent to the client in prior calls to this endpoint, assuming the membership of those members has not changed. + */ + @Json(name = "state") override val stateEvents: List? = null +) : TokenChunkEvent { + override val events: List + get() = chunk.orEmpty() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt index 50cc50beb2..465b0faac8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEvent.kt @@ -21,7 +21,7 @@ import org.matrix.android.sdk.api.session.events.model.Event internal interface TokenChunkEvent { val start: String? val end: String? - val events: List? + val events: List val stateEvents: List? fun hasMore() = start != end diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt index fb636a9314..1a497b8835 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -124,7 +124,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri direction: PaginationDirection): Result { monarchy .awaitTransaction { realm -> - Timber.v("Start persisting ${receivedChunk.events?.size} events in $roomId towards $direction") + Timber.v("Start persisting ${receivedChunk.events.size} events in $roomId towards $direction") val nextToken: String? val prevToken: String? @@ -149,14 +149,14 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri } ?: ChunkEntity.create(realm, prevToken, nextToken) - if (receivedChunk.events.isNullOrEmpty() && !receivedChunk.hasMore()) { + if (receivedChunk.events.isEmpty() && !receivedChunk.hasMore()) { handleReachEnd(realm, roomId, direction, currentChunk) } else { handlePagination(realm, roomId, direction, receivedChunk, currentChunk) } } - return if (receivedChunk.events.isNullOrEmpty()) { - if (receivedChunk.start != receivedChunk.end) { + return if (receivedChunk.events.isEmpty()) { + if (receivedChunk.hasMore()) { Result.SHOULD_FETCH_MORE } else { Result.REACHED_END @@ -189,7 +189,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri receivedChunk: TokenChunkEvent, currentChunk: ChunkEntity ) { - Timber.v("Add ${receivedChunk.events?.size} events in chunk(${currentChunk.nextToken} | ${currentChunk.prevToken}") + Timber.v("Add ${receivedChunk.events.size} events in chunk(${currentChunk.nextToken} | ${currentChunk.prevToken}") val roomMemberContentsByUser = HashMap() val eventList = receivedChunk.events val stateEvents = receivedChunk.stateEvents @@ -204,8 +204,8 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri roomMemberContentsByUser[stateEvent.stateKey] = stateEvent.content.toModel() } } - val eventIds = ArrayList(eventList?.size ?: 0) - eventList?.forEach { event -> + val eventIds = ArrayList(eventList.size) + eventList.forEach { event -> if (event.eventId == null || event.senderId == null) { return@forEach } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt index 9d66474a73..b3e4a5aa05 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/GetUploadsTask.kt @@ -56,8 +56,8 @@ internal class DefaultGetUploadsTask @Inject constructor( private val roomAPI: RoomAPI, private val tokenStore: SyncTokenStore, @SessionDatabase private val monarchy: Monarchy, - private val globalErrorReceiver: GlobalErrorReceiver) - : GetUploadsTask { + private val globalErrorReceiver: GlobalErrorReceiver +) : GetUploadsTask { override suspend fun execute(params: GetUploadsTask.Params): GetUploadsResult { val result: GetUploadsResult @@ -95,7 +95,7 @@ internal class DefaultGetUploadsTask @Inject constructor( nextToken = chunk.end ?: "", hasMore = chunk.hasMore() ) - events = chunk.events ?: emptyList() + events = chunk.events } var uploadEvents = listOf()