diff --git a/.travis.yml b/.travis.yml index 6b9e9f9946..85bddac7f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,10 +23,10 @@ android: - platform-tools # The BuildTools version used by your project - - build-tools-28.0.3 + - build-tools-29.0.3 # The SDK version used to compile your project - - android-28 + - android-29 before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock diff --git a/CHANGES.md b/CHANGES.md index 16b39cc6b1..a1748f3623 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ Features ✨: Improvements 🙌: - Verification DM / Handle concurrent .start after .ready (#794) + - Reimplementation of multiple attachment picker - Cross-Signing | Update Shield Logic for DM (#963) - Cross-Signing | Complete security new session design update (#1135) - Cross-Signing | Setup key backup as part of SSSS bootstrapping (#1201) @@ -30,10 +31,10 @@ Translations 🗣: - SDK API changes ⚠️: - - + - Increase targetSdkVersion to 29 Build 🧱: - - + - Compile with Android SDK 29 (Android Q) Other changes: - Increase File Logger capacities ( + use dev log preferences) diff --git a/matrix-sdk-android-rx/build.gradle b/matrix-sdk-android-rx/build.gradle index 8907ee8b50..734ff0c130 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 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 - targetSdkVersion 28 + targetSdkVersion 29 versionCode 1 versionName "1.0" diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index ca6c2ce6d9..c10aaf3545 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -19,12 +19,12 @@ androidExtensions { } android { - compileSdkVersion 28 + compileSdkVersion 29 testOptions.unitTests.includeAndroidResources = true defaultConfig { minSdkVersion 16 - targetSdkVersion 28 + targetSdkVersion 29 versionCode 1 versionName "0.0.1" // Multidex is useful for tests diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/FileQualifiers.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/FileQualifiers.kt index acd6703c77..aa39fc6fe8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/FileQualifiers.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/FileQualifiers.kt @@ -29,3 +29,7 @@ annotation class SessionCacheDirectory @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class CacheDirectory + +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class ExternalFilesDirectory diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt index e929016d4f..4d6082d50b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt @@ -56,6 +56,9 @@ internal interface MatrixComponent { @CacheDirectory fun cacheDir(): File + @ExternalFilesDirectory + fun externalFilesDir(): File? + fun olmManager(): OlmManager fun taskExecutor(): TaskExecutor diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt index 0af22dd65a..785aecdf8e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt @@ -57,6 +57,13 @@ internal object MatrixModule { return context.cacheDir } + @JvmStatic + @Provides + @ExternalFilesDirectory + fun providesExternalFilesDir(context: Context): File? { + return context.getExternalFilesDir(null) + } + @JvmStatic @Provides @MatrixScope diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkInfoReceiver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkInfoReceiver.kt index e8daf9b79b..645329628b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkInfoReceiver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkInfoReceiver.kt @@ -14,6 +14,9 @@ * limitations under the License. */ +// This BroadcastReceiver is used only if the build code is below 24. +@file:Suppress("DEPRECATION") + package im.vector.matrix.android.internal.network import android.content.BroadcastReceiver diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt index 9eaea8cc34..d3f4a4c244 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultFileService.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session -import android.os.Environment import arrow.core.Try import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.content.ContentUrlResolver @@ -25,6 +24,7 @@ import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments import im.vector.matrix.android.internal.di.CacheDirectory +import im.vector.matrix.android.internal.di.ExternalFilesDirectory import im.vector.matrix.android.internal.di.SessionCacheDirectory import im.vector.matrix.android.internal.di.Unauthenticated import im.vector.matrix.android.internal.extensions.foldToCallback @@ -44,6 +44,8 @@ import javax.inject.Inject internal class DefaultFileService @Inject constructor( @CacheDirectory private val cacheDirectory: File, + @ExternalFilesDirectory + private val externalFilesDirectory: File?, @SessionCacheDirectory private val sessionCacheDirectory: File, private val contentUrlResolver: ContentUrlResolver, @@ -103,7 +105,7 @@ internal class DefaultFileService @Inject constructor( private fun copyFile(file: File, downloadMode: FileService.DownloadMode): File { return when (downloadMode) { FileService.DownloadMode.TO_EXPORT -> - file.copyTo(File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), file.name), true) + file.copyTo(File(externalFilesDirectory, file.name), true) FileService.DownloadMode.FOR_EXTERNAL_SHARE -> file.copyTo(File(File(cacheDirectory, "ext_share"), file.name), true) FileService.DownloadMode.FOR_INTERNAL_USE -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Debouncer.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Debouncer.kt index 6a294d8d6c..575551da1b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Debouncer.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Debouncer.kt @@ -23,11 +23,9 @@ internal class Debouncer(private val handler: Handler) { private val runnables = HashMap() fun debounce(identifier: String, r: Runnable, millis: Long): Boolean { - if (runnables.containsKey(identifier)) { - // debounce - val old = runnables[identifier] - handler.removeCallbacks(old) - } + // debounce + runnables[identifier]?.let { runnable -> handler.removeCallbacks(runnable) } + insertRunnable(identifier, r, millis) return true } diff --git a/tools/release/sign_apk.sh b/tools/release/sign_apk.sh index 7f421c15f4..866510ba13 100755 --- a/tools/release/sign_apk.sh +++ b/tools/release/sign_apk.sh @@ -17,7 +17,7 @@ PARAM_KEYSTORE_PATH=$1 PARAM_APK=$2 # Other params -BUILD_TOOLS_VERSION="28.0.3" +BUILD_TOOLS_VERSION="29.0.3" MIN_SDK_VERSION=19 echo "Signing APK with build-tools version ${BUILD_TOOLS_VERSION} for min SDK version ${MIN_SDK_VERSION}..." diff --git a/vector/build.gradle b/vector/build.gradle index ee437eabee..2a898f5621 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -104,13 +104,13 @@ 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 28 + compileSdkVersion 29 defaultConfig { applicationId "im.vector.riotx" // Set to API 19 because motionLayout is min API 18. // In the future we may consider using an alternative of MotionLayout to support API 16. But for security reason, maybe not. minSdkVersion 19 - targetSdkVersion 28 + targetSdkVersion 29 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/debug/java/im/vector/riotx/receivers/DebugReceiver.kt b/vector/src/debug/java/im/vector/riotx/receivers/DebugReceiver.kt index 887fba364c..914d7923df 100644 --- a/vector/src/debug/java/im/vector/riotx/receivers/DebugReceiver.kt +++ b/vector/src/debug/java/im/vector/riotx/receivers/DebugReceiver.kt @@ -21,7 +21,7 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.SharedPreferences -import android.preference.PreferenceManager +import androidx.preference.PreferenceManager import androidx.core.content.edit import im.vector.riotx.core.utils.lsFiles import timber.log.Timber diff --git a/vector/src/gplay/java/im/vector/riotx/push/fcm/FcmHelper.kt b/vector/src/gplay/java/im/vector/riotx/push/fcm/FcmHelper.kt index 136949d66f..d62aaf0f3d 100755 --- a/vector/src/gplay/java/im/vector/riotx/push/fcm/FcmHelper.kt +++ b/vector/src/gplay/java/im/vector/riotx/push/fcm/FcmHelper.kt @@ -19,7 +19,7 @@ package im.vector.riotx.push.fcm import android.app.Activity import android.content.Context -import android.preference.PreferenceManager +import androidx.preference.PreferenceManager import android.widget.Toast import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability diff --git a/vector/src/main/java/im/vector/riotx/VectorApplication.kt b/vector/src/main/java/im/vector/riotx/VectorApplication.kt index bd85596924..680550e818 100644 --- a/vector/src/main/java/im/vector/riotx/VectorApplication.kt +++ b/vector/src/main/java/im/vector/riotx/VectorApplication.kt @@ -171,7 +171,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration. MultiDex.install(this) } - override fun onConfigurationChanged(newConfig: Configuration?) { + override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) vectorConfiguration.onConfigurationChanged() } diff --git a/vector/src/main/java/im/vector/riotx/core/files/FileSaver.kt b/vector/src/main/java/im/vector/riotx/core/files/FileSaver.kt index 677f7894e8..f978e20ca9 100644 --- a/vector/src/main/java/im/vector/riotx/core/files/FileSaver.kt +++ b/vector/src/main/java/im/vector/riotx/core/files/FileSaver.kt @@ -17,7 +17,10 @@ package im.vector.riotx.core.files import android.app.DownloadManager +import android.content.ContentValues import android.content.Context +import android.os.Build +import android.provider.MediaStore import androidx.annotation.WorkerThread import arrow.core.Try import okio.buffer @@ -54,10 +57,24 @@ fun addEntryToDownloadManager(context: Context, mimeType: String, title: String = file.name, description: String = file.name) { - val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager? - try { - downloadManager?.addCompletedDownload(title, description, true, mimeType, file.absolutePath, file.length(), true) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val contentValues = ContentValues().apply { + put(MediaStore.Downloads.TITLE, title) + put(MediaStore.Downloads.DISPLAY_NAME, description) + put(MediaStore.Downloads.MIME_TYPE, mimeType) + put(MediaStore.Downloads.SIZE, file.length()) + } + context.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)?.let { uri -> + context.contentResolver.openOutputStream(uri)?.use { outputStream -> + outputStream.sink().buffer().write(file.inputStream().use { it.readBytes() }) + } + } + } else { + val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager? + @Suppress("DEPRECATION") + downloadManager?.addCompletedDownload(title, description, true, mimeType, file.absolutePath, file.length(), true) + } } catch (e: Exception) { Timber.e(e, "## addEntryToDownloadManager(): Exception") } diff --git a/vector/src/main/java/im/vector/riotx/core/glide/VectorGlideModelLoader.kt b/vector/src/main/java/im/vector/riotx/core/glide/VectorGlideModelLoader.kt index 1a90b0c34f..191ab6d972 100644 --- a/vector/src/main/java/im/vector/riotx/core/glide/VectorGlideModelLoader.kt +++ b/vector/src/main/java/im/vector/riotx/core/glide/VectorGlideModelLoader.kt @@ -95,7 +95,7 @@ class VectorGlideDataFetcher(private val activeSessionHolder: ActiveSessionHolde override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) { Timber.v("Load data: $data") - if (data.isLocalFile()) { + if (data.isLocalFile() && data.url != null) { val initialFile = File(data.url) callback.onDataReady(FileInputStream(initialFile)) return diff --git a/vector/src/main/java/im/vector/riotx/core/images/ImageTools.kt b/vector/src/main/java/im/vector/riotx/core/images/ImageTools.kt deleted file mode 100644 index 84cba7392f..0000000000 --- a/vector/src/main/java/im/vector/riotx/core/images/ImageTools.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.riotx.core.images - -import android.content.Context -import android.net.Uri -import android.provider.MediaStore -import androidx.exifinterface.media.ExifInterface -import timber.log.Timber -import javax.inject.Inject - -class ImageTools @Inject constructor(private val context: Context) { - - /** - * Gets the [ExifInterface] value for the orientation for this local bitmap Uri. - * - * @param uri The URI to find the orientation for. Must be local. - * @return The orientation value, which may be [ExifInterface.ORIENTATION_UNDEFINED]. - */ - fun getOrientationForBitmap(uri: Uri): Int { - var orientation = ExifInterface.ORIENTATION_UNDEFINED - - if (uri.scheme == "content") { - val proj = arrayOf(MediaStore.Images.Media.DATA) - try { - val cursor = context.contentResolver.query(uri, proj, null, null, null) - cursor?.use { - if (it.moveToFirst()) { - val idxData = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATA) - val path = it.getString(idxData) - if (path.isNullOrBlank()) { - Timber.w("Cannot find path in media db for uri $uri") - return orientation - } - val exif = ExifInterface(path) - orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED) - } - } - } catch (e: Exception) { - // eg SecurityException from com.google.android.apps.photos.content.GooglePhotosImageProvider URIs - // eg IOException from trying to parse the returned path as a file when it is an http uri. - Timber.e(e, "Cannot get orientation for bitmap") - } - } else if (uri.scheme == "file") { - try { - val path = uri.path - if (path != null) { - val exif = ExifInterface(path) - orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED) - } - } catch (e: Exception) { - Timber.e(e, "Cannot get EXIF for file uri $uri") - } - } - - return orientation - } -} diff --git a/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt b/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt index 2e5786b57d..f451308c36 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/EllipsizingTextView.kt @@ -333,7 +333,7 @@ class EllipsizingTextView @JvmOverloads constructor(context: Context, attrs: Att * @param workingText text to strip end punctuation from * @return Text without end punctuation. */ - fun stripEndPunctuation(workingText: CharSequence?): String { + fun stripEndPunctuation(workingText: CharSequence): String { return mEndPunctPattern!!.matcher(workingText).replaceFirst("") } } diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt index 2649662ee5..ea954ecf27 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt @@ -134,7 +134,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { restorables.forEach { it.onSaveInstanceState(outState) } } - override fun onRestoreInstanceState(savedInstanceState: Bundle?) { + override fun onRestoreInstanceState(savedInstanceState: Bundle) { restorables.forEach { it.onRestoreInstanceState(savedInstanceState) } super.onRestoreInstanceState(savedInstanceState) } diff --git a/vector/src/main/java/im/vector/riotx/core/services/CallService.kt b/vector/src/main/java/im/vector/riotx/core/services/CallService.kt index f36b289aaa..30ab62d5b2 100644 --- a/vector/src/main/java/im/vector/riotx/core/services/CallService.kt +++ b/vector/src/main/java/im/vector/riotx/core/services/CallService.kt @@ -128,13 +128,13 @@ class CallService : VectorService() { * Display a call in progress notification. */ private fun displayCallInProgressNotification(intent: Intent) { - val callId = intent.getStringExtra(EXTRA_CALL_ID) + val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: "" val notification = notificationUtils.buildPendingCallNotification( intent.getBooleanExtra(EXTRA_IS_VIDEO, false), - intent.getStringExtra(EXTRA_ROOM_NAME), - intent.getStringExtra(EXTRA_ROOM_ID), - intent.getStringExtra(EXTRA_MATRIX_ID), + intent.getStringExtra(EXTRA_ROOM_NAME) ?: "", + intent.getStringExtra(EXTRA_ROOM_ID) ?: "", + intent.getStringExtra(EXTRA_MATRIX_ID) ?: "", callId) startForeground(NOTIFICATION_ID, notification) diff --git a/vector/src/main/java/im/vector/riotx/core/ui/views/KeysBackupBanner.kt b/vector/src/main/java/im/vector/riotx/core/ui/views/KeysBackupBanner.kt index 8d314f9e58..83a4e88ad5 100755 --- a/vector/src/main/java/im/vector/riotx/core/ui/views/KeysBackupBanner.kt +++ b/vector/src/main/java/im/vector/riotx/core/ui/views/KeysBackupBanner.kt @@ -17,7 +17,7 @@ package im.vector.riotx.core.ui.views import android.content.Context -import android.preference.PreferenceManager +import androidx.preference.PreferenceManager import android.util.AttributeSet import android.view.View import android.view.ViewGroup diff --git a/vector/src/main/java/im/vector/riotx/core/utils/Debouncer.kt b/vector/src/main/java/im/vector/riotx/core/utils/Debouncer.kt index 627d757574..95035f6c96 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/Debouncer.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/Debouncer.kt @@ -37,9 +37,10 @@ class Debouncer(private val handler: Handler) { fun cancel(identifier: String) { if (runnables.containsKey(identifier)) { - val old = runnables[identifier] - handler.removeCallbacks(old) - runnables.remove(identifier) + runnables[identifier]?.let { + handler.removeCallbacks(it) + runnables.remove(identifier) + } } } diff --git a/vector/src/main/java/im/vector/riotx/core/utils/FileUtils.kt b/vector/src/main/java/im/vector/riotx/core/utils/FileUtils.kt index 1a19b49872..839356a129 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/FileUtils.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/FileUtils.kt @@ -73,7 +73,7 @@ private fun logAction(file: File): Boolean { */ private fun recursiveActionOnFile(file: File, action: ActionOnFile): Boolean { if (file.isDirectory) { - file.list().forEach { + file.list()?.forEach { val result = recursiveActionOnFile(File(file, it), action) if (!result) { diff --git a/vector/src/main/java/im/vector/riotx/core/utils/RingtoneUtils.kt b/vector/src/main/java/im/vector/riotx/core/utils/RingtoneUtils.kt index 1d2b97e27e..5345658207 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/RingtoneUtils.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/RingtoneUtils.kt @@ -20,7 +20,7 @@ import android.content.Context import android.media.Ringtone import android.media.RingtoneManager import android.net.Uri -import android.preference.PreferenceManager +import androidx.preference.PreferenceManager import androidx.core.content.edit import im.vector.riotx.features.settings.VectorPreferences diff --git a/vector/src/main/java/im/vector/riotx/core/utils/SystemUtils.kt b/vector/src/main/java/im/vector/riotx/core/utils/SystemUtils.kt index b77553a3d6..d82134caf5 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/SystemUtils.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/SystemUtils.kt @@ -84,7 +84,7 @@ fun requestDisablingBatteryOptimization(activity: Activity, fragment: Fragment?, */ fun copyToClipboard(context: Context, text: CharSequence, showToast: Boolean = true, @StringRes toastMessage: Int = R.string.copied_to_clipboard) { val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - clipboard.primaryClip = ClipData.newPlainText("", text) + clipboard.setPrimaryClip(ClipData.newPlainText("", text)) if (showToast) { context.toast(toastMessage) } diff --git a/vector/src/main/java/im/vector/riotx/core/utils/TextUtils.kt b/vector/src/main/java/im/vector/riotx/core/utils/TextUtils.kt index 75f6893c7c..9519eb1f9d 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/TextUtils.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/TextUtils.kt @@ -35,8 +35,8 @@ object TextUtils { if (value < 1000) return value.toString() // deal with easy case val e = suffixes.floorEntry(value) - val divideBy = e.key - val suffix = e.value + val divideBy = e?.key + val suffix = e?.value val truncated = value / (divideBy!! / 10) // the number part of the output times 10 val hasDecimal = truncated < 100 && truncated / 10.0 != (truncated / 10).toDouble() diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt index 46a90803ca..6c91f70131 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt @@ -43,7 +43,7 @@ class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { } fun getOutput(intent: Intent): List { - return intent.getParcelableArrayListExtra(ATTACHMENTS_PREVIEW_RESULT) + return intent.getParcelableArrayListExtra(ATTACHMENTS_PREVIEW_RESULT) ?: emptyList() } fun getKeepOriginalSize(intent: Intent): Boolean { diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysExporter.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysExporter.kt index cae8b50523..b9b75588f1 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysExporter.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keys/KeysExporter.kt @@ -40,7 +40,7 @@ class KeysExporter(private val session: Session) { runCatching { val data = awaitCallback { session.cryptoService().exportRoomKeys(password, it) } withContext(Dispatchers.IO) { - val parentDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + val parentDir = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) val file = File(parentDir, "riotx-keys-" + System.currentTimeMillis() + ".txt") writeToFile(data, file) diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt index a9bdeee2d6..8dc8855583 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt @@ -95,7 +95,7 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor(): VectorBaseF // used just to have default link representation val clickableSpan = object : ClickableSpan() { - override fun onClick(widget: View?) {} + override fun onClick(widget: View) {} } val start = helperText.indexOf(clickableText) val end = start + clickableText.length diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt index 924e25a4d7..fbc69505fa 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt @@ -153,7 +153,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { } override fun onFailure(failure: Throwable) { - toast(failure.localizedMessage) + toast(failure.localizedMessage ?: getString(R.string.unexpected_error)) hideWaitingView() } }) diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt index a224cfb387..9175d6c081 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt @@ -167,7 +167,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() GlobalScope.launch(Dispatchers.Main) { Try { withContext(Dispatchers.IO) { - val parentDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + val parentDir = context?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) val file = File(parentDir, "recovery-key-" + System.currentTimeMillis() + ".txt") writeToFile(data, file) diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecureStorageViewModel.kt index 9cd60eba43..530e0934cb 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -163,8 +163,8 @@ class SharedSecureStorageViewModel @AssistedInject constructor( @JvmStatic override fun create(viewModelContext: ViewModelContext, state: SharedSecureStorageViewState): SharedSecureStorageViewModel? { val activity: SharedSecureStorageActivity = viewModelContext.activity() - val args: SharedSecureStorageActivity.Args = activity.intent.getParcelableExtra(MvRx.KEY_ARG) - return activity.viewModelFactory.create(state, args) + val args: SharedSecureStorageActivity.Args? = activity.intent.getParcelableExtra(MvRx.KEY_ARG) + return args?.let { activity.viewModelFactory.create(state, it) } } } } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt index c7a5b3a011..f5f92c381d 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -384,7 +384,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( Unit } } catch (failure: Throwable) { - _viewEvents.post(VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage)) + _viewEvents.post( + VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage ?: stringProvider.getString(R.string.unexpected_error))) } } diff --git a/vector/src/main/java/im/vector/riotx/features/disclaimer/DisclaimerDialog.kt b/vector/src/main/java/im/vector/riotx/features/disclaimer/DisclaimerDialog.kt index ac6b23a109..86b7fd9fc8 100644 --- a/vector/src/main/java/im/vector/riotx/features/disclaimer/DisclaimerDialog.kt +++ b/vector/src/main/java/im/vector/riotx/features/disclaimer/DisclaimerDialog.kt @@ -17,7 +17,7 @@ package im.vector.riotx.features.disclaimer import android.app.Activity -import android.preference.PreferenceManager +import androidx.preference.PreferenceManager import android.view.ViewGroup import android.widget.TextView import androidx.appcompat.app.AlertDialog diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index f58d7be718..8c45090c91 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -782,7 +782,7 @@ class RoomDetailFragment @Inject constructor( updateComposerText("") } is RoomDetailViewEvents.SlashCommandResultError -> { - displayCommandError(sendMessageResult.throwable.localizedMessage) + displayCommandError(sendMessageResult.throwable.localizedMessage ?: getString(R.string.unexpected_error)) } is RoomDetailViewEvents.SlashCommandNotImplemented -> { displayCommandError(getString(R.string.not_implemented)) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 2b221b9fef..f34a578832 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -440,11 +440,11 @@ class MessageItemFactory @Inject constructor( Spanned.SPAN_INCLUSIVE_EXCLUSIVE) spannable.setSpan(object : ClickableSpan() { - override fun onClick(widget: View?) { + override fun onClick(widget: View) { callback?.onEditedDecorationClicked(informationData) } - override fun updateDrawState(ds: TextPaint?) { + override fun updateDrawState(ds: TextPaint) { // nop } }, diff --git a/vector/src/main/java/im/vector/riotx/features/media/ImageMediaViewerActivity.kt b/vector/src/main/java/im/vector/riotx/features/media/ImageMediaViewerActivity.kt index d9da08254f..8ba21468b7 100644 --- a/vector/src/main/java/im/vector/riotx/features/media/ImageMediaViewerActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/media/ImageMediaViewerActivity.kt @@ -56,10 +56,17 @@ class ImageMediaViewerActivity : VectorBaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(im.vector.riotx.R.layout.activity_image_media_viewer) - mediaData = intent.getParcelableExtra(EXTRA_MEDIA_DATA) + + if (intent.hasExtra(EXTRA_MEDIA_DATA)) { + mediaData = intent.getParcelableExtra(EXTRA_MEDIA_DATA)!! + } else { + finish() + } + intent.extras?.getString(EXTRA_SHARED_TRANSITION_NAME)?.let { ViewCompat.setTransitionName(imageTransitionView, it) } + if (mediaData.url.isNullOrEmpty()) { supportFinishAfterTransition() return diff --git a/vector/src/main/java/im/vector/riotx/features/media/VideoMediaViewerActivity.kt b/vector/src/main/java/im/vector/riotx/features/media/VideoMediaViewerActivity.kt index 9c3312f3bb..e92b58c681 100644 --- a/vector/src/main/java/im/vector/riotx/features/media/VideoMediaViewerActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/media/VideoMediaViewerActivity.kt @@ -37,11 +37,14 @@ class VideoMediaViewerActivity : VectorBaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(im.vector.riotx.R.layout.activity_video_media_viewer) - val mediaData = intent.getParcelableExtra(EXTRA_MEDIA_DATA) - configureToolbar(videoMediaViewerToolbar, mediaData) - imageContentRenderer.render(mediaData.thumbnailMediaData, ImageContentRenderer.Mode.FULL_SIZE, videoMediaViewerThumbnailView) - videoContentRenderer.render(mediaData, videoMediaViewerThumbnailView, videoMediaViewerLoading, videoMediaViewerVideoView, videoMediaViewerErrorView) + if (intent.hasExtra(EXTRA_MEDIA_DATA)) { + val mediaData = intent.getParcelableExtra(EXTRA_MEDIA_DATA)!! + + configureToolbar(videoMediaViewerToolbar, mediaData) + imageContentRenderer.render(mediaData.thumbnailMediaData, ImageContentRenderer.Mode.FULL_SIZE, videoMediaViewerThumbnailView) + videoContentRenderer.render(mediaData, videoMediaViewerThumbnailView, videoMediaViewerLoading, videoMediaViewerVideoView, videoMediaViewerErrorView) + } } private fun configureToolbar(toolbar: Toolbar, mediaData: VideoContentRenderer.Data) { diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationBroadcastReceiver.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationBroadcastReceiver.kt index 53d69bce2d..04b267fb3a 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationBroadcastReceiver.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationBroadcastReceiver.kt @@ -97,7 +97,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { val message = getReplyMessage(intent) val roomId = intent.getStringExtra(KEY_ROOM_ID) - if (message.isNullOrBlank() || roomId.isBlank()) { + if (message.isNullOrBlank() || roomId.isNullOrBlank()) { // ignore this event // Can this happen? should we update notification? return diff --git a/vector/src/main/java/im/vector/riotx/features/rageshake/VectorFileLogger.kt b/vector/src/main/java/im/vector/riotx/features/rageshake/VectorFileLogger.kt index d4563eb243..bcc48fb96c 100644 --- a/vector/src/main/java/im/vector/riotx/features/rageshake/VectorFileLogger.kt +++ b/vector/src/main/java/im/vector/riotx/features/rageshake/VectorFileLogger.kt @@ -77,7 +77,7 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec sFileHandler?.formatter = LogFormatter() sLogger.useParentHandlers = false sLogger.level = Level.ALL - sLogger.addHandler(sFileHandler) + sFileHandler?.let { sLogger.addHandler(it) } } } catch (e: Throwable) { Timber.e(e, "Failed to initialize FileLogger") diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewActivity.kt index a7cb8f2f68..3cb442127f 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/roompreview/RoomPreviewActivity.kt @@ -70,7 +70,7 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { if (isFirstCreation()) { val args = intent.getParcelableExtra(ARG) - if (args.worldReadable) { + if (args?.worldReadable == true) { // TODO Room preview: Note: M does not recommend to use /events anymore, so for now we just display the room preview // TODO the same way if it was not world readable addFragment(R.id.simpleFragmentContainer, RoomPreviewNoPreviewFragment::class.java, args) diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorLocale.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorLocale.kt index 805fa53e96..e1a89ab3c4 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorLocale.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorLocale.kt @@ -19,7 +19,7 @@ package im.vector.riotx.features.settings import android.content.Context import android.content.res.Configuration import android.os.Build -import android.preference.PreferenceManager +import androidx.preference.PreferenceManager import androidx.core.content.edit import im.vector.riotx.BuildConfig import im.vector.riotx.R @@ -59,9 +59,9 @@ object VectorLocale { val preferences = PreferenceManager.getDefaultSharedPreferences(context) if (preferences.contains(APPLICATION_LOCALE_LANGUAGE_KEY)) { - applicationLocale = Locale(preferences.getString(APPLICATION_LOCALE_LANGUAGE_KEY, ""), - preferences.getString(APPLICATION_LOCALE_COUNTRY_KEY, ""), - preferences.getString(APPLICATION_LOCALE_VARIANT_KEY, "") + applicationLocale = Locale(preferences.getString(APPLICATION_LOCALE_LANGUAGE_KEY, "")!!, + preferences.getString(APPLICATION_LOCALE_COUNTRY_KEY, "")!!, + preferences.getString(APPLICATION_LOCALE_VARIANT_KEY, "")!! ) } else { applicationLocale = Locale.getDefault() diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt index 9473c26055..faa48d5686 100755 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt @@ -29,7 +29,6 @@ import im.vector.riotx.R import im.vector.riotx.features.homeserver.ServerUrlsRepository import im.vector.riotx.features.themes.ThemeUtils import timber.log.Timber -import java.io.File import javax.inject.Inject class VectorPreferences @Inject constructor(private val context: Context) { @@ -68,7 +67,7 @@ class VectorPreferences @Inject constructor(private val context: Context) { const val SETTINGS_ENCRYPTION_EXPORT_E2E_ROOM_KEYS_PREFERENCE_KEY = "SETTINGS_ENCRYPTION_EXPORT_E2E_ROOM_KEYS_PREFERENCE_KEY" const val SETTINGS_ENCRYPTION_IMPORT_E2E_ROOM_KEYS_PREFERENCE_KEY = "SETTINGS_ENCRYPTION_IMPORT_E2E_ROOM_KEYS_PREFERENCE_KEY" const val SETTINGS_ENCRYPTION_NEVER_SENT_TO_PREFERENCE_KEY = "SETTINGS_ENCRYPTION_NEVER_SENT_TO_PREFERENCE_KEY" - const val SETTINGS_SHOW_DEVICES_LIST_PREFERENCE_KEY = "SETTINGS_SHOW_DEVICES_LIST_PREFERENCE_KEY" + const val SETTINGS_SHOW_DEVICES_LIST_PREFERENCE_KEY = "SETTINGS_SHOW_DEVICES_LIST_PREFERENCE_KEY" const val SETTINGS_SECURE_MESSAGE_RECOVERY_PREFERENCE_KEY = "SETTINGS_SECURE_MESSAGE_RECOVERY_PREFERENCE_KEY" @@ -427,11 +426,11 @@ class VectorPreferences @Inject constructor(private val context: Context) { val toneUri = getNotificationRingTone() ?: return null try { - val proj = arrayOf(MediaStore.Audio.Media.DATA) + val proj = arrayOf(MediaStore.Audio.Media.DISPLAY_NAME) return context.contentResolver.query(toneUri, proj, null, null, null)?.use { - val columnIndex = it.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA) + val columnIndex = it.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME) it.moveToFirst() - File(it.getString(columnIndex)).nameWithoutExtension + it.getString(columnIndex) } } catch (e: Exception) { Timber.e(e, "## getNotificationRingToneName() failed") diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt index 4324c79dfd..d357c6ae82 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -332,7 +332,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( } override fun onFailure(failure: Throwable) { - appContext.toast(failure.localizedMessage) + appContext.toast(failure.localizedMessage ?: getString(R.string.unexpected_error)) hideLoadingView() } })