Skip to content

Commit 426e5de

Browse files
committed
Merge branch 'release/25.06.3'
2 parents 3818b16 + cbf97b2 commit 426e5de

File tree

422 files changed

+4045
-1842
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

422 files changed

+4045
-1842
lines changed

CHANGES.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
Changes in Element X v25.06.2
2+
=============================
3+
4+
## What's Changed
5+
### 🐛 Bugfixes
6+
* Fix crash when using Element Call on API <= 30 by @jmartinesp in https://github.com/element-hq/element-x-android/pull/4847
7+
* Element Call: add delay before selecting the default audio device by @jmartinesp in https://github.com/element-hq/element-x-android/pull/4854
8+
* Fix for message composer losing focus in Compose 1.8.0 by @jmartinesp in https://github.com/element-hq/element-x-android/pull/4853
9+
### Dependency upgrades
10+
* chore(deps): update plugin dependencycheck to v12.1.2 by @renovate in https://github.com/element-hq/element-x-android/pull/4840
11+
* deps (matrix rust sdk) : bump version to 25.06.10 by @ganfra in https://github.com/element-hq/element-x-android/pull/4855
12+
### Others
13+
* feat: Support matrix: links by @ShadowRZ in https://github.com/element-hq/element-x-android/pull/4839
14+
15+
16+
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v25.06.1...v25.06.2
17+
118
## What's Changed
219
### ✨ Features
320
* Enable support for Android Auto. by @bmarty in https://github.com/element-hq/element-x-android/pull/4818
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
3+
<string name="banner_migrate_to_native_sliding_sync_action">"Keluar &amp; Tingkatkan"</string>
4+
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Homeserver Anda tidak lagi mendukung protokol lama. Silakan keluar dan masuk kembali untuk terus menggunakan aplikasi."</string>
5+
</resources>

appnav/src/test/kotlin/io/element/android/appnav/di/MatrixSessionCacheTest.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
1616
import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
1717
import io.element.android.services.appnavstate.test.FakeAppForegroundStateService
1818
import io.element.android.tests.testutils.testCoroutineDispatchers
19-
import kotlinx.coroutines.ExperimentalCoroutinesApi
2019
import kotlinx.coroutines.test.TestScope
2120
import kotlinx.coroutines.test.runTest
2221
import org.junit.Test
@@ -29,7 +28,6 @@ class MatrixSessionCacheTest {
2928
assertThat(matrixSessionCache.getOrNull(A_SESSION_ID)).isNull()
3029
}
3130

