Merge pull request #3227 from vector-im/dependabot/gradle/kotlin_version-1.5.0

Bump kotlin_version from 1.4.32 to 1.5.0
This commit is contained in:
Benoit Marty 2021-05-17 16:15:27 +02:00 committed by GitHub
commit 1a70fa0fcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 336 additions and 241 deletions

View File

@ -17,6 +17,8 @@ SDK API changes ⚠️:
-
Build 🧱:
- Compile with Kotlin 1.5.
- Upgrade some dependencies: gradle wrapper, third party lib, etc.
- Sign APK with build tools 30.0.3
Test:

View File

@ -2,8 +2,8 @@
buildscript {
// Ref: https://kotlinlang.org/releases.html
ext.kotlin_version = '1.4.32'
ext.kotlin_coroutines_version = "1.4.2"
ext.kotlin_version = '1.5.0'
ext.kotlin_coroutines_version = "1.5.0-RC"
repositories {
google()
jcenter()
@ -12,7 +12,7 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'com.android.tools.build:gradle:4.2.0'
classpath 'com.google.gms:google-services:4.3.5'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.2.0'

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=ca42877db3519b667cd531c414be517b294b0467059d401e7133f0e55b9bf265
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.1-all.zip
distributionSha256Sum=13bf8d3cf8eeeb5770d19741a59bde9bd966dd78d17f1bbad787a05ef19d1c2d
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -226,12 +226,12 @@ class QrCodeTest : InstrumentedTest {
private fun checkHeader(byteArray: ByteArray) {
// MATRIX
byteArray[0] shouldBeEqualTo 'M'.toByte()
byteArray[1] shouldBeEqualTo 'A'.toByte()
byteArray[2] shouldBeEqualTo 'T'.toByte()
byteArray[3] shouldBeEqualTo 'R'.toByte()
byteArray[4] shouldBeEqualTo 'I'.toByte()
byteArray[5] shouldBeEqualTo 'X'.toByte()
byteArray[0] shouldBeEqualTo 'M'.code.toByte()
byteArray[1] shouldBeEqualTo 'A'.code.toByte()
byteArray[2] shouldBeEqualTo 'T'.code.toByte()
byteArray[3] shouldBeEqualTo 'R'.code.toByte()
byteArray[4] shouldBeEqualTo 'I'.code.toByte()
byteArray[5] shouldBeEqualTo 'X'.code.toByte()
// Version
byteArray[6] shouldBeEqualTo 2

View File

@ -20,6 +20,7 @@ import android.annotation.SuppressLint
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.util.safeCapitalize
/**
* Ref: https://github.com/matrix-org/matrix-doc/issues/1236
@ -39,6 +40,6 @@ data class WidgetContent(
@SuppressLint("DefaultLocale")
fun getHumanName(): String {
return (name ?: type ?: "").capitalize()
return (name ?: type ?: "").safeCapitalize()
}
}

View File

@ -117,22 +117,22 @@ sealed class MatrixItem(
var first = dn[startIndex]
// LEFT-TO-RIGHT MARK
if (dn.length >= 2 && 0x200e == first.toInt()) {
if (dn.length >= 2 && 0x200e == first.code) {
startIndex++
first = dn[startIndex]
}
// check if its the start of a surrogate pair
if (first.toInt() in 0xD800..0xDBFF && dn.length > startIndex + 1) {
if (first.code in 0xD800..0xDBFF && dn.length > startIndex + 1) {
val second = dn[startIndex + 1]
if (second.toInt() in 0xDC00..0xDFFF) {
if (second.code in 0xDC00..0xDFFF) {
length++
}
}
dn.substring(startIndex, startIndex + length)
}
.toUpperCase(Locale.ROOT)
.uppercase(Locale.ROOT)
}
companion object {

View File

@ -345,7 +345,7 @@ internal abstract class SASDefaultVerificationTransaction(
}
protected fun hashUsingAgreedHashMethod(toHash: String): String? {
if ("sha256" == accepted?.hash?.toLowerCase(Locale.ROOT)) {
if ("sha256" == accepted?.hash?.lowercase(Locale.ROOT)) {
val olmUtil = OlmUtility()
val hashBytes = olmUtil.sha256(toHash)
olmUtil.releaseUtility()
@ -355,7 +355,7 @@ internal abstract class SASDefaultVerificationTransaction(
}
private fun macUsingAgreedMethod(message: String, info: String): String? {
return when (accepted?.messageAuthenticationCode?.toLowerCase(Locale.ROOT)) {
return when (accepted?.messageAuthenticationCode?.lowercase(Locale.ROOT)) {
SAS_MAC_SHA256_LONGKDF -> getSAS().calculateMacLongKdf(message, info)
SAS_MAC_SHA256 -> getSAS().calculateMac(message, info)
else -> null

View File

@ -48,7 +48,7 @@ fun QrCodeData.toEncodedString(): String {
// TransactionId
transactionId.forEach {
result += it.toByte()
result += it.code.toByte()
}
// Keys

View File

@ -291,7 +291,7 @@ internal class DefaultFileService @Inject constructor(
Timber.v("Get size of ${it.absolutePath}")
true
}
.sumBy { it.length().toInt() }
.sumOf { it.length().toInt() }
}
override fun clearCache() {

View File

@ -117,7 +117,7 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor(
return withOlmUtility { olmUtility ->
threePids.map { threePid ->
base64ToBase64Url(
olmUtility.sha256(threePid.value.toLowerCase(Locale.ROOT)
olmUtility.sha256(threePid.value.lowercase(Locale.ROOT)
+ " " + threePid.toMedium() + " " + pepper)
)
}

View File

@ -27,7 +27,7 @@ fun String.md5() = try {
digest.update(toByteArray())
digest.digest()
.joinToString("") { String.format("%02X", it) }
.toLowerCase(Locale.ROOT)
.lowercase(Locale.ROOT)
} catch (exc: Exception) {
// Should not happen, but just in case
hashCode().toString()

View File

@ -17,6 +17,7 @@
package org.matrix.android.sdk.internal.util
import timber.log.Timber
import java.util.Locale
/**
* Convert a string to an UTF8 String
@ -24,7 +25,7 @@ import timber.log.Timber
* @param s the string to convert
* @return the utf-8 string
*/
fun convertToUTF8(s: String): String {
internal fun convertToUTF8(s: String): String {
return try {
val bytes = s.toByteArray(Charsets.UTF_8)
String(bytes)
@ -40,7 +41,7 @@ fun convertToUTF8(s: String): String {
* @param s the string to convert
* @return the utf-16 string
*/
fun convertFromUTF8(s: String): String {
internal fun convertFromUTF8(s: String): String {
return try {
val bytes = s.toByteArray()
String(bytes, Charsets.UTF_8)
@ -56,7 +57,7 @@ fun convertFromUTF8(s: String): String {
* @param subString the string to search for
* @return whether a match was found
*/
fun String.caseInsensitiveFind(subString: String): Boolean {
internal fun String.caseInsensitiveFind(subString: String): Boolean {
// add sanity checks
if (subString.isEmpty() || isEmpty()) {
return false
@ -78,3 +79,14 @@ internal val spaceChars = "[\u00A0\u2000-\u200B\u2800\u3000]".toRegex()
* Strip all the UTF-8 chars which are actually spaces
*/
internal fun String.replaceSpaceChars() = replace(spaceChars, "")
// String.capitalize is now deprecated
internal fun String.safeCapitalize(): String {
return replaceFirstChar { char ->
if (char.isLowerCase()) {
char.titlecase(Locale.getDefault())
} else {
char.toString()
}
}
}

View File

@ -19,13 +19,14 @@ import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.app.AppCompatActivity
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
import im.vector.app.push.fcm.FcmHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -38,7 +39,8 @@ import javax.inject.Inject
class TestPushFromPushGateway @Inject constructor(private val context: AppCompatActivity,
private val stringProvider: StringProvider,
private val errorFormatter: ErrorFormatter,
private val pushersManager: PushersManager)
private val pushersManager: PushersManager,
private val activeSessionHolder: ActiveSessionHolder)
: TroubleshootTest(R.string.settings_troubleshoot_test_push_loop_title) {
private var action: Job? = null
@ -50,7 +52,7 @@ class TestPushFromPushGateway @Inject constructor(private val context: AppCompat
status = TestStatus.FAILED
return
}
action = GlobalScope.launch {
action = activeSessionHolder.getActiveSession().coroutineScope.launch {
val result = runCatching { pushersManager.testPush(fcmToken) }
withContext(Dispatchers.Main) {

View File

@ -22,10 +22,10 @@ import androidx.lifecycle.OnLifecycleEvent
import arrow.core.Option
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.utils.BehaviorDataSource
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.ui.UiStateRepository
import io.reactivex.disposables.CompositeDisposable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.Session
@ -63,30 +63,30 @@ class AppStateHandler @Inject constructor(
fun getCurrentRoomGroupingMethod(): RoomGroupingMethod? = selectedSpaceDataSource.currentValue?.orNull()
fun setCurrentSpace(spaceId: String?, session: Session? = null) {
val uSession = session ?: activeSessionHolder.getSafeActiveSession()
val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return
if (selectedSpaceDataSource.currentValue?.orNull() is RoomGroupingMethod.BySpace
&& spaceId == selectedSpaceDataSource.currentValue?.orNull()?.space()?.roomId) return
val spaceSum = spaceId?.let { uSession?.getRoomSummary(spaceId) }
val spaceSum = spaceId?.let { uSession.getRoomSummary(spaceId) }
selectedSpaceDataSource.post(Option.just(RoomGroupingMethod.BySpace(spaceSum)))
if (spaceId != null) {
GlobalScope.launch(Dispatchers.IO) {
uSession.coroutineScope.launch(Dispatchers.IO) {
tryOrNull {
uSession?.getRoom(spaceId)?.loadRoomMembersIfNeeded()
uSession.getRoom(spaceId)?.loadRoomMembersIfNeeded()
}
}
}
}
fun setCurrentGroup(groupId: String?, session: Session? = null) {
val uSession = session ?: activeSessionHolder.getSafeActiveSession()
val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return
if (selectedSpaceDataSource.currentValue?.orNull() is RoomGroupingMethod.ByLegacyGroup
&& groupId == selectedSpaceDataSource.currentValue?.orNull()?.group()?.groupId) return
val activeGroup = groupId?.let { uSession?.getGroupSummary(groupId) }
val activeGroup = groupId?.let { uSession.getGroupSummary(groupId) }
selectedSpaceDataSource.post(Option.just(RoomGroupingMethod.ByLegacyGroup(activeGroup)))
if (groupId != null) {
GlobalScope.launch {
uSession.coroutineScope.launch {
tryOrNull {
uSession?.getGroup(groupId)?.fetchGroupData()
uSession.getGroup(groupId)?.fetchGroupData()
}
}
}

View File

@ -45,7 +45,7 @@ fun getMimeTypeFromUri(context: Context, uri: Uri): String? {
if (null != mimeType) {
// the mimetype is sometimes in uppercase.
mimeType = mimeType.toLowerCase(Locale.ROOT)
mimeType = mimeType.lowercase(Locale.ROOT)
}
} catch (e: Exception) {
Timber.e(e, "Failed to open resource input stream")

View File

@ -42,13 +42,13 @@ fun CharSequence.splitEmoji(): List<CharSequence> {
while (index < length) {
val firstChar = get(index)
if (firstChar.toInt() == 0x200e) {
if (firstChar.code == 0x200e) {
// Left to right mark. What should I do with it?
} else if (firstChar.toInt() in 0xD800..0xDBFF && index + 1 < length) {
} else if (firstChar.code in 0xD800..0xDBFF && index + 1 < length) {
// We have the start of a surrogate pair
val secondChar = get(index + 1)
if (secondChar.toInt() in 0xDC00..0xDFFF) {
if (secondChar.code in 0xDC00..0xDFFF) {
// We have an emoji
result.add("$firstChar$secondChar")
index++

View File

@ -43,8 +43,7 @@ import im.vector.app.R
import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.themes.ThemeUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okio.buffer
import okio.sink
import okio.source
@ -57,6 +56,7 @@ import timber.log.Timber
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.lang.IllegalStateException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@ -344,90 +344,93 @@ private fun appendTimeToFilename(name: String): String {
return """${filename}_$dateExtension.$fileExtension"""
}
fun saveMedia(context: Context, file: File, title: String, mediaMimeType: String?, notificationUtils: NotificationUtils) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val filename = appendTimeToFilename(title)
suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType: String?, notificationUtils: NotificationUtils) {
withContext(Dispatchers.IO) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val filename = appendTimeToFilename(title)
val values = ContentValues().apply {
put(MediaStore.Images.Media.TITLE, filename)
put(MediaStore.Images.Media.DISPLAY_NAME, filename)
put(MediaStore.Images.Media.MIME_TYPE, mediaMimeType)
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
}
val externalContentUri = when {
mediaMimeType?.isMimeTypeImage() == true -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
mediaMimeType?.isMimeTypeVideo() == true -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
mediaMimeType?.isMimeTypeAudio() == true -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
else -> MediaStore.Downloads.EXTERNAL_CONTENT_URI
}
val values = ContentValues().apply {
put(MediaStore.Images.Media.TITLE, filename)
put(MediaStore.Images.Media.DISPLAY_NAME, filename)
put(MediaStore.Images.Media.MIME_TYPE, mediaMimeType)
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
}
val externalContentUri = when {
mediaMimeType?.isMimeTypeImage() == true -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
mediaMimeType?.isMimeTypeVideo() == true -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
mediaMimeType?.isMimeTypeAudio() == true -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
else -> MediaStore.Downloads.EXTERNAL_CONTENT_URI
}
val uri = context.contentResolver.insert(externalContentUri, values)
if (uri == null) {
Toast.makeText(context, R.string.error_saving_media_file, Toast.LENGTH_LONG).show()
} else {
val source = file.inputStream().source().buffer()
context.contentResolver.openOutputStream(uri)?.sink()?.buffer()?.let { sink ->
source.use { input ->
sink.use { output ->
output.writeAll(input)
val uri = context.contentResolver.insert(externalContentUri, values)
if (uri == null) {
Toast.makeText(context, R.string.error_saving_media_file, Toast.LENGTH_LONG).show()
throw IllegalStateException(context.getString(R.string.error_saving_media_file))
} else {
val source = file.inputStream().source().buffer()
context.contentResolver.openOutputStream(uri)?.sink()?.buffer()?.let { sink ->
source.use { input ->
sink.use { output ->
output.writeAll(input)
}
}
}
notificationUtils.buildDownloadFileNotification(
uri,
filename,
mediaMimeType ?: MimeTypes.OctetStream
).let { notification ->
notificationUtils.showNotificationMessage("DL", uri.hashCode(), notification)
}
}
notificationUtils.buildDownloadFileNotification(
uri,
filename,
mediaMimeType ?: MimeTypes.OctetStream
).let { notification ->
notificationUtils.showNotificationMessage("DL", uri.hashCode(), notification)
}
} else {
saveMediaLegacy(context, mediaMimeType, title, file)
}
} else {
saveMediaLegacy(context, mediaMimeType, title, file)
}
}
@Suppress("DEPRECATION")
private fun saveMediaLegacy(context: Context, mediaMimeType: String?, title: String, file: File) {
private fun saveMediaLegacy(context: Context,
mediaMimeType: String?,
title: String,
file: File) {
val state = Environment.getExternalStorageState()
if (Environment.MEDIA_MOUNTED != state) {
context.toast(context.getString(R.string.error_saving_media_file))
return
throw IllegalStateException(context.getString(R.string.error_saving_media_file))
}
GlobalScope.launch(Dispatchers.IO) {
val dest = when {
mediaMimeType?.isMimeTypeImage() == true -> Environment.DIRECTORY_PICTURES
mediaMimeType?.isMimeTypeVideo() == true -> Environment.DIRECTORY_MOVIES
mediaMimeType?.isMimeTypeAudio() == true -> Environment.DIRECTORY_MUSIC
else -> Environment.DIRECTORY_DOWNLOADS
val dest = when {
mediaMimeType?.isMimeTypeImage() == true -> Environment.DIRECTORY_PICTURES
mediaMimeType?.isMimeTypeVideo() == true -> Environment.DIRECTORY_MOVIES
mediaMimeType?.isMimeTypeAudio() == true -> Environment.DIRECTORY_MUSIC
else -> Environment.DIRECTORY_DOWNLOADS
}
val downloadDir = Environment.getExternalStoragePublicDirectory(dest)
try {
val outputFilename = if (title.substringAfterLast('.', "").isEmpty()) {
val extension = mediaMimeType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(it) }
"$title.$extension"
} else {
title
}
val downloadDir = Environment.getExternalStoragePublicDirectory(dest)
try {
val outputFilename = if (title.substringAfterLast('.', "").isEmpty()) {
val extension = mediaMimeType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(it) }
"$title.$extension"
} else {
title
}
val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename)
if (savedFile != null) {
val downloadManager = context.getSystemService<DownloadManager>()
downloadManager?.addCompletedDownload(
savedFile.name,
title,
true,
mediaMimeType ?: MimeTypes.OctetStream,
savedFile.absolutePath,
savedFile.length(),
true)
addToGallery(savedFile, mediaMimeType, context)
}
} catch (error: Throwable) {
GlobalScope.launch(Dispatchers.Main) {
context.toast(context.getString(R.string.error_saving_media_file))
}
val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename)
if (savedFile != null) {
val downloadManager = context.getSystemService<DownloadManager>()
downloadManager?.addCompletedDownload(
savedFile.name,
title,
true,
mediaMimeType ?: MimeTypes.OctetStream,
savedFile.absolutePath,
savedFile.length(),
true)
addToGallery(savedFile, mediaMimeType, context)
}
} catch (error: Throwable) {
context.toast(context.getString(R.string.error_saving_media_file))
throw error
}
}

View File

@ -112,7 +112,7 @@ fun getFileExtension(fileUri: String): String? {
val ext = filename.substring(dotPos + 1)
if (ext.isNotBlank()) {
return ext.toLowerCase(Locale.ROOT)
return ext.lowercase(Locale.ROOT)
}
}
}
@ -131,5 +131,5 @@ fun getSizeOfFiles(root: File): Int {
Timber.v("Get size of ${it.absolutePath}")
true
}
.sumBy { it.length().toInt() }
.sumOf { it.length().toInt() }
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021 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.app.core.utils
import java.util.Locale
// String.capitalize is now deprecated
fun String.safeCapitalize(locale: Locale): String {
return replaceFirstChar { char ->
if (char.isLowerCase()) {
char.titlecase(locale)
} else {
char.toString()
}
}
}

View File

@ -33,11 +33,12 @@ import im.vector.app.features.call.utils.awaitCreateOffer
import im.vector.app.features.call.utils.awaitSetLocalDescription
import im.vector.app.features.call.utils.awaitSetRemoteDescription
import im.vector.app.features.call.utils.mapToCallCandidate
import im.vector.app.features.session.coroutineScope
import io.reactivex.disposables.Disposable
import io.reactivex.subjects.PublishSubject
import io.reactivex.subjects.ReplaySubject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -103,6 +104,9 @@ class WebRtcCall(val mxCall: MxCall,
private val listeners = CopyOnWriteArrayList<Listener>()
private val sessionScope: CoroutineScope?
get() = sessionProvider.get()?.coroutineScope
fun addListener(listener: Listener) {
listeners.add(listener)
}
@ -191,7 +195,7 @@ class WebRtcCall(val mxCall: MxCall,
fun onIceCandidate(iceCandidate: IceCandidate) = iceCandidateSource.onNext(iceCandidate)
fun onRenegotiationNeeded(restartIce: Boolean) {
GlobalScope.launch(dispatcher) {
sessionScope?.launch(dispatcher) {
if (mxCall.state != CallState.CreateOffer && mxCall.opponentVersion == 0) {
Timber.v("Opponent does not support renegotiation: ignoring onRenegotiationNeeded event")
return@launch
@ -262,7 +266,7 @@ class WebRtcCall(val mxCall: MxCall,
localSurfaceRenderers.addIfNeeded(localViewRenderer)
remoteSurfaceRenderers.addIfNeeded(remoteViewRenderer)
GlobalScope.launch(dispatcher) {
sessionScope?.launch(dispatcher) {
when (mode) {
VectorCallActivity.INCOMING_ACCEPT -> {
internalAcceptIncomingCall()
@ -283,7 +287,7 @@ class WebRtcCall(val mxCall: MxCall,
}
fun acceptIncomingCall() {
GlobalScope.launch {
sessionScope?.launch {
Timber.v("## VOIP acceptIncomingCall from state ${mxCall.state}")
if (mxCall.state == CallState.LocalRinging) {
internalAcceptIncomingCall()
@ -564,7 +568,7 @@ class WebRtcCall(val mxCall: MxCall,
}
fun updateRemoteOnHold(onHold: Boolean) {
GlobalScope.launch(dispatcher) {
sessionScope?.launch(dispatcher) {
if (remoteOnHold == onHold) return@launch
val direction: RtpTransceiver.RtpTransceiverDirection
if (onHold) {
@ -688,7 +692,7 @@ class WebRtcCall(val mxCall: MxCall,
}
fun onAddStream(stream: MediaStream) {
GlobalScope.launch(dispatcher) {
sessionScope?.launch(dispatcher) {
// reportError("Weird-looking stream: " + stream);
if (stream.audioTracks.size > 1 || stream.videoTracks.size > 1) {
Timber.e("## VOIP StreamObserver weird looking stream: $stream")
@ -712,7 +716,7 @@ class WebRtcCall(val mxCall: MxCall,
}
fun onRemoveStream() {
GlobalScope.launch(dispatcher) {
sessionScope?.launch(dispatcher) {
remoteSurfaceRenderers
.mapNotNull { it.get() }
.forEach { remoteVideoTrack?.removeSink(it) }
@ -734,7 +738,7 @@ class WebRtcCall(val mxCall: MxCall,
}
val wasRinging = mxCall.state is CallState.LocalRinging
mxCall.state = CallState.Terminated
GlobalScope.launch(dispatcher) {
sessionScope?.launch(dispatcher) {
release()
}
onCallEnded(callId)
@ -750,7 +754,7 @@ class WebRtcCall(val mxCall: MxCall,
// Call listener
fun onCallIceCandidateReceived(iceCandidatesContent: CallCandidatesContent) {
GlobalScope.launch(dispatcher) {
sessionScope?.launch(dispatcher) {
iceCandidatesContent.candidates.forEach {
if (it.sdpMid.isNullOrEmpty() || it.candidate.isNullOrEmpty()) {
return@forEach
@ -763,7 +767,7 @@ class WebRtcCall(val mxCall: MxCall,
}
fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
GlobalScope.launch(dispatcher) {
sessionScope?.launch(dispatcher) {
Timber.v("## VOIP onCallAnswerReceived ${callAnswerContent.callId}")
val sdp = SessionDescription(SessionDescription.Type.ANSWER, callAnswerContent.answer.sdp)
try {
@ -779,7 +783,7 @@ class WebRtcCall(val mxCall: MxCall,
}
fun onCallNegotiateReceived(callNegotiateContent: CallNegotiateContent) {
GlobalScope.launch(dispatcher) {
sessionScope?.launch(dispatcher) {
val description = callNegotiateContent.description
val type = description?.type
val sdpText = description?.sdp

View File

@ -18,8 +18,8 @@ package im.vector.app.features.crypto.keys
import android.content.Context
import android.net.Uri
import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCallback
@ -33,7 +33,7 @@ class KeysExporter(private val session: Session) {
* Export keys and return the file path with the callback
*/
fun export(context: Context, password: String, uri: Uri, callback: MatrixCallback<Boolean>) {
GlobalScope.launch(Dispatchers.Main) {
session.coroutineScope.launch(Dispatchers.Main) {
runCatching {
withContext(Dispatchers.IO) {
val data = awaitCallback<ByteArray> { session.cryptoService().exportRoomKeys(password, it) }

View File

@ -20,8 +20,8 @@ import android.content.Context
import android.net.Uri
import im.vector.app.core.intent.getMimeTypeFromUri
import im.vector.app.core.resources.openResource
import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCallback
@ -41,7 +41,7 @@ class KeysImporter(private val session: Session) {
mimetype: String?,
password: String,
callback: MatrixCallback<ImportRoomKeysResult>) {
GlobalScope.launch(Dispatchers.Main) {
session.coroutineScope.launch(Dispatchers.Main) {
runCatching {
withContext(Dispatchers.IO) {
val resource = openResource(context, uri, mimetype ?: getMimeTypeFromUri(context, uri))

View File

@ -25,6 +25,7 @@ import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import arrow.core.Try
import com.google.android.material.bottomsheet.BottomSheetDialog
import im.vector.app.R
@ -37,7 +38,6 @@ import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.FragmentKeysBackupSetupStep3Binding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.IOException
@ -163,7 +163,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment<Fr
}
private fun exportRecoveryKeyToFile(uri: Uri, data: String) {
GlobalScope.launch(Dispatchers.Main) {
lifecycleScope.launch(Dispatchers.Main) {
Try {
withContext(Dispatchers.IO) {
requireContext().contentResolver.openOutputStream(uri)

View File

@ -24,6 +24,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.R
@ -35,7 +36,6 @@ import im.vector.app.core.utils.toast
import im.vector.app.databinding.FragmentBootstrapSaveKeyBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -80,7 +80,7 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
private val saveStartForActivityResult = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) {
val uri = activityResult.data?.data ?: return@registerStartForActivityResult
GlobalScope.launch(Dispatchers.IO) {
lifecycleScope.launch(Dispatchers.IO) {
try {
sharedViewModel.handle(BootstrapActions.SaveKeyToUri(requireContext().contentResolver!!.openOutputStream(uri)!!))
} catch (failure: Throwable) {

View File

@ -33,7 +33,6 @@ import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.Session
@ -427,7 +426,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
}
private fun tentativeRestoreBackup(res: Map<String, String>?) {
GlobalScope.launch(Dispatchers.IO) {
viewModelScope.launch(Dispatchers.IO) {
try {
val secret = res?.get(KEYBACKUP_SECRET_SSSS_NAME) ?: return@launch Unit.also {
Timber.v("## Keybackup secret not restored from SSSS")

View File

@ -27,9 +27,9 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorPreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.auth.UIABaseAuth
@ -184,7 +184,7 @@ class HomeActivityViewModel @AssistedInject constructor(
private fun maybeBootstrapCrossSigningAfterInitialSync() {
// We do not use the viewModel context because we do not want to tie this action to activity view model
GlobalScope.launch(Dispatchers.IO) {
activeSessionHolder.getSafeActiveSession()?.coroutineScope?.launch(Dispatchers.IO) {
val session = activeSessionHolder.getSafeActiveSession() ?: return@launch
tryOrNull("## MaybeBootstrapCrossSigning: Failed to download keys") {

View File

@ -1745,20 +1745,19 @@ class RoomDetailFragment @Inject constructor(
session.coroutineScope.launch {
val result = runCatching { session.fileService().downloadFile(messageContent = action.messageContent) }
if (!isAdded) return@launch
result.fold(
{
saveMedia(
context = requireContext(),
file = it,
title = action.messageContent.body,
mediaMimeType = action.messageContent.mimeType ?: getMimeTypeFromUri(requireContext(), it.toUri()),
notificationUtils = notificationUtils
)
},
{
result.mapCatching {
saveMedia(
context = requireContext(),
file = it,
title = action.messageContent.body,
mediaMimeType = action.messageContent.mimeType ?: getMimeTypeFromUri(requireContext(), it.toUri()),
notificationUtils = notificationUtils
)
}
.onFailure {
if (!isAdded) return@onFailure
showErrorInSnackbar(it)
}
)
}
}

View File

@ -449,7 +449,7 @@ class RoomDetailViewModel @AssistedInject constructor(
widgetSessionId = widgetSessionId.substring(0, 7)
}
val roomId: String = room.roomId
val confId = roomId.substring(1, roomId.indexOf(":") - 1) + widgetSessionId.toLowerCase(VectorLocale.applicationLocale)
val confId = roomId.substring(1, roomId.indexOf(":") - 1) + widgetSessionId.lowercase(VectorLocale.applicationLocale)
val preferredJitsiDomain = tryOrNull {
rawService.getElementWellknown(session.myUserId)

View File

@ -50,7 +50,7 @@ class MatrixItemColorProvider @Inject constructor(
fun getColorFromUserId(userId: String?): Int {
var hash = 0
userId?.toList()?.map { chr -> hash = (hash shl 5) - hash + chr.toInt() }
userId?.toList()?.map { chr -> hash = (hash shl 5) - hash + chr.code }
return when (abs(hash) % 8) {
1 -> R.color.riotx_username_2
@ -66,7 +66,7 @@ class MatrixItemColorProvider @Inject constructor(
@ColorRes
private fun getColorFromRoomId(roomId: String?): Int {
return when ((roomId?.toList()?.sumBy { it.toInt() } ?: 0) % 3) {
return when ((roomId?.toList()?.sumOf { it.code } ?: 0) % 3) {
1 -> R.color.riotx_avatar_fill_2
2 -> R.color.riotx_avatar_fill_3
else -> R.color.riotx_avatar_fill_1

View File

@ -18,6 +18,7 @@ package im.vector.app.features.media
import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.resources.StringProvider
import kotlinx.coroutines.CoroutineScope
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@ -30,24 +31,31 @@ class AttachmentProviderFactory @Inject constructor(
private val session: Session
) {
fun createProvider(attachments: List<TimelineEvent>): RoomEventsAttachmentProvider {
fun createProvider(attachments: List<TimelineEvent>,
coroutineScope: CoroutineScope
): RoomEventsAttachmentProvider {
return RoomEventsAttachmentProvider(
attachments,
imageContentRenderer,
vectorDateFormatter,
session.fileService(),
stringProvider
attachments = attachments,
imageContentRenderer = imageContentRenderer,
dateFormatter = vectorDateFormatter,
fileService = session.fileService(),
coroutineScope = coroutineScope,
stringProvider = stringProvider
)
}
fun createProvider(attachments: List<AttachmentData>, room: Room?): DataAttachmentRoomProvider {
fun createProvider(attachments: List<AttachmentData>,
room: Room?,
coroutineScope: CoroutineScope
): DataAttachmentRoomProvider {
return DataAttachmentRoomProvider(
attachments,
room,
imageContentRenderer,
vectorDateFormatter,
session.fileService(),
stringProvider
attachments = attachments,
room = room,
imageContentRenderer = imageContentRenderer,
dateFormatter = vectorDateFormatter,
fileService = session.fileService(),
coroutineScope = coroutineScope,
stringProvider = stringProvider
)
}
}

View File

@ -31,8 +31,8 @@ import im.vector.lib.attachmentviewer.AttachmentInfo
import im.vector.lib.attachmentviewer.AttachmentSourceProvider
import im.vector.lib.attachmentviewer.ImageLoaderTarget
import im.vector.lib.attachmentviewer.VideoLoaderTarget
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.session.events.model.isVideoMessage
@ -44,6 +44,7 @@ abstract class BaseAttachmentProvider<Type>(
private val attachments: List<Type>,
private val imageContentRenderer: ImageContentRenderer,
protected val fileService: FileService,
private val coroutineScope: CoroutineScope,
private val dateFormatter: VectorDateFormatter,
private val stringProvider: StringProvider
) : AttachmentSourceProvider {
@ -155,7 +156,7 @@ abstract class BaseAttachmentProvider<Type>(
target.onVideoURLReady(info.uid, data.url)
} else {
target.onVideoFileLoading(info.uid)
GlobalScope.launch(Dispatchers.Main) {
coroutineScope.launch(Dispatchers.IO) {
val result = runCatching {
fileService.downloadFile(
fileName = data.filename,
@ -178,5 +179,5 @@ abstract class BaseAttachmentProvider<Type>(
// TODO("Not yet implemented")
}
abstract fun getFileForSharing(position: Int, callback: ((File?) -> Unit))
abstract suspend fun getFileForSharing(position: Int): File?
}

View File

@ -19,10 +19,8 @@ package im.vector.app.features.media
import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.resources.StringProvider
import im.vector.lib.attachmentviewer.AttachmentInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.CoroutineScope
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@ -35,8 +33,16 @@ class DataAttachmentRoomProvider(
imageContentRenderer: ImageContentRenderer,
dateFormatter: VectorDateFormatter,
fileService: FileService,
coroutineScope: CoroutineScope,
stringProvider: StringProvider
) : BaseAttachmentProvider<AttachmentData>(attachments, imageContentRenderer, fileService, dateFormatter, stringProvider) {
) : BaseAttachmentProvider<AttachmentData>(
attachments = attachments,
imageContentRenderer = imageContentRenderer,
fileService = fileService,
coroutineScope = coroutineScope,
dateFormatter = dateFormatter,
stringProvider = stringProvider
) {
override fun getAttachmentInfoAt(position: Int): AttachmentInfo {
return getItem(position).let {
@ -78,20 +84,17 @@ class DataAttachmentRoomProvider(
return room?.getTimeLineEvent(item.eventId)
}
override fun getFileForSharing(position: Int, callback: (File?) -> Unit) {
val item = getItem(position)
GlobalScope.launch {
val result = runCatching {
fileService.downloadFile(
fileName = item.filename,
mimeType = item.mimeType,
url = item.url,
elementToDecrypt = item.elementToDecrypt
)
}
withContext(Dispatchers.Main) {
callback(result.getOrNull())
}
}
override suspend fun getFileForSharing(position: Int): File? {
return getItem(position)
.let { item ->
tryOrNull {
fileService.downloadFile(
fileName = item.filename,
mimeType = item.mimeType,
url = item.url,
elementToDecrypt = item.elementToDecrypt
)
}
}
}
}

View File

@ -19,10 +19,8 @@ package im.vector.app.features.media
import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.resources.StringProvider
import im.vector.lib.attachmentviewer.AttachmentInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.CoroutineScope
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
@ -41,8 +39,16 @@ class RoomEventsAttachmentProvider(
imageContentRenderer: ImageContentRenderer,
dateFormatter: VectorDateFormatter,
fileService: FileService,
coroutineScope: CoroutineScope,
stringProvider: StringProvider
) : BaseAttachmentProvider<TimelineEvent>(attachments, imageContentRenderer, fileService, dateFormatter, stringProvider) {
) : BaseAttachmentProvider<TimelineEvent>(
attachments = attachments,
imageContentRenderer = imageContentRenderer,
fileService = fileService,
coroutineScope = coroutineScope,
dateFormatter = dateFormatter,
stringProvider = stringProvider
) {
override fun getAttachmentInfoAt(position: Int): AttachmentInfo {
return getItem(position).let {
@ -121,24 +127,19 @@ class RoomEventsAttachmentProvider(
return getItem(position)
}
override fun getFileForSharing(position: Int, callback: (File?) -> Unit) {
getItem(position).let { timelineEvent ->
val messageContent = timelineEvent.root.getClearContent().toModel<MessageContent>()
as? MessageWithAttachmentContent
?: return@let
GlobalScope.launch {
val result = runCatching {
fileService.downloadFile(
fileName = messageContent.body,
mimeType = messageContent.mimeType,
url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt())
override suspend fun getFileForSharing(position: Int): File? {
return getItem(position)
.let { timelineEvent ->
timelineEvent.root.getClearContent().toModel<MessageContent>() as? MessageWithAttachmentContent
}
withContext(Dispatchers.Main) {
callback(result.getOrNull())
?.let { messageContent ->
tryOrNull {
fileService.downloadFile(
fileName = messageContent.body,
mimeType = messageContent.mimeType,
url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt())
}
}
}
}
}
}

View File

@ -28,7 +28,7 @@ import androidx.core.transition.addListener
import androidx.core.view.ViewCompat
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.transition.Transition
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
@ -42,6 +42,9 @@ import im.vector.app.features.themes.ActivityOtherThemes
import im.vector.app.features.themes.ThemeUtils
import im.vector.lib.attachmentviewer.AttachmentCommands
import im.vector.lib.attachmentviewer.AttachmentViewerActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import timber.log.Timber
import javax.inject.Inject
@ -119,11 +122,11 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmen
val inMemoryData = intent.getParcelableArrayListExtra<AttachmentData>(EXTRA_IN_MEMORY_DATA)
val sourceProvider = if (inMemoryData != null) {
initialIndex = inMemoryData.indexOfFirst { it.eventId == args.eventId }.coerceAtLeast(0)
dataSourceFactory.createProvider(inMemoryData, room)
dataSourceFactory.createProvider(inMemoryData, room, lifecycleScope)
} else {
val events = room?.getAttachmentMessages().orEmpty()
initialIndex = events.indexOfFirst { it.eventId == args.eventId }.coerceAtLeast(0)
dataSourceFactory.createProvider(events)
dataSourceFactory.createProvider(events, lifecycleScope)
}
sourceProvider.interactionListener = this
setSourceProvider(sourceProvider)
@ -264,9 +267,15 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmen
}
override fun onShareTapped() {
currentSourceProvider?.getFileForSharing(currentPosition) { data ->
if (data != null && lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
shareMedia(this@VectorAttachmentViewerActivity, data, getMimeTypeFromUri(this@VectorAttachmentViewerActivity, data.toUri()))
lifecycleScope.launch(Dispatchers.IO) {
val file = currentSourceProvider?.getFileForSharing(currentPosition) ?: return@launch
withContext(Dispatchers.Main) {
shareMedia(
this@VectorAttachmentViewerActivity,
file,
getMimeTypeFromUri(this@VectorAttachmentViewerActivity, file.toUri())
)
}
}
}

View File

@ -25,8 +25,9 @@ import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.files.LocalFilesHelper
import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
@ -39,6 +40,9 @@ class VideoContentRenderer @Inject constructor(private val localFilesHelper: Loc
private val activeSessionHolder: ActiveSessionHolder,
private val errorFormatter: ErrorFormatter) {
private val sessionScope: CoroutineScope
get() = activeSessionHolder.getActiveSession().coroutineScope
@Parcelize
data class Data(
override val eventId: String,
@ -76,7 +80,7 @@ class VideoContentRenderer @Inject constructor(private val localFilesHelper: Loc
thumbnailView.isVisible = true
loadingView.isVisible = true
GlobalScope.launch {
sessionScope.launch {
val result = runCatching {
activeSessionHolder.getActiveSession().fileService()
.downloadFile(
@ -119,7 +123,7 @@ class VideoContentRenderer @Inject constructor(private val localFilesHelper: Loc
thumbnailView.isVisible = true
loadingView.isVisible = true
GlobalScope.launch {
sessionScope.launch {
val result = runCatching {
activeSessionHolder.getActiveSession().fileService()
.downloadFile(

View File

@ -60,6 +60,7 @@ class PinLocker @Inject constructor(
return liveState
}
@Suppress("EXPERIMENTAL_API_USAGE")
private fun computeState() {
GlobalScope.launch {
val state = if (shouldBeLocked && pinCodeStore.hasEncodedPin()) {

View File

@ -88,6 +88,7 @@ class VectorFileLogger @Inject constructor(
}
}
@Suppress("EXPERIMENTAL_API_USAGE")
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
fileHandler ?: return
GlobalScope.launch(Dispatchers.IO) {

View File

@ -19,6 +19,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.EmojiChooserFragmentBinding
@ -51,6 +52,8 @@ class EmojiChooserFragment @Inject constructor(
}
}
override fun getCoroutineScope() = lifecycleScope
override fun firstVisibleSectionChange(section: Int) {
viewModel.setCurrentSection(section)
}

View File

@ -31,8 +31,8 @@ import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
import im.vector.app.R
import im.vector.app.features.reactions.data.EmojiDataSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.math.abs
@ -221,7 +221,7 @@ class EmojiRecyclerAdapter @Inject constructor(
}
override fun getItemCount() = dataSource.rawData.categories
.sumBy { emojiCategory -> 1 /* Section */ + emojiCategory.emojis.size }
.sumOf { emojiCategory -> 1 /* Section */ + emojiCategory.emojis.size }
abstract class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(s: String?)
@ -278,6 +278,7 @@ class EmojiRecyclerAdapter @Inject constructor(
}
interface InteractionListener {
fun getCoroutineScope(): CoroutineScope
fun firstVisibleSectionChange(section: Int)
}
@ -323,11 +324,11 @@ class EmojiRecyclerAdapter @Inject constructor(
// Log.i("SCROLL SPEED","scroll speed $dy")
isFastScroll = abs(dy) > 50
val visible = (recyclerView.layoutManager as GridLayoutManager).findFirstCompletelyVisibleItemPosition()
GlobalScope.launch {
interactionListener?.getCoroutineScope()?.launch {
val section = getSectionForAbsoluteIndex(visible)
if (section != currentFirstVisibleSection) {
currentFirstVisibleSection = section
GlobalScope.launch(Dispatchers.Main) {
interactionListener?.getCoroutineScope()?.launch(Dispatchers.Main) {
interactionListener?.firstVisibleSectionChange(currentFirstVisibleSection)
}
}

View File

@ -21,6 +21,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
@ -36,6 +37,7 @@ import im.vector.app.databinding.FragmentRoomUploadsBinding
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.roomprofile.RoomProfileArgs
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject
@ -76,13 +78,21 @@ class RoomUploadsFragment @Inject constructor(
shareMedia(requireContext(), it.file, getMimeTypeFromUri(requireContext(), it.file.toUri()))
}
is RoomUploadsViewEvents.FileReadyForSaving -> {
saveMedia(
context = requireContext(),
file = it.file,
title = it.title,
mediaMimeType = getMimeTypeFromUri(requireContext(), it.file.toUri()),
notificationUtils = notificationUtils
)
lifecycleScope.launch {
runCatching {
saveMedia(
context = requireContext(),
file = it.file,
title = it.title,
mediaMimeType = getMimeTypeFromUri(requireContext(), it.file.toUri()),
notificationUtils = notificationUtils
)
}.onFailure { failure ->
if (!isAdded) return@onFailure
showErrorInSnackbar(failure)
}
}
Unit
}
is RoomUploadsViewEvents.Failure -> showFailure(it.throwable)
}.exhaustive

View File

@ -181,7 +181,7 @@ object VectorLocale {
}
}
// sort by human display names
.sortedBy { localeToLocalisedString(it).toLowerCase(it) }
.sortedBy { localeToLocalisedString(it).lowercase(it) }
supportedLocales.clear()
supportedLocales.addAll(list)

View File

@ -52,7 +52,6 @@ import im.vector.app.features.MainActivityArgs
import im.vector.app.features.workers.signout.SignOutUiWorker
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.failure.isInvalidPassword
@ -224,7 +223,7 @@ class VectorSettingsGeneralFragment @Inject constructor(
it.summary = TextUtils.formatFileSize(requireContext(), size.toLong())
it.onPreferenceClickListener = Preference.OnPreferenceClickListener {
GlobalScope.launch(Dispatchers.Main) {
lifecycleScope.launch(Dispatchers.Main) {
// On UI Thread
displayLoadingView()

View File

@ -24,6 +24,7 @@ import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.epoxy.noResultItem
import im.vector.app.core.epoxy.profiles.profileSectionItem
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.safeCapitalize
import im.vector.app.features.settings.VectorLocale
import im.vector.app.features.settings.VectorPreferences
import java.util.Locale
@ -46,7 +47,7 @@ class LocalePickerController @Inject constructor(
}
localeItem {
id(data.currentLocale.toString())
title(VectorLocale.localeToLocalisedString(data.currentLocale).capitalize(data.currentLocale))
title(VectorLocale.localeToLocalisedString(data.currentLocale).safeCapitalize(data.currentLocale))
if (vectorPreferences.developerMode()) {
subtitle(VectorLocale.localeToLocalisedStringInfo(data.currentLocale))
}
@ -75,7 +76,7 @@ class LocalePickerController @Inject constructor(
.forEach {
localeItem {
id(it.toString())
title(VectorLocale.localeToLocalisedString(it).capitalize(it))
title(VectorLocale.localeToLocalisedString(it).safeCapitalize(it))
if (vectorPreferences.developerMode()) {
subtitle(VectorLocale.localeToLocalisedStringInfo(it))
}

View File

@ -36,11 +36,11 @@ import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import im.vector.app.features.rageshake.BugReporter
import im.vector.app.features.rageshake.ReportType
import im.vector.app.features.roomprofile.RoomProfileActivity
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.spaces.manage.ManageType
import im.vector.app.features.spaces.manage.SpaceManageActivity
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.session.events.model.EventType
@ -140,7 +140,7 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment<BottomS
AlertDialog.Builder(requireContext())
.setMessage(getString(R.string.space_leave_prompt_msg))
.setPositiveButton(R.string.leave) { _, _ ->
GlobalScope.launch {
session.coroutineScope.launch {
try {
session.getRoom(spaceArgs.spaceId)?.leave(null)
} catch (failure: Throwable) {

View File

@ -22,7 +22,7 @@ import dagger.assisted.AssistedInject
import dagger.assisted.AssistedFactory
import im.vector.app.R
import im.vector.app.core.resources.StringProvider
import kotlinx.coroutines.GlobalScope
import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.query.QueryStringValue
@ -465,7 +465,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
}
private fun launchWidgetAPIAction(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict, block: suspend () -> Unit): Job {
return GlobalScope.launch {
// We should probably use a scope tight to the lifecycle here...
return session.coroutineScope.launch {
kotlin.runCatching {
block()
}.fold(

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<array name="com_google_android_gms_fonts_certs">
<item>@array/com_google_android_gms_fonts_certs_dev</item>
<item>@array/com_google_android_gms_fonts_certs_prod</item>
</array>
<string-array name="com_google_android_gms_fonts_certs_dev">
<item>
<item tools:ignore="Typos">
MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
</item>
</string-array>
<string-array name="com_google_android_gms_fonts_certs_prod">
<item>
<item tools:ignore="Typos">
MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
</item>
</string-array>
</resources>
</resources>