Repository: element-hq/element-x-android (language: kotlin)

Issue:
EC 1:1 incoming call: still displayed in light color under certain conditions.
### Steps to reproduce

1. If EXA is running, kill it first;
2. Start EXA, then remove it from the recent tasks list (make sure EXA disables battery optimization and allows running in the background);
3. Lock the phone (the screen needs to be turned off) and wait for an incoming call, then answer the call.

### Outcome

#### What did you expect?
n/a

#### What happened instead?
![Image](https://github.com/user-attachments/assets/1e5aed5f-4db8-42d9-8d0d-5c7b3aca9876)![Image](https://github.com/user-attachments/assets/93188154-f422-46f4-93ac-d1f029941611)

At this time, the incoming call interface and EC WebUI are displayed in light color, regardless of the color setting of EXA.
**This phenomenon only occurs once since the last time EXA was started**, and each subsequent incoming call can return to normal.

### Your phone model

OnePlus MT2111

### Operating system version

Oxygen OS 15 (Android 15)

### Application version and app store

Element X for Android 25.4.3

### Homeserver

Synapse 1.128.0

### Will you send logs?

No

### Are you willing to provide a PR?

No
Incoming Call screen does not render the caller avatar
### Steps to reproduce

1. Kill the application
2. Lock the screen
3. Receive a call from someone who has set an avatar


### Outcome

#### What did you expect?

The incoming call screen (IncomingCallActivity) render the caller avatar

#### What happened instead?

The incoming call screen render an fallback initial avatar

From the log:

> java.lang.IllegalStateException: Unable to create a fetcher that supports: AvatarData(id=@user:matrix.org, name=Benoit, url=mxc://matrix.org/jRqrnjimPBREDACTED, size=IncomingCall)

So it seems that the `DefaultLoggedInImageLoaderFactory` is not correctly set in this configuration.

### Your phone model

Emu on API 35

### Operating system version

Android 15

### Application version and app store

Latest develop

### Homeserver

Any

### Will you send logs?

Yes

### Are you willing to provide a PR?

Yes


Discussion hints:
Hello @YamatoRyou , I cannot reproduce this issue on my device, nor on my emulator, both running Android 15 OS.

Do you have more details to provide?

But I have discovered this issue, which is related to this screen: #4633
> Hello [@YamatoRyou](https://github.com/YamatoRyou) , I cannot reproduce this issue on my device, nor on my emulator, both running Android 15 OS.
> 
> Do you have more details to provide?
> 
> But I have discovered this issue, which is related to this screen: [#4633](https://github.com/element-hq/element-x-android/issues/4633)

The following steps can be added to increase the chances of reproducing the problem:
4. Set the system's color scheme to light;
5. Set EXA's color scheme to dark.
6. Repeat steps 1 to 3 mentioned above.



Pull request: Render caller avatar on Incoming call screen
<!-- Please read [CONTRIBUTING.md](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md) before submitting your pull request -->
 
## Content

Ensure that the image loader is set, else the IncomingCallActivity will not be able to render the avatar.
<!-- Describe shortly what has been changed -->

Note that this is a temporary fix, we may want to revisit how we handle the Coil image loader singleton.

This PR also ensure that a background color is set else we can end up with light text on light background. 

## Motivation and context

<!-- Provide link to the corresponding issue if applicable or explain the context -->
Fixes #4633
Fixes #4629 


## Screenshots / GIFs

<!--
We have screenshot tests in the project, so attaching screenshots to a PR is not mandatory, as far as there
is a Composable Preview covering the changes. In this case, the change will appear in the file diff.
Note that all the UI composables should be covered by a Composable Preview.

Providing a video of the change is still very useful for the reviewer and for the history of the project.

You can use a table like this to show screenshots comparison.
Uncomment this markdown table below and edit the last line `|||`:
|copy screenshot of before here|copy screenshot of after here|

|Before|After|
|-|-|
|||
 -->


|Before|After|
|-|-|
|![IncomingCall-Before2](https://github.com/user-attachments/assets/09a0846b-5769-413a-ae15-c3fb310c8123)|![IncomingCall-After](https://github.com/user-attachments/assets/3d72958c-2165-4b9d-a552-a1fce2b94a71)|



## Tests

<!-- Explain how you tested your development -->

- Set the system color to light
- Set the app theme to dark
- Kill the app
- Lock the device (screen is off)
- Receive a call from a DM where other user has an avatar
- Observe that the screen now renders the caller avatar and is rendering light text on dark background. Previously it was only rendering initials avatar, with light text on light background.

## Tested devices

- [ ] Physical
- [x] Emulator
- OS version(s):

## Checklist

<!-- Depending on the Pull Request content, it can be acceptable if some of the following checkboxes stay unchecked. -->

- [ ] Changes have been tested on an Android device or Android emulator with API 24
- [ ] UI change has been tested on both light and dark themes
- [ ] Accessibility has been taken into account. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#accessibility
- [ ] Pull request is based on the develop branch
- [ ] Pull request title will be used in the release note, it clearly define what will change for the user
- [ ] Pull request includes screenshots or videos if containing UI changes
- [ ] You've made a self review of your PR


Test command: ./gradlew :features:call:impl:testDebugUnitTest --tests "io.element.android.features.call.utils.DefaultActiveCallManagerTest"

Fix patch:
diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt
index 59e19d3fe2f..8804a91f79f 100644
--- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt
+++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt
@@ -13,6 +13,8 @@ import android.os.PowerManager
 import androidx.annotation.VisibleForTesting
 import androidx.core.app.NotificationManagerCompat
 import androidx.core.content.getSystemService
+import coil3.SingletonImageLoader
+import coil3.annotation.DelicateCoilApi
 import com.squareup.anvil.annotations.ContributesBinding
 import io.element.android.appconfig.ElementCallConfig
 import io.element.android.features.call.api.CallType
@@ -23,6 +25,8 @@ import io.element.android.libraries.di.AppScope
 import io.element.android.libraries.di.ApplicationContext
 import io.element.android.libraries.di.SingleIn
 import io.element.android.libraries.matrix.api.MatrixClientProvider
+import io.element.android.libraries.matrix.api.core.SessionId
+import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder
 import io.element.android.libraries.push.api.notifications.ForegroundServiceType
 import io.element.android.libraries.push.api.notifications.NotificationIdProvider
 import io.element.android.libraries.push.api.notifications.OnMissedCallNotificationHandler
@@ -89,6 +93,7 @@ class DefaultActiveCallManager @Inject constructor(
     private val matrixClientProvider: MatrixClientProvider,
     private val defaultCurrentCallService: DefaultCurrentCallService,
     private val appForegroundStateService: AppForegroundStateService,
+    private val imageLoaderHolder: ImageLoaderHolder,
 ) : ActiveCallManager {
     private val tag = "DefaultActiveCallManager"
     private var timedOutCallJob: Job? = null
@@ -125,6 +130,7 @@ class DefaultActiveCallManager @Inject constructor(
             )
 
             timedOutCallJob = coroutineScope.launch {
+                setUpCoil(notificationData.sessionId)
                 showIncomingCallNotification(notificationData)
 
                 // Wait for the ringing call to time out
@@ -140,6 +146,13 @@ class DefaultActiveCallManager @Inject constructor(
         }
     }
 
+    @OptIn(DelicateCoilApi::class)
+    private suspend fun setUpCoil(sessionId: SessionId) {
+        val matrixClient = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: return
+        // Ensure that the image loader is set, else the IncomingCallActivity will not be able to render the caller avatar
+        SingletonImageLoader.setUnsafe(imageLoaderHolder.get(matrixClient))
+    }
+
     /**
      * Called when the incoming call timed out. It will remove the active call and remove any associated UI, adding a 'missed call' notification.
      */
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/background/OnboardingBackground.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/background/OnboardingBackground.kt
index 8ea58deb40c..b4830e0904a 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/background/OnboardingBackground.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/background/OnboardingBackground.kt
@@ -8,6 +8,7 @@
 package io.element.android.libraries.designsystem.background
 
 import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -32,13 +33,17 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
 @Suppress("ModifierMissing")
 @Composable
 fun OnboardingBackground() {
-    Box(modifier = Modifier.fillMaxSize()) {
+    Box(
+        modifier = Modifier
+            .fillMaxSize()
+            .background(ElementTheme.colors.bgCanvasDefault)
+    ) {
         val isLightTheme = ElementTheme.isLightTheme
         Canvas(
             modifier = Modifier
-            .fillMaxWidth()
-            .height(220.dp)
-            .align(Alignment.BottomCenter)
+                .fillMaxWidth()
+                .height(220.dp)
+                .align(Alignment.BottomCenter)
         ) {
             val gradientBrush = ShaderBrush(
                 LinearGradientShader(


Test patch:
diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt
index 5f7309344a4..2059e9836e4 100644
--- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt
+++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt
@@ -325,5 +325,6 @@ class DefaultActiveCallManagerTest {
         matrixClientProvider = matrixClientProvider,
         defaultCurrentCallService = DefaultCurrentCallService(),
         appForegroundStateService = FakeAppForegroundStateService(),
+        imageLoaderHolder = FakeImageLoaderHolder(),
     )
 }