32-
@OptIn(ExperimentalCoroutinesApi::class)
3331
@Test
3432
fun `test getSyncOrchestratorOrNull`() = runTest {
3533
val fakeAuthenticationService = FakeMatrixAuthenticationService()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Main changes in this version: add support for tombstoned rooms, improve notification reliability.
2+
Full changelog: https://github.com/element-hq/element-x-android/releases

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import io.element.android.services.appnavstate.api.ActiveRoomsHolder
4444
import io.element.android.services.appnavstate.api.AppForegroundStateService
4545
import io.element.android.services.toolbox.api.systemclock.SystemClock
4646
import kotlinx.coroutines.CoroutineScope
47+
import kotlinx.coroutines.delay
4748
import kotlinx.coroutines.flow.launchIn
4849
import kotlinx.coroutines.flow.onEach
4950
import kotlinx.coroutines.launch
@@ -52,6 +53,7 @@ import kotlinx.serialization.json.jsonObject
5253
import kotlinx.serialization.json.jsonPrimitive
5354
import timber.log.Timber
5455
import java.util.UUID
56+
import kotlin.time.Duration.Companion.seconds
5557

5658
class CallScreenPresenter @AssistedInject constructor(
5759
@Assisted private val callType: CallType,
@@ -165,6 +167,13 @@ class CallScreenPresenter @AssistedInject constructor(
165167
// If the call was joined, we need to hang up first. Then the UI will be dismissed automatically.
166168
sendHangupMessage(widgetId, interceptor)
167169
isJoinedCall = false
170+
171+
coroutineScope.launch {
172+
// Wait for a couple of seconds to receive the hangup message
173+
// If we don't get it in time, we close the screen anyway
174+
delay(2.seconds)
175+
close(callWidgetDriver.value, navigator)
176+
}
168177
} else {
169178
coroutineScope.launch {
170179
close(callWidgetDriver.value, navigator)

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenView.kt

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import io.element.android.features.call.impl.pip.PictureInPictureEvents
3838
import io.element.android.features.call.impl.pip.PictureInPictureState
3939
import io.element.android.features.call.impl.pip.PictureInPictureStateProvider
4040
import io.element.android.features.call.impl.pip.aPictureInPictureState
41+
import io.element.android.features.call.impl.utils.InvalidAudioDeviceReason
4142
import io.element.android.features.call.impl.utils.WebViewAudioManager
4243
import io.element.android.features.call.impl.utils.WebViewPipController
4344
import io.element.android.features.call.impl.utils.WebViewWidgetMessageInterceptor
@@ -105,6 +106,14 @@ internal fun CallScreenView(
105106
} else {
106107
var webViewAudioManager by remember { mutableStateOf<WebViewAudioManager?>(null) }
107108
val coroutineScope = rememberCoroutineScope()
109+
110+
var invalidAudioDeviceReason by remember { mutableStateOf<InvalidAudioDeviceReason?>(null) }
111+
invalidAudioDeviceReason?.let {
112+
InvalidAudioDeviceDialog(invalidAudioDeviceReason = it) {
113+
invalidAudioDeviceReason = null
114+
}
115+
}
116+
108117
CallWebView(
109118
modifier = Modifier
110119
.padding(padding)
@@ -130,7 +139,11 @@ internal fun CallScreenView(
130139
},
131140
onError = { state.eventSink(CallScreenEvents.OnWebViewError(it)) },
132141
)
133-
webViewAudioManager = WebViewAudioManager(webView, coroutineScope)
142+
webViewAudioManager = WebViewAudioManager(
143+
webView = webView,
144+
coroutineScope = coroutineScope,
145+
onInvalidAudioDeviceAdded = { invalidAudioDeviceReason = it },
146+
)
134147
state.eventSink(CallScreenEvents.SetupMessageChannels(interceptor))
135148
val pipController = WebViewPipController(webView)
136149
pipState.eventSink(PictureInPictureEvents.SetPipController(pipController))
@@ -157,6 +170,21 @@ internal fun CallScreenView(
157170
}
158171
}
159172

173+
@Composable
174+
private fun InvalidAudioDeviceDialog(
175+
invalidAudioDeviceReason: InvalidAudioDeviceReason,
176+
onDismiss: () -> Unit,
177+
) {
178+
ErrorDialog(
179+
content = when (invalidAudioDeviceReason) {
180+
InvalidAudioDeviceReason.BT_AUDIO_DEVICE_DISABLED -> {
181+
stringResource(R.string.call_invalid_audio_device_bluetooth_devices_disabled)
182+
}
183+
},
184+
onSubmit = onDismiss,
185+
)
186+
}
187+
160188
@Composable
161189
private fun CallWebView(
162190
url: AsyncData<String>,
@@ -277,3 +305,9 @@ internal fun CallScreenPipViewPreview(
277305
requestPermissions = { _, _ -> },
278306
)
279307
}
308+
309+
@PreviewsDayNight
310+
@Composable
311+
internal fun InvalidAudioDeviceDialogPreview() = ElementPreview {
312+
InvalidAudioDeviceDialog(invalidAudioDeviceReason = InvalidAudioDeviceReason.BT_AUDIO_DEVICE_DISABLED) {}
313+
}

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,22 @@ import kotlin.time.Duration.Companion.milliseconds
4040
class WebViewAudioManager(
4141
private val webView: WebView,
4242
private val coroutineScope: CoroutineScope,
43+
private val onInvalidAudioDeviceAdded: (InvalidAudioDeviceReason) -> Unit,
4344
) {
44-
// The list of device types that are considered as communication devices, sorted by likelihood of it being used for communication.
45+
/**
46+
* Whether to disable bluetooth audio devices. This must be done on Android versions lower than Android 12,
47+
* since the WebView approach breaks when using the legacy Bluetooth audio APIs.
48+
*/
49+
private val disableBluetoothAudioDevices = Build.VERSION.SDK_INT < Build.VERSION_CODES.S
50+
51+
/**
52+
* This flag indicates whether the WebView audio is enabled or not. By default, it is enabled.
53+
*/
54+
private val isWebViewAudioEnabled = AtomicBoolean(true)
55+
56+
/**
57+
* The list of device types that are considered as communication devices, sorted by likelihood of it being used for communication.
58+
*/
4559
private val wantedDeviceTypes = listOf(
4660
// Paired bluetooth device with microphone
4761
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
@@ -60,6 +74,10 @@ class WebViewAudioManager(
6074

6175
private val audioManager = webView.context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
6276

77+
/**
78+
* This wake lock is used to turn off the screen when the proximity sensor is triggered during a call,
79+
* if the selected audio device is the built-in earpiece.
80+
*/
6381
private val proximitySensorWakeLock by lazy {
6482
webView.context.getSystemService<PowerManager>()
6583
?.takeIf { it.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) }
@@ -296,12 +314,13 @@ class WebViewAudioManager(
296314
* @param availableDevices The list of available audio devices to select from. If not provided, it will use the current list of audio devices.
297315
*/
298316
private fun selectDefaultAudioDevice(availableDevices: List<AudioDeviceInfo> = listAudioDevices()) {
299-
val selectedDevice = availableDevices.minByOrNull {
300-
wantedDeviceTypes.indexOf(it.type).let { index ->
301-
// If the device type is not in the wantedDeviceTypes list, we give it a low priority
302-
if (index == -1) Int.MAX_VALUE else index
317+
val selectedDevice = availableDevices
318+
.minByOrNull {
319+
wantedDeviceTypes.indexOf(it.type).let { index ->
320+
// If the device type is not in the wantedDeviceTypes list, we give it a low priority
321+
if (index == -1) Int.MAX_VALUE else index
322+
}
303323
}
304-
}
305324

306325
expectedNewCommunicationDeviceId = selectedDevice?.id
307326
audioManager.selectAudioDevice(selectedDevice)
@@ -361,6 +380,13 @@ class WebViewAudioManager(
361380
// On Android 11 and lower, we don't have the concept of communication devices
362381
// We have to call the right methods based on the device type
363382
if (device != null) {
383+
if (device.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO && disableBluetoothAudioDevices) {
384+
Timber.w("Bluetooth audio devices are disabled on this Android version")
385+
setAudioEnabled(false)
386+
onInvalidAudioDeviceAdded(InvalidAudioDeviceReason.BT_AUDIO_DEVICE_DISABLED)
387+
return
388+
}
389+
setAudioEnabled(true)
364390
isSpeakerphoneOn = device.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
365391
isBluetoothScoOn = device.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO
366392
} else {
@@ -380,6 +406,19 @@ class WebViewAudioManager(
380406
proximitySensorWakeLock?.release()
381407
}
382408
}
409+
410+
/**
411+
* Sets whether the audio is enabled for Element Call in the WebView.
412+
* It will only perform the change if the audio state has changed.
413+
*/
414+
private fun setAudioEnabled(enabled: Boolean) {
415+
coroutineScope.launch(Dispatchers.Main) {
416+
Timber.d("Setting audio enabled in Element Call: $enabled")
417+
if (isWebViewAudioEnabled.getAndSet(enabled) != enabled) {
418+
webView.evaluateJavascript("controls.setAudioEnabled($enabled);", null)
419+
}
420+
}
421+
}
383422
}
384423

385424
/**
@@ -434,6 +473,10 @@ private fun isBuiltIn(type: Int): Boolean = when (type) {
434473
else -> false
435474
}
436475

476+
enum class InvalidAudioDeviceReason {
477+
BT_AUDIO_DEVICE_DISABLED,
478+
}
479+
437480
/**
438481
* This class is used to serialize the audio device information to JSON.
439482
*/

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewPipController.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import android.webkit.WebView
1111
import kotlin.coroutines.resume
1212
import kotlin.coroutines.suspendCoroutine
1313

14+
/**
15+
* Documentation about the `controls` command can be found here:
16+
* https://github.com/element-hq/element-call/blob/livekit/docs/controls.md#picture-in-picture
17+
*/
1418
class WebViewPipController(
1519
private val webView: WebView,
1620
) : PipController {

features/call/impl/src/main/res/values-fr/translations.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
<string name="call_foreground_service_channel_title_android">"Appel en cours"</string>
44
<string name="call_foreground_service_message_android">"Cliquez pour retourner à l’appel."</string>
55
<string name="call_foreground_service_title_android">"☎️ Appel en cours"</string>
6+
<string name="call_invalid_audio_device_bluetooth_devices_disabled">"Element Call ne prend pas en charge l’utilisation d’accessoires Bluetooth dans cette version d’Android. Sélectionnez une autre sortie audio."</string>
67
<string name="screen_incoming_call_subtitle_android">"Appel Element entrant"</string>
78
</resources>

features/call/impl/src/main/res/values-nb/translations.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
<string name="call_foreground_service_channel_title_android">"Pågående samtale"</string>
44
<string name="call_foreground_service_message_android">"Trykk for å gå tilbake til samtalen"</string>
55
<string name="call_foreground_service_title_android">"☎️ Samtale pågår"</string>
6+
<string name="call_invalid_audio_device_bluetooth_devices_disabled">"Element Call støtter ikke bruk av Bluetooth-lydenheter i denne Android-versjonen. Velg en annen lydenhet."</string>
67
<string name="screen_incoming_call_subtitle_android">"Innkommende Element-anrop"</string>
78
</resources>

0 commit comments

Comments
 (0)