diff --git a/.github/ISSUE_TEMPLATE/story.yml b/.github/ISSUE_TEMPLATE/story.yml
deleted file mode 100644
index 436b92b507..0000000000
--- a/.github/ISSUE_TEMPLATE/story.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: User story issue
-description: Second-level planning issue template. A story should take about a week or a sprint to finish.
-title: "[Story]
"
-labels: [T-Story]
-
-body:
-- type: textarea
- attributes:
- label: Story
- description: A story should take roughly a week or a sprint to finish. Each story is usually made up of a number of tasks that take half to a full day.
- value: |
- As a user…
- I want to…
- so that I can…
-
- ## Scope
-
- ```[tasklist]
- ### Tasklist
- - [ ] Task 1
- ```
-
- - [ ] QA signoff on completion
- - [ ] Design signoff on completion
- - [ ] Product signoff on completion
-
-
- ## Stretch goals
- None at this time
-
-
- ## Out of scope
- -
- validations:
- required: false
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 724060aa40..fdcbd7047a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -38,7 +38,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.8.0
+ uses: gradle/gradle-build-action@v2.9.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble debug APK
@@ -55,7 +55,7 @@ jobs:
name: elementx-debug
path: |
app/build/outputs/apk/debug/*.apk
- - uses: rnkdsh/action-upload-diawi@v1.5.1
+ - uses: rnkdsh/action-upload-diawi@v1.5.3
id: diawi
# Do not fail the whole build if Diawi upload fails
continue-on-error: true
diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml
index f05db3c791..772df369b8 100644
--- a/.github/workflows/danger.yml
+++ b/.github/workflows/danger.yml
@@ -11,7 +11,7 @@ jobs:
- run: |
npm install --save-dev @babel/plugin-transform-flow-strip-types
- name: Danger
- uses: danger/danger-js@11.2.8
+ uses: danger/danger-js@11.3.0
with:
args: "--dangerfile ./tools/danger/dangerfile.js"
env:
diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml
index 8b78a3c447..b433ebe459 100644
--- a/.github/workflows/maestro.yml
+++ b/.github/workflows/maestro.yml
@@ -40,7 +40,7 @@ jobs:
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
- - uses: mobile-dev-inc/action-maestro-cloud@v1.5.0
+ - uses: mobile-dev-inc/action-maestro-cloud@v1.6.0
with:
api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }}
# Doc says (https://github.com/mobile-dev-inc/action-maestro-cloud#android):
diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml
index 124afbc98f..fcde864311 100644
--- a/.github/workflows/nightlyReports.yml
+++ b/.github/workflows/nightlyReports.yml
@@ -62,7 +62,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.8.0
+ uses: gradle/gradle-build-action@v2.9.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Dependency analysis
diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml
index 417acc9341..1b880a68fd 100644
--- a/.github/workflows/quality.yml
+++ b/.github/workflows/quality.yml
@@ -40,7 +40,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.8.0
+ uses: gradle/gradle-build-action@v2.9.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run code quality check suite
@@ -60,7 +60,7 @@ jobs:
yarn add danger-plugin-lint-report --dev
- name: Danger lint
if: always()
- uses: danger/danger-js@11.2.8
+ uses: danger/danger-js@11.3.0
with:
args: "--dangerfile ./tools/danger/dangerfile-lint.js"
env:
diff --git a/.github/workflows/recordScreenshots.yml b/.github/workflows/recordScreenshots.yml
index e9ea93619c..829f6e1a46 100644
--- a/.github/workflows/recordScreenshots.yml
+++ b/.github/workflows/recordScreenshots.yml
@@ -24,7 +24,7 @@ jobs:
java-version: '17'
# Add gradle cache, this should speed up the process
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.8.0
+ uses: gradle/gradle-build-action@v2.9.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Record screenshots
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index cc8fc9055c..bd6179b82e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -25,7 +25,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.8.0
+ uses: gradle/gradle-build-action@v2.9.0
- name: Create app bundle
env:
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml
index b511835a60..3250214e22 100644
--- a/.github/workflows/sonar.yml
+++ b/.github/workflows/sonar.yml
@@ -32,7 +32,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.8.0
+ uses: gradle/gradle-build-action@v2.9.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: 🔊 Publish results to Sonar
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index f662e5352d..52c6656980 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -44,7 +44,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.8.0
+ uses: gradle/gradle-build-action@v2.9.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
diff --git a/.idea/dictionaries/shared.xml b/.idea/dictionaries/shared.xml
index 2fc10f455b..c3aa70ef23 100644
--- a/.idea/dictionaries/shared.xml
+++ b/.idea/dictionaries/shared.xml
@@ -4,6 +4,7 @@
backstack
ftue
homeserver
+ konsist
kover
measurables
onboarding
diff --git a/.maestro/tests/account/changeServer.yaml b/.maestro/tests/account/changeServer.yaml
index c6b092e018..b56b59e2f1 100644
--- a/.maestro/tests/account/changeServer.yaml
+++ b/.maestro/tests/account/changeServer.yaml
@@ -10,9 +10,9 @@ appId: ${APP_ID}
- tapOn:
id: "change_server-server"
# Test server that does not support sliding sync.
-- inputText: "gnuradio"
+- inputText: "https://kieranml.ems-support.element.dev"
- hideKeyboard
-- tapOn: "gnuradio.org"
+- tapOn: "kieranml.ems-support.element.dev"
- extendedWaitUntil:
visible: "This server currently doesn’t support sliding sync."
timeout: 10000
diff --git a/CHANGES.md b/CHANGES.md
index 05e070f678..796c0d783c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,66 @@
+Changes in Element X v0.2.4 (2023-10-12)
+========================================
+
+Features ✨
+----------
+ - [Rich text editor] Add full screen mode ([#1447](https://github.com/vector-im/element-x-android/issues/1447))
+ - Improve rendering of m.emote. ([#1497](https://github.com/vector-im/element-x-android/issues/1497))
+ - Improve deleted session behavior. ([#1520](https://github.com/vector-im/element-x-android/issues/1520))
+
+Bugfixes 🐛
+----------
+ - WebP images can't be sent as media. ([#1483](https://github.com/vector-im/element-x-android/issues/1483))
+ - Fix back button not working in bottom sheets. ([#1517](https://github.com/vector-im/element-x-android/issues/1517))
+ - Render body of unknown msgtype in the timeline and in the room list ([#1539](https://github.com/vector-im/element-x-android/issues/1539))
+
+Other changes
+-------------
+ - Room : makes subscribeToSync/unsubscribeFromSync suspendable. ([#1457](https://github.com/vector-im/element-x-android/issues/1457))
+ - Add some Konsist tests. ([#1526](https://github.com/vector-im/element-x-android/issues/1526))
+
+
+Changes in Element X v0.2.3 (2023-09-27)
+========================================
+
+Features ✨
+----------
+ - Handle installation of Apks from the media viewer. ([#1432](https://github.com/vector-im/element-x-android/pull/1432))
+ - Integrate SDK 0.1.58 ([#1437](https://github.com/vector-im/element-x-android/pull/1437))
+
+Other changes
+-------------
+ - Element call: add custom parameters to Element Call urls. ([#1434](https://github.com/vector-im/element-x-android/issues/1434))
+
+
+Changes in Element X v0.2.2 (2023-09-21)
+========================================
+
+Bugfixes 🐛
+----------
+ - Add animation when rendering the timeline to avoid glitches. ([#1323](https://github.com/vector-im/element-x-android/issues/1323))
+ - Fix crash when trying to take a photo or record a video. ([#1395](https://github.com/vector-im/element-x-android/issues/1395))
+
+
+Changes in Element X v0.2.1 (2023-09-20)
+========================================
+
+Features ✨
+----------
+ - Bump Rust SDK to `v0.1.56`
+ - [Rich text editor] Add link support to rich text editor ([#1309](https://github.com/vector-im/element-x-android/issues/1309))
+ - Let the SDK figure the best scheme given an homeserver URL (thus allowing HTTP homeservers) ([#1382](https://github.com/vector-im/element-x-android/issues/1382))
+
+Bugfixes 🐛
+----------
+ - Fix ANR on RoomList when notification settings change. ([#1370](https://github.com/vector-im/element-x-android/issues/1370))
+
+Other changes
+-------------
+ - Element Call: support scheme `io.element.call` ([#1377](https://github.com/vector-im/element-x-android/issues/1377))
+ - [DI] Rework how dagger components are created and provided. ([#1378](https://github.com/vector-im/element-x-android/issues/1378))
+ - Remove usage of async-uniffi as it leads to a deadlocks and memory leaks. ([#1381](https://github.com/vector-im/element-x-android/issues/1381))
+
+
Changes in Element X v0.2.0 (2023-09-18)
========================================
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 11e6aeba19..3a729e7992 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -18,6 +18,7 @@
* [knit](#knit)
* [lint](#lint)
* [Unit tests](#unit-tests)
+ * [konsist](#konsist)
* [Tests](#tests)
* [Accessibility](#accessibility)
* [Jetpack Compose](#jetpack-compose)
@@ -156,6 +157,10 @@ Make sure the following commands execute without any error:
./gradlew test
+#### konsist
+
+[konsist](https://github.com/LemonAppDev/konsist) is setup in the project to check that the architecture and the naming rules are followed. Konsist tests are classical Unit tests.
+
### Tests
Element X is currently supported on Android Marshmallow (API 23+): please test your change on an Android device (or Android emulator) running with API 23. Many issues can happen (including crashes) on older devices.
@@ -171,11 +176,11 @@ For instance, when updating the image `src` of an ImageView, please also conside
### Jetpack Compose
-When adding or editing `@Composable`, make sure that you create an internal function annotated with `@DayNightPreviews`, with a name suffixed by `Preview`, and having `ElementPreview` as the root composable.
+When adding or editing `@Composable`, make sure that you create an internal function annotated with `@PreviewsDayNight`, with a name suffixed by `Preview`, and having `ElementPreview` as the root composable.
Example:
```kotlin
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PinIconPreview() = ElementPreview {
PinIcon()
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2b77c7076e..10300ae00b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -20,14 +20,16 @@
+
-
+
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
index e94a69a365..62af9cf4b2 100644
Binary files a/app/src/main/ic_launcher-playstore.png and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt
index 2ebfd2ad12..3b35a0ebf4 100644
--- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt
+++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt
@@ -34,7 +34,7 @@ import com.bumble.appyx.core.integrationpoint.NodeComponentActivity
import com.bumble.appyx.core.plugin.NodeReadyObserver
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag
-import io.element.android.libraries.designsystem.utils.LocalSnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher
import io.element.android.libraries.theme.ElementTheme
import io.element.android.x.di.AppBindings
import io.element.android.x.intent.SafeUriHandler
diff --git a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt
index 914be65946..185b3834c5 100644
--- a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt
+++ b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt
@@ -18,7 +18,7 @@ package io.element.android.x.di
import com.squareup.anvil.annotations.ContributesTo
import io.element.android.features.rageshake.api.reporter.BugReporter
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.tracing.TracingService
diff --git a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt
index f8ab5532b0..17ba415762 100644
--- a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt
+++ b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt
@@ -28,7 +28,7 @@ import io.element.android.features.messages.impl.timeline.components.customreact
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.meta.BuildType
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.DefaultPreferences
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
index f08c673055..793d5ca60d 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
index fff7bbbfd7..d1ff05833e 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
index 00b5a99258..78a93b86f1 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
index 9965e382c1..e8f321ff17 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
index f248938976..f411c1016c 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
index 347a31e2ea..5380a9e861 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
index b1d83c1244..b31de82585 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
index b4090e12b5..5e6654b50c 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
index 0537ce0a8b..a368522d59 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
index 60ada6a758..889388eab6 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
index 7311a98da8..9aebd17d21 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
index dfae045e0c..af59382417 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
index 8282551ecf..97afc844cb 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
index 4efec9cdb2..92e763d12f 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
index ecf5481f94..d71ab178fe 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml
new file mode 100644
index 0000000000..b9f3d03986
--- /dev/null
+++ b/app/src/main/res/xml/network_security_config.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+ localhost
+ 127.0.0.1
+
+ 10.0.2.2
+
+ onion
+
+
+
+ home.arpa
+ local
+ test
+
+ home
+ lan
+ localdomain
+
+
+
+
+
+
+
+
+
+
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt
index e55f059d14..6bf7687fef 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt
@@ -16,8 +16,8 @@
package io.element.android.appnav
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
-import io.element.android.libraries.designsystem.utils.SnackbarMessage
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt
index 065cfaafc2..891301532c 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt
@@ -26,8 +26,10 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.bumble.appyx.core.composable.Children
+import com.bumble.appyx.core.composable.PermanentChild
import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
@@ -48,14 +50,18 @@ import io.element.android.features.ftue.api.state.FtueState
import io.element.android.features.invitelist.api.InviteListEntryPoint
import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus
+import io.element.android.features.lockscreen.api.LockScreenEntryPoint
+import io.element.android.features.lockscreen.api.LockScreenState
+import io.element.android.features.lockscreen.api.LockScreenStateService
import io.element.android.features.preferences.api.PreferencesEntryPoint
import io.element.android.features.roomlist.api.RoomListEntryPoint
import io.element.android.features.verifysession.api.VerifySessionEntryPoint
import io.element.android.libraries.architecture.BackstackNode
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
import io.element.android.libraries.architecture.createNode
+import io.element.android.libraries.architecture.waitForChildAttached
import io.element.android.libraries.deeplink.DeeplinkData
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.MAIN_SPACE
@@ -68,6 +74,7 @@ import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import timber.log.Timber
@@ -86,6 +93,8 @@ class LoggedInFlowNode @AssistedInject constructor(
private val networkMonitor: NetworkMonitor,
private val notificationDrawerManager: NotificationDrawerManager,
private val ftueState: FtueState,
+ private val lockScreenEntryPoint: LockScreenEntryPoint,
+ private val lockScreenStateService: LockScreenStateService,
private val matrixClient: MatrixClient,
snackbarDispatcher: SnackbarDispatcher,
) : BackstackNode(
@@ -93,6 +102,10 @@ class LoggedInFlowNode @AssistedInject constructor(
initialElement = NavTarget.RoomList,
savedStateMap = buildContext.savedStateMap,
),
+ permanentNavModel = PermanentNavModel(
+ navTargets = setOf(NavTarget.LoggedInPermanent, NavTarget.LockPermanent),
+ savedStateMap = buildContext.savedStateMap,
+ ),
buildContext = buildContext,
plugins = plugins
) {
@@ -121,9 +134,19 @@ class LoggedInFlowNode @AssistedInject constructor(
backstack.push(NavTarget.Ftue)
}
},
- onStop = {
- //Counterpart startSync is done in observeSyncStateAndNetworkStatus method.
+ onResume = {
coroutineScope.launch {
+ lockScreenStateService.entersForeground()
+ }
+ },
+ onPause = {
+ coroutineScope.launch {
+ lockScreenStateService.entersBackground()
+ }
+ },
+ onStop = {
+ coroutineScope.launch {
+ //Counterpart startSync is done in observeSyncStateAndNetworkStatus method.
syncService.stopSync()
}
},
@@ -159,7 +182,10 @@ class LoggedInFlowNode @AssistedInject constructor(
sealed interface NavTarget : Parcelable {
@Parcelize
- data object Permanent : NavTarget
+ data object LoggedInPermanent : NavTarget
+
+ @Parcelize
+ data object LockPermanent : NavTarget
@Parcelize
data object RoomList : NavTarget
@@ -188,9 +214,12 @@ class LoggedInFlowNode @AssistedInject constructor(
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) {
- NavTarget.Permanent -> {
+ NavTarget.LoggedInPermanent -> {
createNode(buildContext)
}
+ NavTarget.LockPermanent -> {
+ lockScreenEntryPoint.createNode(this, buildContext)
+ }
NavTarget.RoomList -> {
val callback = object : RoomListEntryPoint.Callback {
override fun onRoomClicked(roomId: RoomId) {
@@ -308,30 +337,37 @@ class LoggedInFlowNode @AssistedInject constructor(
}
}
- @Composable
- override fun View(modifier: Modifier) {
- Box(modifier = modifier) {
- Children(
- navModel = backstack,
- modifier = Modifier,
- // Animate navigation to settings and to a room
- transitionHandler = rememberDefaultTransitionHandler(),
- )
-
- val isFtueDisplayed by ftueState.shouldDisplayFlow.collectAsState()
-
- if (!isFtueDisplayed) {
- PermanentChild(navTarget = NavTarget.Permanent)
- }
+ internal suspend fun attachInviteList(deeplinkData: DeeplinkData.InviteList) = withContext(lifecycleScope.coroutineContext) {
+ notificationDrawerManager.clearMembershipNotificationForSession(deeplinkData.sessionId)
+ backstack.singleTop(NavTarget.RoomList)
+ backstack.push(NavTarget.InviteList)
+ waitForChildAttached { navTarget ->
+ navTarget is NavTarget.InviteList
}
}
- internal suspend fun attachRoom(deeplinkData: DeeplinkData.Room) {
- backstack.push(NavTarget.Room(deeplinkData.roomId))
- }
-
- internal suspend fun attachInviteList(deeplinkData: DeeplinkData.InviteList) {
- notificationDrawerManager.clearMembershipNotificationForSession(deeplinkData.sessionId)
- backstack.push(NavTarget.InviteList)
+ @Composable
+ override fun View(modifier: Modifier) {
+ Box(modifier = modifier) {
+ val lockScreenState by lockScreenStateService.state.collectAsState()
+ when (lockScreenState) {
+ LockScreenState.Unlocked -> {
+ Children(
+ navModel = backstack,
+ modifier = Modifier,
+ // Animate navigation to settings and to a room
+ transitionHandler = rememberDefaultTransitionHandler(),
+ )
+ val isFtueDisplayed by ftueState.shouldDisplayFlow.collectAsState()
+ if (!isFtueDisplayed) {
+ PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)
+ }
+ }
+ LockScreenState.Locked -> {
+ MoveActivityToBackgroundBackHandler()
+ PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LockPermanent)
+ }
+ }
+ }
}
}
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/MoveActivityToBackgroundBackHandler.kt b/appnav/src/main/kotlin/io/element/android/appnav/MoveActivityToBackgroundBackHandler.kt
new file mode 100644
index 0000000000..5d959f9464
--- /dev/null
+++ b/appnav/src/main/kotlin/io/element/android/appnav/MoveActivityToBackgroundBackHandler.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.appnav
+
+import android.content.Context
+import android.content.ContextWrapper
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.BackHandler
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+
+@Composable
+fun MoveActivityToBackgroundBackHandler(enabled: Boolean = true) {
+
+ fun Context.findActivity(): ComponentActivity? = when (this) {
+ is ComponentActivity -> this
+ is ContextWrapper -> baseContext.findActivity()
+ else -> null
+ }
+
+ val context = LocalContext.current
+ BackHandler(enabled = enabled) {
+ context.findActivity()?.moveTaskToBack(false)
+ }
+}
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt
index 60095c17d0..df98998ba5 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt
@@ -45,6 +45,7 @@ import io.element.android.appnav.root.RootView
import io.element.android.features.login.api.oidc.OidcAction
import io.element.android.features.login.api.oidc.OidcActionFlow
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
+import io.element.android.features.signedout.api.SignedOutEntryPoint
import io.element.android.libraries.architecture.BackstackNode
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
import io.element.android.libraries.architecture.createNode
@@ -54,6 +55,7 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.SessionId
+import io.element.android.libraries.sessionstorage.api.LoggedInState
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -69,6 +71,7 @@ class RootFlowNode @AssistedInject constructor(
private val matrixClientsHolder: MatrixClientsHolder,
private val presenter: RootPresenter,
private val bugReportEntryPoint: BugReportEntryPoint,
+ private val signedOutEntryPoint: SignedOutEntryPoint,
private val intentResolver: IntentResolver,
private val oidcActionFlow: OidcActionFlow,
) : BackstackNode(
@@ -97,13 +100,20 @@ class RootFlowNode @AssistedInject constructor(
.distinctUntilChanged()
.onEach { navState ->
Timber.v("navState=$navState")
- if (navState.isLoggedIn) {
- tryToRestoreLatestSession(
- onSuccess = { sessionId -> switchToLoggedInFlow(sessionId, navState.cacheIndex) },
- onFailure = { switchToNotLoggedInFlow() }
- )
- } else {
- switchToNotLoggedInFlow()
+ when (navState.loggedInState) {
+ is LoggedInState.LoggedIn -> {
+ if (navState.loggedInState.isTokenValid) {
+ tryToRestoreLatestSession(
+ onSuccess = { sessionId -> switchToLoggedInFlow(sessionId, navState.cacheIndex) },
+ onFailure = { switchToNotLoggedInFlow() }
+ )
+ } else {
+ switchToSignedOutFlow(SessionId(navState.loggedInState.sessionId))
+ }
+ }
+ LoggedInState.NotLoggedIn -> {
+ switchToNotLoggedInFlow()
+ }
}
}
.launchIn(lifecycleScope)
@@ -118,6 +128,10 @@ class RootFlowNode @AssistedInject constructor(
backstack.safeRoot(NavTarget.NotLoggedInFlow)
}
+ private fun switchToSignedOutFlow(sessionId: SessionId) {
+ backstack.safeRoot(NavTarget.SignedOutFlow(sessionId))
+ }
+
private suspend fun restoreSessionIfNeeded(
sessionId: SessionId,
onFailure: () -> Unit = {},
@@ -179,6 +193,11 @@ class RootFlowNode @AssistedInject constructor(
val navId: Int
) : NavTarget
+ @Parcelize
+ data class SignedOutFlow(
+ val sessionId: SessionId
+ ) : NavTarget
+
@Parcelize
data object BugReport : NavTarget
}
@@ -198,6 +217,15 @@ class RootFlowNode @AssistedInject constructor(
createNode(buildContext, plugins = listOf(inputs, callback))
}
NavTarget.NotLoggedInFlow -> createNode(buildContext)
+ is NavTarget.SignedOutFlow -> {
+ signedOutEntryPoint.nodeBuilder(this, buildContext)
+ .params(
+ SignedOutEntryPoint.Params(
+ sessionId = navTarget.sessionId
+ )
+ )
+ .build()
+ }
NavTarget.SplashScreen -> splashNode(buildContext)
NavTarget.BugReport -> {
val callback = object : BugReportEntryPoint.Callback {
@@ -234,7 +262,7 @@ class RootFlowNode @AssistedInject constructor(
.apply {
when (deeplinkData) {
is DeeplinkData.Root -> attachRoot()
- is DeeplinkData.Room -> attachRoom(deeplinkData)
+ is DeeplinkData.Room -> attachRoom(deeplinkData.roomId)
is DeeplinkData.InviteList -> attachInviteList(deeplinkData)
}
}
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt
index 6950b9b699..5ddbb164d8 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt
@@ -31,7 +31,10 @@ class LoggedInNode @AssistedInject constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List,
private val loggedInPresenter: LoggedInPresenter,
-) : Node(buildContext, plugins = plugins) {
+) : Node(
+ buildContext = buildContext,
+ plugins = plugins
+) {
@Composable
override fun View(modifier: Modifier) {
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt
index 37e6e9591d..cadcef338d 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt
@@ -25,7 +25,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
@Composable
@@ -47,7 +47,7 @@ fun LoggedInView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview {
LoggedInView(
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt
index 6d045a431a..d82ef72145 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt
@@ -33,7 +33,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Surface
@@ -81,7 +81,7 @@ fun SyncStateView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun SyncStateViewPreview() = ElementPreview {
// Add a box to see the shadow
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt
index f9aefa724e..1f8d9cfebb 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt
@@ -34,7 +34,7 @@ import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorVi
import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Scaffold
@@ -100,7 +100,7 @@ private fun LoadingRoomTopBar(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun LoadingRoomNodeViewPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = ElementPreview {
LoadingRoomNodeView(
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt
index 2b063a2cbf..fa583645b2 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt
@@ -46,6 +46,7 @@ import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.services.appnavstate.api.AppNavigationStateService
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -60,6 +61,7 @@ class RoomLoadedFlowNode @AssistedInject constructor(
private val messagesEntryPoint: MessagesEntryPoint,
private val roomDetailsEntryPoint: RoomDetailsEntryPoint,
private val appNavigationStateService: AppNavigationStateService,
+ private val appCoroutineScope: CoroutineScope,
roomComponentFactory: RoomComponentFactory,
roomMembershipObserver: RoomMembershipObserver,
) : BackstackNode(
@@ -91,6 +93,16 @@ class RoomLoadedFlowNode @AssistedInject constructor(
appNavigationStateService.onNavigateToRoom(id, inputs.room.roomId)
fetchRoomMembers()
},
+ onResume = {
+ appCoroutineScope.launch {
+ inputs.room.subscribeToSync()
+ }
+ },
+ onPause = {
+ appCoroutineScope.launch {
+ inputs.room.unsubscribeFromSync()
+ }
+ },
onDestroy = {
Timber.v("OnDestroy")
appNavigationStateService.onLeavingRoom(id)
@@ -169,9 +181,7 @@ class RoomLoadedFlowNode @AssistedInject constructor(
// because this node enters 'onDestroy' before his children, so it can leads to
// using the room in a child node where it's already closed.
DisposableEffect(Unit) {
- inputs.room.subscribeToSync()
onDispose {
- inputs.room.unsubscribeFromSync()
if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
inputs.room.destroy()
}
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavState.kt b/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavState.kt
index ed3ac15972..09f9767f62 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavState.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavState.kt
@@ -16,6 +16,8 @@
package io.element.android.appnav.root
+import io.element.android.libraries.sessionstorage.api.LoggedInState
+
/**
* [RootNavState] produced by [RootNavStateFlowFactory].
*/
@@ -26,7 +28,7 @@ data class RootNavState(
*/
val cacheIndex: Int,
/**
- * true if we are currently loggedIn.
+ * LoggedInState.
*/
- val isLoggedIn: Boolean
+ val loggedInState: LoggedInState,
)
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavStateFlowFactory.kt b/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavStateFlowFactory.kt
index 0e8d93b0c9..e6a46f5950 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavStateFlowFactory.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavStateFlowFactory.kt
@@ -22,9 +22,9 @@ import io.element.android.appnav.di.MatrixClientsHolder
import io.element.android.features.login.api.LoginUserStory
import io.element.android.features.preferences.api.CacheService
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
+import io.element.android.libraries.sessionstorage.api.LoggedInState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject
@@ -47,9 +47,14 @@ class RootNavStateFlowFactory @Inject constructor(
fun create(savedStateMap: SavedStateMap?): Flow {
return combine(
cacheIndexFlow(savedStateMap),
- isUserLoggedInFlow(),
- ) { cacheIndex, isLoggedIn ->
- RootNavState(cacheIndex = cacheIndex, isLoggedIn = isLoggedIn)
+ authenticationService.loggedInStateFlow(),
+ loginUserStory.loginFlowIsDone,
+ ) { cacheIndex, loggedInState, loginFlowIsDone ->
+ if (loginFlowIsDone) {
+ RootNavState(cacheIndex = cacheIndex, loggedInState = loggedInState)
+ } else {
+ RootNavState(cacheIndex = cacheIndex, loggedInState = LoggedInState.NotLoggedIn)
+ }
}
}
@@ -72,16 +77,6 @@ class RootNavStateFlowFactory @Inject constructor(
}
}
- private fun isUserLoggedInFlow(): Flow {
- return combine(
- authenticationService.isLoggedIn(),
- loginUserStory.loginFlowIsDone
- ) { isLoggedIn, loginFlowIsDone ->
- isLoggedIn && loginFlowIsDone
- }
- .distinctUntilChanged()
- }
-
/**
* @return a flow of integer that increments the value by one each time a new element is emitted upstream.
*/
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt b/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt
index ab8fccf819..53eae2395e 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/root/RootView.kt
@@ -27,7 +27,7 @@ import io.element.android.features.rageshake.api.crash.CrashDetectionEvents
import io.element.android.features.rageshake.api.crash.CrashDetectionView
import io.element.android.features.rageshake.api.detection.RageshakeDetectionEvents
import io.element.android.features.rageshake.api.detection.RageshakeDetectionView
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.services.apperror.impl.AppErrorView
@@ -66,7 +66,7 @@ fun RootView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RootPreview(@PreviewParameter(RootStateProvider::class) rootState: RootState) = ElementPreview {
RootView(rootState) {
diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt
index a3bf1359f9..a25e4d8134 100644
--- a/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt
+++ b/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt
@@ -35,6 +35,8 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
@@ -85,6 +87,7 @@ class RoomFlowNodeTest {
plugins: List,
messagesEntryPoint: MessagesEntryPoint = FakeMessagesEntryPoint(),
roomDetailsEntryPoint: RoomDetailsEntryPoint = FakeRoomDetailsEntryPoint(),
+ coroutineScope: CoroutineScope,
) = RoomLoadedFlowNode(
buildContext = BuildContext.root(savedStateMap = null),
plugins = plugins,
@@ -92,16 +95,21 @@ class RoomFlowNodeTest {
roomDetailsEntryPoint = roomDetailsEntryPoint,
appNavigationStateService = FakeAppNavigationStateService(),
roomMembershipObserver = RoomMembershipObserver(),
+ appCoroutineScope = coroutineScope,
roomComponentFactory = FakeRoomComponentFactory(),
)
@Test
- fun `given a room flow node when initialized then it loads messages entry point`() {
+ fun `given a room flow node when initialized then it loads messages entry point`() = runTest {
// GIVEN
val room = FakeMatrixRoom()
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
val inputs = RoomLoadedFlowNode.Inputs(room)
- val roomFlowNode = aRoomFlowNode(listOf(inputs), fakeMessagesEntryPoint)
+ val roomFlowNode = aRoomFlowNode(
+ plugins = listOf(inputs),
+ messagesEntryPoint = fakeMessagesEntryPoint,
+ coroutineScope = this
+ )
// WHEN
val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper()
@@ -113,13 +121,18 @@ class RoomFlowNodeTest {
}
@Test
- fun `given a room flow node when callback on room details is triggered then it loads room details entry point`() {
+ fun `given a room flow node when callback on room details is triggered then it loads room details entry point`() = runTest {
// GIVEN
val room = FakeMatrixRoom()
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
val inputs = RoomLoadedFlowNode.Inputs(room)
- val roomFlowNode = aRoomFlowNode(listOf(inputs), fakeMessagesEntryPoint, fakeRoomDetailsEntryPoint)
+ val roomFlowNode = aRoomFlowNode(
+ plugins = listOf(inputs),
+ messagesEntryPoint = fakeMessagesEntryPoint,
+ roomDetailsEntryPoint = fakeRoomDetailsEntryPoint,
+ coroutineScope = this
+ )
val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper()
// WHEN
fakeMessagesEntryPoint.callback?.onRoomDetailsClicked()
diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
index 6771718d81..5b382d1dc5 100644
--- a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
+++ b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
@@ -42,7 +42,7 @@ class RootPresenterTest {
@Test
fun `present - initial state`() = runTest {
- val presenter = createPresenter()
+ val presenter = createRootPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -54,7 +54,7 @@ class RootPresenterTest {
@Test
fun `present - passes app error state`() = runTest {
- val presenter = createPresenter(
+ val presenter = createRootPresenter(
appErrorService = DefaultAppErrorStateService().apply {
showError("Bad news", "Something bad happened")
}
@@ -75,7 +75,7 @@ class RootPresenterTest {
}
}
- private fun createPresenter(
+ private fun createRootPresenter(
appErrorService: AppErrorStateService = DefaultAppErrorStateService()
): RootPresenter {
val crashDataStore = FakeCrashDataStore()
diff --git a/appnav/src/test/kotlin/io/element/android/appnav/di/MatrixClientsHolderTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/di/MatrixClientsHolderTest.kt
new file mode 100644
index 0000000000..6dce8a6310
--- /dev/null
+++ b/appnav/src/test/kotlin/io/element/android/appnav/di/MatrixClientsHolderTest.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.appnav.di
+
+import com.bumble.appyx.core.state.MutableSavedStateMapImpl
+import com.google.common.truth.Truth.assertThat
+import io.element.android.libraries.matrix.test.A_SESSION_ID
+import io.element.android.libraries.matrix.test.FakeMatrixClient
+import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+class MatrixClientsHolderTest {
+ @Test
+ fun `test getOrNull`() {
+ val fakeAuthenticationService = FakeAuthenticationService()
+ val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService)
+ assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isNull()
+ }
+
+ @Test
+ fun `test getOrRestore`() = runTest {
+ val fakeAuthenticationService = FakeAuthenticationService()
+ val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService)
+ val fakeMatrixClient = FakeMatrixClient()
+ fakeAuthenticationService.givenMatrixClient(fakeMatrixClient)
+ assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isNull()
+ assertThat(matrixClientsHolder.getOrRestore(A_SESSION_ID).getOrNull()).isEqualTo(fakeMatrixClient)
+ // Do it again to it the cache
+ assertThat(matrixClientsHolder.getOrRestore(A_SESSION_ID).getOrNull()).isEqualTo(fakeMatrixClient)
+ assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isEqualTo(fakeMatrixClient)
+ }
+
+ @Test
+ fun `test remove`() = runTest {
+ val fakeAuthenticationService = FakeAuthenticationService()
+ val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService)
+ val fakeMatrixClient = FakeMatrixClient()
+ fakeAuthenticationService.givenMatrixClient(fakeMatrixClient)
+ assertThat(matrixClientsHolder.getOrRestore(A_SESSION_ID).getOrNull()).isEqualTo(fakeMatrixClient)
+ assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isEqualTo(fakeMatrixClient)
+ // Remove
+ matrixClientsHolder.remove(A_SESSION_ID)
+ assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isNull()
+ }
+
+ @Test
+ fun `test remove all`() = runTest {
+ val fakeAuthenticationService = FakeAuthenticationService()
+ val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService)
+ val fakeMatrixClient = FakeMatrixClient()
+ fakeAuthenticationService.givenMatrixClient(fakeMatrixClient)
+ assertThat(matrixClientsHolder.getOrRestore(A_SESSION_ID).getOrNull()).isEqualTo(fakeMatrixClient)
+ assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isEqualTo(fakeMatrixClient)
+ // Remove all
+ matrixClientsHolder.removeAll()
+ assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isNull()
+ }
+
+ @Test
+ fun `test save and restore`() = runTest {
+ val fakeAuthenticationService = FakeAuthenticationService()
+ val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService)
+ val fakeMatrixClient = FakeMatrixClient()
+ fakeAuthenticationService.givenMatrixClient(fakeMatrixClient)
+ matrixClientsHolder.getOrRestore(A_SESSION_ID)
+ val savedStateMap = MutableSavedStateMapImpl { true }
+ matrixClientsHolder.saveIntoSavedState(savedStateMap)
+ assertThat(savedStateMap.size).isEqualTo(1)
+ // Test Restore with non-empty map
+ matrixClientsHolder.restoreWithSavedState(savedStateMap)
+ // Empty the map
+ matrixClientsHolder.removeAll()
+ assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isNull()
+ // Restore again
+ matrixClientsHolder.restoreWithSavedState(savedStateMap)
+ assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isEqualTo(fakeMatrixClient)
+ }
+}
diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt
index efada670b0..fcfba8f7a1 100644
--- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt
+++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt
@@ -42,7 +42,7 @@ class LoggedInPresenterTest {
@Test
fun `present - initial state`() = runTest {
- val presenter = createPresenter()
+ val presenter = createLoggedInPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -54,7 +54,7 @@ class LoggedInPresenterTest {
@Test
fun `present - show sync spinner`() = runTest {
val roomListService = FakeRoomListService()
- val presenter = createPresenter(roomListService, NetworkStatus.Online)
+ val presenter = createLoggedInPresenter(roomListService, NetworkStatus.Online)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -66,7 +66,7 @@ class LoggedInPresenterTest {
}
}
- private fun createPresenter(
+ private fun createLoggedInPresenter(
roomListService: RoomListService = FakeRoomListService(),
networkStatus: NetworkStatus = NetworkStatus.Offline
): LoggedInPresenter {
diff --git a/build.gradle.kts b/build.gradle.kts
index 4343899218..487776d948 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -45,7 +45,7 @@ plugins {
}
tasks.register("clean").configure {
- delete(rootProject.buildDir)
+ delete(rootProject.layout.buildDirectory)
}
allprojects {
@@ -62,7 +62,7 @@ allprojects {
config.from(files("$rootDir/tools/detekt/detekt.yml"))
}
dependencies {
- detektPlugins("io.nlopez.compose.rules:detekt:0.2.3")
+ detektPlugins("io.nlopez.compose.rules:detekt:0.3.0")
}
// KtLint
@@ -86,7 +86,7 @@ allprojects {
reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.CHECKSTYLE)
}
filter {
- exclude { element -> element.file.path.contains("$buildDir/generated/") }
+ exclude { element -> element.file.path.contains("${layout.buildDirectory.asFile.get()}/generated/") }
}
}
// Dependency check
@@ -176,10 +176,13 @@ koverMerged {
"*_ModuleKt",
"anvil.hint.binding.io.element.*",
"anvil.hint.merge.*",
+ "anvil.hint.multibinding.io.element.*",
"anvil.module.*",
"com.airbnb.android.showkase*",
"io.element.android.libraries.designsystem.showkase.*",
+ "io.element.android.x.di.DaggerAppComponent*",
"*_Factory",
+ "*_Factory_Impl",
"*_Factory$*",
"*_Module",
"*_Module$*",
@@ -228,11 +231,11 @@ koverMerged {
name = "Global minimum code coverage."
target = kotlinx.kover.api.VerificationTarget.ALL
bound {
- minValue = 55
+ minValue = 60
// Setting a max value, so that if coverage is bigger, it means that we have to change minValue.
// For instance if we have minValue = 20 and maxValue = 30, and current code coverage is now 31.32%, update
// minValue to 25 and maxValue to 35.
- maxValue = 65
+ maxValue = 70
counter = kotlinx.kover.api.CounterType.INSTRUCTION
valueType = kotlinx.kover.api.VerificationValueType.COVERED_PERCENTAGE
}
@@ -247,6 +250,10 @@ koverMerged {
excludes += "io.element.android.appnav.loggedin.LoggedInPresenter$*"
// Some options can't be tested at the moment
excludes += "io.element.android.features.preferences.impl.developer.DeveloperSettingsPresenter$*"
+ // Temporary until we have actually something to test.
+ excludes += "io.element.android.features.lockscreen.impl.auth.PinAuthenticationPresenter"
+ excludes += "io.element.android.features.lockscreen.impl.auth.PinAuthenticationPresenter$*"
+ excludes += "io.element.android.features.lockscreen.impl.create.CreatePinPresenter"
}
bound {
minValue = 85
@@ -354,7 +361,7 @@ subprojects {
subprojects {
tasks.withType() {
doLast {
- fileTree(buildDir).apply { include("**/*ShowkaseExtension*.kt") }.files.forEach { file ->
+ fileTree(layout.buildDirectory).apply { include("**/*ShowkaseExtension*.kt") }.files.forEach { file ->
ReplaceRegExp().apply {
setMatch("^public fun Showkase.getMetadata")
setReplace("@Suppress(\"DEPRECATION\") public fun Showkase.getMetadata")
diff --git a/changelog.d/1309.feature b/changelog.d/1309.feature
deleted file mode 100644
index da6a3d6132..0000000000
--- a/changelog.d/1309.feature
+++ /dev/null
@@ -1 +0,0 @@
-[Rich text editor] Add link support to rich text editor
diff --git a/changelog.d/1370.bugfix b/changelog.d/1370.bugfix
deleted file mode 100644
index 00b3a43ccb..0000000000
--- a/changelog.d/1370.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Fix ANR on RoomList when notification settings change.
diff --git a/changelog.d/1378.misc b/changelog.d/1378.misc
deleted file mode 100644
index 56afd29966..0000000000
--- a/changelog.d/1378.misc
+++ /dev/null
@@ -1 +0,0 @@
-[DI] Rework how dagger components are created and provided.
diff --git a/changelog.d/1382.feature b/changelog.d/1382.feature
deleted file mode 100644
index 1ccf533815..0000000000
--- a/changelog.d/1382.feature
+++ /dev/null
@@ -1 +0,0 @@
-Let the SDK figure the best scheme given an homeserver URL (thus allowing HTTP homeservers)
diff --git a/changelog.d/1481.bugfix b/changelog.d/1481.bugfix
new file mode 100644
index 0000000000..6cb5e63d50
--- /dev/null
+++ b/changelog.d/1481.bugfix
@@ -0,0 +1 @@
+Always register the pusher when application starts
diff --git a/changelog.d/1519.bugfix b/changelog.d/1519.bugfix
new file mode 100644
index 0000000000..35de17de6b
--- /dev/null
+++ b/changelog.d/1519.bugfix
@@ -0,0 +1 @@
+Ensure screen does not turn off when playing a video
diff --git a/changelog.d/1563.misc b/changelog.d/1563.misc
new file mode 100644
index 0000000000..8de1c9b6d1
--- /dev/null
+++ b/changelog.d/1563.misc
@@ -0,0 +1 @@
+Remove usage of blocking methods.
diff --git a/docs/_developer_onboarding.md b/docs/_developer_onboarding.md
index 8a587b5a08..a100a2beba 100644
--- a/docs/_developer_onboarding.md
+++ b/docs/_developer_onboarding.md
@@ -117,21 +117,16 @@ You can also have access to the aars through the [release](https://github.com/ma
#### Build the SDK locally
+Easiest way: run the script [./tools/sdk/build_rust_sdk.sh](./tools/sdk/build_rust_sdk.sh) and just answer the questions.
+
+Legacy way:
+
If you need to locally build the sdk-android you can use
the [build](https://github.com/matrix-org/matrix-rust-components-kotlin/blob/main/scripts/build.sh) script.
-For this, you first need to ensure to setup :
+For this please check the [prerequisites](https://github.com/matrix-org/matrix-rust-components-kotlin/blob/main/README.md#prerequisites) from the repo.
-- rust environment (check https://rust-lang.github.io/rustup/ if needed)
-- cargo-ndk < 2.12.0
-```shell
-cargo install cargo-ndk --version 2.11.0
-```
-- android targets
-```shell
-rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android
-```
-- checkout both [matrix-rust-sdk](https://github.com/matrix-org/matrix-rust-sdk) and [matrix-rust-components-kotlin](https://github.com/matrix-org/matrix-rust-components-kotlin) repositories
+Checkout both [matrix-rust-sdk](https://github.com/matrix-org/matrix-rust-sdk) and [matrix-rust-components-kotlin](https://github.com/matrix-org/matrix-rust-components-kotlin) repositories
```shell
git clone git@github.com:matrix-org/matrix-rust-sdk.git
git clone git@github.com:matrix-org/matrix-rust-components-kotlin.git
@@ -151,14 +146,10 @@ So for example to build the sdk against aarch64-linux-android target and copy th
./scripts/build.sh -p [YOUR MATRIX RUST SDK PATH] -t aarch64-linux-android -o [YOUR element-x-android PATH]/libraries/rustsdk/matrix-rust-sdk.aar
```
-Finally let the `matrix/impl` module use this aar by changing the dependencies from `libs.matrix.sdk` to `projects.libraries.rustsdk`:
-
-```groovy
-dependencies {
- api(projects.libraries.rustsdk) // <- use the local version of the sdk. Uncomment this line.
- //implementation(libs.matrix.sdk) // <- use the released version. Comment this line.
-}
-```
+Troubleshooting:
+ - You may need to set `ANDROID_NDK_HOME` e.g `export ANDROID_NDK_HOME=~/Library/Android/sdk/ndk`.
+ - If you get the error `thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', .cargo/registry/src/index.crates.io-6f17d22bba15001f/cargo-ndk-2.11.0/src/cli.rs:345:18` try updating your Cargo NDK version. In this case, 2.11.0 is too old so `cargo install cargo-ndk` to install a newer version.
+ - If you get the error `Unsupported class file major version 64` try changing your JVM version. In this case, Java 20 is not supported in Gradle yet, so downgrade to an earlier version (Java 17 worked in this case).
You are good to test your local rust development now!
@@ -280,11 +271,12 @@ Follow these steps to install and configure the plugin and templates:
1. Install the AS plugin for generating modules :
[Generate Module from Template](https://plugins.jetbrains.com/plugin/13586-generate-module-from-template)
-2. Import file templates in AS :
+2. Run the script `tools/templates/generate_templates.sh` to generate the template zip file
+3. Import file templates in AS :
- Navigate to File/Manage IDE Settings/Import Settings
- - Pick the `tools/templates/file_templates.zip` files
+ - Pick the `tmp/file_templates.zip` files
- Click on OK
-3. Configure generate-module-from-template plugin :
+4. Configure generate-module-from-template plugin :
- Navigate to AS/Settings/Tools/Module Template Settings
- Click on + / Import From File
- Pick the `tools/templates/FeatureModule.json`
diff --git a/docs/debug_proxying.md b/docs/debug_proxying.md
new file mode 100644
index 0000000000..77c109461b
--- /dev/null
+++ b/docs/debug_proxying.md
@@ -0,0 +1,22 @@
+# Setup a debug mitm proxy to inspect all the app's network traffic
+
+1) Install mitmproxy: `brew install mitmproxy`.
+ 1) Launch `mitmweb` from a terminal. It will pop up mitmproxy's web interface in a web browser.
+1) Configure Android Emulator.
+ 1) Launch your android emulator.
+ 1) Open its settings page and go to Settings -> Proxy (nb this tab isn't visible when running the emu inside the Android Studio window, you need to set it so it runs in its own window).
+ 1) Disable "Use Android Studio HTTP proxy settings" and pick "Manual proxy configuration".
+ 1) Set `127.0.0.1` as "Host name" and `8080` as "Port number".
+ 1) Click "Apply" and verify that "Proxy status" is "Success" and close the settings window.
+
+1) Install the mitmproxy CA cert (this is needed to see traffic from java/kotlin code, it's not needed for traffic coming from native code e.g. the matrix-rust-sdk).
+ 1) Open the emulator Chrome browser app
+ 1) Go to the url `mitm.it`
+ 1) Follow the instructions to install the CA cert on Android devices.
+
+1) Slightly modify the Element X app source code.
+ 1) Go to the `RustMatrixClientFactory.create()` method.
+ 1) Add `.disableSslVerification()` in the `ClientBuilder` method chain.
+1) Build and run the Element X app.
+1) Enjoy, you will see all the traffic in mitmproxy's web interface.
+
diff --git a/fastlane/metadata/android/en-US/changelogs/40002010.txt b/fastlane/metadata/android/en-US/changelogs/40002010.txt
new file mode 100644
index 0000000000..97ceaa8d5a
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40002010.txt
@@ -0,0 +1,2 @@
+Main changes in this version: Element Call, design update, bugfixes
+Full changelog: https://github.com/vector-im/element-x-android/releases
diff --git a/fastlane/metadata/android/en-US/changelogs/40002020.txt b/fastlane/metadata/android/en-US/changelogs/40002020.txt
new file mode 100644
index 0000000000..882eb88054
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40002020.txt
@@ -0,0 +1,2 @@
+Main changes in this version: bugfixes
+Full changelog: https://github.com/vector-im/element-x-android/releases
diff --git a/fastlane/metadata/android/en-US/changelogs/40002030.txt b/fastlane/metadata/android/en-US/changelogs/40002030.txt
new file mode 100644
index 0000000000..ff4f86ce7e
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40002030.txt
@@ -0,0 +1,2 @@
+Main changes in this version: bugfixes.
+Full changelog: https://github.com/vector-im/element-x-android/releases
diff --git a/fastlane/metadata/android/en-US/changelogs/40002040.txt b/fastlane/metadata/android/en-US/changelogs/40002040.txt
new file mode 100644
index 0000000000..ff4f86ce7e
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40002040.txt
@@ -0,0 +1,2 @@
+Main changes in this version: bugfixes.
+Full changelog: https://github.com/vector-im/element-x-android/releases
diff --git a/fastlane/metadata/android/en-US/images/featureGraphic.png b/fastlane/metadata/android/en-US/images/featureGraphic.png
index 37975de877..eb6cabd122 100644
Binary files a/fastlane/metadata/android/en-US/images/featureGraphic.png and b/fastlane/metadata/android/en-US/images/featureGraphic.png differ
diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png
index cfe22b43cd..a3107af4b1 100644
Binary files a/fastlane/metadata/android/en-US/images/icon.png and b/fastlane/metadata/android/en-US/images/icon.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
index b1668d1b2d..3214245ded 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
index 30d6a71a01..652d91fa39 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
index c62d890ca2..e91fb3c260 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
index 0a612798a6..9e9f87ef0f 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png differ
diff --git a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt
index c4d193f901..5052f0927f 100644
--- a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt
+++ b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt
@@ -24,7 +24,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.analytics.api.AnalyticsOptInEvents
import io.element.android.libraries.designsystem.components.LINK_TAG
import io.element.android.libraries.designsystem.components.list.ListItemContent
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart
import io.element.android.libraries.designsystem.theme.components.ListItem
@@ -70,7 +70,7 @@ fun AnalyticsPreferencesView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AnalyticsPreferencesViewPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) =
ElementPreview {
diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt
index ddcd305f87..7aa007c9e5 100644
--- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt
+++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt
@@ -30,7 +30,6 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Poll
-import androidx.compose.material.icons.rounded.Check
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -45,10 +44,10 @@ import io.element.android.features.analytics.api.AnalyticsOptInEvents
import io.element.android.features.analytics.api.Config
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
-import io.element.android.libraries.designsystem.atomic.molecules.InfoListItem
-import io.element.android.libraries.designsystem.atomic.molecules.InfoListOrganism
+import io.element.android.libraries.designsystem.atomic.organisms.InfoListItem
+import io.element.android.libraries.designsystem.atomic.organisms.InfoListOrganism
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart
import io.element.android.libraries.designsystem.theme.components.Button
@@ -56,6 +55,7 @@ import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -147,7 +147,7 @@ private fun CheckIcon(modifier: Modifier = Modifier) {
.size(20.dp)
.background(color = MaterialTheme.colorScheme.background, shape = CircleShape)
.padding(2.dp),
- imageVector = Icons.Rounded.Check,
+ resourceId = CommonDrawables.ic_compound_check,
contentDescription = null,
tint = ElementTheme.colors.textActionAccent,
)
@@ -209,7 +209,7 @@ private fun AnalyticsOptInFooter(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AnalyticsOptInViewPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreview {
AnalyticsOptInView(
diff --git a/features/analytics/impl/src/main/res/values-de/translations.xml b/features/analytics/impl/src/main/res/values-de/translations.xml
index a214069ae3..74eff06c69 100644
--- a/features/analytics/impl/src/main/res/values-de/translations.xml
+++ b/features/analytics/impl/src/main/res/values-de/translations.xml
@@ -1,8 +1,8 @@
"Wir zeichnen keine persönlichen Daten auf und erstellen keine Profile."
- "Teilen Sie anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen."
- "Sie können alle unsere Bedingungen lesen%1$s."
+ "Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen."
+ "Du kannst alle unsere Bedingungen lesen %1$s."
"hier"
"Du kannst diese Funktion jederzeit deaktivieren"
"Wir geben deine Daten nicht an Dritte weiter"
diff --git a/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml b/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml
index 81349674b7..433c5b051e 100644
--- a/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml
@@ -1,8 +1,8 @@
"我們不會紀錄或剖繪您的個人資料"
- "分享匿名的使用數據以協助我們釐清問題"
- "您可以到 %1$s 閱讀我們的條款。"
+ "分享匿名的使用數據以協助我們釐清問題。"
+ "您可以到%1$s閱讀我們的條款。"
"這裡"
"您可以在任何時候關閉它"
"我們不會和第三方分享您的資料"
diff --git a/features/call/src/main/AndroidManifest.xml b/features/call/src/main/AndroidManifest.xml
index 1aed77cd95..877b7fb0a8 100644
--- a/features/call/src/main/AndroidManifest.xml
+++ b/features/call/src/main/AndroidManifest.xml
@@ -39,7 +39,6 @@
-
@@ -53,6 +52,14 @@
+
+
+
+
+
+
+
+
diff --git a/features/call/src/main/kotlin/io/element/android/features/call/CallIntentDataParser.kt b/features/call/src/main/kotlin/io/element/android/features/call/CallIntentDataParser.kt
index a664e562f3..b903b437d8 100644
--- a/features/call/src/main/kotlin/io/element/android/features/call/CallIntentDataParser.kt
+++ b/features/call/src/main/kotlin/io/element/android/features/call/CallIntentDataParser.kt
@@ -17,29 +17,88 @@
package io.element.android.features.call
import android.net.Uri
-import java.net.URLDecoder
+import javax.inject.Inject
-object CallIntentDataParser {
+class CallIntentDataParser @Inject constructor() {
- private val validHttpSchemes = sequenceOf("http", "https")
+ private val validHttpSchemes = sequenceOf("https")
fun parse(data: String?): String? {
val parsedUrl = data?.let { Uri.parse(data) } ?: return null
val scheme = parsedUrl.scheme
return when {
- scheme in validHttpSchemes && parsedUrl.host == "call.element.io" -> data
+ scheme in validHttpSchemes && parsedUrl.host == "call.element.io" -> parsedUrl
scheme == "element" && parsedUrl.host == "call" -> {
// We use this custom scheme to load arbitrary URLs for other instances of Element Call,
// so we can only verify it's an HTTP/HTTPs URL with a non-empty host
- parsedUrl.getQueryParameter("url")
- ?.let { URLDecoder.decode(it, "utf-8") }
- ?.takeIf {
- val internalUri = Uri.parse(it)
- internalUri.scheme in validHttpSchemes && !internalUri.host.isNullOrBlank()
- }
+ parsedUrl.getUrlParameter()
+ }
+ scheme == "io.element.call" && parsedUrl.host == null -> {
+ // We use this custom scheme to load arbitrary URLs for other instances of Element Call,
+ // so we can only verify it's an HTTP/HTTPs URL with a non-empty host
+ parsedUrl.getUrlParameter()
}
// This should never be possible, but we still need to take into account the possibility
else -> null
- }
+ }?.withCustomParameters()
+ }
+
+ private fun Uri.getUrlParameter(): Uri? {
+ return getQueryParameter("url")
+ ?.let { urlParameter ->
+ Uri.parse(urlParameter).takeIf { uri ->
+ uri.scheme in validHttpSchemes && !uri.host.isNullOrBlank()
+ }
+ }
}
}
+
+/**
+ * Ensure the uri has the following parameters and value in the fragment:
+ * - appPrompt=false
+ * - confineToRoom=true
+ * to ensure that the rendering will bo correct on the embedded Webview.
+ */
+private fun Uri.withCustomParameters(): String {
+ val builder = buildUpon()
+ // Remove the existing query parameters
+ builder.clearQuery()
+ queryParameterNames.forEach {
+ if (it == APP_PROMPT_PARAMETER || it == CONFINE_TO_ROOM_PARAMETER) return@forEach
+ builder.appendQueryParameter(it, getQueryParameter(it))
+ }
+ // Remove the existing fragment parameters, and build the new fragment
+ val currentFragment = fragment ?: ""
+ // Reset the current fragment
+ builder.fragment("")
+ val queryFragmentPosition = currentFragment.lastIndexOf("?")
+ val newFragment = if (queryFragmentPosition == -1) {
+ // No existing query, build it.
+ "$currentFragment?$APP_PROMPT_PARAMETER=false&$CONFINE_TO_ROOM_PARAMETER=true"
+ } else {
+ buildString {
+ append(currentFragment.substring(0, queryFragmentPosition + 1))
+ val queryFragment = currentFragment.substring(queryFragmentPosition + 1)
+ // Replace the existing parameters
+ val newQueryFragment = queryFragment
+ .replace("$APP_PROMPT_PARAMETER=true", "$APP_PROMPT_PARAMETER=false")
+ .replace("$CONFINE_TO_ROOM_PARAMETER=false", "$CONFINE_TO_ROOM_PARAMETER=true")
+ append(newQueryFragment)
+ // Ensure the parameters are there
+ if (!newQueryFragment.contains("$APP_PROMPT_PARAMETER=false")) {
+ if (newQueryFragment.isNotEmpty()) {
+ append("&")
+ }
+ append("$APP_PROMPT_PARAMETER=false")
+ }
+ if (!newQueryFragment.contains("$CONFINE_TO_ROOM_PARAMETER=true")) {
+ append("&$CONFINE_TO_ROOM_PARAMETER=true")
+ }
+ }
+ }
+ // We do not want to encode the Fragment part, so append it manually
+ return builder.build().toString() + "#" + newFragment
+}
+
+private const val APP_PROMPT_PARAMETER = "appPrompt"
+private const val CONFINE_TO_ROOM_PARAMETER = "confineToRoom"
diff --git a/features/call/src/main/kotlin/io/element/android/features/call/CallScreenView.kt b/features/call/src/main/kotlin/io/element/android/features/call/CallScreenView.kt
index 08ad687f91..0f5b90cbc8 100644
--- a/features/call/src/main/kotlin/io/element/android/features/call/CallScreenView.kt
+++ b/features/call/src/main/kotlin/io/element/android/features/call/CallScreenView.kt
@@ -24,8 +24,6 @@ import android.webkit.WebView
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -33,10 +31,11 @@ import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.viewinterop.AndroidView
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
typealias RequestPermissionCallback = (Array) -> Unit
@@ -58,7 +57,7 @@ internal fun CallScreenView(
title = { Text(stringResource(R.string.element_call)) },
navigationIcon = {
BackButton(
- imageVector = Icons.Default.Close,
+ resourceId = CommonDrawables.ic_compound_close,
onClick = onClose
)
}
@@ -138,7 +137,7 @@ private fun WebView.setup(userAgent: String, onPermissionsRequested: (Permission
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun CallScreenViewPreview() {
ElementTheme {
diff --git a/features/call/src/main/kotlin/io/element/android/features/call/ElementCallActivity.kt b/features/call/src/main/kotlin/io/element/android/features/call/ElementCallActivity.kt
index 69ef3963cb..481634a4ca 100644
--- a/features/call/src/main/kotlin/io/element/android/features/call/ElementCallActivity.kt
+++ b/features/call/src/main/kotlin/io/element/android/features/call/ElementCallActivity.kt
@@ -39,6 +39,7 @@ import javax.inject.Inject
class ElementCallActivity : ComponentActivity() {
@Inject lateinit var userAgentProvider: UserAgentProvider
+ @Inject lateinit var callIntentDataParser: CallIntentDataParser
private lateinit var audioManager: AudioManager
@@ -129,7 +130,7 @@ class ElementCallActivity : ComponentActivity() {
finishAndRemoveTask()
}
- private fun parseUrl(url: String?): String? = CallIntentDataParser.parse(url)
+ private fun parseUrl(url: String?): String? = callIntentDataParser.parse(url)
private fun registerPermissionResultLauncher(): ActivityResultLauncher> {
return registerForActivityResult(
diff --git a/features/call/src/main/res/values-cs/translations.xml b/features/call/src/main/res/values-cs/translations.xml
new file mode 100644
index 0000000000..dfe6327828
--- /dev/null
+++ b/features/call/src/main/res/values-cs/translations.xml
@@ -0,0 +1,6 @@
+
+
+ "Probíhající hovor"
+ "Klepněte pro návrat k hovoru"
+ "☎️ Probíhá hovor"
+
diff --git a/features/call/src/main/res/values-ru/translations.xml b/features/call/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..c77095f3f2
--- /dev/null
+++ b/features/call/src/main/res/values-ru/translations.xml
@@ -0,0 +1,6 @@
+
+
+ "Текущий вызов"
+ "Коснитесь, чтобы вернуться к вызову"
+ "Идёт вызов"
+
diff --git a/features/call/src/main/res/values-zh-rTW/translations.xml b/features/call/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..9a6be228ce
--- /dev/null
+++ b/features/call/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,5 @@
+
+
+ "點擊以返回到通話頁面"
+ "☎️ 通話中"
+
diff --git a/features/call/src/test/kotlin/io/element/android/features/call/CallIntentDataParserTest.kt b/features/call/src/test/kotlin/io/element/android/features/call/CallIntentDataParserTest.kt
new file mode 100644
index 0000000000..ae82767f45
--- /dev/null
+++ b/features/call/src/test/kotlin/io/element/android/features/call/CallIntentDataParserTest.kt
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.call
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import java.net.URLEncoder
+
+@RunWith(RobolectricTestRunner::class)
+class CallIntentDataParserTest {
+
+ private val callIntentDataParser = CallIntentDataParser()
+
+ @Test
+ fun `a null data returns null`() {
+ val url: String? = null
+ assertThat(callIntentDataParser.parse(url)).isNull()
+ }
+
+ @Test
+ fun `empty data returns null`() {
+ doTest("", null)
+ }
+
+ @Test
+ fun `invalid data returns null`() {
+ doTest("!", null)
+ }
+
+ @Test
+ fun `data with no scheme returns null`() {
+ doTest("test", null)
+ }
+
+ @Test
+ fun `Element Call http urls returns null`() {
+ doTest("http://call.element.io", null)
+ doTest("http://call.element.io/some-actual-call?with=parameters", null)
+ }
+
+ @Test
+ fun `Element Call urls will be returned as is`() {
+ doTest(
+ url = "https://call.element.io",
+ expectedResult = "https://call.element.io#?$EXTRA_PARAMS"
+ )
+ }
+
+ @Test
+ fun `Element Call url with url param gets url extracted`() {
+ doTest(
+ url = VALID_CALL_URL_WITH_PARAM,
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS"
+ )
+ }
+
+ @Test
+ fun `HTTP and HTTPS urls that don't come from EC return null`() {
+ doTest("http://app.element.io", null)
+ doTest("https://app.element.io", null, testEmbedded = false)
+ doTest("http://", null)
+ doTest("https://", null)
+ }
+
+ @Test
+ fun `Element Call url with no url returns null`() {
+ val embeddedUrl = VALID_CALL_URL_WITH_PARAM
+ val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
+ val url = "io.element.call:/?no_url=$encodedUrl"
+ assertThat(callIntentDataParser.parse(url)).isNull()
+ }
+
+ @Test
+ fun `element scheme with no call host returns null`() {
+ val embeddedUrl = VALID_CALL_URL_WITH_PARAM
+ val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
+ val url = "element://no-call?url=$encodedUrl"
+ assertThat(callIntentDataParser.parse(url)).isNull()
+ }
+
+ @Test
+ fun `element scheme with no data returns null`() {
+ val url = "element://call?url="
+ assertThat(callIntentDataParser.parse(url)).isNull()
+ }
+
+ @Test
+ fun `Element Call url with no data returns null`() {
+ val url = "io.element.call:/?url="
+ assertThat(callIntentDataParser.parse(url)).isNull()
+ }
+
+ @Test
+ fun `element invalid scheme returns null`() {
+ val embeddedUrl = VALID_CALL_URL_WITH_PARAM
+ val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
+ val url = "bad.scheme:/?url=$encodedUrl"
+ assertThat(callIntentDataParser.parse(url)).isNull()
+ }
+
+ @Test
+ fun `Element Call url with url extra param appPrompt gets url extracted`() {
+ doTest(
+ url = "${VALID_CALL_URL_WITH_PARAM}&appPrompt=true",
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS"
+ )
+ }
+
+ @Test
+ fun `Element Call url with url extra param in fragment appPrompt gets url extracted`() {
+ doTest(
+ url = "${VALID_CALL_URL_WITH_PARAM}#?appPrompt=true",
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#?appPrompt=false&confineToRoom=true"
+ )
+ }
+
+ @Test
+ fun `Element Call url with url extra param in fragment appPrompt and other gets url extracted`() {
+ doTest(
+ url = "${VALID_CALL_URL_WITH_PARAM}#?appPrompt=true&otherParam=maybe",
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#?appPrompt=false&otherParam=maybe&confineToRoom=true"
+ )
+ }
+
+ @Test
+ fun `Element Call url with url extra param confineToRoom gets url extracted`() {
+ doTest(
+ url = "${VALID_CALL_URL_WITH_PARAM}&confineToRoom=false",
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS"
+ )
+ }
+
+ @Test
+ fun `Element Call url with url extra param in fragment confineToRoom gets url extracted`() {
+ doTest(
+ url = "${VALID_CALL_URL_WITH_PARAM}#?confineToRoom=false",
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#?confineToRoom=true&appPrompt=false"
+ )
+ }
+
+ @Test
+ fun `Element Call url with url extra param in fragment confineToRoom and more gets url extracted`() {
+ doTest(
+ url = "${VALID_CALL_URL_WITH_PARAM}#?confineToRoom=false&otherParam=maybe",
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#?confineToRoom=true&otherParam=maybe&appPrompt=false"
+ )
+ }
+
+ @Test
+ fun `Element Call url with url fragment gets url extracted`() {
+ doTest(
+ url = "${VALID_CALL_URL_WITH_PARAM}#fragment",
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#fragment?$EXTRA_PARAMS"
+ )
+ }
+
+ @Test
+ fun `Element Call url with url fragment with params gets url extracted`() {
+ doTest(
+ url = "${VALID_CALL_URL_WITH_PARAM}#fragment?otherParam=maybe",
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#fragment?otherParam=maybe&$EXTRA_PARAMS"
+ )
+ }
+
+ @Test
+ fun `Element Call url with url fragment with other params gets url extracted`() {
+ doTest(
+ url = "${VALID_CALL_URL_WITH_PARAM}#?otherParam=maybe",
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#?otherParam=maybe&$EXTRA_PARAMS"
+ )
+ }
+
+ @Test
+ fun `Element Call url with empty fragment`() {
+ doTest(
+ url = "${VALID_CALL_URL_WITH_PARAM}#",
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS"
+ )
+ }
+
+ @Test
+ fun `Element Call url with empty fragment query`() {
+ doTest(
+ url = "${VALID_CALL_URL_WITH_PARAM}#?",
+ expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS"
+ )
+ }
+
+ private fun doTest(url: String, expectedResult: String?, testEmbedded: Boolean = true) {
+ // Test direct parsing
+ assertThat(callIntentDataParser.parse(url)).isEqualTo(expectedResult)
+
+ if (testEmbedded) {
+ // Test embedded url, scheme 1
+ val encodedUrl = URLEncoder.encode(url, "utf-8")
+ val urlScheme1 = "element://call?url=$encodedUrl"
+ assertThat(callIntentDataParser.parse(urlScheme1)).isEqualTo(expectedResult)
+
+ // Test embedded url, scheme 2
+ val urlScheme2 = "io.element.call:/?url=$encodedUrl"
+ assertThat(callIntentDataParser.parse(urlScheme2)).isEqualTo(expectedResult)
+ }
+ }
+
+ companion object {
+ const val VALID_CALL_URL_WITH_PARAM = "https://call.element.io/some-actual-call?with=parameters"
+ const val EXTRA_PARAMS = "appPrompt=false&confineToRoom=true"
+ }
+}
diff --git a/features/call/src/test/kotlin/io/element/android/features/call/CallIntentDataParserTests.kt b/features/call/src/test/kotlin/io/element/android/features/call/CallIntentDataParserTests.kt
deleted file mode 100644
index da41692b40..0000000000
--- a/features/call/src/test/kotlin/io/element/android/features/call/CallIntentDataParserTests.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2023 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 io.element.android.features.call
-
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.robolectric.RobolectricTestRunner
-import java.net.URLEncoder
-
-@RunWith(RobolectricTestRunner::class)
-class CallIntentDataParserTests {
-
- @Test
- fun `a null data returns null`() {
- val url: String? = null
- assertThat(CallIntentDataParser.parse(url)).isNull()
- }
-
- @Test
- fun `empty data returns null`() {
- val url = ""
- assertThat(CallIntentDataParser.parse(url)).isNull()
- }
-
- @Test
- fun `invalid data returns null`() {
- val url = "!"
- assertThat(CallIntentDataParser.parse(url)).isNull()
- }
-
- @Test
- fun `data with no scheme returns null`() {
- val url = "test"
- assertThat(CallIntentDataParser.parse(url)).isNull()
- }
-
- @Test
- fun `Element Call urls will be returned as is`() {
- val httpBaseUrl = "http://call.element.io"
- val httpCallUrl = "http://call.element.io/some-actual-call?with=parameters"
- val httpsBaseUrl = "https://call.element.io"
- val httpsCallUrl = "https://call.element.io/some-actual-call?with=parameters"
- assertThat(CallIntentDataParser.parse(httpBaseUrl)).isEqualTo(httpBaseUrl)
- assertThat(CallIntentDataParser.parse(httpCallUrl)).isEqualTo(httpCallUrl)
- assertThat(CallIntentDataParser.parse(httpsBaseUrl)).isEqualTo(httpsBaseUrl)
- assertThat(CallIntentDataParser.parse(httpsCallUrl)).isEqualTo(httpsCallUrl)
- }
-
- @Test
- fun `HTTP and HTTPS urls that don't come from EC return null`() {
- val httpBaseUrl = "http://app.element.io"
- val httpsBaseUrl = "https://app.element.io"
- val httpInvalidUrl = "http://"
- val httpsInvalidUrl = "http://"
- assertThat(CallIntentDataParser.parse(httpBaseUrl)).isNull()
- assertThat(CallIntentDataParser.parse(httpsBaseUrl)).isNull()
- assertThat(CallIntentDataParser.parse(httpInvalidUrl)).isNull()
- assertThat(CallIntentDataParser.parse(httpsInvalidUrl)).isNull()
- }
-
- @Test
- fun `element scheme with call host and url param gets url extracted`() {
- val embeddedUrl = "http://call.element.io/some-actual-call?with=parameters"
- val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
- val url = "element://call?url=$encodedUrl"
- assertThat(CallIntentDataParser.parse(url)).isEqualTo(embeddedUrl)
- }
-
- @Test
- fun `element scheme with call host and no url param returns null`() {
- val embeddedUrl = "http://call.element.io/some-actual-call?with=parameters"
- val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
- val url = "element://call?no-url=$encodedUrl"
- assertThat(CallIntentDataParser.parse(url)).isNull()
- }
-
- @Test
- fun `element scheme with no call host returns null`() {
- val embeddedUrl = "http://call.element.io/some-actual-call?with=parameters"
- val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
- val url = "element://no-call?url=$encodedUrl"
- assertThat(CallIntentDataParser.parse(url)).isNull()
- }
-
- @Test
- fun `element scheme with no data returns null`() {
- val url = "element://call?url="
- assertThat(CallIntentDataParser.parse(url)).isNull()
- }
-}
diff --git a/features/createroom/impl/build.gradle.kts b/features/createroom/impl/build.gradle.kts
index 3ea54ca4ea..ae9818b727 100644
--- a/features/createroom/impl/build.gradle.kts
+++ b/features/createroom/impl/build.gradle.kts
@@ -48,6 +48,7 @@ dependencies {
implementation(projects.libraries.deeplink)
implementation(projects.libraries.mediapickers.api)
implementation(projects.libraries.mediaupload.api)
+ implementation(projects.libraries.permissions.api)
implementation(projects.libraries.usersearch.impl)
implementation(projects.services.analytics.api)
implementation(libs.coil.compose)
@@ -64,6 +65,7 @@ dependencies {
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.mediapickers.test)
testImplementation(projects.libraries.mediaupload.test)
+ testImplementation(projects.libraries.permissions.test)
testImplementation(projects.libraries.usersearch.test)
testImplementation(projects.tests.testutils)
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt
index f952b2b3ec..40a597b649 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt
@@ -32,7 +32,7 @@ import io.element.android.features.createroom.impl.components.UserListView
import io.element.android.features.createroom.impl.userlist.UserListEvents
import io.element.android.features.createroom.impl.userlist.UserListState
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Scaffold
@@ -84,7 +84,7 @@ fun AddPeopleView(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun AddPeopleViewTopBar(
+private fun AddPeopleViewTopBar(
hasSelectedUsers: Boolean,
modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {},
@@ -109,7 +109,7 @@ fun AddPeopleViewTopBar(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AddPeopleViewPreview(@PreviewParameter(AddPeopleUserListStateProvider::class) state: UserListState) = ElementPreview {
AddPeopleView(state = state)
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt
index 05dfcfc063..00b4206a73 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt
@@ -31,7 +31,7 @@ import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
import io.element.android.features.createroom.impl.configureroom.RoomPrivacyItem
import io.element.android.features.createroom.impl.configureroom.roomPrivacyItems
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.RadioButton
@@ -57,7 +57,7 @@ fun RoomPrivacyOption(
) {
Icon(
modifier = Modifier.padding(horizontal = 8.dp),
- imageVector = roomPrivacyItem.icon,
+ resourceId = roomPrivacyItem.icon,
contentDescription = "",
tint = MaterialTheme.colorScheme.secondary,
)
@@ -90,7 +90,7 @@ fun RoomPrivacyOption(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoomPrivacyOptionPreview() = ElementPreview {
val aRoomPrivacyItem = roomPrivacyItems().first()
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/UserListView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/UserListView.kt
index 33bd8399a2..61a0fcbd1b 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/UserListView.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/UserListView.kt
@@ -26,7 +26,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.features.createroom.impl.userlist.UserListEvents
import io.element.android.features.createroom.impl.userlist.UserListState
import io.element.android.features.createroom.impl.userlist.UserListStateProvider
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.SelectedUsersList
@@ -76,7 +76,7 @@ fun UserListView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun UserListViewPreview(@PreviewParameter(UserListStateProvider::class) state: UserListState) = ElementPreview {
UserListView(state = state)
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt
index 21d8f8d13f..4cefa82a31 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt
@@ -18,6 +18,7 @@ package io.element.android.features.createroom.impl.configureroom
import android.net.Uri
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
@@ -40,6 +41,8 @@ import io.element.android.libraries.matrix.api.createroom.RoomVisibility
import io.element.android.libraries.matrix.ui.media.AvatarAction
import io.element.android.libraries.mediapickers.api.PickerProvider
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
+import io.element.android.libraries.permissions.api.PermissionsEvents
+import io.element.android.libraries.permissions.api.PermissionsPresenter
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
@@ -52,10 +55,15 @@ class ConfigureRoomPresenter @Inject constructor(
private val mediaPickerProvider: PickerProvider,
private val mediaPreProcessor: MediaPreProcessor,
private val analyticsService: AnalyticsService,
+ permissionsPresenterFactory: PermissionsPresenter.Factory,
) : Presenter {
+ private val cameraPermissionPresenter: PermissionsPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA)
+ private var pendingPermissionRequest = false
+
@Composable
override fun present(): ConfigureRoomState {
+ val cameraPermissionState = cameraPermissionPresenter.present()
val createRoomConfig = dataStore.getCreateRoomConfig().collectAsState(CreateRoomConfig())
val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(
@@ -75,6 +83,13 @@ class ConfigureRoomPresenter @Inject constructor(
}
}
+ LaunchedEffect(cameraPermissionState.permissionGranted) {
+ if (cameraPermissionState.permissionGranted && pendingPermissionRequest) {
+ pendingPermissionRequest = false
+ cameraPhotoPicker.launch()
+ }
+ }
+
val localCoroutineScope = rememberCoroutineScope()
val createRoomAction: MutableState> = remember { mutableStateOf(Async.Uninitialized) }
@@ -93,7 +108,12 @@ class ConfigureRoomPresenter @Inject constructor(
is ConfigureRoomEvents.HandleAvatarAction -> {
when (event.action) {
AvatarAction.ChoosePhoto -> galleryImagePicker.launch()
- AvatarAction.TakePhoto -> cameraPhotoPicker.launch()
+ AvatarAction.TakePhoto -> if (cameraPermissionState.permissionGranted) {
+ cameraPhotoPicker.launch()
+ } else {
+ pendingPermissionRequest = true
+ cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions)
+ }
AvatarAction.Remove -> dataStore.setAvatarUri(uri = null)
}
}
@@ -106,6 +126,7 @@ class ConfigureRoomPresenter @Inject constructor(
config = createRoomConfig.value,
avatarActions = avatarActions,
createRoomAction = createRoomAction.value,
+ cameraPermissionState = cameraPermissionState,
eventSink = ::handleEvents,
)
}
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt
index 2e34f3bda2..7e16cedaa7 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt
@@ -20,12 +20,14 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction
import io.element.android.features.createroom.impl.CreateRoomConfig
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.matrix.api.core.RoomId
+import io.element.android.libraries.permissions.api.PermissionsState
import kotlinx.collections.immutable.ImmutableList
data class ConfigureRoomState(
val config: CreateRoomConfig,
val avatarActions: ImmutableList,
val createRoomAction: Async,
+ val cameraPermissionState: PermissionsState,
val eventSink: (ConfigureRoomEvents) -> Unit
) {
val isCreateButtonEnabled: Boolean = config.roomName.isNullOrEmpty().not()
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt
index 0e31e9e1c0..eafbe6915b 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomStateProvider.kt
@@ -20,6 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.createroom.impl.CreateRoomConfig
import io.element.android.features.createroom.impl.userlist.aListOfSelectedUsers
import io.element.android.libraries.architecture.Async
+import io.element.android.libraries.permissions.api.aPermissionsState
import kotlinx.collections.immutable.persistentListOf
open class ConfigureRoomStateProvider : PreviewParameterProvider {
@@ -41,5 +42,6 @@ fun aConfigureRoomState() = ConfigureRoomState(
config = CreateRoomConfig(),
avatarActions = persistentListOf(),
createRoomAction = Async.Uninitialized,
+ cameraPermissionState = aPermissionsState(showDialog = false),
eventSink = { },
)
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt
index 872526fe09..8c4de1af33 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt
@@ -54,7 +54,7 @@ import io.element.android.libraries.designsystem.components.LabelledTextField
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Scaffold
@@ -65,6 +65,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet
import io.element.android.libraries.matrix.ui.components.SelectedUsersList
import io.element.android.libraries.matrix.ui.components.UnsavedAvatar
+import io.element.android.libraries.permissions.api.PermissionsView
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.launch
@@ -73,9 +74,9 @@ import kotlinx.coroutines.launch
@Composable
fun ConfigureRoomView(
state: ConfigureRoomState,
+ onBackPressed: () -> Unit,
+ onRoomCreated: (RoomId) -> Unit,
modifier: Modifier = Modifier,
- onBackPressed: () -> Unit = {},
- onRoomCreated: (RoomId) -> Unit = {},
) {
val coroutineScope = rememberCoroutineScope()
val focusManager = LocalFocusManager.current
@@ -172,11 +173,15 @@ fun ConfigureRoomView(
else -> Unit
}
+
+ PermissionsView(
+ state = state.cameraPermissionState,
+ )
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun ConfigureRoomToolbar(
+private fun ConfigureRoomToolbar(
isNextActionEnabled: Boolean,
modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {},
@@ -202,7 +207,7 @@ fun ConfigureRoomToolbar(
}
@Composable
-fun RoomNameWithAvatar(
+private fun RoomNameWithAvatar(
avatarUri: Uri?,
roomName: String,
modifier: Modifier = Modifier,
@@ -230,7 +235,7 @@ fun RoomNameWithAvatar(
}
@Composable
-fun RoomTopic(
+private fun RoomTopic(
topic: String,
modifier: Modifier = Modifier,
onTopicChanged: (String) -> Unit = {},
@@ -249,7 +254,7 @@ fun RoomTopic(
}
@Composable
-fun RoomPrivacyOptions(
+private fun RoomPrivacyOptions(
selected: RoomPrivacy?,
modifier: Modifier = Modifier,
onOptionSelected: (RoomPrivacyItem) -> Unit = {},
@@ -273,10 +278,12 @@ private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier =
})
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ConfigureRoomViewPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreview {
ConfigureRoomView(
state = state,
+ onBackPressed = {},
+ onRoomCreated = {},
)
}
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt
index 462dedba00..8193dfe5d9 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/RoomPrivacyItem.kt
@@ -16,19 +16,17 @@
package io.element.android.features.createroom.impl.configureroom
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Lock
-import androidx.compose.material.icons.outlined.Public
+import androidx.annotation.DrawableRes
import androidx.compose.runtime.Composable
-import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import io.element.android.features.createroom.impl.R
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
data class RoomPrivacyItem(
val privacy: RoomPrivacy,
- val icon: ImageVector,
+ @DrawableRes val icon: Int,
val title: String,
val description: String,
)
@@ -40,13 +38,13 @@ fun roomPrivacyItems(): ImmutableList {
when (it) {
RoomPrivacy.Private -> RoomPrivacyItem(
privacy = it,
- icon = Icons.Outlined.Lock,
+ icon = CommonDrawables.ic_compound_lock,
title = stringResource(R.string.screen_create_room_private_option_title),
description = stringResource(R.string.screen_create_room_private_option_description),
)
RoomPrivacy.Public -> RoomPrivacyItem(
privacy = it,
- icon = Icons.Outlined.Public,
+ icon = CommonDrawables.ic_compound_public,
title = stringResource(R.string.screen_create_room_public_option_title),
description = stringResource(R.string.screen_create_room_public_option_description),
)
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt
index 3bea37300c..452efa2b85 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt
@@ -27,8 +27,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -41,17 +39,17 @@ import androidx.compose.ui.unit.dp
import io.element.android.features.createroom.impl.R
import io.element.android.features.createroom.impl.components.UserListView
import io.element.android.libraries.architecture.Async
-import io.element.android.libraries.designsystem.VectorIcons
import io.element.android.libraries.designsystem.components.ProgressDialog
+import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Icon
-import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -128,7 +126,7 @@ fun CreateRoomRootView(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun CreateRoomRootViewTopBar(
+private fun CreateRoomRootViewTopBar(
modifier: Modifier = Modifier,
onClosePressed: () -> Unit = {},
) {
@@ -141,19 +139,16 @@ fun CreateRoomRootViewTopBar(
)
},
navigationIcon = {
- IconButton(onClick = onClosePressed) {
- Icon(
- imageVector = Icons.Default.Close,
- contentDescription = stringResource(id = CommonStrings.action_close),
- tint = MaterialTheme.colorScheme.primary,
- )
- }
+ BackButton(
+ resourceId = CommonDrawables.ic_compound_close,
+ onClick = onClosePressed,
+ )
}
)
}
@Composable
-fun CreateRoomActionButtonsList(
+private fun CreateRoomActionButtonsList(
state: CreateRoomRootState,
modifier: Modifier = Modifier,
onNewRoomClicked: () -> Unit = {},
@@ -161,12 +156,12 @@ fun CreateRoomActionButtonsList(
) {
Column(modifier = modifier) {
CreateRoomActionButton(
- iconRes = VectorIcons.Groups,
+ iconRes = CommonDrawables.ic_groups,
text = stringResource(id = R.string.screen_create_room_action_create_room),
onClick = onNewRoomClicked,
)
CreateRoomActionButton(
- iconRes = VectorIcons.Share,
+ iconRes = CommonDrawables.ic_compound_share_android,
text = stringResource(id = CommonStrings.action_invite_friends_to_app, state.applicationName),
onClick = onInvitePeopleClicked,
)
@@ -174,7 +169,7 @@ fun CreateRoomActionButtonsList(
}
@Composable
-fun CreateRoomActionButton(
+private fun CreateRoomActionButton(
@DrawableRes iconRes: Int,
text: String,
modifier: Modifier = Modifier,
@@ -202,7 +197,7 @@ fun CreateRoomActionButton(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun CreateRoomRootViewPreview(@PreviewParameter(CreateRoomRootStateProvider::class) state: CreateRoomRootState) =
ElementPreview {
diff --git a/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml b/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml
index 2caa011abc..a078802c36 100644
--- a/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml
@@ -4,6 +4,10 @@
"邀請朋友使用 Element"
"邀請夥伴"
"建立聊天室時發生錯誤"
+ "聊天室裡的訊息會被加密。聊天室建立後,無法停用加密功能。"
+ "私密聊天室(僅限邀請)"
+ "訊息未加密,任何人都可以查看。您可以在之後啟用加密功能。"
+ "公開聊天室(任何人)"
"聊天室名稱"
"主題(非必填)"
"建立聊天室"
diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt
index a330384f60..96935ed598 100644
--- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt
+++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt
@@ -37,6 +37,8 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction
import io.element.android.libraries.mediapickers.test.FakePickerProvider
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
+import io.element.android.libraries.permissions.test.FakePermissionsPresenter
+import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule
import io.mockk.every
@@ -55,6 +57,7 @@ import org.robolectric.RobolectricTestRunner
import java.io.File
private const val AN_URI_FROM_CAMERA = "content://uri_from_camera"
+private const val AN_URI_FROM_CAMERA_2 = "content://uri_from_camera_2"
private const val AN_URI_FROM_GALLERY = "content://uri_from_gallery"
@RunWith(RobolectricTestRunner::class)
@@ -70,6 +73,7 @@ class ConfigureRoomPresenterTests {
private lateinit var fakePickerProvider: FakePickerProvider
private lateinit var fakeMediaPreProcessor: FakeMediaPreProcessor
private lateinit var fakeAnalyticsService: FakeAnalyticsService
+ private lateinit var fakePermissionsPresenter: FakePermissionsPresenter
@Before
fun setup() {
@@ -79,12 +83,14 @@ class ConfigureRoomPresenterTests {
fakePickerProvider = FakePickerProvider()
fakeMediaPreProcessor = FakeMediaPreProcessor()
fakeAnalyticsService = FakeAnalyticsService()
+ fakePermissionsPresenter = FakePermissionsPresenter()
presenter = ConfigureRoomPresenter(
dataStore = createRoomDataStore,
matrixClient = fakeMatrixClient,
mediaPickerProvider = fakePickerProvider,
mediaPreProcessor = fakeMediaPreProcessor,
analyticsService = fakeAnalyticsService,
+ permissionsPresenterFactory = FakePermissionsPresenterFactory(fakePermissionsPresenter),
)
mockkStatic(File::readBytes)
@@ -170,8 +176,6 @@ class ConfigureRoomPresenterTests {
// Room avatar
// Pick avatar
fakePickerProvider.givenResult(null)
- newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
- newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto))
// From gallery
val uriFromGallery = Uri.parse(AN_URI_FROM_GALLERY)
fakePickerProvider.givenResult(uriFromGallery)
@@ -182,10 +186,23 @@ class ConfigureRoomPresenterTests {
// From camera
val uriFromCamera = Uri.parse(AN_URI_FROM_CAMERA)
fakePickerProvider.givenResult(uriFromCamera)
+ assertThat(newState.cameraPermissionState.permissionGranted).isFalse()
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto))
newState = awaitItem()
+ assertThat(newState.cameraPermissionState.showDialog).isTrue()
+ fakePermissionsPresenter.setPermissionGranted()
+ newState = awaitItem()
+ assertThat(newState.cameraPermissionState.permissionGranted).isTrue()
+ newState = awaitItem()
expectedConfig = expectedConfig.copy(avatarUri = uriFromCamera)
assertThat(newState.config).isEqualTo(expectedConfig)
+ // Do it again, no permission is requested
+ val uriFromCamera2 = Uri.parse(AN_URI_FROM_CAMERA_2)
+ fakePickerProvider.givenResult(uriFromCamera2)
+ newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto))
+ newState = awaitItem()
+ expectedConfig = expectedConfig.copy(avatarUri = uriFromCamera2)
+ assertThat(newState.config).isEqualTo(expectedConfig)
// Remove
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.Remove))
newState = awaitItem()
diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt
index a8f20713e4..cf6c30434b 100644
--- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt
+++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt
@@ -22,7 +22,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import io.element.android.features.ftue.impl.R
import io.element.android.libraries.designsystem.atomic.pages.SunsetPage
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
@Composable
@@ -45,7 +45,7 @@ fun MigrationScreenView(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun MigrationViewPreview() = ElementPreview {
MigrationScreenView(
diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt
index f3bffca590..43bdaa3732 100644
--- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt
+++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenter.kt
@@ -65,7 +65,7 @@ class NotificationsOptInPresenter @AssistedInject constructor(
if (notificationsPermissionsState.permissionGranted) {
callback.onNotificationsOptInFinished()
} else {
- notificationsPermissionsState.eventSink(PermissionsEvents.OpenSystemDialog)
+ notificationsPermissionsState.eventSink(PermissionsEvents.RequestPermissions)
}
}
NotificationsOptInEvents.NotNowClicked -> {
diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInStateProvider.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInStateProvider.kt
index 230e125c1b..49596856ba 100644
--- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInStateProvider.kt
+++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInStateProvider.kt
@@ -28,6 +28,6 @@ open class NotificationsOptInStateProvider : PreviewParameterProvider
"Jedná se o jednorázový proces, prosíme o strpení."
"Nastavení vašeho účtu"
+ "Nastavení můžete později změnit."
+ "Povolte oznámení a nezmeškejte žádnou zprávu"
"Hovory, hlasování, vyhledávání a další budou přidány koncem tohoto roku."
"Historie zpráv šifrovaných místností nebude v této aktualizaci k dispozici."
"Rádi bychom se od vás dozvěděli, co si o tom myslíte, dejte nám vědět prostřednictvím stránky s nastavením."
diff --git a/features/ftue/impl/src/main/res/values-ru/translations.xml b/features/ftue/impl/src/main/res/values-ru/translations.xml
index d72497bf98..ab225b5d0f 100644
--- a/features/ftue/impl/src/main/res/values-ru/translations.xml
+++ b/features/ftue/impl/src/main/res/values-ru/translations.xml
@@ -2,6 +2,8 @@
"Это одноразовый процесс, спасибо, что подождали."
"Настройка учетной записи."
+ "Вы можете изменить настройки позже."
+ "Разрешите уведомления и никогда не пропустите сообщение"
"Звонки, опросы, поиск и многое другое будут добавлены позже в этом году."
"История сообщений для зашифрованных комнат в этом обновлении будет недоступна."
"Мы будем рады услышать ваше мнение, сообщите нам об этом через страницу настроек."
diff --git a/features/ftue/impl/src/main/res/values-sk/translations.xml b/features/ftue/impl/src/main/res/values-sk/translations.xml
index aa76053cea..c7eb1f83be 100644
--- a/features/ftue/impl/src/main/res/values-sk/translations.xml
+++ b/features/ftue/impl/src/main/res/values-sk/translations.xml
@@ -8,6 +8,6 @@
"História správ pre zašifrované miestnosti nebude v tejto aktualizácii k dispozícii."
"Radi by sme od vás počuli, dajte nám vedieť, čo si myslíte, prostredníctvom stránky nastavení."
"Poďme na to!"
- "Tu je to, čo potrebujete vedieť:"
+ "Toto by ste mali vedieť:"
"Vitajte v %1$s!"
diff --git a/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml
index b8b510aac5..1edd145776 100644
--- a/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml
@@ -1,5 +1,11 @@
- "設定您的帳號"
+ "這是一次性的程序,感謝您耐心等候。"
+ "正在設定您的帳號。"
+ "通話、投票、搜尋等更多功能將在今年登場。"
+ "在這次的更新,您無法查看聊天室內被加密的歷史訊息。"
+ "我們很樂意聽取您的意見,請到設定頁面告訴我們您的想法。"
"開始吧!"
+ "我們有些事想告訴您:"
+ "歡迎使用 %1$s!"
diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt
index 9bfa1cf33c..db71128efd 100644
--- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt
+++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt
@@ -25,6 +25,7 @@ import io.element.android.libraries.permissions.api.PermissionStateProvider
import io.element.android.libraries.permissions.api.PermissionsPresenter
import io.element.android.libraries.permissions.impl.FakePermissionStateProvider
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
+import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider
import io.element.android.tests.testutils.WarmUpRule
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -130,11 +131,7 @@ class NotificationsOptInPresenterTests {
permissionStateProvider: PermissionStateProvider = FakePermissionStateProvider(),
sdkIntVersion: Int = Build.VERSION_CODES.TIRAMISU,
) = NotificationsOptInPresenter(
- permissionsPresenterFactory = object : PermissionsPresenter.Factory {
- override fun create(permission: String): PermissionsPresenter {
- return permissionsPresenter
- }
- },
+ permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter),
callback = object : NotificationsOptInNode.Callback {
override fun onNotificationsOptInFinished() {
isFinished = true
diff --git a/features/invitelist/impl/build.gradle.kts b/features/invitelist/impl/build.gradle.kts
index d8fb25585f..110eb7946e 100644
--- a/features/invitelist/impl/build.gradle.kts
+++ b/features/invitelist/impl/build.gradle.kts
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
-@Suppress("DSL_SCOPE_VIOLATION")
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListState.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListState.kt
index c1e00727f9..43644cd6aa 100644
--- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListState.kt
+++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListState.kt
@@ -25,10 +25,10 @@ import kotlinx.collections.immutable.ImmutableList
@Immutable
data class InviteListState(
val inviteList: ImmutableList,
- val declineConfirmationDialog: InviteDeclineConfirmationDialog = InviteDeclineConfirmationDialog.Hidden,
- val acceptedAction: Async = Async.Uninitialized,
- val declinedAction: Async = Async.Uninitialized,
- val eventSink: (InviteListEvents) -> Unit = {}
+ val declineConfirmationDialog: InviteDeclineConfirmationDialog,
+ val acceptedAction: Async,
+ val declinedAction: Async,
+ val eventSink: (InviteListEvents) -> Unit
)
sealed interface InviteDeclineConfirmationDialog {
diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListStateProvider.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListStateProvider.kt
index d4d1f5c166..f187398689 100644
--- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListStateProvider.kt
+++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListStateProvider.kt
@@ -39,6 +39,10 @@ open class InviteListStateProvider : PreviewParameterProvider {
internal fun aInviteListState() = InviteListState(
inviteList = aInviteListInviteSummaryList(),
+ declineConfirmationDialog = InviteDeclineConfirmationDialog.Hidden,
+ acceptedAction = Async.Uninitialized,
+ declinedAction = Async.Uninitialized,
+ eventSink = {},
)
internal fun aInviteListInviteSummaryList(): ImmutableList {
diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt
index c92abbebaa..d6d2d2be49 100644
--- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt
+++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt
@@ -39,7 +39,7 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
@@ -111,7 +111,7 @@ fun InviteListView(
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
-fun InviteListContent(
+private fun InviteListContent(
state: InviteListState,
modifier: Modifier = Modifier,
onBackClicked: () -> Unit = {},
@@ -170,7 +170,7 @@ fun InviteListContent(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun InviteListViewPreview(@PreviewParameter(InviteListStateProvider::class) state: InviteListState) = ElementPreview {
InviteListView(state)
diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt
index 180419b90a..b48cea95da 100644
--- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt
+++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt
@@ -45,7 +45,7 @@ import io.element.android.features.invitelist.impl.model.InviteListInviteSummary
import io.element.android.features.invitelist.impl.model.InviteSender
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
import io.element.android.libraries.designsystem.components.avatar.Avatar
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.ButtonSize
@@ -77,7 +77,7 @@ internal fun InviteSummaryRow(
}
@Composable
-internal fun DefaultInviteSummaryRow(
+private fun DefaultInviteSummaryRow(
invite: InviteListInviteSummary,
onAcceptClicked: () -> Unit = {},
onDeclineClicked: () -> Unit = {},
@@ -184,7 +184,7 @@ private fun SenderRow(sender: InviteSender) {
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun InviteSummaryRowPreview(@PreviewParameter(InviteListInviteSummaryProvider::class) data: InviteListInviteSummary) = ElementPreview {
InviteSummaryRow(data)
diff --git a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomState.kt b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomState.kt
index 3f14833cf0..df6d44df4d 100644
--- a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomState.kt
+++ b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomState.kt
@@ -19,10 +19,10 @@ package io.element.android.features.leaveroom.api
import io.element.android.libraries.matrix.api.core.RoomId
data class LeaveRoomState(
- val confirmation: Confirmation = Confirmation.Hidden,
- val progress: Progress = Progress.Hidden,
- val error: Error = Error.Hidden,
- val eventSink: (LeaveRoomEvent) -> Unit = {},
+ val confirmation: Confirmation,
+ val progress: Progress,
+ val error: Error,
+ val eventSink: (LeaveRoomEvent) -> Unit,
) {
sealed interface Confirmation {
data object Hidden : Confirmation
diff --git a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomStateProvider.kt b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomStateProvider.kt
index e9b08bcd18..f82b58cba3 100644
--- a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomStateProvider.kt
+++ b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomStateProvider.kt
@@ -22,32 +22,32 @@ import io.element.android.libraries.matrix.api.core.RoomId
class LeaveRoomStateProvider : PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
- LeaveRoomState(
+ aLeaveRoomState(
confirmation = LeaveRoomState.Confirmation.Hidden,
progress = LeaveRoomState.Progress.Hidden,
error = LeaveRoomState.Error.Hidden,
),
- LeaveRoomState(
+ aLeaveRoomState(
confirmation = LeaveRoomState.Confirmation.Generic(A_ROOM_ID),
progress = LeaveRoomState.Progress.Hidden,
error = LeaveRoomState.Error.Hidden,
),
- LeaveRoomState(
+ aLeaveRoomState(
confirmation = LeaveRoomState.Confirmation.PrivateRoom(A_ROOM_ID),
progress = LeaveRoomState.Progress.Hidden,
error = LeaveRoomState.Error.Hidden,
),
- LeaveRoomState(
+ aLeaveRoomState(
confirmation = LeaveRoomState.Confirmation.LastUserInRoom(A_ROOM_ID),
progress = LeaveRoomState.Progress.Hidden,
error = LeaveRoomState.Error.Hidden,
),
- LeaveRoomState(
+ aLeaveRoomState(
confirmation = LeaveRoomState.Confirmation.Hidden,
progress = LeaveRoomState.Progress.Shown,
error = LeaveRoomState.Error.Hidden,
),
- LeaveRoomState(
+ aLeaveRoomState(
confirmation = LeaveRoomState.Confirmation.Hidden,
progress = LeaveRoomState.Progress.Hidden,
error = LeaveRoomState.Error.Shown,
@@ -56,3 +56,14 @@ class LeaveRoomStateProvider : PreviewParameterProvider {
}
private val A_ROOM_ID = RoomId("!aRoomId:aDomain")
+
+fun aLeaveRoomState(
+ confirmation: LeaveRoomState.Confirmation = LeaveRoomState.Confirmation.Hidden,
+ progress: LeaveRoomState.Progress = LeaveRoomState.Progress.Hidden,
+ error: LeaveRoomState.Error = LeaveRoomState.Error.Hidden,
+) = LeaveRoomState(
+ confirmation = confirmation,
+ progress = progress,
+ error = error,
+ eventSink = {},
+)
diff --git a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt
index c916b78c5e..82bae809e8 100644
--- a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt
+++ b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt
@@ -27,7 +27,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.ui.strings.CommonStrings
@@ -107,7 +107,7 @@ private fun LeaveRoomErrorDialog(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun LeaveRoomViewPreview(
@PreviewParameter(LeaveRoomStateProvider::class) state: LeaveRoomState
diff --git a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt
index fff7c00410..a508b4f448 100644
--- a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt
+++ b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt
@@ -44,7 +44,7 @@ class LeaveRoomPresenterImplTest {
@Test
fun `present - initial state hides all dialogs`() = runTest {
- val presenter = createPresenter()
+ val presenter = createLeaveRoomPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -57,7 +57,7 @@ class LeaveRoomPresenterImplTest {
@Test
fun `present - show generic confirmation`() = runTest {
- val presenter = createPresenter(
+ val presenter = createLeaveRoomPresenter(
client = FakeMatrixClient().apply {
givenGetRoomResult(
roomId = A_ROOM_ID,
@@ -77,7 +77,7 @@ class LeaveRoomPresenterImplTest {
@Test
fun `present - show private room confirmation`() = runTest {
- val presenter = createPresenter(
+ val presenter = createLeaveRoomPresenter(
client = FakeMatrixClient().apply {
givenGetRoomResult(
roomId = A_ROOM_ID,
@@ -97,7 +97,7 @@ class LeaveRoomPresenterImplTest {
@Test
fun `present - show last user in room confirmation`() = runTest {
- val presenter = createPresenter(
+ val presenter = createLeaveRoomPresenter(
client = FakeMatrixClient().apply {
givenGetRoomResult(
roomId = A_ROOM_ID,
@@ -118,7 +118,7 @@ class LeaveRoomPresenterImplTest {
@Test
fun `present - leaving a room leaves the room`() = runTest {
val roomMembershipObserver = RoomMembershipObserver()
- val presenter = createPresenter(
+ val presenter = createLeaveRoomPresenter(
client = FakeMatrixClient().apply {
givenGetRoomResult(
roomId = A_ROOM_ID,
@@ -140,7 +140,7 @@ class LeaveRoomPresenterImplTest {
@Test
fun `present - show error if leave room fails`() = runTest {
- val presenter = createPresenter(
+ val presenter = createLeaveRoomPresenter(
client = FakeMatrixClient().apply {
givenGetRoomResult(
roomId = A_ROOM_ID,
@@ -164,7 +164,7 @@ class LeaveRoomPresenterImplTest {
@Test
fun `present - show progress indicator while leaving a room`() = runTest {
- val presenter = createPresenter(
+ val presenter = createLeaveRoomPresenter(
client = FakeMatrixClient().apply {
givenGetRoomResult(
roomId = A_ROOM_ID,
@@ -186,7 +186,7 @@ class LeaveRoomPresenterImplTest {
@Test
fun `present - hide error hides the error`() = runTest {
- val presenter = createPresenter(
+ val presenter = createLeaveRoomPresenter(
client = FakeMatrixClient().apply {
givenGetRoomResult(
roomId = A_ROOM_ID,
@@ -212,7 +212,7 @@ class LeaveRoomPresenterImplTest {
}
}
-private fun TestScope.createPresenter(
+private fun TestScope.createLeaveRoomPresenter(
client: MatrixClient = FakeMatrixClient(),
roomMembershipObserver: RoomMembershipObserver = RoomMembershipObserver(),
): LeaveRoomPresenter = LeaveRoomPresenterImpl(
diff --git a/features/leaveroom/test/build.gradle.kts b/features/leaveroom/test/build.gradle.kts
new file mode 100644
index 0000000000..56d07b7883
--- /dev/null
+++ b/features/leaveroom/test/build.gradle.kts
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2022 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.
+ */
+
+plugins {
+ id("io.element.android-compose-library")
+}
+
+android {
+ namespace = "io.element.android.features.leaveroom.test"
+}
+
+dependencies {
+ implementation(projects.libraries.core)
+ implementation(projects.libraries.architecture)
+ implementation(projects.libraries.matrix.api)
+ api(projects.features.leaveroom.api)
+}
diff --git a/features/leaveroom/fake/src/main/kotlin/io/element/android/features/leaveroom/fake/LeaveRoomPresenterFake.kt b/features/leaveroom/test/src/main/kotlin/io/element/android/features/leaveroom/fake/FakeLeaveRoomPresenter.kt
similarity index 81%
rename from features/leaveroom/fake/src/main/kotlin/io/element/android/features/leaveroom/fake/LeaveRoomPresenterFake.kt
rename to features/leaveroom/test/src/main/kotlin/io/element/android/features/leaveroom/fake/FakeLeaveRoomPresenter.kt
index 28c12b54ba..0aac5a0d74 100644
--- a/features/leaveroom/fake/src/main/kotlin/io/element/android/features/leaveroom/fake/LeaveRoomPresenterFake.kt
+++ b/features/leaveroom/test/src/main/kotlin/io/element/android/features/leaveroom/fake/FakeLeaveRoomPresenter.kt
@@ -20,9 +20,8 @@ import androidx.compose.runtime.Composable
import io.element.android.features.leaveroom.api.LeaveRoomEvent
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
import io.element.android.features.leaveroom.api.LeaveRoomState
-import javax.inject.Inject
-class LeaveRoomPresenterFake @Inject constructor() : LeaveRoomPresenter {
+class FakeLeaveRoomPresenter : LeaveRoomPresenter {
val events = mutableListOf()
@@ -30,7 +29,12 @@ class LeaveRoomPresenterFake @Inject constructor() : LeaveRoomPresenter {
events += event
}
- private var state = LeaveRoomState(eventSink = ::handleEvent)
+ private var state = LeaveRoomState(
+ confirmation = LeaveRoomState.Confirmation.Hidden,
+ progress = LeaveRoomState.Progress.Hidden,
+ error = LeaveRoomState.Error.Hidden,
+ eventSink = ::handleEvent,
+ )
set(value) {
field = value.copy(eventSink = ::handleEvent)
}
diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt
index 716b6d88c6..f4443e92f4 100644
--- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt
+++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt
@@ -21,7 +21,7 @@ import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@@ -37,12 +37,12 @@ import coil.request.ImageRequest
import io.element.android.features.location.api.internal.StaticMapPlaceholder
import io.element.android.features.location.api.internal.StaticMapUrlBuilder
import io.element.android.features.location.api.internal.centerBottomEdge
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
import timber.log.Timber
-import io.element.android.libraries.designsystem.R as DesignSystemR
/**
* Shows a static map image downloaded via a third party service's static maps API.
@@ -64,7 +64,7 @@ fun StaticMapView(
contentAlignment = Alignment.Center
) {
val context = LocalContext.current
- var retryHash by remember { mutableStateOf(0) }
+ var retryHash by remember { mutableIntStateOf(0) }
val builder = remember { StaticMapUrlBuilder(context) }
val painter = rememberAsyncImagePainter(
model = if (constraints.isZero) {
@@ -102,7 +102,7 @@ fun StaticMapView(
contentScale = ContentScale.Fit,
)
Icon(
- resourceId = DesignSystemR.drawable.pin,
+ resourceId = CommonDrawables.pin,
contentDescription = null,
tint = Color.Unspecified,
modifier = Modifier.centerBottomEdge(this),
@@ -119,7 +119,7 @@ fun StaticMapView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun StaticMapViewPreview() = ElementPreview {
StaticMapView(
diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt
index bcab870d69..f13349f0d8 100644
--- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt
+++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt
@@ -30,15 +30,15 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
-import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.element.android.features.location.api.R
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.BooleanProvider
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
@@ -78,10 +78,10 @@ internal fun StaticMapPlaceholder(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun StaticMapPlaceholderPreview(
- @PreviewParameter(BooleanParameterProvider::class) values: Boolean
+ @PreviewParameter(BooleanProvider::class) values: Boolean
) = ElementPreview {
StaticMapPlaceholder(
showProgress = values,
@@ -91,8 +91,3 @@ internal fun StaticMapPlaceholderPreview(
onLoadMapClick = {},
)
}
-
-internal class BooleanParameterProvider : PreviewParameterProvider {
- override val values: Sequence
- get() = sequenceOf(true, false)
-}
diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsState.kt
index d58361a82f..7f53876df8 100644
--- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsState.kt
+++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsState.kt
@@ -17,9 +17,9 @@
package io.element.android.features.location.impl.common.permissions
data class PermissionsState(
- val permissions: Permissions = Permissions.NoneGranted,
- val shouldShowRationale: Boolean = false,
- val eventSink: (PermissionsEvents) -> Unit = {},
+ val permissions: Permissions,
+ val shouldShowRationale: Boolean,
+ val eventSink: (PermissionsEvents) -> Unit,
) {
sealed interface Permissions {
data object AllGranted : Permissions
diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationState.kt
index 5dae23c998..a41449801b 100644
--- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationState.kt
+++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationState.kt
@@ -17,11 +17,11 @@
package io.element.android.features.location.impl.send
data class SendLocationState(
- val permissionDialog: Dialog = Dialog.None,
- val mode: Mode = Mode.PinLocation,
- val hasLocationPermission: Boolean = false,
- val appName: String = "AppName",
- val eventSink: (SendLocationEvents) -> Unit = {},
+ val permissionDialog: Dialog,
+ val mode: Mode,
+ val hasLocationPermission: Boolean,
+ val appName: String,
+ val eventSink: (SendLocationEvents) -> Unit,
) {
sealed interface Mode {
data object SenderLocation : Mode
diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationStateProvider.kt
index 15f16f593a..7cc3534f19 100644
--- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationStateProvider.kt
+++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationStateProvider.kt
@@ -23,35 +23,44 @@ private const val APP_NAME = "ApplicationName"
class SendLocationStateProvider : PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
- SendLocationState(
+ aSendLocationState(
permissionDialog = SendLocationState.Dialog.None,
mode = SendLocationState.Mode.PinLocation,
hasLocationPermission = false,
- appName = APP_NAME,
),
- SendLocationState(
+ aSendLocationState(
permissionDialog = SendLocationState.Dialog.PermissionDenied,
mode = SendLocationState.Mode.PinLocation,
hasLocationPermission = false,
- appName = APP_NAME,
),
- SendLocationState(
+ aSendLocationState(
permissionDialog = SendLocationState.Dialog.PermissionRationale,
mode = SendLocationState.Mode.PinLocation,
hasLocationPermission = false,
- appName = APP_NAME,
),
- SendLocationState(
+ aSendLocationState(
permissionDialog = SendLocationState.Dialog.None,
mode = SendLocationState.Mode.PinLocation,
hasLocationPermission = true,
- appName = APP_NAME,
),
- SendLocationState(
+ aSendLocationState(
permissionDialog = SendLocationState.Dialog.None,
mode = SendLocationState.Mode.SenderLocation,
hasLocationPermission = true,
- appName = APP_NAME,
),
)
}
+
+private fun aSendLocationState(
+ permissionDialog: SendLocationState.Dialog,
+ mode: SendLocationState.Mode,
+ hasLocationPermission: Boolean,
+): SendLocationState {
+ return SendLocationState(
+ permissionDialog = permissionDialog,
+ mode = mode,
+ hasLocationPermission = hasLocationPermission,
+ appName = APP_NAME,
+ eventSink = {}
+ )
+}
diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt
index e06b60b1a7..e70fe79fdf 100644
--- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt
+++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt
@@ -52,7 +52,7 @@ import io.element.android.features.location.impl.R
import io.element.android.features.location.impl.common.PermissionDeniedDialog
import io.element.android.features.location.impl.common.PermissionRationaleDialog
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.BottomSheetScaffold
@@ -60,13 +60,13 @@ import io.element.android.libraries.designsystem.theme.components.FloatingAction
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.maplibre.compose.CameraMode
import io.element.android.libraries.maplibre.compose.CameraMoveStartedReason
import io.element.android.libraries.maplibre.compose.MapboxMap
import io.element.android.libraries.maplibre.compose.rememberCameraPositionState
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
-import io.element.android.libraries.designsystem.R as DesignSystemR
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
@@ -203,7 +203,7 @@ fun SendLocationView(
),
)
Icon(
- resourceId = DesignSystemR.drawable.pin,
+ resourceId = CommonDrawables.pin,
contentDescription = null,
tint = Color.Unspecified,
modifier = Modifier.centerBottomEdge(this),
@@ -223,7 +223,7 @@ fun SendLocationView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun SendLocationViewPreview(
@PreviewParameter(SendLocationStateProvider::class) state: SendLocationState
diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt
index caf07cdeaf..82ac402011 100644
--- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt
+++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt
@@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.LocationSearching
import androidx.compose.material.icons.filled.MyLocation
-import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -42,7 +41,7 @@ import io.element.android.features.location.impl.common.MapDefaults
import io.element.android.features.location.impl.common.PermissionDeniedDialog
import io.element.android.features.location.impl.common.PermissionRationaleDialog
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
@@ -51,6 +50,7 @@ import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.maplibre.compose.CameraMode
import io.element.android.libraries.maplibre.compose.CameraMoveStartedReason
import io.element.android.libraries.maplibre.compose.IconAnchor
@@ -62,7 +62,6 @@ import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.theme.compound.generated.TypographyTokens
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.toImmutableMap
-import io.element.android.libraries.designsystem.R as DesignSystemR
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable
@@ -125,7 +124,10 @@ fun ShowLocationView(
},
actions = {
IconButton(onClick = { state.eventSink(ShowLocationEvents.Share) }) {
- Icon(imageVector = Icons.Outlined.Share, contentDescription = stringResource(CommonStrings.action_share))
+ Icon(
+ resourceId = CommonDrawables.ic_compound_share_android,
+ contentDescription = stringResource(CommonStrings.action_share),
+ )
}
}
)
@@ -163,7 +165,7 @@ fun ShowLocationView(
MapboxMap(
styleUri = rememberTileStyleUrl(),
modifier = Modifier.fillMaxSize(),
- images = mapOf(PIN_ID to DesignSystemR.drawable.pin).toImmutableMap(),
+ images = mapOf(PIN_ID to CommonDrawables.pin).toImmutableMap(),
cameraPositionState = cameraPositionState,
uiSettings = MapDefaults.uiSettings,
symbolManagerSettings = MapDefaults.symbolManagerSettings,
@@ -183,7 +185,7 @@ fun ShowLocationView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ShowLocationViewPreview(@PreviewParameter(ShowLocationStateProvider::class) state: ShowLocationState) = ElementPreview {
ShowLocationView(
diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/PermissionsStateFactory.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/PermissionsStateFactory.kt
new file mode 100644
index 0000000000..8fe90bea57
--- /dev/null
+++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/PermissionsStateFactory.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.location.impl
+
+import io.element.android.features.location.impl.common.permissions.PermissionsState
+
+fun aPermissionsState(
+ permissions: PermissionsState.Permissions = PermissionsState.Permissions.NoneGranted,
+ shouldShowRationale: Boolean = false,
+): PermissionsState {
+ return PermissionsState(
+ permissions = permissions,
+ shouldShowRationale = shouldShowRationale,
+ eventSink = {},
+ )
+}
diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsPresenterFake.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsPresenterFake.kt
index ad653e4df4..dfeb18d4db 100644
--- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsPresenterFake.kt
+++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsPresenterFake.kt
@@ -26,7 +26,11 @@ class PermissionsPresenterFake : PermissionsPresenter {
events += event
}
- private var state = PermissionsState(eventSink = ::handleEvent)
+ private var state = PermissionsState(
+ permissions = PermissionsState.Permissions.NoneGranted,
+ shouldShowRationale = false,
+ eventSink = ::handleEvent
+ )
set(value) {
field = value.copy(eventSink = ::handleEvent)
}
diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
index c0b4cb7d35..b6f0dc8260 100644
--- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
+++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
@@ -22,6 +22,7 @@ import app.cash.turbine.test
import com.google.common.truth.Truth
import im.vector.app.features.analytics.plan.Composer
import io.element.android.features.location.api.Location
+import io.element.android.features.location.impl.aPermissionsState
import io.element.android.features.location.impl.common.actions.FakeLocationActions
import io.element.android.features.location.impl.common.permissions.PermissionsEvents
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
@@ -32,7 +33,7 @@ import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.room.SendLocationInvocation
-import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule
import kotlinx.coroutines.delay
@@ -65,7 +66,7 @@ class SendLocationPresenterTest {
@Test
fun `initial state with permissions granted`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.AllGranted,
shouldShowRationale = false,
)
@@ -92,7 +93,7 @@ class SendLocationPresenterTest {
@Test
fun `initial state with permissions partially granted`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.SomeGranted,
shouldShowRationale = false,
)
@@ -119,7 +120,7 @@ class SendLocationPresenterTest {
@Test
fun `initial state with permissions denied`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false,
)
@@ -145,7 +146,7 @@ class SendLocationPresenterTest {
@Test
fun `initial state with permissions denied once`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true,
)
@@ -171,7 +172,7 @@ class SendLocationPresenterTest {
@Test
fun `rationale dialog dismiss`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true,
)
@@ -202,7 +203,7 @@ class SendLocationPresenterTest {
@Test
fun `rationale dialog continue`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true,
)
@@ -230,7 +231,7 @@ class SendLocationPresenterTest {
@Test
fun `permission denied dialog dismiss`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false,
)
@@ -261,7 +262,7 @@ class SendLocationPresenterTest {
@Test
fun `share sender location`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.AllGranted,
shouldShowRationale = false,
)
@@ -317,7 +318,7 @@ class SendLocationPresenterTest {
@Test
fun `share pin location`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false,
)
@@ -373,7 +374,7 @@ class SendLocationPresenterTest {
@Test
fun `composer context passes through analytics`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false,
)
@@ -419,7 +420,7 @@ class SendLocationPresenterTest {
@Test
fun `open settings activity`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false,
)
diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt
index 9eb0c3e1e2..28beb77819 100644
--- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt
+++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt
@@ -21,6 +21,7 @@ import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
import io.element.android.features.location.api.Location
+import io.element.android.features.location.impl.aPermissionsState
import io.element.android.features.location.impl.common.actions.FakeLocationActions
import io.element.android.features.location.impl.common.permissions.PermissionsEvents
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
@@ -55,7 +56,7 @@ class ShowLocationPresenterTest {
@Test
fun `emits initial state with no location permission`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false,
)
@@ -75,7 +76,7 @@ class ShowLocationPresenterTest {
@Test
fun `emits initial state location permission denied once`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true,
)
@@ -94,7 +95,7 @@ class ShowLocationPresenterTest {
@Test
fun `emits initial state with location permission`() = runTest {
- permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.AllGranted))
+ permissionsPresenterFake.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -109,7 +110,7 @@ class ShowLocationPresenterTest {
@Test
fun `emits initial state with partial location permission`() = runTest {
- permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.SomeGranted))
+ permissionsPresenterFake.givenState(aPermissionsState(permissions = PermissionsState.Permissions.SomeGranted))
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -137,7 +138,7 @@ class ShowLocationPresenterTest {
@Test
fun `centers on user location`() = runTest {
- permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.AllGranted))
+ permissionsPresenterFake.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -166,7 +167,7 @@ class ShowLocationPresenterTest {
@Test
fun `rationale dialog dismiss`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true,
)
@@ -197,7 +198,7 @@ class ShowLocationPresenterTest {
@Test
fun `rationale dialog continue`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true,
)
@@ -225,7 +226,7 @@ class ShowLocationPresenterTest {
@Test
fun `permission denied dialog dismiss`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false,
)
@@ -256,7 +257,7 @@ class ShowLocationPresenterTest {
@Test
fun `open settings activity`() = runTest {
permissionsPresenterFake.givenState(
- PermissionsState(
+ aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false,
)
@@ -290,7 +291,6 @@ class ShowLocationPresenterTest {
}
}
-
companion object {
private const val A_DESCRIPTION = "My happy place"
}
diff --git a/features/leaveroom/fake/src/main/kotlin/io/element/android/features/leaveroom/fake/LeaveRoomPresenterFakeModule.kt b/features/lockscreen/api/build.gradle.kts
similarity index 57%
rename from features/leaveroom/fake/src/main/kotlin/io/element/android/features/leaveroom/fake/LeaveRoomPresenterFakeModule.kt
rename to features/lockscreen/api/build.gradle.kts
index b20b88db1c..97f472517c 100644
--- a/features/leaveroom/fake/src/main/kotlin/io/element/android/features/leaveroom/fake/LeaveRoomPresenterFakeModule.kt
+++ b/features/lockscreen/api/build.gradle.kts
@@ -14,17 +14,14 @@
* limitations under the License.
*/
-package io.element.android.features.leaveroom.fake
-
-import com.squareup.anvil.annotations.ContributesTo
-import dagger.Binds
-import dagger.Module
-import io.element.android.features.leaveroom.api.LeaveRoomPresenter
-import io.element.android.libraries.di.SessionScope
-
-@Module
-@ContributesTo(SessionScope::class)
-interface LeaveRoomPresenterFakeModule {
- @Binds
- fun leaveRoomPresenter(leaveRoomPresenter: LeaveRoomPresenterFake): LeaveRoomPresenter
+plugins {
+ id("io.element.android-library")
+}
+
+android {
+ namespace = "io.element.android.features.lockscreen.api"
+}
+
+dependencies {
+ implementation(projects.libraries.architecture)
}
diff --git a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt
new file mode 100644
index 0000000000..3c9aceb2c8
--- /dev/null
+++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.api
+
+import io.element.android.libraries.architecture.SimpleFeatureEntryPoint
+
+interface LockScreenEntryPoint : SimpleFeatureEntryPoint
diff --git a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenState.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenState.kt
new file mode 100644
index 0000000000..d1e53cfdcc
--- /dev/null
+++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenState.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.api
+
+sealed interface LockScreenState {
+ data object Unlocked : LockScreenState
+ data object Locked : LockScreenState
+}
diff --git a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenStateService.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenStateService.kt
new file mode 100644
index 0000000000..2f2e6b2376
--- /dev/null
+++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenStateService.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.api
+
+import kotlinx.coroutines.flow.StateFlow
+
+interface LockScreenStateService {
+ val state: StateFlow
+
+ suspend fun entersForeground()
+ suspend fun entersBackground()
+ suspend fun unlock()
+}
diff --git a/features/lockscreen/impl/build.gradle.kts b/features/lockscreen/impl/build.gradle.kts
new file mode 100644
index 0000000000..af63538db5
--- /dev/null
+++ b/features/lockscreen/impl/build.gradle.kts
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+plugins {
+ id("io.element.android-compose-library")
+ alias(libs.plugins.anvil)
+ alias(libs.plugins.ksp)
+ id("kotlin-parcelize")
+}
+
+android {
+ namespace = "io.element.android.features.lockscreen.impl"
+}
+
+anvil {
+ generateDaggerFactories.set(true)
+}
+
+dependencies {
+ implementation(projects.anvilannotations)
+ anvil(projects.anvilcodegen)
+ api(projects.features.lockscreen.api)
+ implementation(projects.libraries.core)
+ implementation(projects.libraries.architecture)
+ implementation(projects.libraries.matrix.api)
+ implementation(projects.libraries.matrixui)
+ implementation(projects.libraries.designsystem)
+ implementation(projects.libraries.featureflag.api)
+ implementation(projects.libraries.cryptography.api)
+
+ testImplementation(libs.test.junit)
+ testImplementation(libs.coroutines.test)
+ testImplementation(libs.molecule.runtime)
+ testImplementation(libs.test.truth)
+ testImplementation(libs.test.turbine)
+ testImplementation(projects.libraries.matrix.test)
+ testImplementation(projects.libraries.cryptography.test)
+ testImplementation(projects.libraries.cryptography.impl)
+
+ ksp(libs.showkase.processor)
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt
new file mode 100644
index 0000000000..736be374cd
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl
+
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.features.lockscreen.api.LockScreenEntryPoint
+import io.element.android.libraries.architecture.createNode
+import io.element.android.libraries.di.AppScope
+import javax.inject.Inject
+
+@ContributesBinding(AppScope::class)
+class DefaultLockScreenEntryPoint @Inject constructor() : LockScreenEntryPoint {
+
+ override fun createNode(parentNode: Node, buildContext: BuildContext): Node {
+ return parentNode.createNode(buildContext)
+ }
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt
new file mode 100644
index 0000000000..d2989d53cc
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl
+
+import android.os.Parcelable
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.bumble.appyx.core.composable.Children
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import com.bumble.appyx.navmodel.backstack.BackStack
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import io.element.android.anvilannotations.ContributesNode
+import io.element.android.features.lockscreen.impl.auth.PinAuthenticationNode
+import io.element.android.features.lockscreen.impl.create.CreatePinNode
+import io.element.android.libraries.architecture.BackstackNode
+import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
+import io.element.android.libraries.architecture.createNode
+import io.element.android.libraries.di.AppScope
+import kotlinx.parcelize.Parcelize
+
+@ContributesNode(AppScope::class)
+class LockScreenFlowNode @AssistedInject constructor(
+ @Assisted buildContext: BuildContext,
+ @Assisted plugins: List,
+) : BackstackNode(
+ backstack = BackStack(
+ initialElement = NavTarget.Auth,
+ savedStateMap = buildContext.savedStateMap,
+ ),
+ buildContext = buildContext,
+ plugins = plugins,
+) {
+
+ sealed interface NavTarget : Parcelable {
+ @Parcelize
+ data object Auth : NavTarget
+
+ @Parcelize
+ data object Create : NavTarget
+ }
+
+ override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
+ return when (navTarget) {
+ NavTarget.Auth -> {
+ createNode(buildContext)
+ }
+ NavTarget.Create -> {
+ createNode(buildContext)
+ }
+ }
+ }
+
+ @Composable
+ override fun View(modifier: Modifier) {
+ Children(
+ navModel = backstack,
+ modifier = modifier,
+ transitionHandler = rememberDefaultTransitionHandler(),
+ )
+ }
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationEvents.kt
new file mode 100644
index 0000000000..f9f46c430a
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationEvents.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.auth
+
+sealed interface PinAuthenticationEvents {
+ data object Unlock : PinAuthenticationEvents
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationNode.kt
new file mode 100644
index 0000000000..d236d40cf1
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationNode.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.auth
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import io.element.android.anvilannotations.ContributesNode
+import io.element.android.libraries.di.AppScope
+
+@ContributesNode(AppScope::class)
+class PinAuthenticationNode @AssistedInject constructor(
+ @Assisted buildContext: BuildContext,
+ @Assisted plugins: List,
+ private val presenter: PinAuthenticationPresenter,
+) : Node(buildContext, plugins = plugins) {
+
+ @Composable
+ override fun View(modifier: Modifier) {
+ val state = presenter.present()
+ PinAuthenticationView(
+ state = state,
+ modifier = modifier
+ )
+ }
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationPresenter.kt
new file mode 100644
index 0000000000..ecc82f421c
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationPresenter.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.auth
+
+import androidx.compose.runtime.Composable
+import io.element.android.features.lockscreen.api.LockScreenStateService
+import io.element.android.libraries.architecture.Presenter
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+class PinAuthenticationPresenter @Inject constructor(
+ private val pinStateService: LockScreenStateService,
+ private val coroutineScope: CoroutineScope,
+) : Presenter {
+
+ @Composable
+ override fun present(): PinAuthenticationState {
+
+ fun handleEvents(event: PinAuthenticationEvents) {
+ when (event) {
+ PinAuthenticationEvents.Unlock -> coroutineScope.launch { pinStateService.unlock() }
+ }
+ }
+ return PinAuthenticationState(
+ eventSink = ::handleEvents
+ )
+ }
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationState.kt
new file mode 100644
index 0000000000..387467534f
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationState.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.auth
+
+data class PinAuthenticationState(
+ val eventSink: (PinAuthenticationEvents) -> Unit
+)
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationStateProvider.kt
new file mode 100644
index 0000000000..a2612ed858
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationStateProvider.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.auth
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+
+open class PinAuthenticationStateProvider : PreviewParameterProvider {
+ override val values: Sequence
+ get() = sequenceOf(
+ aPinAuthenticationState(),
+ )
+}
+
+fun aPinAuthenticationState() = PinAuthenticationState(
+ eventSink = {}
+)
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationView.kt
new file mode 100644
index 0000000000..2b62e46800
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/auth/PinAuthenticationView.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.auth
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.systemBarsPadding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Lock
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
+import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.theme.components.Button
+import io.element.android.libraries.designsystem.theme.components.Surface
+
+@Composable
+fun PinAuthenticationView(
+ state: PinAuthenticationState,
+ modifier: Modifier = Modifier,
+) {
+ Surface(modifier) {
+ HeaderFooterPage(
+ modifier = Modifier
+ .systemBarsPadding()
+ .fillMaxSize(),
+ header = { PinAuthenticationHeader(modifier = Modifier.padding(top = 60.dp, bottom = 12.dp)) },
+ footer = { PinAuthenticationFooter(state) },
+ )
+ }
+}
+
+@Composable
+private fun PinAuthenticationHeader(
+ modifier: Modifier = Modifier,
+) {
+ IconTitleSubtitleMolecule(
+ modifier = modifier,
+ title = "Element X is locked",
+ subTitle = null,
+ iconImageVector = Icons.Default.Lock,
+ )
+}
+
+@Composable
+private fun PinAuthenticationFooter(state: PinAuthenticationState) {
+ Button(
+ modifier = Modifier.fillMaxWidth(),
+ text = "Unlock",
+ onClick = {
+ state.eventSink(PinAuthenticationEvents.Unlock)
+ }
+ )
+}
+
+@Composable
+@PreviewsDayNight
+internal fun PinAuthenticationViewPreview(@PreviewParameter(PinAuthenticationStateProvider::class) state: PinAuthenticationState) {
+ ElementPreview {
+ PinAuthenticationView(
+ state = state,
+ )
+ }
+}
+
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt
new file mode 100644
index 0000000000..deb3095e69
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.create
+
+sealed interface CreatePinEvents {
+ object MyEvent : CreatePinEvents
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinNode.kt
new file mode 100644
index 0000000000..3689c0cc76
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinNode.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.create
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import io.element.android.anvilannotations.ContributesNode
+import io.element.android.libraries.di.AppScope
+
+@ContributesNode(AppScope::class)
+class CreatePinNode @AssistedInject constructor(
+ @Assisted buildContext: BuildContext,
+ @Assisted plugins: List,
+ private val presenter: CreatePinPresenter,
+) : Node(buildContext, plugins = plugins) {
+
+ @Composable
+ override fun View(modifier: Modifier) {
+ val state = presenter.present()
+ CreatePinView(
+ state = state,
+ modifier = modifier
+ )
+ }
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt
new file mode 100644
index 0000000000..08ba24e074
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.create
+
+import androidx.compose.runtime.Composable
+import io.element.android.libraries.architecture.Presenter
+import javax.inject.Inject
+
+class CreatePinPresenter @Inject constructor() : Presenter {
+
+ @Composable
+ override fun present(): CreatePinState {
+
+ fun handleEvents(event: CreatePinEvents) {
+ when (event) {
+ CreatePinEvents.MyEvent -> Unit
+ }
+ }
+
+ return CreatePinState(
+ eventSink = ::handleEvents
+ )
+ }
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt
new file mode 100644
index 0000000000..67311639ad
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.create
+
+data class CreatePinState(
+ val eventSink: (CreatePinEvents) -> Unit
+)
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt
new file mode 100644
index 0000000000..a918b5193e
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.create
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+
+open class CreatePinStateProvider : PreviewParameterProvider {
+ override val values: Sequence
+ get() = sequenceOf(
+ aCreatePinState(),
+ // Add other states here
+ )
+}
+
+fun aCreatePinState() = CreatePinState(
+ eventSink = {}
+)
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt
new file mode 100644
index 0000000000..120c0b6079
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.create
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.theme.components.Text
+import timber.log.Timber
+
+@Composable
+fun CreatePinView(
+ state: CreatePinState,
+ modifier: Modifier = Modifier,
+) {
+ Timber.d("CreatePinView: $state")
+ Box(modifier, contentAlignment = Alignment.Center) {
+ Text(
+ "CreatePin feature view",
+ color = MaterialTheme.colorScheme.primary,
+ )
+ }
+}
+
+@Composable
+@PreviewsDayNight
+internal fun CreatePinViewPreview(@PreviewParameter(CreatePinStateProvider::class) state: CreatePinState) {
+ ElementPreview {
+ CreatePinView(
+ state = state,
+ )
+ }
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt
new file mode 100644
index 0000000000..e7529e9280
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.pin
+
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.features.lockscreen.impl.pin.storage.PinCodeStore
+import io.element.android.libraries.cryptography.api.EncryptionDecryptionService
+import io.element.android.libraries.cryptography.api.EncryptionResult
+import io.element.android.libraries.cryptography.api.SecretKeyProvider
+import io.element.android.libraries.di.AppScope
+import javax.inject.Inject
+
+private const val SECRET_KEY_ALIAS = "SECRET_KEY_ALIAS_PIN_CODE"
+
+@ContributesBinding(AppScope::class)
+class DefaultPinCodeManager @Inject constructor(
+ private val secretKeyProvider: SecretKeyProvider,
+ private val encryptionDecryptionService: EncryptionDecryptionService,
+ private val pinCodeStore: PinCodeStore,
+) : PinCodeManager {
+
+ override suspend fun isPinCodeAvailable(): Boolean {
+ return pinCodeStore.hasPinCode()
+ }
+
+ override suspend fun createPinCode(pinCode: String) {
+ val secretKey = secretKeyProvider.getOrCreateKey(SECRET_KEY_ALIAS)
+ val encryptedPinCode = encryptionDecryptionService.encrypt(secretKey, pinCode.toByteArray()).toBase64()
+ pinCodeStore.saveEncryptedPinCode(encryptedPinCode)
+ }
+
+ override suspend fun verifyPinCode(pinCode: String): Boolean {
+ val encryptedPinCode = pinCodeStore.getEncryptedCode() ?: return false
+ return try {
+ val secretKey = secretKeyProvider.getOrCreateKey(SECRET_KEY_ALIAS)
+ val decryptedPinCode = encryptionDecryptionService.decrypt(secretKey, EncryptionResult.fromBase64(encryptedPinCode))
+ decryptedPinCode.contentEquals(pinCode.toByteArray())
+ } catch (failure: Throwable) {
+ false
+ }
+ }
+
+ override suspend fun deletePinCode() {
+ pinCodeStore.deleteEncryptedPinCode()
+ }
+
+ override suspend fun getRemainingPinCodeAttemptsNumber(): Int {
+ return pinCodeStore.getRemainingPinCodeAttemptsNumber()
+ }
+
+ override suspend fun onWrongPin(): Int {
+ return pinCodeStore.onWrongPin()
+ }
+
+ override suspend fun resetCounter() {
+ pinCodeStore.resetCounter()
+ }
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt
new file mode 100644
index 0000000000..5f84f5296d
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.pin
+
+/**
+ * This interface is the main interface to manage the pin code.
+ * Implementation should take care of encrypting the pin code and storing it.
+ */
+interface PinCodeManager {
+ /**
+ * @return true if a pin code is available.
+ */
+ suspend fun isPinCodeAvailable(): Boolean
+
+ /**
+ * Creates a new encrypted pin code.
+ * @param pinCode the clear pin code to create
+ */
+ suspend fun createPinCode(pinCode: String)
+
+ /**
+ * @return true if the pin code is correct.
+ */
+ suspend fun verifyPinCode(pinCode: String): Boolean
+
+ /**
+ * Deletes the previously created pin code.
+ */
+ suspend fun deletePinCode()
+
+ /**
+ * @return the number of remaining attempts before the pin code is blocked.
+ */
+ suspend fun getRemainingPinCodeAttemptsNumber(): Int
+
+ /**
+ * Should be called when the pin code is incorrect.
+ * Will decrement the remaining attempts number.
+ * @return the number of remaining attempts before the pin code is blocked.
+ */
+ suspend fun onWrongPin(): Int
+
+ /**
+ * Resets the counter of attempts for PIN code.
+ */
+ suspend fun resetCounter()
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/EncryptedPinCodeStorage.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/EncryptedPinCodeStorage.kt
new file mode 100644
index 0000000000..2345eaf481
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/EncryptedPinCodeStorage.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.pin.storage
+
+/**
+ * Should be implemented by any class that provides access to the encrypted PIN code.
+ * All methods are suspending in case there are async IO operations involved.
+ */
+interface EncryptedPinCodeStorage {
+ /**
+ * Returns the encrypted PIN code.
+ */
+ suspend fun getEncryptedCode(): String?
+
+ /**
+ * Saves the encrypted PIN code to some persistable storage.
+ */
+ suspend fun saveEncryptedPinCode(pinCode: String)
+
+ /**
+ * Deletes the PIN code from some persistable storage.
+ */
+ suspend fun deleteEncryptedPinCode()
+
+ /**
+ * Returns whether the PIN code is stored or not.
+ */
+ suspend fun hasPinCode(): Boolean
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/PinCodeStore.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/PinCodeStore.kt
new file mode 100644
index 0000000000..e72cbca2db
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/PinCodeStore.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.pin.storage
+
+interface PinCodeStore : EncryptedPinCodeStorage {
+
+ interface Listener {
+ fun onPinSetUpChange(isConfigured: Boolean)
+ }
+
+ /**
+ * Returns the remaining PIN code attempts. When this reaches 0 the PIN code access won't be available for some time.
+ */
+ suspend fun getRemainingPinCodeAttemptsNumber(): Int
+
+ /**
+ * Should decrement the number of remaining PIN code attempts.
+ * @return The remaining attempts.
+ */
+ suspend fun onWrongPin(): Int
+
+ /**
+ * Resets the counter of attempts for PIN code and biometric access.
+ */
+ suspend fun resetCounter()
+
+ /**
+ * Adds a listener to be notified when the PIN code us created or removed.
+ */
+ fun addListener(listener: Listener)
+
+ /**
+ * Removes a listener to be notified when the PIN code us created or removed.
+ */
+ fun removeListener(listener: Listener)
+}
+
+
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/SharedPreferencesPinCodeStore.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/SharedPreferencesPinCodeStore.kt
new file mode 100644
index 0000000000..27f4636400
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/storage/SharedPreferencesPinCodeStore.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.pin.storage
+
+import android.content.SharedPreferences
+import androidx.core.content.edit
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.libraries.core.coroutine.CoroutineDispatchers
+import io.element.android.libraries.di.AppScope
+import io.element.android.libraries.di.SingleIn
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import kotlinx.coroutines.withContext
+import java.util.concurrent.CopyOnWriteArrayList
+import javax.inject.Inject
+
+private const val ENCODED_PIN_CODE_KEY = "ENCODED_PIN_CODE_KEY"
+private const val REMAINING_PIN_CODE_ATTEMPTS_KEY = "REMAINING_PIN_CODE_ATTEMPTS_KEY"
+private const val MAX_PIN_CODE_ATTEMPTS_NUMBER_BEFORE_LOGOUT = 3
+
+@SingleIn(AppScope::class)
+@ContributesBinding(AppScope::class)
+class SharedPreferencesPinCodeStore @Inject constructor(
+ private val dispatchers: CoroutineDispatchers,
+ private val sharedPreferences: SharedPreferences,
+) : PinCodeStore {
+
+ private val listeners = CopyOnWriteArrayList()
+ private val mutex = Mutex()
+
+ override suspend fun getEncryptedCode(): String? = withContext(dispatchers.io) {
+ sharedPreferences.getString(ENCODED_PIN_CODE_KEY, null)
+ }
+
+ override suspend fun saveEncryptedPinCode(pinCode: String) = withContext(dispatchers.io) {
+ sharedPreferences.edit {
+ putString(ENCODED_PIN_CODE_KEY, pinCode)
+ }
+ withContext(dispatchers.main) {
+ listeners.forEach { it.onPinSetUpChange(isConfigured = true) }
+ }
+ }
+
+ override suspend fun deleteEncryptedPinCode() = withContext(dispatchers.io) {
+ // Also reset the counters
+ resetCounter()
+ sharedPreferences.edit {
+ remove(ENCODED_PIN_CODE_KEY)
+ }
+ withContext(dispatchers.main) {
+ listeners.forEach { it.onPinSetUpChange(isConfigured = false) }
+ }
+ }
+
+ override suspend fun hasPinCode(): Boolean = withContext(dispatchers.io) {
+ sharedPreferences.contains(ENCODED_PIN_CODE_KEY)
+ }
+
+ override suspend fun getRemainingPinCodeAttemptsNumber(): Int = withContext(dispatchers.io) {
+ mutex.withLock {
+ sharedPreferences.getInt(REMAINING_PIN_CODE_ATTEMPTS_KEY, MAX_PIN_CODE_ATTEMPTS_NUMBER_BEFORE_LOGOUT)
+ }
+ }
+
+ override suspend fun onWrongPin(): Int = withContext(dispatchers.io) {
+ mutex.withLock {
+ val remaining = getRemainingPinCodeAttemptsNumber() - 1
+ sharedPreferences.edit {
+ putInt(REMAINING_PIN_CODE_ATTEMPTS_KEY, remaining)
+ }
+ remaining
+ }
+ }
+
+ override suspend fun resetCounter() = withContext(dispatchers.io) {
+ mutex.withLock {
+ sharedPreferences.edit {
+ remove(REMAINING_PIN_CODE_ATTEMPTS_KEY)
+ }
+ }
+ }
+
+ override fun addListener(listener: PinCodeStore.Listener) {
+ listeners.add(listener)
+ }
+
+ override fun removeListener(listener: PinCodeStore.Listener) {
+ listeners.remove(listener)
+ }
+}
diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/state/DefaultLockScreenStateService.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/state/DefaultLockScreenStateService.kt
new file mode 100644
index 0000000000..dbfeca2c6a
--- /dev/null
+++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/state/DefaultLockScreenStateService.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.state
+
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.features.lockscreen.api.LockScreenState
+import io.element.android.features.lockscreen.api.LockScreenStateService
+import io.element.android.libraries.di.AppScope
+import io.element.android.libraries.di.SingleIn
+import io.element.android.libraries.featureflag.api.FeatureFlagService
+import io.element.android.libraries.featureflag.api.FeatureFlags
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+private const val GRACE_PERIOD_IN_MILLIS = 90 * 1000L
+
+@SingleIn(AppScope::class)
+@ContributesBinding(AppScope::class)
+class DefaultLockScreenStateService @Inject constructor(
+ private val featureFlagService: FeatureFlagService,
+) : LockScreenStateService {
+
+ private val _lockScreenState = MutableStateFlow(LockScreenState.Unlocked)
+ override val state: StateFlow = _lockScreenState
+
+ private var lockJob: Job? = null
+
+ override suspend fun unlock() {
+ if (featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock)) {
+ _lockScreenState.value = LockScreenState.Unlocked
+ }
+ }
+
+ override suspend fun entersForeground() {
+ lockJob?.cancel()
+ }
+
+ override suspend fun entersBackground() = coroutineScope {
+ lockJob = launch {
+ if (featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock)) {
+ delay(GRACE_PERIOD_IN_MILLIS)
+ _lockScreenState.value = LockScreenState.Locked
+ }
+ }
+ }
+}
diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt
new file mode 100644
index 0000000000..8b14d15e5e
--- /dev/null
+++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.pin
+
+import com.google.common.truth.Truth.assertThat
+import io.element.android.features.lockscreen.impl.pin.storage.InMemoryPinCodeStore
+import io.element.android.libraries.cryptography.impl.AESEncryptionDecryptionService
+import io.element.android.libraries.cryptography.test.SimpleSecretKeyProvider
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+class DefaultPinCodeManagerTest {
+
+ private val pinCodeStore = InMemoryPinCodeStore()
+ private val secretKeyProvider = SimpleSecretKeyProvider()
+ private val encryptionDecryptionService = AESEncryptionDecryptionService()
+ private val pinCodeManager = DefaultPinCodeManager(secretKeyProvider, encryptionDecryptionService, pinCodeStore)
+
+ @Test
+ fun `given a pin code when create and delete assert no pin code left`() = runTest {
+ pinCodeManager.createPinCode("1234")
+ assertThat(pinCodeManager.isPinCodeAvailable()).isTrue()
+ pinCodeManager.deletePinCode()
+ assertThat(pinCodeManager.isPinCodeAvailable()).isFalse()
+ }
+
+ @Test
+ fun `given a pin code when create and verify with the same pin succeed`() = runTest {
+ val pinCode = "1234"
+ pinCodeManager.createPinCode(pinCode)
+ assertThat(pinCodeManager.verifyPinCode(pinCode)).isTrue()
+ }
+
+ @Test
+ fun `given a pin code when create and verify with a different pin fails`() = runTest {
+ pinCodeManager.createPinCode("1234")
+ assertThat(pinCodeManager.verifyPinCode("1235")).isFalse()
+ }
+}
diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryPinCodeStore.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryPinCodeStore.kt
new file mode 100644
index 0000000000..0b7c2f256b
--- /dev/null
+++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryPinCodeStore.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.pin.storage
+
+private const val DEFAULT_REMAINING_ATTEMPTS = 3
+
+class InMemoryPinCodeStore : PinCodeStore {
+
+ private var pinCode: String? = null
+ private var remainingAttempts: Int = DEFAULT_REMAINING_ATTEMPTS
+
+ override suspend fun getRemainingPinCodeAttemptsNumber(): Int {
+ return remainingAttempts
+ }
+
+ override suspend fun onWrongPin(): Int {
+ return remainingAttempts--
+ }
+
+ override suspend fun resetCounter() {
+ remainingAttempts = DEFAULT_REMAINING_ATTEMPTS
+ }
+
+ override fun addListener(listener: PinCodeStore.Listener) {
+ // no-op
+ }
+
+ override fun removeListener(listener: PinCodeStore.Listener) {
+ // no-op
+ }
+
+ override suspend fun getEncryptedCode(): String? {
+ return pinCode
+ }
+
+ override suspend fun saveEncryptedPinCode(pinCode: String) {
+ this.pinCode = pinCode
+ }
+
+ override suspend fun deleteEncryptedPinCode() {
+ pinCode = null
+ }
+
+ override suspend fun hasPinCode(): Boolean {
+ return pinCode != null
+ }
+}
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProvider.kt
index b6aea81951..139893d600 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProvider.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProvider.kt
@@ -16,8 +16,9 @@
package io.element.android.features.login.impl.accountprovider
-data class AccountProvider constructor(
- val title: String,
+data class AccountProvider(
+ val url: String,
+ val title: String = url.removePrefix("https://").removePrefix("http://"),
val subtitle: String? = null,
val isPublic: Boolean = false,
val isMatrixOrg: Boolean = false,
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderProvider.kt
index 71e1abd591..35fd7246f2 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderProvider.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderProvider.kt
@@ -17,6 +17,7 @@
package io.element.android.features.login.impl.accountprovider
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import io.element.android.features.login.impl.util.LoginConstants
open class AccountProviderProvider : PreviewParameterProvider {
override val values: Sequence
@@ -31,7 +32,7 @@ open class AccountProviderProvider : PreviewParameterProvider {
}
fun anAccountProvider() = AccountProvider(
- title = "matrix.org",
+ url = LoginConstants.MATRIX_ORG_URL,
subtitle = "Matrix.org is an open network for secure, decentralized communication.",
isPublic = true,
isMatrixOrg = true,
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt
index 9c3fb967a8..2f6f736935 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt
@@ -23,8 +23,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -35,11 +33,12 @@ import androidx.compose.ui.unit.dp
import io.element.android.features.login.impl.R
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
/**
@@ -75,7 +74,7 @@ fun AccountProviderView(
} else {
RoundedIconAtom(
size = RoundedIconAtomSize.Medium,
- imageVector = Icons.Filled.Search,
+ resourceId = CommonDrawables.ic_compound_search,
tint = MaterialTheme.colorScheme.primary,
)
}
@@ -111,7 +110,7 @@ fun AccountProviderView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AccountProviderViewPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) = ElementPreview {
AccountProviderView(
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt
index 8813a6a037..726e241253 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt
@@ -63,7 +63,7 @@ class ChangeServerPresenter @Inject constructor(
changeServerAction: MutableState>,
) = launch {
suspend {
- authenticationService.setHomeserver(data.title).map {
+ authenticationService.setHomeserver(data.url).map {
authenticationService.getHomeserverDetails().value!!
// Valid, remember user choice
accountProviderDataSource.userSelection(data)
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt
index e61d149463..98e9278405 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt
@@ -25,7 +25,7 @@ import io.element.android.features.login.impl.error.ChangeServerError
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
@Composable
@@ -68,7 +68,7 @@ fun ChangeServerView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ChangeServerViewPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) = ElementPreview {
ChangeServerView(
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt
index 6ef5be06ab..a151b34303 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt
@@ -21,6 +21,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import io.element.android.features.login.impl.R
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
@@ -39,3 +41,12 @@ internal fun SlidingSyncNotSupportedDialog(
content = stringResource(R.string.screen_change_server_error_no_sliding_sync_message),
)
}
+
+@PreviewsDayNight
+@Composable
+internal fun SlidingSyncNotSupportedDialogPreview() = ElementPreview {
+ SlidingSyncNotSupportedDialog(
+ onLearnMoreClicked = {},
+ onDismiss = {},
+ )
+}
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt
index 9771570e92..33c7a8da57 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt
@@ -33,7 +33,7 @@ import io.element.android.features.login.impl.oidc.OidcUrlParser
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
@@ -97,7 +97,7 @@ fun OidcView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun OidcViewPreview(@PreviewParameter(OidcStateProvider::class) state: OidcState) = ElementPreview {
OidcView(
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt
index dfdb7dcf99..786d8aaeae 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt
@@ -19,6 +19,7 @@ package io.element.android.features.login.impl.screens.changeaccountprovider
import androidx.compose.runtime.Composable
import io.element.android.features.login.impl.accountprovider.AccountProvider
import io.element.android.features.login.impl.changeserver.ChangeServerPresenter
+import io.element.android.features.login.impl.util.LoginConstants
import io.element.android.libraries.architecture.Presenter
import javax.inject.Inject
@@ -33,7 +34,7 @@ class ChangeAccountProviderPresenter @Inject constructor(
// Just matrix.org by default for now
accountProviders = listOf(
AccountProvider(
- title = "matrix.org",
+ url = LoginConstants.MATRIX_ORG_URL,
subtitle = null,
isPublic = true,
isMatrixOrg = true,
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt
index c329367a06..0d375fb2d6 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt
@@ -45,7 +45,7 @@ import io.element.android.features.login.impl.changeserver.ChangeServerEvents
import io.element.android.features.login.impl.changeserver.ChangeServerView
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TopAppBar
@@ -109,6 +109,7 @@ fun ChangeAccountProviderView(
// Other
AccountProviderView(
item = AccountProvider(
+ url = "",
title = stringResource(id = R.string.screen_change_account_provider_other),
),
onClick = onOtherProviderClicked
@@ -124,7 +125,7 @@ fun ChangeAccountProviderView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ChangeAccountProviderViewPreview(@PreviewParameter(ChangeAccountProviderStateProvider::class) state: ChangeAccountProviderState) = ElementPreview {
ChangeAccountProviderView(
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt
index a647441800..21896e4408 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt
@@ -76,7 +76,7 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor(
fun handleEvents(event: ConfirmAccountProviderEvents) {
when (event) {
ConfirmAccountProviderEvents.Continue -> {
- localCoroutineScope.submit(accountProvider.title, loginFlowAction)
+ localCoroutineScope.submit(accountProvider.url, loginFlowAction)
}
ConfirmAccountProviderEvents.ClearError -> loginFlowAction.value = Async.Uninitialized
}
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt
index c2c98101a5..2fcb57af94 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderState.kt
@@ -27,7 +27,7 @@ data class ConfirmAccountProviderState(
val loginFlow: Async,
val eventSink: (ConfirmAccountProviderEvents) -> Unit
) {
- val submitEnabled: Boolean get() = accountProvider.title.isNotEmpty() && (loginFlow is Async.Uninitialized || loginFlow is Async.Loading)
+ val submitEnabled: Boolean get() = accountProvider.url.isNotEmpty() && (loginFlow is Async.Uninitialized || loginFlow is Async.Loading)
}
sealed interface LoginFlow {
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt
index 3b30a88a0f..22862c4467 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt
@@ -36,13 +36,14 @@ import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMo
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
+import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun ConfirmAccountProviderView(
@@ -86,7 +87,7 @@ fun ConfirmAccountProviderView(
footer = {
ButtonColumnMolecule {
Button(
- text = stringResource(id = R.string.screen_account_provider_continue),
+ text = stringResource(id = CommonStrings.action_continue),
showProgress = isLoading,
onClick = { eventSink.invoke(ConfirmAccountProviderEvents.Continue) },
enabled = state.submitEnabled || isLoading,
@@ -138,7 +139,7 @@ fun ConfirmAccountProviderView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ConfirmAccountProviderViewPreview(
@PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt
index acc5d87e68..8a69cdf442 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt
@@ -31,9 +31,6 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
-import androidx.compose.material.icons.filled.Close
-import androidx.compose.material.icons.filled.Visibility
-import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
@@ -61,7 +58,7 @@ import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubti
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.form.textFieldState
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Icon
@@ -72,6 +69,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.theme.components.autofill
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.theme.ElementTheme
@@ -141,7 +139,7 @@ fun LoginPasswordView(
Spacer(modifier = Modifier.weight(1f))
// Submit
Button(
- text = stringResource(R.string.screen_login_submit),
+ text = stringResource(CommonStrings.action_continue),
showProgress = isLoading,
onClick = ::submit,
enabled = state.submitEnabled || isLoading,
@@ -169,7 +167,7 @@ fun LoginPasswordView(
@OptIn(ExperimentalComposeUiApi::class)
@Composable
-internal fun LoginForm(
+private fun LoginForm(
state: LoginPasswordState,
isLoading: Boolean,
onSubmit: () -> Unit,
@@ -201,7 +199,7 @@ internal fun LoginForm(
eventSink(LoginPasswordEvents.SetLogin(it))
}),
placeholder = {
- Text(text = stringResource(R.string.screen_login_username_hint))
+ Text(text = stringResource(CommonStrings.common_username))
},
onValueChange = {
loginFieldState = it
@@ -220,7 +218,7 @@ internal fun LoginForm(
IconButton(onClick = {
loginFieldState = ""
}) {
- Icon(imageVector = Icons.Filled.Close, contentDescription = stringResource(CommonStrings.action_clear))
+ Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = stringResource(CommonStrings.action_clear))
}
}
} else null,
@@ -248,17 +246,17 @@ internal fun LoginForm(
eventSink(LoginPasswordEvents.SetPassword(it))
},
placeholder = {
- Text(text = stringResource(R.string.screen_login_password_hint))
+ Text(text = stringResource(CommonStrings.common_password))
},
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
val image =
- if (passwordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff
+ if (passwordVisible) CommonDrawables.ic_compound_visibility_on else CommonDrawables.ic_compound_visibility_off
val description =
if (passwordVisible) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password)
IconButton(onClick = { passwordVisible = !passwordVisible }) {
- Icon(imageVector = image, description)
+ Icon(resourceId = image, description)
}
},
keyboardOptions = KeyboardOptions(
@@ -274,7 +272,7 @@ internal fun LoginForm(
}
@Composable
-internal fun LoginErrorDialog(error: Throwable, onDismiss: () -> Unit) {
+private fun LoginErrorDialog(error: Throwable, onDismiss: () -> Unit) {
ErrorDialog(
title = stringResource(id = CommonStrings.dialog_title_error),
content = stringResource(loginError(error)),
@@ -282,7 +280,7 @@ internal fun LoginErrorDialog(error: Throwable, onDismiss: () -> Unit) {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun LoginPasswordViewPreview(@PreviewParameter(LoginPasswordStateProvider::class) state: LoginPasswordState) = ElementPreview {
LoginPasswordView(
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt
index b6ffac8bd1..8dce1bd78e 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt
@@ -19,6 +19,7 @@ package io.element.android.features.login.impl.screens.searchaccountprovider
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.login.impl.changeserver.aChangeServerState
import io.element.android.features.login.impl.resolver.HomeserverData
+import io.element.android.features.login.impl.util.LoginConstants
import io.element.android.libraries.architecture.Async
open class SearchAccountProviderStateProvider : PreviewParameterProvider {
@@ -49,7 +50,7 @@ fun aHomeserverDataList(): List {
}
fun aHomeserverData(
- homeserverUrl: String = "https://matrix.org",
+ homeserverUrl: String = LoginConstants.MATRIX_ORG_URL,
isWellknownValid: Boolean = true,
supportSlidingSync: Boolean = true,
): HomeserverData {
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt
index d715ba9806..29781acff1 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt
@@ -32,9 +32,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
-import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
@@ -57,18 +54,20 @@ import io.element.android.features.login.impl.accountprovider.AccountProviderVie
import io.element.android.features.login.impl.changeserver.ChangeServerEvents
import io.element.android.features.login.impl.changeserver.ChangeServerView
import io.element.android.features.login.impl.resolver.HomeserverData
+import io.element.android.features.login.impl.util.LoginConstants
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.form.textFieldState
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings
@@ -105,7 +104,7 @@ fun SearchAccountProviderView(
item {
IconTitleSubtitleMolecule(
modifier = Modifier.padding(top = 16.dp, bottom = 40.dp, start = 16.dp, end = 16.dp),
- iconImageVector = Icons.Filled.Search,
+ iconResourceId = CommonDrawables.ic_compound_search,
title = stringResource(id = R.string.screen_account_provider_form_title),
subTitle = stringResource(id = R.string.screen_account_provider_form_subtitle),
)
@@ -141,7 +140,7 @@ fun SearchAccountProviderView(
eventSink(SearchAccountProviderEvents.UserInput(""))
}) {
Icon(
- imageVector = Icons.Filled.Close,
+ resourceId = CommonDrawables.ic_compound_close,
contentDescription = stringResource(CommonStrings.action_clear)
)
}
@@ -197,9 +196,9 @@ fun SearchAccountProviderView(
@Composable
private fun HomeserverData.toAccountProvider(): AccountProvider {
- val isMatrixOrg = homeserverUrl == "https://matrix.org"
+ val isMatrixOrg = homeserverUrl == LoginConstants.MATRIX_ORG_URL
return AccountProvider(
- title = homeserverUrl.removePrefix("http://").removePrefix("https://"),
+ url = homeserverUrl,
subtitle = if (isMatrixOrg) stringResource(id = R.string.screen_change_account_provider_matrix_org_subtitle) else null,
isPublic = isMatrixOrg, // There is no need to know for other servers right now
isMatrixOrg = isMatrixOrg,
@@ -208,7 +207,7 @@ private fun HomeserverData.toAccountProvider(): AccountProvider {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun SearchAccountProviderViewPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) = ElementPreview {
SearchAccountProviderView(
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenter.kt
index 9c07204ab2..039986343b 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenter.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenter.kt
@@ -18,6 +18,7 @@ package io.element.android.features.login.impl.screens.waitlistscreen
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -58,14 +59,14 @@ class WaitListPresenter @AssistedInject constructor(
mutableStateOf(Async.Uninitialized)
}
- val attemptNumber: MutableState = remember { mutableStateOf(0) }
+ val attemptNumber = remember { mutableIntStateOf(0) }
fun handleEvents(event: WaitListEvents) {
when (event) {
WaitListEvents.AttemptLogin -> {
// Do not attempt to login on first resume of the View.
- attemptNumber.value++
- if (attemptNumber.value > 1) {
+ attemptNumber.intValue++
+ if (attemptNumber.intValue > 1) {
coroutineScope.loginAttempt(formState, loginAction)
}
}
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt
index dc117f05c2..8a3f49fdc0 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt
@@ -36,7 +36,7 @@ import io.element.android.features.login.impl.error.loginError
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.atomic.pages.SunsetPage
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.TextButton
@@ -144,7 +144,7 @@ private fun OverallContent(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun WaitListViewPreview(@PreviewParameter(WaitListStateProvider::class) state: WaitListState) = ElementPreview {
WaitListView(
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt
index 152b1a094c..98fd62d7b0 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt
@@ -19,14 +19,14 @@ package io.element.android.features.login.impl.util
import io.element.android.features.login.impl.accountprovider.AccountProvider
object LoginConstants {
- const val MATRIX_ORG_URL = "matrix.org"
+ const val MATRIX_ORG_URL = "https://matrix.org"
- const val DEFAULT_HOMESERVER_URL = "matrix.org"
+ const val DEFAULT_HOMESERVER_URL = "https://matrix.org"
const val SLIDING_SYNC_READ_MORE_URL = "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md"
}
val defaultAccountProvider = AccountProvider(
- title = LoginConstants.DEFAULT_HOMESERVER_URL,
+ url = LoginConstants.DEFAULT_HOMESERVER_URL,
subtitle = null,
isPublic = LoginConstants.DEFAULT_HOMESERVER_URL == LoginConstants.MATRIX_ORG_URL,
isMatrixOrg = LoginConstants.DEFAULT_HOMESERVER_URL == LoginConstants.MATRIX_ORG_URL,
diff --git a/features/login/impl/src/main/res/values-cs/translations.xml b/features/login/impl/src/main/res/values-cs/translations.xml
index 5d697bf34a..3edf7d1003 100644
--- a/features/login/impl/src/main/res/values-cs/translations.xml
+++ b/features/login/impl/src/main/res/values-cs/translations.xml
@@ -18,6 +18,7 @@
"Adresa URL domovského serveru"
"Můžete se připojit pouze k serveru, který podporuje klouzavou synchronizaci. Správce vašeho domovského serveru jej bude muset nakonfigurovat. %1$s"
"Jaká je adresa vašeho serveru?"
+ "Vyberte váš server"
"Tento účet byl deaktivován."
"Nesprávné uživatelské jméno nebo heslo"
"Toto není platný identifikátor uživatele. Očekávaný formát: \'@user:homeserver.org\'"
@@ -37,11 +38,5 @@ Díky za trpělivost!"
"Vítá vás %1$s"
"Jste v pořadníku!"
"Jdete do toho!"
- "Pokračovat"
- "Pokračovat"
- "Vyberte svůj server"
- "Heslo"
- "Pokračovat"
"Matrix je otevřená síť pro bezpečnou a decentralizovanou komunikaci."
- "Uživatelské jméno"
diff --git a/features/login/impl/src/main/res/values-de/translations.xml b/features/login/impl/src/main/res/values-de/translations.xml
index a7511c0f09..ac7df075db 100644
--- a/features/login/impl/src/main/res/values-de/translations.xml
+++ b/features/login/impl/src/main/res/values-de/translations.xml
@@ -37,11 +37,5 @@ Danke für deine Geduld!"
"Willkommen bei %1$s!"
"Du bist fast am Ziel."
"Du bist dabei."
- "Weiter"
- "Weiter"
- "Wähle deinen Server aus"
- "Passwort"
- "Weiter"
"Matrix ist ein offenes Netzwerk für eine sichere, dezentrale Kommunikation."
- "Benutzername"
diff --git a/features/login/impl/src/main/res/values-es/translations.xml b/features/login/impl/src/main/res/values-es/translations.xml
index 0735fb7fd2..6f023f6373 100644
--- a/features/login/impl/src/main/res/values-es/translations.xml
+++ b/features/login/impl/src/main/res/values-es/translations.xml
@@ -11,10 +11,4 @@
"El servidor seleccionado no admite contraseñas ni inicio de sesión OIDC. Póngase en contacto con su administrador o elija otro homeserver."
"Introduce tus datos"
"¡Hola de nuevo!"
- "Continuar"
- "Continuar"
- "Selecciona tu servidor"
- "Contraseña"
- "Continuar"
- "Usuario"
diff --git a/features/login/impl/src/main/res/values-fr/translations.xml b/features/login/impl/src/main/res/values-fr/translations.xml
index a566198a45..3b7dae468c 100644
--- a/features/login/impl/src/main/res/values-fr/translations.xml
+++ b/features/login/impl/src/main/res/values-fr/translations.xml
@@ -37,11 +37,5 @@ Merci pour votre patience !"
"Bienvenue dans %1$s !"
"Vous y êtes presque."
"Vous y êtes."
- "Continuer"
- "Continuer"
- "Sélectionnez votre serveur"
- "Mot de passe"
- "Continuer"
"Matrix est un réseau ouvert pour une communication sécurisée et décentralisée."
- "Nom d’utilisateur"
diff --git a/features/login/impl/src/main/res/values-it/translations.xml b/features/login/impl/src/main/res/values-it/translations.xml
index f7321f1d52..227b85bed7 100644
--- a/features/login/impl/src/main/res/values-it/translations.xml
+++ b/features/login/impl/src/main/res/values-it/translations.xml
@@ -11,10 +11,4 @@
"L\'homeserver selezionato non supporta la password o l\'accesso OIDC. Contatta il tuo amministratore o scegli un altro homeserver."
"Inserisci i tuoi dati"
"Bentornato!"
- "Continua"
- "Continua"
- "Seleziona il tuo server"
- "Password"
- "Continua"
- "Nome utente"
diff --git a/features/login/impl/src/main/res/values-ro/translations.xml b/features/login/impl/src/main/res/values-ro/translations.xml
index e780269a39..2241c63ec9 100644
--- a/features/login/impl/src/main/res/values-ro/translations.xml
+++ b/features/login/impl/src/main/res/values-ro/translations.xml
@@ -37,11 +37,5 @@ Vă mulțumim pentru răbdare!"
"Bun venit la %1$s"
"Sunteți pe lista de așteptare"
"Sunteți conectat!"
- "Continuați"
- "Continuați"
- "Selectați serverul"
- "Parola"
- "Continuați"
"Matrix este o rețea deschisă pentru o comunicare sigură și descentralizată."
- "Utilizator"
diff --git a/features/login/impl/src/main/res/values-ru/translations.xml b/features/login/impl/src/main/res/values-ru/translations.xml
index 3ecb8a52d0..733b0d93d2 100644
--- a/features/login/impl/src/main/res/values-ru/translations.xml
+++ b/features/login/impl/src/main/res/values-ru/translations.xml
@@ -14,10 +14,11 @@
"Используйте другого поставщика учетных записей, например, собственный частный сервер или рабочую учетную запись."
"Сменить поставщика учетной записи"
"Нам не удалось связаться с этим домашним сервером. Убедитесь, что вы правильно ввели URL-адрес домашнего сервера. Если URL-адрес указан правильно, обратитесь к администратору домашнего сервера за дополнительной помощью."
- "В настоящее время этот сервер не поддерживает скользящую синхронизацию."
+ "К сожалению данный сервер не поддерживает sliding sync."
"URL-адрес домашнего сервера"
- "Вы можете подключиться только к существующему серверу, поддерживающему скользящую синхронизацию. Администратору домашнего сервера потребуется настроить его. %1$s"
+ "Вы можете подключиться только к существующему серверу, поддерживающему sliding sync. Администратору домашнего сервера потребуется настроить его. %1$s"
"Какой адрес у вашего сервера?"
+ "Выберите свой сервер"
"Данная учетная запись была деактивирована."
"Неверное имя пользователя и/или пароль"
"Это не корректный идентификатор пользователя. Ожидаемый формат: \'@user:homeserver.org\'"
@@ -37,11 +38,5 @@
"Добро пожаловать в %1$s!"
"Почти готово!"
"Вы зарегистрированы!"
- "Продолжить"
- "Продолжить"
- "Выберите свой сервер"
- "Пароль"
- "Продолжить"
"Matrix — это открытая сеть для безопасной децентрализованной связи."
- "Имя пользователя"
diff --git a/features/login/impl/src/main/res/values-sk/translations.xml b/features/login/impl/src/main/res/values-sk/translations.xml
index e94990554f..31656d628a 100644
--- a/features/login/impl/src/main/res/values-sk/translations.xml
+++ b/features/login/impl/src/main/res/values-sk/translations.xml
@@ -18,6 +18,7 @@
"Adresa URL domovského servera"
"Pripojiť sa môžete len k existujúcemu serveru, ktorý podporuje kĺzavú synchronizáciu. Váš správca domovského servera ju bude musieť nakonfigurovať. %1$s"
"Aká je adresa vášho servera?"
+ "Vyberte svoj server"
"Tento účet bol deaktivovaný."
"Nesprávne používateľské meno a/alebo heslo"
"Toto nie je platný identifikátor používateľa. Očakávaný formát: \'@pouzivatel:homeserver.sk\'"
@@ -37,11 +38,5 @@
"Vitajte v %1$s"
"Ste na čakanej listine!"
"Ste dnu!"
- "Pokračovať"
- "Pokračovať"
- "Vyberte svoj server"
- "Heslo"
- "Pokračovať"
"Matrix je otvorená sieť pre bezpečnú a decentralizovanú komunikáciu."
- "Používateľské meno"
diff --git a/features/login/impl/src/main/res/values-zh-rTW/translations.xml b/features/login/impl/src/main/res/values-zh-rTW/translations.xml
index e4bfd61b35..45f10295f3 100644
--- a/features/login/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/login/impl/src/main/res/values-zh-rTW/translations.xml
@@ -1,20 +1,30 @@
+ "更改帳號提供者"
+ "家伺服器位址"
+ "輸入關鍵字或網域名稱。"
+ "搜尋公司、社群、私有伺服器"
+ "尋找帳號提供者"
+ "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。"
"您即將登入 %s"
+ "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。"
"您即將在 %s 建立帳號"
+ "Matrix.org 由 Matrix.org 基金會營運,是用於安全、去中心化通訊的公共 Matrix 網路上的大型免費伺服器。"
"其他"
- "此伺服器目前不支援 sliding sync。"
+ "使用不同的帳戶提供者,例如您自己的伺服器或工作帳號。"
+ "更改帳號提供者"
+ "此伺服器目前不支援滑動同步(sliding sync)。"
"家伺服器 URL"
+ "這個帳號已被停用。"
+ "不正確的使用者名稱或密碼"
"輸入您的詳細資料"
"歡迎回來!"
"登入 %1$s"
+ "更改帳號提供者"
+ "Matrix 是一個開放網路,為了安全、去中心化的通訊而生。"
+ "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。"
"您即將登入 %1$s"
"您即將在 %1$s 建立帳號"
"歡迎使用 %1$s!"
- "繼續"
- "繼續"
- "選擇您的伺服器"
- "密碼"
- "繼續"
- "使用者名稱"
+ "Matrix 是一個開放網路,為了安全、去中心化的通訊而生。"
diff --git a/features/login/impl/src/main/res/values/localazy.xml b/features/login/impl/src/main/res/values/localazy.xml
index e09f4fe693..c9797db5ac 100644
--- a/features/login/impl/src/main/res/values/localazy.xml
+++ b/features/login/impl/src/main/res/values/localazy.xml
@@ -18,6 +18,7 @@
"Homeserver URL"
"You can only connect to an existing server that supports sliding sync. Your homeserver admin will need to configure it. %1$s"
"What is the address of your server?"
+ "Select your server"
"This account has been deactivated."
"Incorrect username and/or password"
"This is not a valid user identifier. Expected format: ‘@user:homeserver.org’"
@@ -37,11 +38,5 @@ Thanks for your patience!"
"Welcome to %1$s!"
"You’re almost there."
"You\'re in."
- "Continue"
- "Continue"
- "Select your server"
- "Password"
- "Continue"
"Matrix is an open network for secure, decentralised communication."
- "Username"
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt
index f94ce4e65d..44c36128e2 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt
@@ -63,7 +63,7 @@ class ChangeServerPresenterTest {
val initialState = awaitItem()
assertThat(initialState.changeServerAction).isEqualTo(Async.Uninitialized)
authenticationService.givenHomeserver(A_HOMESERVER)
- initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(A_HOMESERVER_URL)))
+ initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL)))
val loadingState = awaitItem()
assertThat(loadingState.changeServerAction).isInstanceOf(Async.Loading::class.java)
val successState = awaitItem()
@@ -83,7 +83,7 @@ class ChangeServerPresenterTest {
}.test {
val initialState = awaitItem()
assertThat(initialState.changeServerAction).isEqualTo(Async.Uninitialized)
- initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(A_HOMESERVER_URL)))
+ initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL)))
val loadingState = awaitItem()
assertThat(loadingState.changeServerAction).isInstanceOf(Async.Loading::class.java)
val failureState = awaitItem()
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/error/ErrorFormatterTests.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/error/ErrorFormatterTest.kt
similarity index 98%
rename from features/login/impl/src/test/kotlin/io/element/android/features/login/impl/error/ErrorFormatterTests.kt
rename to features/login/impl/src/test/kotlin/io/element/android/features/login/impl/error/ErrorFormatterTest.kt
index c1d7e5bb6c..17c6ce3e4a 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/error/ErrorFormatterTests.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/error/ErrorFormatterTest.kt
@@ -22,7 +22,7 @@ import io.element.android.libraries.matrix.api.auth.AuthenticationException
import io.element.android.libraries.ui.strings.CommonStrings
import org.junit.Test
-class ErrorFormatterTests {
+class ErrorFormatterTest {
// region loginError
@Test
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt
index edf198fe35..4ecbe10b4e 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt
@@ -50,6 +50,7 @@ class ChangeAccountProviderPresenterTest {
assertThat(initialState.accountProviders).isEqualTo(
listOf(
AccountProvider(
+ url = "https://matrix.org",
title = "matrix.org",
subtitle = null,
isPublic = true,
diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutPreferenceScreen.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutPreferenceView.kt
similarity index 91%
rename from features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutPreferenceScreen.kt
rename to features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutPreferenceView.kt
index 3b0a88476a..24b7c8a266 100644
--- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutPreferenceScreen.kt
+++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutPreferenceView.kt
@@ -16,8 +16,6 @@
package io.element.android.features.logout.api
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Logout
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
@@ -27,8 +25,9 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.utils.CommonDrawables
@Composable
fun LogoutPreferenceView(
@@ -75,17 +74,17 @@ fun LogoutPreferenceView(
}
@Composable
-fun LogoutPreferenceContent(
+private fun LogoutPreferenceContent(
onClick: () -> Unit = {},
) {
PreferenceText(
title = stringResource(id = R.string.screen_signout_preference_item),
- icon = Icons.Filled.Logout,
+ iconResourceId = CommonDrawables.ic_compound_leave,
onClick = onClick
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun LogoutPreferenceViewPreview() = ElementPreview {
LogoutPreferenceView(
diff --git a/features/logout/impl/build.gradle.kts b/features/logout/impl/build.gradle.kts
index 52a9f76b41..f5ee8dd951 100644
--- a/features/logout/impl/build.gradle.kts
+++ b/features/logout/impl/build.gradle.kts
@@ -38,7 +38,6 @@ dependencies {
implementation(projects.libraries.testtags)
implementation(projects.libraries.uiStrings)
implementation(projects.libraries.dateformatter.api)
- implementation(libs.accompanist.placeholder)
api(projects.features.logout.api)
ksp(libs.showkase.processor)
diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessageComposerContext.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessageComposerContext.kt
index 5a0596c7bb..d9a5e50e11 100644
--- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessageComposerContext.kt
+++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessageComposerContext.kt
@@ -16,7 +16,7 @@
package io.element.android.features.messages.api
-import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
/**
* Hoist-able state of the message composer.
diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts
index cd0b6dda2d..e886a3aeaa 100644
--- a/features/messages/impl/build.gradle.kts
+++ b/features/messages/impl/build.gradle.kts
@@ -48,18 +48,17 @@ dependencies {
implementation(projects.libraries.mediapickers.api)
implementation(projects.libraries.featureflag.api)
implementation(projects.libraries.mediaupload.api)
+ implementation(projects.libraries.permissions.api)
implementation(projects.libraries.preferences.api)
implementation(projects.features.networkmonitor.api)
implementation(projects.services.analytics.api)
implementation(libs.coil.compose)
implementation(libs.datetime)
- implementation(libs.accompanist.flowlayout)
implementation(libs.jsoup)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.constraintlayout.compose)
implementation(libs.androidx.media3.exoplayer)
implementation(libs.androidx.media3.ui)
- implementation(libs.accompanist.systemui)
implementation(libs.vanniktech.blurhash)
implementation(libs.telephoto.zoomableimage)
implementation(libs.matrix.emojibase.bindings)
@@ -77,6 +76,7 @@ dependencies {
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.libraries.mediaupload.test)
testImplementation(projects.libraries.mediapickers.test)
+ testImplementation(projects.libraries.permissions.test)
testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.textcomposer.test)
testImplementation(libs.test.mockk)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/ExpandableBottomSheetScaffold.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/ExpandableBottomSheetScaffold.kt
new file mode 100644
index 0000000000..8b28a216b0
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/ExpandableBottomSheetScaffold.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+@file:OptIn(ExperimentalMaterial3Api::class)
+
+package io.element.android.features.messages.impl
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.material3.BottomSheetScaffold
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.SheetState
+import androidx.compose.material3.SheetValue
+import androidx.compose.material3.rememberBottomSheetScaffoldState
+import androidx.compose.material3.rememberStandardBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.SubcomposeLayout
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.min
+import kotlin.math.roundToInt
+
+/**
+ * A [BottomSheetScaffold] that allows the sheet to be expanded the screen height
+ * of the sheet contents.
+ *
+ * @param content The main content.
+ * @param sheetContent The sheet content.
+ * @param sheetDragHandle The drag handle for the sheet.
+ * @param sheetSwipeEnabled Whether the sheet can be swiped. This value is ignored and swipe is disabled if the sheet content overflows.
+ * @param sheetShape The shape of the sheet.
+ * @param sheetTonalElevation The tonal elevation of the sheet.
+ * @param sheetShadowElevation The shadow elevation of the sheet.
+ * @param modifier The modifier for the layout.
+ * @param sheetContentKey The key for the sheet content. If the key changes, the sheet will be remeasured.
+ */
+@Composable
+internal fun ExpandableBottomSheetScaffold(
+ content: @Composable (padding: PaddingValues) -> Unit,
+ sheetContent: @Composable (subcomposing: Boolean) -> Unit,
+ sheetDragHandle: @Composable () -> Unit,
+ sheetSwipeEnabled: Boolean,
+ sheetShape: Shape,
+ sheetTonalElevation: Dp,
+ sheetShadowElevation: Dp,
+ modifier: Modifier = Modifier,
+ sheetContentKey: Int? = null,
+) {
+ val scaffoldState = rememberBottomSheetScaffoldState(
+ bottomSheetState = rememberStandardBottomSheetState(
+ initialValue = SheetValue.PartiallyExpanded,
+ skipHiddenState = true,
+ )
+ )
+
+ // If the content overflows, we disable swipe to prevent the sheet from intercepting
+ // scroll events of the sheet content.
+ var contentOverflows by remember { mutableStateOf(false) }
+ val sheetSwipeEnabledIfPossible by remember(contentOverflows, sheetSwipeEnabled) {
+ derivedStateOf {
+ sheetSwipeEnabled && !contentOverflows
+ }
+ }
+
+ LaunchedEffect(sheetSwipeEnabledIfPossible) {
+ if (!sheetSwipeEnabledIfPossible) {
+ scaffoldState.bottomSheetState.partialExpand()
+ }
+ }
+
+ @Composable
+ fun Scaffold(
+ sheetContent: @Composable () -> Unit,
+ dragHandle: @Composable () -> Unit,
+ peekHeight: Dp,
+ ) {
+ BottomSheetScaffold(
+ modifier = Modifier,
+ scaffoldState = scaffoldState,
+ sheetPeekHeight = peekHeight,
+ sheetSwipeEnabled = sheetSwipeEnabledIfPossible,
+ sheetDragHandle = dragHandle,
+ sheetShape = sheetShape,
+ content = content,
+ sheetContent = { sheetContent() },
+ sheetTonalElevation = sheetTonalElevation,
+ sheetShadowElevation = sheetShadowElevation,
+ )
+ }
+
+ SubcomposeLayout(
+ modifier = modifier,
+ measurePolicy = { constraints: Constraints ->
+ val sheetContentSub = subcompose(Slot.SheetContent(sheetContentKey)) { sheetContent(true) }.map {
+ it.measure(Constraints(maxWidth = constraints.maxWidth))
+ }.first()
+ val dragHandleSub = subcompose(Slot.DragHandle) { sheetDragHandle() }.map {
+ it.measure(Constraints(maxWidth = constraints.maxWidth))
+ }.firstOrNull()
+ val dragHandleHeight = dragHandleSub?.height?.toDp() ?: 0.dp
+
+ val maxHeight = constraints.maxHeight.toDp()
+ val contentHeight = sheetContentSub.height.toDp() + dragHandleHeight
+
+ contentOverflows = contentHeight > maxHeight
+
+ val peekHeight = min(
+ maxHeight, // prevent the sheet from expanding beyond the screen
+ contentHeight
+ )
+
+ val scaffoldPlaceables = subcompose(Slot.Scaffold) {
+ Scaffold({
+ Layout(
+ modifier = Modifier.fillMaxHeight(),
+ measurePolicy = { measurables, constraints ->
+ val constraintHeight = constraints.maxHeight
+ val offset = scaffoldState.bottomSheetState.getOffset() ?: 0
+ val height = Integer.max(0, constraintHeight - offset)
+ val top = measurables[0].measure(
+ constraints.copy(
+ minHeight = height,
+ maxHeight = height
+ )
+ )
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ top.place(x = 0, y = 0)
+ }
+ },
+ content = { sheetContent(false) })
+ }, sheetDragHandle, peekHeight)
+ }.map { measurable: Measurable ->
+ measurable.measure(constraints)
+ }
+ val scaffoldPlaceable = scaffoldPlaceables.first()
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ scaffoldPlaceable.place(0, 0)
+ }
+ })
+}
+
+private fun SheetState.getOffset(): Int? = try {
+ requireOffset().roundToInt()
+} catch (e: IllegalStateException) {
+ null
+}
+
+private sealed class Slot {
+ data class SheetContent(val key: Int?) : Slot()
+ data object DragHandle : Slot()
+ data object Scaffold : Slot()
+}
+
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt
index 1dc46aa2bd..8645553f0b 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt
@@ -30,7 +30,6 @@ import androidx.compose.runtime.setValue
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import im.vector.app.features.analytics.plan.PollEnd
import io.element.android.features.messages.impl.actionlist.ActionListEvents
import io.element.android.features.messages.impl.actionlist.ActionListPresenter
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
@@ -39,6 +38,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
import io.element.android.features.messages.impl.timeline.TimelineEvents
import io.element.android.features.messages.impl.timeline.TimelinePresenter
+import io.element.android.features.messages.impl.timeline.TimelineState
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter
@@ -55,6 +55,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
import io.element.android.features.messages.impl.utils.messagesummary.MessageSummaryFormatter
+import io.element.android.features.messages.impl.voicemessages.VoiceMessageComposerPresenter
import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.features.preferences.api.store.PreferencesStore
@@ -64,9 +65,11 @@ import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
-import io.element.android.libraries.designsystem.utils.SnackbarMessage
-import io.element.android.libraries.designsystem.utils.collectSnackbarMessageAsState
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
+import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
+import io.element.android.libraries.featureflag.api.FeatureFlagService
+import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
@@ -75,8 +78,7 @@ import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
import io.element.android.libraries.matrix.ui.room.canRedactAsState
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
-import io.element.android.libraries.textcomposer.MessageComposerMode
-import io.element.android.services.analytics.api.AnalyticsService
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -85,6 +87,7 @@ import timber.log.Timber
class MessagesPresenter @AssistedInject constructor(
private val room: MatrixRoom,
private val composerPresenter: MessageComposerPresenter,
+ private val voiceMessageComposerPresenter: VoiceMessageComposerPresenter,
private val timelinePresenter: TimelinePresenter,
private val actionListPresenter: ActionListPresenter,
private val customReactionPresenter: CustomReactionPresenter,
@@ -95,8 +98,8 @@ class MessagesPresenter @AssistedInject constructor(
private val messageSummaryFormatter: MessageSummaryFormatter,
private val dispatchers: CoroutineDispatchers,
private val clipboardHelper: ClipboardHelper,
- private val analyticsService: AnalyticsService,
private val preferencesStore: PreferencesStore,
+ private val featureFlagsService: FeatureFlagService,
@Assisted private val navigator: MessagesNavigator,
) : Presenter {
@@ -109,6 +112,7 @@ class MessagesPresenter @AssistedInject constructor(
override fun present(): MessagesState {
val localCoroutineScope = rememberCoroutineScope()
val composerState = composerPresenter.present()
+ val voiceMessageComposerState = voiceMessageComposerPresenter.present()
val timelineState = timelinePresenter.present()
val actionListState = actionListPresenter.present()
val customReactionState = customReactionPresenter.present()
@@ -147,6 +151,11 @@ class MessagesPresenter @AssistedInject constructor(
val enableTextFormatting by preferencesStore.isRichTextEditorEnabledFlow().collectAsState(initial = true)
+ var enableVoiceMessages by remember { mutableStateOf(false) }
+ LaunchedEffect(featureFlagsService) {
+ enableVoiceMessages = featureFlagsService.isFeatureEnabled(FeatureFlags.VoiceMessages)
+ }
+
fun handleEvents(event: MessagesEvents) {
when (event) {
is MessagesEvents.HandleAction -> {
@@ -155,6 +164,7 @@ class MessagesPresenter @AssistedInject constructor(
targetEvent = event.event,
composerState = composerState,
enableTextFormatting = enableTextFormatting,
+ timelineState = timelineState,
)
}
is MessagesEvents.ToggleReaction -> {
@@ -178,6 +188,7 @@ class MessagesPresenter @AssistedInject constructor(
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
userHasPermissionToRedact = userHasPermissionToRedact,
composerState = composerState,
+ voiceMessageComposerState = voiceMessageComposerState,
timelineState = timelineState,
actionListState = actionListState,
customReactionState = customReactionState,
@@ -188,6 +199,7 @@ class MessagesPresenter @AssistedInject constructor(
showReinvitePrompt = showReinvitePrompt,
inviteProgress = inviteProgress.value,
enableTextFormatting = enableTextFormatting,
+ enableVoiceMessages = enableVoiceMessages,
eventSink = { handleEvents(it) }
)
}
@@ -206,6 +218,7 @@ class MessagesPresenter @AssistedInject constructor(
targetEvent: TimelineItem.Event,
composerState: MessageComposerState,
enableTextFormatting: Boolean,
+ timelineState: TimelineState,
) = launch {
when (action) {
TimelineItemAction.Copy -> handleCopyContents(targetEvent)
@@ -216,7 +229,7 @@ class MessagesPresenter @AssistedInject constructor(
TimelineItemAction.ViewSource -> handleShowDebugInfoAction(targetEvent)
TimelineItemAction.Forward -> handleForwardAction(targetEvent)
TimelineItemAction.ReportContent -> handleReportAction(targetEvent)
- TimelineItemAction.EndPoll -> handleEndPollAction(targetEvent)
+ TimelineItemAction.EndPoll -> handleEndPollAction(targetEvent, timelineState)
}
}
@@ -266,7 +279,7 @@ class MessagesPresenter @AssistedInject constructor(
targetEvent: TimelineItem.Event,
composerState: MessageComposerState,
enableTextFormatting: Boolean,
- ) {
+ ) {
val composerMode = MessageComposerMode.Edit(
targetEvent.eventId,
(targetEvent.content as? TimelineItemTextBasedContent)?.let {
@@ -344,11 +357,11 @@ class MessagesPresenter @AssistedInject constructor(
navigator.onReportContentClicked(event.eventId, event.senderId)
}
- private suspend fun handleEndPollAction(event: TimelineItem.Event) {
- event.eventId?.let {
- room.endPoll(it, "The poll with event id: $it has ended.")
- analyticsService.capture(PollEnd())
- }
+ private fun handleEndPollAction(
+ event: TimelineItem.Event,
+ timelineState: TimelineState,
+ ) {
+ event.eventId?.let { timelineState.eventSink(TimelineEvents.PollEndClicked(it)) }
}
private suspend fun handleCopyContents(event: TimelineItem.Event) {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt
index 46aad1e191..0a121b50a3 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt
@@ -23,9 +23,10 @@ import io.element.android.features.messages.impl.timeline.TimelineState
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
+import io.element.android.features.messages.impl.voicemessages.VoiceMessageComposerState
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.avatar.AvatarData
-import io.element.android.libraries.designsystem.utils.SnackbarMessage
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.RoomId
@Immutable
@@ -36,6 +37,7 @@ data class MessagesState(
val userHasPermissionToSendMessage: Boolean,
val userHasPermissionToRedact: Boolean,
val composerState: MessageComposerState,
+ val voiceMessageComposerState: VoiceMessageComposerState,
val timelineState: TimelineState,
val actionListState: ActionListState,
val customReactionState: CustomReactionState,
@@ -46,5 +48,6 @@ data class MessagesState(
val inviteProgress: Async,
val showReinvitePrompt: Boolean,
val enableTextFormatting: Boolean,
+ val enableVoiceMessages: Boolean,
val eventSink: (MessagesEvents) -> Unit
)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
index a88ebcbcd8..3b0b87ea39 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
@@ -25,11 +25,12 @@ import io.element.android.features.messages.impl.timeline.components.customreact
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
+import io.element.android.features.messages.impl.voicemessages.aVoiceMessageComposerState
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.wysiwyg.compose.RichTextEditorState
import kotlinx.collections.immutable.persistentSetOf
@@ -45,6 +46,7 @@ open class MessagesStateProvider : PreviewParameterProvider {
roomName = Async.Uninitialized,
roomAvatar = Async.Uninitialized,
),
+ aMessagesState().copy(composerState = aMessageComposerState().copy(showTextFormatting = true)),
)
}
@@ -55,12 +57,11 @@ fun aMessagesState() = MessagesState(
userHasPermissionToSendMessage = true,
userHasPermissionToRedact = false,
composerState = aMessageComposerState().copy(
- richTextEditorState = RichTextEditorState("Hello", fake = true).apply {
- requestFocus()
- },
+ richTextEditorState = RichTextEditorState("Hello", initialFocus = true),
isFullScreen = false,
mode = MessageComposerMode.Normal("Hello"),
),
+ voiceMessageComposerState = aVoiceMessageComposerState(),
timelineState = aTimelineState().copy(
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
),
@@ -83,5 +84,6 @@ fun aMessagesState() = MessagesState(
inviteProgress = Async.Uninitialized,
showReinvitePrompt = false,
enableTextFormatting = true,
+ enableVoiceMessages = true,
eventSink = {}
)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
index 4d9b5541bd..971df06053 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
@@ -19,8 +19,8 @@ package io.element.android.features.messages.impl
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
@@ -32,13 +32,13 @@ import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
@@ -50,6 +50,7 @@ import io.element.android.features.messages.impl.actionlist.ActionListEvents
import io.element.android.features.messages.impl.actionlist.ActionListView
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
import io.element.android.features.messages.impl.attachments.Attachment
+import io.element.android.features.messages.impl.messagecomposer.AttachmentsBottomSheet
import io.element.android.features.messages.impl.messagecomposer.AttachmentsState
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents
import io.element.android.features.messages.impl.messagecomposer.MessageComposerView
@@ -71,14 +72,15 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.theme.components.BottomSheetDragHandle
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.LogCompositions
-import io.element.android.libraries.designsystem.utils.SnackbarHost
-import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
+import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
import io.element.android.libraries.theme.ElementTheme
@@ -86,7 +88,6 @@ import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
import timber.log.Timber
-@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable
fun MessagesView(
state: MessagesState,
@@ -277,40 +278,60 @@ private fun MessagesViewContent(
modifier: Modifier = Modifier,
onSwipeToReply: (TimelineItem.Event) -> Unit,
) {
- Column(
+ Box(
modifier = modifier
.fillMaxSize()
.navigationBarsPadding()
- .imePadding()
+ .imePadding(),
) {
- // Hide timeline if composer is full screen
- if (!state.composerState.isFullScreen) {
- TimelineView(
- state = state.timelineState,
- modifier = Modifier.weight(1f),
- onMessageClicked = onMessageClicked,
- onMessageLongClicked = onMessageLongClicked,
- onUserDataClicked = onUserDataClicked,
- onTimestampClicked = onTimestampClicked,
- onReactionClicked = onReactionClicked,
- onReactionLongClicked = onReactionLongClicked,
- onMoreReactionsClicked = onMoreReactionsClicked,
- onSwipeToReply = onSwipeToReply,
- )
- }
- if (state.userHasPermissionToSendMessage) {
- MessageComposerView(
- state = state.composerState,
- onSendLocationClicked = onSendLocationClicked,
- onCreatePollClicked = onCreatePollClicked,
- enableTextFormatting = state.enableTextFormatting,
- modifier = Modifier
- .fillMaxWidth()
- .wrapContentHeight(Alignment.Bottom)
- )
- } else {
- CantSendMessageBanner()
- }
+ AttachmentsBottomSheet(
+ state = state.composerState,
+ onSendLocationClicked = onSendLocationClicked,
+ onCreatePollClicked = onCreatePollClicked,
+ enableTextFormatting = state.enableTextFormatting,
+ )
+
+ ExpandableBottomSheetScaffold(
+ sheetDragHandle = if (state.composerState.showTextFormatting) {
+ @Composable { BottomSheetDragHandle() }
+ } else {
+ @Composable {}
+ },
+ sheetSwipeEnabled = state.composerState.showTextFormatting,
+ sheetShape = if (state.composerState.showTextFormatting) MaterialTheme.shapes.large else RectangleShape,
+ content = { paddingValues ->
+ TimelineView(
+ modifier = Modifier.padding(paddingValues),
+ state = state.timelineState,
+ onMessageClicked = onMessageClicked,
+ onMessageLongClicked = onMessageLongClicked,
+ onUserDataClicked = onUserDataClicked,
+ onTimestampClicked = onTimestampClicked,
+ onReactionClicked = onReactionClicked,
+ onReactionLongClicked = onReactionLongClicked,
+ onMoreReactionsClicked = onMoreReactionsClicked,
+ onSwipeToReply = onSwipeToReply,
+ )
+ },
+ sheetContent = { subcomposing: Boolean ->
+ if (state.userHasPermissionToSendMessage) {
+ MessageComposerView(
+ state = state.composerState,
+ voiceMessageState = state.voiceMessageComposerState,
+ subcomposing = subcomposing,
+ enableTextFormatting = state.enableTextFormatting,
+ enableVoiceMessages = state.enableVoiceMessages,
+ modifier = Modifier
+ .fillMaxWidth(),
+ )
+ } else {
+ CantSendMessageBanner()
+ }
+ },
+ sheetContentKey = state.composerState.richTextEditorState.lineCount,
+ sheetTonalElevation = 0.dp,
+ sheetShadowElevation = 0.dp,
+ )
}
}
@@ -390,7 +411,7 @@ private fun CantSendMessageBanner(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class) state: MessagesState) = ElementPreview {
MessagesView(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt
index 588adb1020..c9e485b88a 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt
@@ -108,7 +108,7 @@ class ActionListPresenter @Inject constructor(
buildList {
val isMineOrCanRedact = timelineItem.isMine || userCanRedact
- // TODO Poll: Reply to poll. Ensure to update `fun TimelineItemEventContent.canBeReplied()`
+ // TODO Polls: Reply to poll. Ensure to update `fun TimelineItemEventContent.canBeReplied()`
// when touching this
// if (timelineItem.isRemote) {
// // Can only reply or forward messages already uploaded to the server
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt
index c1561d5458..05db97d3be 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt
@@ -36,8 +36,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ListItem
import androidx.compose.material.Text
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.AddReaction
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
@@ -71,13 +69,14 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.utils.messagesummary.MessageSummaryFormatterImpl
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.text.toSp
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
import io.element.android.libraries.designsystem.theme.components.hide
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
@@ -337,7 +336,7 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
private val emojiRippleRadius = 24.dp
@Composable
-internal fun EmojiReactionsRow(
+private fun EmojiReactionsRow(
highlightedEmojis: ImmutableList,
onEmojiReactionClicked: (String) -> Unit,
onCustomReactionClicked: () -> Unit,
@@ -361,7 +360,7 @@ internal fun EmojiReactionsRow(
contentAlignment = Alignment.Center
) {
Icon(
- imageVector = Icons.Outlined.AddReaction,
+ resourceId = CommonDrawables.ic_september_add_reaction,
contentDescription = "Emojis",
tint = MaterialTheme.colorScheme.secondary,
modifier = Modifier
@@ -411,7 +410,7 @@ private fun EmojiButton(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun SheetContentPreview(
@PreviewParameter(ActionListStateProvider::class) state: ActionListState
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt
index 948c0b0cb1..9a71d7c179 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt
@@ -19,7 +19,7 @@ package io.element.android.features.messages.impl.actionlist.model
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.runtime.Immutable
-import io.element.android.libraries.designsystem.VectorIcons
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.ui.strings.CommonStrings
@Immutable
@@ -28,13 +28,13 @@ sealed class TimelineItemAction(
@DrawableRes val icon: Int,
val destructive: Boolean = false
) {
- data object Forward : TimelineItemAction(CommonStrings.action_forward, VectorIcons.Forward)
- data object Copy : TimelineItemAction(CommonStrings.action_copy, VectorIcons.Copy)
- data object Redact : TimelineItemAction(CommonStrings.action_remove, VectorIcons.Delete, destructive = true)
- data object Reply : TimelineItemAction(CommonStrings.action_reply, VectorIcons.Reply)
- data object ReplyInThread : TimelineItemAction(CommonStrings.action_reply_in_thread, VectorIcons.Reply)
- data object Edit : TimelineItemAction(CommonStrings.action_edit, VectorIcons.Edit)
- data object ViewSource : TimelineItemAction(CommonStrings.action_view_source, VectorIcons.DeveloperMode)
- data object ReportContent : TimelineItemAction(CommonStrings.action_report_content, VectorIcons.ReportContent, destructive = true)
- data object EndPoll : TimelineItemAction(CommonStrings.action_end_poll, VectorIcons.PollEnd)
+ data object Forward : TimelineItemAction(CommonStrings.action_forward, CommonDrawables.ic_september_forward)
+ data object Copy : TimelineItemAction(CommonStrings.action_copy, CommonDrawables.ic_september_copy)
+ data object Redact : TimelineItemAction(CommonStrings.action_remove, CommonDrawables.ic_compound_delete, destructive = true)
+ data object Reply : TimelineItemAction(CommonStrings.action_reply, CommonDrawables.ic_september_reply)
+ data object ReplyInThread : TimelineItemAction(CommonStrings.action_reply_in_thread, CommonDrawables.ic_september_reply)
+ data object Edit : TimelineItemAction(CommonStrings.action_edit, CommonDrawables.ic_september_edit_outline)
+ data object ViewSource : TimelineItemAction(CommonStrings.action_view_source, CommonDrawables.ic_september_view_source)
+ data object ReportContent : TimelineItemAction(CommonStrings.action_report_content, CommonDrawables.ic_compound_chat_problem, destructive = true)
+ data object EndPoll : TimelineItemAction(CommonStrings.action_end_poll, CommonDrawables.ic_poll_end)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt
index 56d7f63eb1..dbcb4c8c3c 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt
@@ -20,8 +20,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomMember
-import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
import io.element.android.libraries.matrix.api.room.message.RoomMessage
+import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@@ -29,29 +29,37 @@ open class ForwardMessagesStateProvider : PreviewParameterProvider
get() = sequenceOf(
aForwardMessagesState(),
- aForwardMessagesState(query = "Test"),
+ aForwardMessagesState(query = "Test", isSearchActive = true),
aForwardMessagesState(resultState = SearchBarResultState.Results(aForwardMessagesRoomList())),
- aForwardMessagesState(resultState = SearchBarResultState.Results(aForwardMessagesRoomList()), query = "Test"),
aForwardMessagesState(
resultState = SearchBarResultState.Results(aForwardMessagesRoomList()),
query = "Test",
+ isSearchActive = true,
+ ),
+ aForwardMessagesState(
+ resultState = SearchBarResultState.Results(aForwardMessagesRoomList()),
+ query = "Test",
+ isSearchActive = true,
selectedRooms = persistentListOf(aRoomDetailsState(roomId = RoomId("!room2:domain")))
),
aForwardMessagesState(
resultState = SearchBarResultState.Results(aForwardMessagesRoomList()),
query = "Test",
+ isSearchActive = true,
selectedRooms = persistentListOf(aRoomDetailsState(roomId = RoomId("!room2:domain"))),
isForwarding = true,
),
aForwardMessagesState(
resultState = SearchBarResultState.Results(aForwardMessagesRoomList()),
query = "Test",
+ isSearchActive = true,
selectedRooms = persistentListOf(aRoomDetailsState(roomId = RoomId("!room2:domain"))),
forwardingSucceeded = persistentListOf(RoomId("!room2:domain")),
),
aForwardMessagesState(
resultState = SearchBarResultState.Results(aForwardMessagesRoomList()),
query = "Test",
+ isSearchActive = true,
selectedRooms = persistentListOf(aRoomDetailsState(roomId = RoomId("!room2:domain"))),
error = Throwable("error"),
),
@@ -78,7 +86,7 @@ fun aForwardMessagesState(
eventSink = {}
)
-internal fun aForwardMessagesRoomList() = listOf(
+internal fun aForwardMessagesRoomList() = persistentListOf(
aRoomDetailsState(),
aRoomDetailsState(roomId = RoomId("!room2:domain"), canonicalAlias = "#element-x-room:matrix.org"),
)
@@ -94,13 +102,13 @@ fun aRoomDetailsState(
unreadNotificationCount: Int = 0,
inviter: RoomMember? = null,
) = RoomSummaryDetails(
- roomId = roomId,
- name = name,
- canonicalAlias = canonicalAlias,
- isDirect = isDirect,
- avatarURLString = avatarURLString,
- lastMessage = lastMessage,
- lastMessageTimestamp = lastMessageTimestamp,
- unreadNotificationCount = unreadNotificationCount,
- inviter = inviter,
- )
+ roomId = roomId,
+ name = name,
+ canonicalAlias = canonicalAlias,
+ isDirect = isDirect,
+ avatarURLString = avatarURLString,
+ lastMessage = lastMessage,
+ lastMessageTimestamp = lastMessageTimestamp,
+ unreadNotificationCount = unreadNotificationCount,
+ inviter = inviter,
+)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt
index 130ce20cbb..1174c0ffcc 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt
@@ -48,7 +48,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialogDefaults
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
@@ -135,7 +135,7 @@ fun ForwardMessagesView(
.padding(paddingValues)
.consumeWindowInsets(paddingValues)
) {
- SearchBar>(
+ SearchBar(
placeHolderTitle = stringResource(CommonStrings.action_search),
query = state.query,
onQueryChange = { state.eventSink(ForwardMessagesEvents.UpdateQuery(it)) },
@@ -204,7 +204,7 @@ fun ForwardMessagesView(
}
@Composable
-internal fun SelectedRooms(
+private fun SelectedRooms(
selectedRooms: ImmutableList,
onRoomRemoved: (RoomSummaryDetails) -> Unit,
modifier: Modifier = Modifier,
@@ -221,7 +221,7 @@ internal fun SelectedRooms(
}
@Composable
-internal fun RoomSummaryView(
+private fun RoomSummaryView(
summary: RoomSummaryDetails,
isSelected: Boolean,
onSelection: (RoomSummaryDetails) -> Unit,
@@ -279,7 +279,7 @@ private fun ForwardingErrorDialog(onDismiss: () -> Unit, modifier: Modifier = Mo
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ForwardMessagesViewPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) = ElementPreview {
ForwardMessagesView(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActions.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActions.kt
index 44bff4f5ab..ef93574134 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActions.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaActions.kt
@@ -16,6 +16,7 @@
package io.element.android.features.messages.impl.media.local
+import android.app.Activity
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
@@ -24,17 +25,25 @@ import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
+import androidx.activity.compose.ManagedActivityResultLauncher
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.FileProvider
import androidx.core.net.toFile
import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.libraries.androidutils.system.startInstallFromSourceIntent
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.meta.BuildMeta
+import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
+import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.File
@@ -50,10 +59,27 @@ class AndroidLocalMediaActions @Inject constructor(
) : LocalMediaActions {
private var activityContext: Context? = null
+ private var apkInstallLauncher: ManagedActivityResultLauncher? = null
+ private var pendingMedia: LocalMedia? = null
@Composable
override fun Configure() {
val context = LocalContext.current
+ val coroutineScope = rememberCoroutineScope()
+ apkInstallLauncher = rememberLauncherForActivityResult(
+ contract = ActivityResultContracts.StartActivityForResult(),
+ ) { activityResult ->
+ if (activityResult.resultCode == Activity.RESULT_OK) {
+ pendingMedia?.let {
+ coroutineScope.launch {
+ openFile(it)
+ }
+ }
+ } else {
+ // User cancelled
+ }
+ pendingMedia = null
+ }
return DisposableEffect(Unit) {
activityContext = context
onDispose {
@@ -99,11 +125,20 @@ class AndroidLocalMediaActions @Inject constructor(
override suspend fun open(localMedia: LocalMedia): Result = withContext(coroutineDispatchers.io) {
require(localMedia.uri.scheme == ContentResolver.SCHEME_FILE)
runCatching {
- val openMediaIntent = Intent(Intent.ACTION_VIEW)
- .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- .setDataAndType(localMedia.toShareableUri(), localMedia.info.mimeType)
- withContext(coroutineDispatchers.main) {
- activityContext!!.startActivity(openMediaIntent)
+ when (localMedia.info.mimeType) {
+ MimeTypes.Apk -> {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ if (activityContext?.packageManager?.canRequestPackageInstalls() == false) {
+ pendingMedia = localMedia
+ activityContext?.startInstallFromSourceIntent(apkInstallLauncher!!).let { }
+ } else {
+ openFile(localMedia)
+ }
+ } else {
+ openFile(localMedia)
+ }
+ }
+ else -> openFile(localMedia)
}
}.onSuccess {
Timber.v("Open media succeed")
@@ -112,6 +147,15 @@ class AndroidLocalMediaActions @Inject constructor(
}
}
+ private suspend fun openFile(localMedia: LocalMedia) {
+ val openMediaIntent = Intent(Intent.ACTION_VIEW)
+ .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ .setDataAndType(localMedia.toShareableUri(), localMedia.info.mimeType)
+ withContext(coroutineDispatchers.main) {
+ activityContext?.startActivity(openMediaIntent)
+ }
+ }
+
private fun LocalMedia.toShareableUri(): Uri {
val mediaAsFile = this.toFile()
val authority = "${buildMeta.applicationId}.fileprovider"
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt
index ff17029497..4348b83a95 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt
@@ -31,7 +31,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Attachment
import androidx.compose.material.icons.outlined.GraphicEq
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -64,9 +63,10 @@ import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo
-import io.element.android.libraries.designsystem.R
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
+import io.element.android.libraries.designsystem.utils.KeepScreenOn
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import io.element.android.libraries.theme.ElementTheme
import me.saket.telephoto.zoomable.ZoomSpec
@@ -124,7 +124,7 @@ private fun MediaImageView(
) {
if (LocalInspectionMode.current) {
Image(
- painter = painterResource(id = R.drawable.sample_background),
+ painter = painterResource(id = CommonDrawables.sample_background),
modifier = modifier.fillMaxSize(),
contentDescription = null,
)
@@ -143,7 +143,7 @@ private fun MediaImageView(
@UnstableApi
@Composable
-fun MediaVideoView(
+private fun MediaVideoView(
localMediaViewState: LocalMediaViewState,
localMedia: LocalMedia?,
modifier: Modifier = Modifier,
@@ -153,6 +153,10 @@ fun MediaVideoView(
override fun onRenderedFirstFrame() {
localMediaViewState.isReady = true
}
+
+ override fun onIsPlayingChanged(isPlaying: Boolean) {
+ localMediaViewState.isPlaying = isPlaying
+ }
}
val exoPlayer = remember {
ExoPlayerWrapper.create(context)
@@ -169,6 +173,7 @@ fun MediaVideoView(
} else {
exoPlayer.setMediaItems(emptyList())
}
+ KeepScreenOn(localMediaViewState.isPlaying)
AndroidView(
factory = {
PlayerView(context).apply {
@@ -197,7 +202,7 @@ fun MediaVideoView(
}
@Composable
-fun MediaPDFView(
+private fun MediaPDFView(
localMediaViewState: LocalMediaViewState,
localMedia: LocalMedia?,
zoomableState: ZoomableState,
@@ -212,7 +217,7 @@ fun MediaPDFView(
}
@Composable
-fun MediaFileView(
+private fun MediaFileView(
localMediaViewState: LocalMediaViewState,
uri: Uri?,
info: MediaInfo?,
@@ -230,7 +235,8 @@ fun MediaFileView(
contentAlignment = Alignment.Center,
) {
Icon(
- imageVector = if (isAudio) Icons.Outlined.GraphicEq else Icons.Outlined.Attachment,
+ imageVector = if (isAudio) Icons.Outlined.GraphicEq else null,
+ resourceId = if (isAudio) null else CommonDrawables.ic_september_attachment,
contentDescription = null,
tint = MaterialTheme.colorScheme.background,
modifier = Modifier
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaViewState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaViewState.kt
index e009c3f6cc..d5af7a78e1 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaViewState.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaViewState.kt
@@ -26,6 +26,7 @@ import androidx.compose.runtime.setValue
@Stable
class LocalMediaViewState {
var isReady: Boolean by mutableStateOf(false)
+ var isPlaying: Boolean by mutableStateOf(false)
}
@Composable
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt
index 13a9ff3bee..91fff1fa72 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt
@@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -33,9 +34,9 @@ import io.element.android.features.messages.impl.media.local.LocalMediaActions
import io.element.android.features.messages.impl.media.local.LocalMediaFactory
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
-import io.element.android.libraries.designsystem.utils.SnackbarMessage
-import io.element.android.libraries.designsystem.utils.collectSnackbarMessageAsState
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
+import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.media.MediaFile
import io.element.android.libraries.ui.strings.CommonStrings
@@ -59,7 +60,7 @@ class MediaViewerPresenter @AssistedInject constructor(
@Composable
override fun present(): MediaViewerState {
val coroutineScope = rememberCoroutineScope()
- var loadMediaTrigger by remember { mutableStateOf(0) }
+ var loadMediaTrigger by remember { mutableIntStateOf(0) }
val mediaFile: MutableState = remember {
mutableStateOf(null)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt
index 18375746c5..86abaf648e 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerState.kt
@@ -19,7 +19,7 @@ package io.element.android.features.messages.impl.media.viewer
import io.element.android.features.messages.impl.media.local.LocalMedia
import io.element.android.features.messages.impl.media.local.MediaInfo
import io.element.android.libraries.architecture.Async
-import io.element.android.libraries.designsystem.utils.SnackbarMessage
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.media.MediaSource
data class MediaViewerState(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt
index 97133da759..cb2775c7b6 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt
@@ -29,9 +29,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Download
-import androidx.compose.material.icons.filled.OpenInNew
-import androidx.compose.material.icons.filled.Share
+import androidx.compose.material.icons.automirrored.filled.OpenInNew
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.runtime.Composable
@@ -49,11 +47,13 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
+import io.element.android.features.messages.impl.R
import io.element.android.features.messages.impl.media.local.LocalMedia
import io.element.android.features.messages.impl.media.local.LocalMediaView
import io.element.android.features.messages.impl.media.local.MediaInfo
import io.element.android.features.messages.impl.media.local.rememberLocalMediaViewState
import io.element.android.libraries.architecture.Async
+import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@@ -61,8 +61,9 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TopAppBar
-import io.element.android.libraries.designsystem.utils.SnackbarHost
-import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
+import io.element.android.libraries.designsystem.utils.CommonDrawables
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
+import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.media.MediaRequestData
import io.element.android.libraries.ui.strings.CommonStrings
@@ -93,6 +94,7 @@ fun MediaViewerView(
topBar = {
MediaViewerTopBar(
actionsEnabled = state.downloadedMedia is Async.Success,
+ mimeType = state.mediaInfo.mimeType,
onBackPressed = onBackPressed,
eventSink = state.eventSink
)
@@ -163,6 +165,7 @@ private fun rememberShowProgress(downloadedMedia: Async): Boolean {
@Composable
private fun MediaViewerTopBar(
actionsEnabled: Boolean,
+ mimeType: String,
onBackPressed: () -> Unit,
eventSink: (MediaViewerEvents) -> Unit,
) {
@@ -176,7 +179,16 @@ private fun MediaViewerTopBar(
eventSink(MediaViewerEvents.OpenWith)
},
) {
- Icon(imageVector = Icons.Default.OpenInNew, contentDescription = stringResource(id = CommonStrings.action_open_with))
+ when (mimeType) {
+ MimeTypes.Apk -> Icon(
+ resourceId = R.drawable.ic_apk_install,
+ contentDescription = stringResource(id = CommonStrings.common_install_apk_android)
+ )
+ else -> Icon(
+ imageVector = Icons.AutoMirrored.Filled.OpenInNew,
+ contentDescription = stringResource(id = CommonStrings.action_open_with)
+ )
+ }
}
IconButton(
enabled = actionsEnabled,
@@ -184,7 +196,10 @@ private fun MediaViewerTopBar(
eventSink(MediaViewerEvents.SaveOnDisk)
},
) {
- Icon(imageVector = Icons.Default.Download, contentDescription = stringResource(id = CommonStrings.action_save))
+ Icon(
+ resourceId = CommonDrawables.ic_compound_download,
+ contentDescription = stringResource(id = CommonStrings.action_save),
+ )
}
IconButton(
enabled = actionsEnabled,
@@ -192,7 +207,10 @@ private fun MediaViewerTopBar(
eventSink(MediaViewerEvents.Share)
},
) {
- Icon(imageVector = Icons.Default.Share, contentDescription = stringResource(id = CommonStrings.action_share))
+ Icon(
+ resourceId = CommonDrawables.ic_compound_share_android,
+ contentDescription = stringResource(id = CommonStrings.action_share)
+ )
}
}
)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt
index f7108fe951..b920e5967d 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt
@@ -22,14 +22,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ListItem
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.AttachFile
-import androidx.compose.material.icons.filled.BarChart
-import androidx.compose.material.icons.filled.Collections
-import androidx.compose.material.icons.filled.FormatColorText
-import androidx.compose.material.icons.filled.LocationOn
-import androidx.compose.material.icons.filled.PhotoCamera
-import androidx.compose.material.icons.filled.Videocam
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
@@ -44,11 +36,12 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.R
import io.element.android.libraries.androidutils.ui.hideKeyboard
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -102,7 +95,7 @@ internal fun AttachmentsBottomSheet(
@OptIn(ExperimentalMaterialApi::class)
@Composable
-internal fun AttachmentSourcePickerMenu(
+private fun AttachmentSourcePickerMenu(
state: MessageComposerState,
onSendLocationClicked: () -> Unit,
onCreatePollClicked: () -> Unit,
@@ -115,22 +108,22 @@ internal fun AttachmentSourcePickerMenu(
) {
ListItem(
modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) },
- icon = { Icon(Icons.Default.Collections, null) },
+ icon = { Icon(CommonDrawables.ic_september_photo_video_library, null) },
text = { Text(stringResource(R.string.screen_room_attachment_source_gallery)) },
)
ListItem(
modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) },
- icon = { Icon(Icons.Default.AttachFile, null) },
+ icon = { Icon(CommonDrawables.ic_september_attachment, null) },
text = { Text(stringResource(R.string.screen_room_attachment_source_files)) },
)
ListItem(
modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera) },
- icon = { Icon(Icons.Default.PhotoCamera, null) },
+ icon = { Icon(CommonDrawables.ic_september_take_photo_camera, null) },
text = { Text(stringResource(R.string.screen_room_attachment_source_camera_photo)) },
)
ListItem(
modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) },
- icon = { Icon(Icons.Default.Videocam, null) },
+ icon = { Icon(CommonDrawables.ic_september_video_call, null) },
text = { Text(stringResource(R.string.screen_room_attachment_source_camera_video)) },
)
if (state.canShareLocation) {
@@ -139,7 +132,7 @@ internal fun AttachmentSourcePickerMenu(
state.eventSink(MessageComposerEvents.PickAttachmentSource.Location)
onSendLocationClicked()
},
- icon = { Icon(Icons.Default.LocationOn, null) },
+ icon = { Icon(CommonDrawables.ic_september_location, null) },
text = { Text(stringResource(R.string.screen_room_attachment_source_location)) },
)
}
@@ -149,21 +142,21 @@ internal fun AttachmentSourcePickerMenu(
state.eventSink(MessageComposerEvents.PickAttachmentSource.Poll)
onCreatePollClicked()
},
- icon = { Icon(Icons.Default.BarChart, null) },
+ icon = { Icon(CommonDrawables.ic_compound_polls, null) },
text = { Text(stringResource(R.string.screen_room_attachment_source_poll)) },
)
}
if (enableTextFormatting) {
ListItem(
modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.ToggleTextFormatting(enabled = true)) },
- icon = { Icon(Icons.Default.FormatColorText, null) },
+ icon = { Icon(CommonDrawables.ic_september_text_formatting, null) },
text = { Text(stringResource(R.string.screen_room_attachment_text_formatting)) },
)
}
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AttachmentSourcePickerMenuPreview() = ElementPreview {
AttachmentSourcePickerMenu(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerContextImpl.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerContextImpl.kt
index 73481cd617..c9a491d8a3 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerContextImpl.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerContextImpl.kt
@@ -23,7 +23,7 @@ import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.messages.api.MessageComposerContext
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn
-import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
import javax.inject.Inject
@SingleIn(RoomScope::class)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerEvents.kt
index 92b180f326..cd03c52e1e 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerEvents.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerEvents.kt
@@ -17,8 +17,8 @@
package io.element.android.features.messages.impl.messagecomposer
import androidx.compose.runtime.Immutable
-import io.element.android.libraries.textcomposer.Message
-import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.libraries.textcomposer.model.Message
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
@Immutable
sealed interface MessageComposerEvents {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt
index a7728c2a00..e4a8d50cb8 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt
@@ -16,6 +16,7 @@
package io.element.android.features.messages.impl.messagecomposer
+import android.Manifest
import android.annotation.SuppressLint
import android.net.Uri
import androidx.compose.runtime.Composable
@@ -34,8 +35,8 @@ import io.element.android.features.messages.impl.attachments.Attachment
import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError
import io.element.android.features.messages.impl.media.local.LocalMediaFactory
import io.element.android.libraries.architecture.Presenter
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
-import io.element.android.libraries.designsystem.utils.SnackbarMessage
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.featureflag.api.FeatureFlagService
@@ -44,8 +45,10 @@ import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.mediapickers.api.PickerProvider
import io.element.android.libraries.mediaupload.api.MediaSender
-import io.element.android.libraries.textcomposer.Message
-import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.libraries.permissions.api.PermissionsEvents
+import io.element.android.libraries.permissions.api.PermissionsPresenter
+import io.element.android.libraries.textcomposer.model.Message
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.wysiwyg.compose.RichTextEditorState
import kotlinx.collections.immutable.persistentListOf
@@ -70,13 +73,18 @@ class MessageComposerPresenter @Inject constructor(
private val analyticsService: AnalyticsService,
private val messageComposerContext: MessageComposerContextImpl,
private val richTextEditorStateFactory: RichTextEditorStateFactory,
+ permissionsPresenterFactory: PermissionsPresenter.Factory
) : Presenter {
+ private val cameraPermissionPresenter = permissionsPresenterFactory.create(Manifest.permission.CAMERA)
+ private var pendingEvent: MessageComposerEvents? = null
+
@SuppressLint("UnsafeOptInUsageError")
@Composable
override fun present(): MessageComposerState {
val localCoroutineScope = rememberCoroutineScope()
+ val cameraPermissionState = cameraPermissionPresenter.present()
val attachmentsState = remember {
mutableStateOf(AttachmentsState.None)
}
@@ -132,15 +140,26 @@ class MessageComposerPresenter @Inject constructor(
}
}
+ LaunchedEffect(cameraPermissionState.permissionGranted) {
+ if (cameraPermissionState.permissionGranted) {
+ when (pendingEvent) {
+ is MessageComposerEvents.PickAttachmentSource.PhotoFromCamera -> cameraPhotoPicker.launch()
+ is MessageComposerEvents.PickAttachmentSource.VideoFromCamera -> cameraVideoPicker.launch()
+ else -> Unit
+ }
+ pendingEvent = null
+ }
+ }
+
fun handleEvents(event: MessageComposerEvents) {
when (event) {
MessageComposerEvents.ToggleFullScreenState -> isFullScreen.value = !isFullScreen.value
-
MessageComposerEvents.CloseSpecialMode -> {
- richTextEditorState.setHtml("")
+ localCoroutineScope.launch {
+ richTextEditorState.setHtml("")
+ }
messageComposerContext.composerMode = MessageComposerMode.Normal("")
}
-
is MessageComposerEvents.SendMessage -> appCoroutineScope.sendMessage(
message = event.message,
updateComposerMode = { messageComposerContext.composerMode = it },
@@ -148,6 +167,16 @@ class MessageComposerPresenter @Inject constructor(
)
is MessageComposerEvents.SetMode -> {
messageComposerContext.composerMode = event.composerMode
+ when (event.composerMode) {
+ is MessageComposerMode.Reply -> event.composerMode.eventId
+ is MessageComposerMode.Edit -> event.composerMode.eventId
+ is MessageComposerMode.Normal -> null
+ is MessageComposerMode.Quote -> null
+ }.let { relatedEventId ->
+ appCoroutineScope.launch {
+ room.enterSpecialMode(relatedEventId)
+ }
+ }
}
MessageComposerEvents.AddAttachment -> localCoroutineScope.launch {
showAttachmentSourcePicker = true
@@ -163,11 +192,21 @@ class MessageComposerPresenter @Inject constructor(
}
MessageComposerEvents.PickAttachmentSource.PhotoFromCamera -> localCoroutineScope.launch {
showAttachmentSourcePicker = false
- cameraPhotoPicker.launch()
+ if (cameraPermissionState.permissionGranted) {
+ cameraPhotoPicker.launch()
+ } else {
+ pendingEvent = event
+ cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions)
+ }
}
MessageComposerEvents.PickAttachmentSource.VideoFromCamera -> localCoroutineScope.launch {
showAttachmentSourcePicker = false
- cameraVideoPicker.launch()
+ if (cameraPermissionState.permissionGranted) {
+ cameraVideoPicker.launch()
+ } else {
+ pendingEvent = event
+ cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions)
+ }
}
MessageComposerEvents.PickAttachmentSource.Location -> {
showAttachmentSourcePicker = false
@@ -301,16 +340,16 @@ class MessageComposerPresenter @Inject constructor(
}
mediaSender.sendMedia(uri, mimeType, compressIfPossible = false, progressCallback).getOrThrow()
}
- .onSuccess {
- attachmentState.value = AttachmentsState.None
- }
- .onFailure { cause ->
- attachmentState.value = AttachmentsState.None
- if (cause is CancellationException) {
- throw cause
- } else {
- val snackbarMessage = SnackbarMessage(sendAttachmentError(cause))
- snackbarDispatcher.post(snackbarMessage)
+ .onSuccess {
+ attachmentState.value = AttachmentsState.None
+ }
+ .onFailure { cause ->
+ attachmentState.value = AttachmentsState.None
+ if (cause is CancellationException) {
+ throw cause
+ } else {
+ val snackbarMessage = SnackbarMessage(sendAttachmentError(cause))
+ snackbarDispatcher.post(snackbarMessage)
+ }
}
- }
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt
index 65fac53fdc..3e8f171626 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt
@@ -17,12 +17,13 @@
package io.element.android.features.messages.impl.messagecomposer
import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
import io.element.android.features.messages.impl.attachments.Attachment
-import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.wysiwyg.compose.RichTextEditorState
import kotlinx.collections.immutable.ImmutableList
-@Immutable
+@Stable
data class MessageComposerState(
val richTextEditorState: RichTextEditorState,
val isFullScreen: Boolean,
@@ -34,7 +35,6 @@ data class MessageComposerState(
val attachmentsState: AttachmentsState,
val eventSink: (MessageComposerEvents) -> Unit,
) {
- val canSendMessage: Boolean = richTextEditorState.messageHtml.isNotEmpty()
val hasFocus: Boolean = richTextEditorState.hasFocus
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt
index d86969fc19..7dbe413e83 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt
@@ -17,7 +17,7 @@
package io.element.android.features.messages.impl.messagecomposer
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
-import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.wysiwyg.compose.RichTextEditorState
open class MessageComposerStateProvider : PreviewParameterProvider {
@@ -28,8 +28,7 @@ open class MessageComposerStateProvider : PreviewParameterProvider Unit,
- onCreatePollClicked: () -> Unit,
+ voiceMessageState: VoiceMessageComposerState,
+ subcomposing: Boolean,
enableTextFormatting: Boolean,
+ enableVoiceMessages: Boolean,
modifier: Modifier = Modifier,
) {
- fun onFullscreenToggle() {
- state.eventSink(MessageComposerEvents.ToggleFullScreenState)
- }
-
fun sendMessage(message: Message) {
state.eventSink(MessageComposerEvents.SendMessage(message))
}
@@ -57,37 +64,74 @@ fun MessageComposerView(
state.eventSink(MessageComposerEvents.Error(error))
}
- Box(modifier = modifier) {
- AttachmentsBottomSheet(
- state = state,
- onSendLocationClicked = onSendLocationClicked,
- onCreatePollClicked = onCreatePollClicked,
- enableTextFormatting = enableTextFormatting,
- )
+ val coroutineScope = rememberCoroutineScope()
+ fun onRequestFocus() {
+ coroutineScope.launch {
+ state.richTextEditorState.requestFocus()
+ }
+ }
- TextComposer(
- state = state.richTextEditorState,
- canSendMessage = state.canSendMessage,
- onRequestFocus = { state.richTextEditorState.requestFocus() },
- onSendMessage = ::sendMessage,
- composerMode = state.mode,
- showTextFormatting = state.showTextFormatting,
- onResetComposerMode = ::onCloseSpecialMode,
- onAddAttachment = ::onAddAttachment,
- onDismissTextFormatting = ::onDismissTextFormatting,
- enableTextFormatting = enableTextFormatting,
- onError = ::onError,
+ fun onVoiceRecordButtonEvent(press: PressEvent) {
+ voiceMessageState.eventSink(VoiceMessageComposerEvents.RecordButtonEvent(press))
+ }
+
+ TextComposer(
+ modifier = modifier,
+ state = state.richTextEditorState,
+ voiceMessageState = voiceMessageState.voiceMessageState,
+ subcomposing = subcomposing,
+ onRequestFocus = ::onRequestFocus,
+ onSendMessage = ::sendMessage,
+ composerMode = state.mode,
+ showTextFormatting = state.showTextFormatting,
+ onResetComposerMode = ::onCloseSpecialMode,
+ onAddAttachment = ::onAddAttachment,
+ onDismissTextFormatting = ::onDismissTextFormatting,
+ enableTextFormatting = enableTextFormatting,
+ enableVoiceMessages = enableVoiceMessages,
+ onVoiceRecordButtonEvent = ::onVoiceRecordButtonEvent,
+ onError = ::onError,
+ )
+}
+
+@PreviewsDayNight
+@Composable
+internal fun MessageComposerViewPreview(
+ @PreviewParameter(MessageComposerStateProvider::class) state: MessageComposerState,
+) = ElementPreview {
+ Column {
+ MessageComposerView(
+ modifier = Modifier.height(IntrinsicSize.Min),
+ state = state,
+ voiceMessageState = aVoiceMessageComposerState(),
+ enableTextFormatting = true,
+ enableVoiceMessages = true,
+ subcomposing = false,
+ )
+ MessageComposerView(
+ modifier = Modifier.height(200.dp),
+ state = state,
+ voiceMessageState = aVoiceMessageComposerState(),
+ enableTextFormatting = true,
+ enableVoiceMessages = true,
+ subcomposing = false,
)
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
-internal fun MessageComposerViewPreview(@PreviewParameter(MessageComposerStateProvider::class) state: MessageComposerState) = ElementPreview {
- MessageComposerView(
- state = state,
- onSendLocationClicked = {},
- onCreatePollClicked = {},
- enableTextFormatting = true,
- )
+internal fun MessageComposerViewVoicePreview(
+ @PreviewParameter(VoiceMessageComposerStateProvider::class) state: VoiceMessageComposerState,
+) = ElementPreview {
+ Column {
+ MessageComposerView(
+ modifier = Modifier.height(IntrinsicSize.Min),
+ state = aMessageComposerState(),
+ voiceMessageState = state,
+ enableTextFormatting = true,
+ enableVoiceMessages = true,
+ subcomposing = false,
+ )
+ }
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt
index 0ce4856ffa..d474e3d25f 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessagePresenter.kt
@@ -30,8 +30,8 @@ import dagger.assisted.AssistedInject
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runUpdatingState
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
-import io.element.android.libraries.designsystem.utils.SnackbarMessage
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.MatrixRoom
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt
index 335d4f9e29..3b6e46fd59 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt
@@ -44,7 +44,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Button
@@ -165,7 +165,7 @@ fun ReportMessageView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ReportMessageViewPreview(@PreviewParameter(ReportMessageStateProvider::class) state: ReportMessageState) = ElementPreview {
ReportMessageView(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt
index e427646a71..ca23904583 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt
@@ -26,4 +26,8 @@ sealed interface TimelineEvents {
val pollStartId: EventId,
val answerId: String
) : TimelineEvents
+
+ data class PollEndClicked(
+ val pollStartId: EventId,
+ ) : TimelineEvents
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt
index a7e86d341f..0d0fafd17d 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt
@@ -26,6 +26,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
+import im.vector.app.features.analytics.plan.PollEnd
import im.vector.app.features.analytics.plan.PollVote
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
import io.element.android.features.messages.impl.timeline.model.TimelineItem
@@ -98,11 +99,18 @@ class TimelinePresenter @Inject constructor(
)
analyticsService.capture(PollVote())
}
+ is TimelineEvents.PollEndClicked -> appScope.launch {
+ room.endPoll(
+ pollStartId = event.pollStartId,
+ text = "The poll with event id: ${event.pollStartId} has ended."
+ )
+ analyticsService.capture(PollEnd())
+ }
}
}
LaunchedEffect(timelineItems.size) {
- computeHasNewItems(timelineItems, prevMostRecentItemId, hasNewItems)
+ computeHasNewItems(timelineItems, prevMostRecentItemId, hasNewItems)
}
LaunchedEffect(Unit) {
@@ -123,7 +131,7 @@ class TimelinePresenter @Inject constructor(
paginationState = paginationState,
timelineItems = timelineItems,
hasNewItems = hasNewItems.value,
- eventSink = { handleEvents(it) }
+ eventSink = ::handleEvents
)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt
index e48eb4f72b..fdb4b309eb 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt
@@ -36,8 +36,6 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowDownward
import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -49,6 +47,8 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.draw.rotate
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -64,10 +64,12 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.canBeRepliedTo
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.animation.alphaAnimation
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.theme.ElementTheme
@@ -101,11 +103,10 @@ fun TimelineView(
// TODO implement this logic once we have support to 'jump to event X' in sliding sync
}
- fun onPollAnswerSelected(pollStartId: EventId, answerId: String) {
- state.eventSink(TimelineEvents.PollAnswerSelected(pollStartId, answerId))
- }
+ // Animate alpha when timeline is first displayed, to avoid flashes or glitching when viewing rooms
+ val alpha by alphaAnimation(label = "alpha for timeline")
- Box(modifier = modifier) {
+ Box(modifier = modifier.alpha(alpha)) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = lazyListState,
@@ -129,7 +130,7 @@ fun TimelineView(
onReactionLongClick = onReactionLongClicked,
onMoreReactionsClick = onMoreReactionsClicked,
onTimestampClicked = onTimestampClicked,
- onPollAnswerSelected = ::onPollAnswerSelected,
+ eventSink = state.eventSink,
onSwipeToReply = onSwipeToReply,
)
}
@@ -154,7 +155,7 @@ fun TimelineView(
}
@Composable
-fun TimelineItemRow(
+private fun TimelineItemRow(
timelineItem: TimelineItem,
highlightedItem: String?,
userHasPermissionToSendMessage: Boolean,
@@ -167,7 +168,7 @@ fun TimelineItemRow(
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
onTimestampClicked: (TimelineItem.Event) -> Unit,
onSwipeToReply: (TimelineItem.Event) -> Unit,
- onPollAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
+ eventSink: (TimelineEvents) -> Unit,
modifier: Modifier = Modifier
) {
when (timelineItem) {
@@ -184,6 +185,7 @@ fun TimelineItemRow(
isHighlighted = highlightedItem == timelineItem.identifier(),
onClick = { onClick(timelineItem) },
onLongClick = { onLongClick(timelineItem) },
+ eventSink = eventSink,
modifier = modifier,
)
} else {
@@ -200,7 +202,7 @@ fun TimelineItemRow(
onMoreReactionsClick = onMoreReactionsClick,
onTimestampClicked = onTimestampClicked,
onSwipeToReply = { onSwipeToReply(timelineItem) },
- onPollAnswerSelected = onPollAnswerSelected,
+ eventSink = eventSink,
modifier = modifier,
)
}
@@ -238,7 +240,7 @@ fun TimelineItemRow(
onReactionClick = onReactionClick,
onReactionLongClick = onReactionLongClick,
onMoreReactionsClick = onMoreReactionsClick,
- onPollAnswerSelected = onPollAnswerSelected,
+ eventSink = eventSink,
onSwipeToReply = {},
)
}
@@ -315,15 +317,17 @@ private fun JumpToBottomButton(
contentColor = ElementTheme.colors.iconSecondary
) {
Icon(
- modifier = Modifier.size(24.dp),
- imageVector = Icons.Filled.ArrowDownward,
+ modifier = Modifier
+ .size(24.dp)
+ .rotate(90f),
+ resourceId = CommonDrawables.ic_compound_arrow_right,
contentDescription = "",
)
}
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineViewPreview(
@PreviewParameter(TimelineItemEventContentProvider::class) content: TimelineItemEventContent
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt
index e73f20a2a0..bcb0efc96e 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt
@@ -39,7 +39,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemGrou
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleStateProvider
import io.element.android.libraries.core.extensions.to01
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
@@ -131,7 +131,7 @@ fun MessageEventBubble(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun MessageEventBubblePreview(@PreviewParameter(BubbleStateProvider::class) state: BubbleState) = ElementPreview {
// Due to position offset, surround with a Box
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt
index 44df6634a1..e4dfde37f9 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt
@@ -31,7 +31,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Surface
@@ -66,7 +66,7 @@ fun MessageStateEventContainer(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun MessageStateEventContainerPreview() = ElementPreview {
Column {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt
index 930adf36cd..d5dd861bf1 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt
@@ -16,6 +16,7 @@
package io.element.android.features.messages.impl.timeline.components
+import androidx.annotation.DrawableRes
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
@@ -29,15 +30,12 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.AddReaction
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@@ -46,12 +44,13 @@ import io.element.android.features.messages.impl.R
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
import io.element.android.features.messages.impl.timeline.model.AggregatedReactionProvider
import io.element.android.features.messages.impl.timeline.model.aTimelineItemReactions
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.text.toDp
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@Composable
@@ -96,7 +95,7 @@ fun MessagesReactionButton(
color = buttonColor
) {
when (content) {
- is MessagesReactionsButtonContent.Icon -> IconContent(imageVector = content.imageVector)
+ is MessagesReactionsButtonContent.Icon -> IconContent(resourceId = content.resourceId)
is MessagesReactionsButtonContent.Text -> TextContent(text = content.text)
is MessagesReactionsButtonContent.Reaction -> ReactionContent(reaction = content.reaction)
}
@@ -105,7 +104,7 @@ fun MessagesReactionButton(
sealed class MessagesReactionsButtonContent {
data class Text(val text: String) : MessagesReactionsButtonContent()
- data class Icon(val imageVector: ImageVector) : MessagesReactionsButtonContent()
+ data class Icon(@DrawableRes val resourceId: Int) : MessagesReactionsButtonContent()
data class Reaction(val reaction: AggregatedReaction) : MessagesReactionsButtonContent()
@@ -129,10 +128,10 @@ private fun TextContent(
@Composable
private fun IconContent(
- imageVector: ImageVector,
+ @DrawableRes resourceId: Int,
modifier: Modifier = Modifier
) = Icon(
- imageVector = imageVector,
+ resourceId = resourceId,
contentDescription = stringResource(id = R.string.screen_room_timeline_add_reaction),
tint = ElementTheme.materialColors.secondary,
modifier = modifier
@@ -165,7 +164,7 @@ private fun ReactionContent(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun MessagesReactionButtonPreview(@PreviewParameter(AggregatedReactionProvider::class) reaction: AggregatedReaction) = ElementPreview {
MessagesReactionButton(
@@ -175,17 +174,17 @@ internal fun MessagesReactionButtonPreview(@PreviewParameter(AggregatedReactionP
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun MessagesAddReactionButtonPreview() = ElementPreview {
MessagesReactionButton(
- content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction),
+ content = MessagesReactionsButtonContent.Icon(CommonDrawables.ic_september_add_reaction),
onClick = {},
onLongClick = {}
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun MessagesReactionExtraButtonsPreview() = ElementPreview {
Row {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ReplySwipeIndicator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ReplySwipeIndicator.kt
index ba061a7918..50ba43f27c 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ReplySwipeIndicator.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ReplySwipeIndicator.kt
@@ -25,10 +25,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.VectorIcons
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.utils.CommonDrawables
/**
* A swipe indicator that appears when swiping to reply to a message.
@@ -49,11 +49,11 @@ fun RowScope.ReplySwipeIndicator(
alpha = swipeProgress()
},
contentDescription = null,
- resourceId = VectorIcons.Reply,
+ resourceId = CommonDrawables.ic_september_reply,
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ReplySwipeIndicatorPreview() = ElementPreview {
Column(modifier = Modifier.fillMaxWidth()) {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt
index 92000e51bd..5bc7efc852 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt
@@ -24,8 +24,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Error
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -38,10 +36,11 @@ import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
import io.element.android.libraries.core.bool.orFalse
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -90,12 +89,17 @@ fun TimelineEventTimestampView(
)
if (hasMessageSendingFailed && tint != null) {
Spacer(modifier = Modifier.width(2.dp))
- Icon(imageVector = Icons.Default.Error, contentDescription = "Error sending message", tint = tint, modifier = Modifier.size(15.dp, 18.dp))
+ Icon(
+ resourceId = CommonDrawables.ic_compound_error,
+ contentDescription = "Error sending message",
+ tint = tint,
+ modifier = Modifier.size(15.dp, 18.dp),
+ )
}
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineEventTimestampViewPreview(@PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event) = ElementPreview {
TimelineEventTimestampView(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt
index 6f51ec8ac7..0ef1c9bc67 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt
@@ -61,6 +61,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.constraintlayout.compose.ConstrainScope
import androidx.constraintlayout.compose.ConstraintLayout
+import io.element.android.features.messages.impl.timeline.TimelineEvents
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
@@ -76,19 +77,19 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
-import io.element.android.libraries.designsystem.VectorIcons
import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.components.EqualWidthColumn
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.swipe.SwipeableActionsState
import io.element.android.libraries.designsystem.swipe.rememberSwipeableActionsState
import io.element.android.libraries.designsystem.text.toPx
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
@@ -123,7 +124,7 @@ fun TimelineItemEventRow(
onReactionLongClick: (emoji: String, eventId: TimelineItem.Event) -> Unit,
onMoreReactionsClick: (eventId: TimelineItem.Event) -> Unit,
onSwipeToReply: () -> Unit,
- onPollAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
+ eventSink: (TimelineEvents) -> Unit,
modifier: Modifier = Modifier
) {
val coroutineScope = rememberCoroutineScope()
@@ -146,7 +147,7 @@ fun TimelineItemEventRow(
}
if (canReply) {
val state: SwipeableActionsState = rememberSwipeableActionsState()
- val offset = state.offset.value
+ val offset = state.offset.floatValue
val swipeThresholdPx = 40.dp.toPx()
val thresholdCrossed = abs(offset) > swipeThresholdPx
SwipeSensitivity(3f) {
@@ -181,7 +182,7 @@ fun TimelineItemEventRow(
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
onMoreReactionsClicked = { onMoreReactionsClick(event) },
- onPollAnswerSelected = onPollAnswerSelected,
+ eventSink = eventSink,
)
}
}
@@ -198,7 +199,7 @@ fun TimelineItemEventRow(
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
onMoreReactionsClicked = { onMoreReactionsClick(event) },
- onPollAnswerSelected = onPollAnswerSelected,
+ eventSink = eventSink,
)
}
}
@@ -212,7 +213,7 @@ fun TimelineItemEventRow(
* @param content the content to display.
*/
@Composable
-fun SwipeSensitivity(
+private fun SwipeSensitivity(
sensitivityFactor: Float,
content: @Composable () -> Unit,
) {
@@ -240,7 +241,7 @@ private fun TimelineItemEventRowContent(
onReactionClicked: (emoji: String) -> Unit,
onReactionLongClicked: (emoji: String) -> Unit,
onMoreReactionsClicked: (event: TimelineItem.Event) -> Unit,
- onPollAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
+ eventSink: (TimelineEvents) -> Unit,
modifier: Modifier = Modifier,
) {
fun ConstrainScope.linkStartOrEnd(event: TimelineItem.Event) = if (event.isMine) {
@@ -299,13 +300,13 @@ private fun TimelineItemEventRowContent(
onTimestampClicked = {
onTimestampClicked(event)
},
- onPollAnswerSelected = onPollAnswerSelected,
+ eventSink = eventSink,
)
}
// Reactions
if (event.reactionsState.reactions.isNotEmpty()) {
- TimelineItemReactions(
+ TimelineItemReactionsView(
reactionsState = event.reactionsState,
isOutgoing = event.isMine,
onReactionClicked = onReactionClicked,
@@ -314,7 +315,7 @@ private fun TimelineItemEventRowContent(
modifier = Modifier
.constrainAs(reactions) {
top.linkTo(message.bottom, margin = (-4).dp)
- this.linkStartOrEnd(event)
+ linkStartOrEnd(event)
}
.zIndex(1f)
.padding(start = if (event.isMine) 16.dp else 36.dp, end = 16.dp)
@@ -371,7 +372,7 @@ private fun MessageEventBubbleContent(
onMessageLongClick: () -> Unit,
inReplyToClick: () -> Unit,
onTimestampClicked: () -> Unit,
- onPollAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
+ eventSink: (TimelineEvents) -> Unit,
@SuppressLint("ModifierParameter") bubbleModifier: Modifier = Modifier, // need to rename this modifier to distinguish it from the following ones
) {
@@ -385,11 +386,12 @@ private fun MessageEventBubbleContent(
) {
TimelineItemEventContentView(
content = event.content,
+ isMine = event.isMine,
interactionSource = interactionSource,
onClick = onMessageClick,
onLongClick = onMessageLongClick,
extraPadding = event.toExtraPadding(),
- onPollAnswerSelected = onPollAnswerSelected,
+ eventSink = eventSink,
modifier = modifier,
)
}
@@ -403,7 +405,11 @@ private fun MessageEventBubbleContent(
horizontalArrangement = spacedBy(4.dp, Alignment.Start),
verticalAlignment = Alignment.CenterVertically,
) {
- Icon(resourceId = VectorIcons.ThreadDecoration, contentDescription = null, tint = ElementTheme.colors.iconSecondary)
+ Icon(
+ resourceId = CommonDrawables.ic_thread_decoration,
+ contentDescription = null,
+ tint = ElementTheme.colors.iconSecondary,
+ )
Text(
text = stringResource(CommonStrings.common_thread),
style = ElementTheme.typography.fontBodyXsRegular,
@@ -623,7 +629,7 @@ private fun textForInReplyTo(inReplyTo: InReplyTo.Ready): String {
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemEventRowPreview() = ElementPreview {
Column {
@@ -648,7 +654,7 @@ internal fun TimelineItemEventRowPreview() = ElementPreview {
onMoreReactionsClick = {},
onTimestampClicked = {},
onSwipeToReply = {},
- onPollAnswerSelected = { _, _ -> },
+ eventSink = {},
)
TimelineItemEventRow(
event = aTimelineItemEvent(
@@ -669,13 +675,13 @@ internal fun TimelineItemEventRowPreview() = ElementPreview {
onMoreReactionsClick = {},
onTimestampClicked = {},
onSwipeToReply = {},
- onPollAnswerSelected = { _, _ -> },
+ eventSink = {},
)
}
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview {
Column {
@@ -708,7 +714,7 @@ internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview {
onMoreReactionsClick = {},
onTimestampClicked = {},
onSwipeToReply = {},
- onPollAnswerSelected = { _, _ -> },
+ eventSink = {},
)
TimelineItemEventRow(
event = aTimelineItemEvent(
@@ -731,7 +737,7 @@ internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview {
onMoreReactionsClick = {},
onTimestampClicked = {},
onSwipeToReply = {},
- onPollAnswerSelected = { _, _ -> },
+ eventSink = {},
)
}
}
@@ -749,7 +755,7 @@ private fun aInReplyToReady(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemEventRowTimestampPreview(
@PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event
@@ -782,14 +788,14 @@ internal fun TimelineItemEventRowTimestampPreview(
onMoreReactionsClick = {},
onTimestampClicked = {},
onSwipeToReply = {},
- onPollAnswerSelected = { _, _ -> },
+ eventSink = {},
)
}
}
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemEventRowWithManyReactionsPreview() = ElementPreview {
Column {
@@ -814,7 +820,7 @@ internal fun TimelineItemEventRowWithManyReactionsPreview() = ElementPreview {
onMoreReactionsClick = {},
onSwipeToReply = {},
onTimestampClicked = {},
- onPollAnswerSelected = { _, _ -> },
+ eventSink = {},
)
}
}
@@ -839,7 +845,7 @@ internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight {
onMoreReactionsClick = {},
onSwipeToReply = {},
onTimestampClicked = {},
- onPollAnswerSelected = { _, _ -> },
+ eventSink = {},
)
}
@@ -860,6 +866,6 @@ internal fun TimelineItemEventTimestampBelowPreview() = ElementPreviewLight {
onMoreReactionsClick = {},
onSwipeToReply = {},
onTimestampClicked = {},
- onPollAnswerSelected = { _, _ -> },
+ eventSink = {},
)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt
index 01800f6348..f0d6871bf6 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt
@@ -16,8 +16,6 @@
package io.element.android.features.messages.impl.timeline.components
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.AddReaction
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.MeasureResult
@@ -28,8 +26,9 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.R
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.utils.CommonDrawables
/**
* A flow layout for reactions that will show a collapse/expand button when the layout wraps over a defined number of rows.
@@ -181,7 +180,7 @@ fun TimelineItemReactionsLayout(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemReactionsLayoutPreview() = ElementPreview {
TimelineItemReactionsLayout(
@@ -197,7 +196,7 @@ internal fun TimelineItemReactionsLayoutPreview() = ElementPreview {
},
addMoreButton = {
MessagesReactionButton(
- content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction),
+ content = MessagesReactionsButtonContent.Icon(CommonDrawables.ic_september_add_reaction),
onClick = {},
onLongClick = {}
)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt
index a5cdf4b6cc..3f625fd6e4 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt
@@ -16,8 +16,6 @@
package io.element.android.features.messages.impl.timeline.components
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.AddReaction
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
@@ -33,12 +31,13 @@ import io.element.android.features.messages.impl.R
import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import kotlinx.collections.immutable.ImmutableList
@Composable
-fun TimelineItemReactions(
+fun TimelineItemReactionsView(
reactionsState: TimelineItemReactions,
isOutgoing: Boolean,
onReactionClicked: (emoji: String) -> Unit,
@@ -47,16 +46,16 @@ fun TimelineItemReactions(
modifier: Modifier = Modifier,
) {
var expanded: Boolean by rememberSaveable { mutableStateOf(false) }
- TimelineItemReactionsView(
- modifier = modifier,
- reactions = reactionsState.reactions,
- expanded = expanded,
- isOutgoing = isOutgoing,
- onReactionClick = onReactionClicked,
- onReactionLongClick = onReactionLongClicked,
- onMoreReactionsClick = onMoreReactionsClicked,
- onToggleExpandClick = { expanded = !expanded },
- )
+ TimelineItemReactionsView(
+ modifier = modifier,
+ reactions = reactionsState.reactions,
+ expanded = expanded,
+ isOutgoing = isOutgoing,
+ onReactionClick = onReactionClicked,
+ onReactionLongClick = onReactionLongClicked,
+ onMoreReactionsClick = onMoreReactionsClicked,
+ onToggleExpandClick = { expanded = !expanded },
+ )
}
@Composable
@@ -96,7 +95,7 @@ private fun TimelineItemReactionsView(
},
addMoreButton = {
MessagesReactionButton(
- content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction),
+ content = MessagesReactionsButtonContent.Icon(CommonDrawables.ic_september_add_reaction),
onClick = onMoreReactionsClick,
onLongClick = {}
)
@@ -116,7 +115,7 @@ private fun TimelineItemReactionsView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemReactionsViewPreview() = ElementPreview {
ContentToPreview(
@@ -124,7 +123,7 @@ internal fun TimelineItemReactionsViewPreview() = ElementPreview {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemReactionsViewFewPreview() = ElementPreview {
ContentToPreview(
@@ -132,7 +131,7 @@ internal fun TimelineItemReactionsViewFewPreview() = ElementPreview {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemReactionsViewIncomingPreview() = ElementPreview {
ContentToPreview(
@@ -140,7 +139,7 @@ internal fun TimelineItemReactionsViewIncomingPreview() = ElementPreview {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemReactionsViewOutgoingPreview() = ElementPreview {
ContentToPreview(
@@ -154,7 +153,7 @@ private fun ContentToPreview(
reactions: ImmutableList,
isOutgoing: Boolean = false
) {
- TimelineItemReactions(
+ TimelineItemReactionsView(
reactionsState = TimelineItemReactions(
reactions
),
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt
index e4a71e909a..ccffcc16ca 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt
@@ -28,6 +28,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
+import io.element.android.features.messages.impl.timeline.TimelineEvents
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
import io.element.android.features.messages.impl.timeline.components.event.noExtraPadding
@@ -35,8 +36,8 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
import io.element.android.features.messages.impl.timeline.util.defaultTimelineContentPadding
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@Composable
fun TimelineItemStateEventRow(
@@ -44,6 +45,7 @@ fun TimelineItemStateEventRow(
isHighlighted: Boolean,
onClick: () -> Unit,
onLongClick: () -> Unit,
+ eventSink: (TimelineEvents) -> Unit,
modifier: Modifier = Modifier
) {
val interactionSource = remember { MutableInteractionSource() }
@@ -65,18 +67,19 @@ fun TimelineItemStateEventRow(
) {
TimelineItemEventContentView(
content = event.content,
+ isMine = event.isMine,
interactionSource = interactionSource,
onClick = onClick,
onLongClick = onLongClick,
extraPadding = noExtraPadding,
- onPollAnswerSelected = { _, _ -> error("Polls are not supported in state events") },
+ eventSink = eventSink,
modifier = Modifier.defaultTimelineContentPadding()
)
}
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemStateEventRowPreview() = ElementPreview {
TimelineItemStateEventRow(
@@ -88,5 +91,6 @@ internal fun TimelineItemStateEventRowPreview() = ElementPreview {
isHighlighted = false,
onClick = {},
onLongClick = {},
+ eventSink = {}
)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt
index 2458686a83..c9936855f1 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt
@@ -20,5 +20,5 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
sealed interface CustomReactionEvents {
data class ShowCustomReactionSheet(val event: TimelineItem.Event) : CustomReactionEvents
- object DismissCustomReactionSheet : CustomReactionEvents
+ data object DismissCustomReactionSheet : CustomReactionEvents
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt
index d1c37fae37..ee9d4c819d 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt
@@ -34,8 +34,8 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.material3.SecondaryTabRow
import androidx.compose.material3.Tab
-import androidx.compose.material3.TabRow
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -49,8 +49,8 @@ import io.element.android.emojibasebindings.Emoji
import io.element.android.emojibasebindings.EmojibaseCategory
import io.element.android.emojibasebindings.EmojibaseDatasource
import io.element.android.emojibasebindings.EmojibaseStore
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@@ -68,12 +68,12 @@ fun EmojiPicker(
) {
val coroutineScope = rememberCoroutineScope()
val categories = remember { emojibaseStore.categories }
- val pagerState = rememberPagerState(pageCount = { EmojibaseCategory.values().size })
+ val pagerState = rememberPagerState(pageCount = { EmojibaseCategory.entries.size })
Column(modifier) {
- TabRow(
+ SecondaryTabRow(
selectedTabIndex = pagerState.currentPage,
) {
- EmojibaseCategory.values().forEachIndexed { index, category ->
+ EmojibaseCategory.entries.forEachIndexed { index, category ->
Tab(
text = {
Icon(
@@ -93,7 +93,7 @@ fun EmojiPicker(
state = pagerState,
modifier = Modifier.fillMaxWidth(),
) { index ->
- val category = EmojibaseCategory.values()[index]
+ val category = EmojibaseCategory.entries[index]
val emojis = categories[category] ?: listOf()
LazyVerticalGrid(
modifier = Modifier.fillMaxSize(),
@@ -132,7 +132,7 @@ fun EmojiPicker(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun EmojiPickerPreview() = ElementPreview {
EmojiPicker(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt
index f96290cb51..b9a1cf622a 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAudioView.kt
@@ -36,7 +36,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContentProvider
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
@@ -86,7 +86,7 @@ fun TimelineItemAudioView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemAudioViewPreview(@PreviewParameter(TimelineItemAudioContentProvider::class) content: TimelineItemAudioContent) =
ElementPreview {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt
index dcbb6c422b..462edb1e60 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt
@@ -16,14 +16,13 @@
package io.element.android.features.messages.impl.timeline.components.event
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Warning
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
import io.element.android.libraries.ui.strings.CommonStrings
@@ -36,13 +35,13 @@ fun TimelineItemEncryptedView(
TimelineItemInformativeView(
text = stringResource(id = CommonStrings.common_decryption_error),
iconDescription = stringResource(id = CommonStrings.dialog_title_warning),
- icon = Icons.Default.Warning,
+ iconResourceId = CommonDrawables.ic_september_decryption_error,
extraPadding = extraPadding,
modifier = modifier
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemEncryptedViewPreview() = ElementPreview {
TimelineItemEncryptedView(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt
similarity index 95%
rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt
rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt
index e882950d6c..dccc020e49 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt
@@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.timeline.components.event
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import io.element.android.features.messages.impl.timeline.TimelineEvents
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
@@ -31,16 +32,16 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
-import io.element.android.libraries.matrix.api.core.EventId
@Composable
fun TimelineItemEventContentView(
content: TimelineItemEventContent,
+ isMine: Boolean,
interactionSource: MutableInteractionSource,
extraPadding: ExtraPadding,
onClick: () -> Unit,
onLongClick: () -> Unit,
- onPollAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
+ eventSink: (TimelineEvents) -> Unit,
modifier: Modifier = Modifier
) {
when (content) {
@@ -95,7 +96,8 @@ fun TimelineItemEventContentView(
)
is TimelineItemPollContent -> TimelineItemPollView(
content = content,
- onAnswerSelected = onPollAnswerSelected,
+ isMine = isMine,
+ eventSink = eventSink,
modifier = modifier,
)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt
index 67423bc8f5..f0f08fa2f5 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt
@@ -24,8 +24,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Attachment
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -37,10 +35,11 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContentProvider
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@Composable
@@ -60,7 +59,7 @@ fun TimelineItemFileView(
contentAlignment = Alignment.Center,
) {
Icon(
- imageVector = Icons.Outlined.Attachment,
+ resourceId = CommonDrawables.ic_september_attachment,
contentDescription = "OpenFile",
tint = ElementTheme.materialColors.primary,
modifier = Modifier
@@ -88,7 +87,7 @@ fun TimelineItemFileView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemFileViewPreview(@PreviewParameter(TimelineItemFileContentProvider::class) content: TimelineItemFileContent) = ElementPreview {
TimelineItemFileView(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt
index 94c2744cf6..b5856e1ce7 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt
@@ -23,7 +23,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContentProvider
import io.element.android.libraries.designsystem.components.BlurHashAsyncImage
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.matrix.ui.media.MediaRequestData
@@ -44,7 +44,7 @@ fun TimelineItemImageView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemImageViewPreview(@PreviewParameter(TimelineItemImageContentProvider::class) content: TimelineItemImageContent) = ElementPreview {
TimelineItemImageView(content)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemInformativeView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemInformativeView.kt
index c09cd7d114..1ff45e428e 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemInformativeView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemInformativeView.kt
@@ -16,31 +16,30 @@
package io.element.android.features.messages.impl.timeline.components.event
+import androidx.annotation.DrawableRes
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@Composable
fun TimelineItemInformativeView(
text: String,
iconDescription: String,
- icon: ImageVector,
+ @DrawableRes iconResourceId: Int,
extraPadding: ExtraPadding,
modifier: Modifier = Modifier
) {
@@ -49,7 +48,7 @@ fun TimelineItemInformativeView(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
- imageVector = icon,
+ resourceId = iconResourceId,
tint = MaterialTheme.colorScheme.secondary,
contentDescription = iconDescription,
modifier = Modifier.size(16.dp)
@@ -64,13 +63,13 @@ fun TimelineItemInformativeView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemInformativeViewPreview() = ElementPreview {
TimelineItemInformativeView(
text = "Info",
iconDescription = "",
- icon = Icons.Default.Delete,
+ iconResourceId = CommonDrawables.ic_compound_delete,
extraPadding = noExtraPadding,
)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt
index ebc9636773..c9d24a1637 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt
@@ -27,7 +27,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.features.location.api.StaticMapView
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContentProvider
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
@@ -56,7 +56,7 @@ fun TimelineItemLocationView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemLocationViewPreview(@PreviewParameter(TimelineItemLocationContentProvider::class) content: TimelineItemLocationContent) =
ElementPreview {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt
index ec784ad331..958845f98c 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt
@@ -19,37 +19,62 @@ package io.element.android.features.messages.impl.timeline.components.event
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.PreviewParameter
+import io.element.android.features.messages.impl.timeline.TimelineEvents
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContentProvider
import io.element.android.features.poll.api.PollContentView
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.matrix.api.core.EventId
import kotlinx.collections.immutable.toImmutableList
@Composable
fun TimelineItemPollView(
content: TimelineItemPollContent,
- onAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
+ isMine: Boolean,
+ eventSink: (TimelineEvents) -> Unit,
modifier: Modifier = Modifier,
) {
+ fun onAnswerSelected(pollStartId: EventId, answerId: String) {
+ eventSink(TimelineEvents.PollAnswerSelected(pollStartId, answerId))
+ }
+
+ fun onPollEnd(pollStartId: EventId) {
+ eventSink(TimelineEvents.PollEndClicked(pollStartId))
+ }
+
PollContentView(
eventId = content.eventId,
question = content.question,
answerItems = content.answerItems.toImmutableList(),
pollKind = content.pollKind,
isPollEnded = content.isEnded,
- onAnswerSelected = onAnswerSelected,
+ isMine = isMine,
+ onAnswerSelected = ::onAnswerSelected,
+ onPollEdit = {}, // TODO Polls: Wire up this callback once poll edit screen is done.
+ onPollEnd = ::onPollEnd,
modifier = modifier,
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemPollViewPreview(@PreviewParameter(TimelineItemPollContentProvider::class) content: TimelineItemPollContent) =
ElementPreview {
TimelineItemPollView(
content = content,
- onAnswerSelected = { _, _ -> },
+ isMine = false,
+ eventSink = {},
+ )
+ }
+
+@PreviewsDayNight
+@Composable
+internal fun TimelineItemPollCreatorViewPreview(@PreviewParameter(TimelineItemPollContentProvider::class) content: TimelineItemPollContent) =
+ ElementPreview {
+ TimelineItemPollView(
+ content = content,
+ isMine = true,
+ eventSink = {},
)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt
index 466265e7f1..cb0fbde6df 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt
@@ -16,14 +16,13 @@
package io.element.android.features.messages.impl.timeline.components.event
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
@@ -35,13 +34,13 @@ fun TimelineItemRedactedView(
TimelineItemInformativeView(
text = stringResource(id = CommonStrings.common_message_removed),
iconDescription = stringResource(id = CommonStrings.common_message_removed),
- icon = Icons.Default.Delete,
+ iconResourceId = CommonDrawables.ic_compound_delete,
extraPadding = extraPadding,
modifier = modifier
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemRedactedViewPreview() = ElementPreview {
TimelineItemRedactedView(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStateView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStateView.kt
index ed8700da0c..fe7b7edeaa 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStateView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStateView.kt
@@ -22,7 +22,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@@ -41,7 +41,7 @@ fun TimelineItemStateView(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemStateViewPreview() = ElementPreview {
TimelineItemStateView(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt
index 17d52e13cd..4282f0253b 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt
@@ -29,7 +29,7 @@ import io.element.android.features.messages.impl.timeline.components.html.HtmlDo
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContentProvider
import io.element.android.libraries.designsystem.components.ClickableLinkText
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.text.toAnnotatedString
import io.element.android.libraries.theme.ElementTheme
@@ -70,7 +70,7 @@ fun TimelineItemTextView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemTextViewPreview(
@PreviewParameter(TimelineItemTextBasedContentProvider::class) content: TimelineItemTextBasedContent
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt
index df7926cda5..b381f2ef4c 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt
@@ -16,14 +16,13 @@
package io.element.android.features.messages.impl.timeline.components.event
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Info
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
@@ -35,13 +34,13 @@ fun TimelineItemUnknownView(
TimelineItemInformativeView(
text = stringResource(id = CommonStrings.common_unsupported_event),
iconDescription = stringResource(id = CommonStrings.dialog_title_warning),
- icon = Icons.Default.Info,
+ iconResourceId = CommonDrawables.ic_compound_info,
extraPadding = extraPadding,
modifier = modifier
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemUnknownViewPreview() = ElementPreview {
TimelineItemUnknownView(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt
index 1cc32e2965..f666a29ac9 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt
@@ -31,7 +31,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContentProvider
import io.element.android.libraries.designsystem.components.BlurHashAsyncImage
import io.element.android.libraries.designsystem.modifiers.roundedBackground
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.matrix.ui.media.MediaRequestData
@@ -63,7 +63,7 @@ fun TimelineItemVideoView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemVideoViewPreview(@PreviewParameter(TimelineItemVideoContentProvider::class) content: TimelineItemVideoContent) = ElementPreview {
TimelineItemVideoView(content)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt
index 9e3a8f4861..ebe1abaf17 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt
@@ -24,21 +24,20 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ExpandLess
-import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
private val CORNER_RADIUS = 8.dp
@@ -77,18 +76,18 @@ fun GroupHeaderView(
color = MaterialTheme.colorScheme.secondary,
style = ElementTheme.typography.fontBodyMdRegular,
)
- val icon = if (isExpanded) {
- Icons.Default.ExpandLess
- } else {
- Icons.Default.ExpandMore
- }
- Icon(icon, "", tint = MaterialTheme.colorScheme.secondary)
+ Icon(
+ modifier = Modifier.rotate(if (isExpanded) 180f else 0f),
+ resourceId = CommonDrawables.ic_compound_chevron_down,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.secondary
+ )
}
}
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun GroupHeaderViewPreview() = ElementPreview {
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt
index fa050229a0..ad6d2790b0 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt
@@ -58,7 +58,7 @@ import io.element.android.features.messages.impl.timeline.components.event.Extra
import io.element.android.features.messages.impl.timeline.components.event.getDpSize
import io.element.android.features.messages.impl.timeline.components.event.noExtraPadding
import io.element.android.libraries.designsystem.components.ClickableLinkText
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.text.toDp
import io.element.android.libraries.designsystem.theme.components.Surface
@@ -612,7 +612,7 @@ private fun HtmlText(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun HtmlDocumentPreview(@PreviewParameter(DocumentProvider::class) document: Document) = ElementPreview {
HtmlDocument(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt
index a775fa1856..b9260e2768 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt
@@ -61,7 +61,7 @@ import io.element.android.features.messages.impl.timeline.model.AggregatedReacti
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
import io.element.android.libraries.designsystem.theme.components.Surface
@@ -160,7 +160,7 @@ private fun SheetContent(
}
@Composable
-fun AggregatedReactionButton(
+private fun AggregatedReactionButton(
reaction: AggregatedReaction,
isHighlighted: Boolean,
onClick: () -> Unit,
@@ -215,7 +215,7 @@ fun AggregatedReactionButton(
}
@Composable
-fun SenderRow(
+private fun SenderRow(
avatarData: AvatarData,
name: String,
userId: String,
@@ -266,7 +266,7 @@ fun SenderRow(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun SheetContentPreview(
@PreviewParameter(ReactionSummaryStateProvider::class) state: ReactionSummaryState
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt
index 1e3bbbc7c1..6959c448cb 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt
@@ -34,11 +34,12 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.R
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.launch
@Composable
@@ -71,7 +72,7 @@ internal fun RetrySendMessageMenu(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-internal fun RetrySendMessageMenuBottomSheet(
+private fun RetrySendMessageMenuBottomSheet(
isVisible: Boolean,
onRetry: () -> Unit,
onRemoveFailed: () -> Unit,
@@ -133,7 +134,7 @@ private fun ColumnScope.RetrySendMenuContents(
ListItem(
headlineContent = {
Text(
- text = stringResource(R.string.screen_room_retry_send_menu_remove_action),
+ text = stringResource(CommonStrings.action_remove),
style = ElementTheme.typography.fontBodyLgRegular,
)
},
@@ -149,7 +150,7 @@ private fun ColumnScope.RetrySendMenuContents(
@Suppress("UNUSED_PARAMETER")
@OptIn(ExperimentalMaterial3Api::class)
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RetrySendMessageMenuPreview(@PreviewParameter(RetrySendMenuStateProvider::class) state: RetrySendMenuState) = ElementPreview {
// TODO restore RetrySendMessageMenuBottomSheet once the issue with bottom sheet not being previewable is fixed
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt
index 65af6bdbf5..0317a68a00 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt
@@ -21,9 +21,6 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Info
-import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -32,7 +29,9 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.R
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@Composable
@@ -47,7 +46,7 @@ fun TimelineEncryptedHistoryBannerView(modifier: Modifier = Modifier) {
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Icon(
- imageVector = Icons.Default.Info,
+ resourceId = CommonDrawables.ic_compound_info,
contentDescription = "Info",
tint = ElementTheme.colors.iconInfoPrimary
)
@@ -59,7 +58,7 @@ fun TimelineEncryptedHistoryBannerView(modifier: Modifier = Modifier) {
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineEncryptedHistoryBannerViewPreview() {
ElementTheme {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt
index 43309c5673..bf69badb9e 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt
@@ -28,7 +28,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModelProvider
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@@ -53,7 +53,7 @@ internal fun TimelineItemDaySeparatorView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineItemDaySeparatorViewPreview(
@PreviewParameter(TimelineItemDaySeparatorModelProvider::class) model: TimelineItemDaySeparatorModel
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemLoadingMoreIndicator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineLoadingMoreIndicator.kt
similarity index 94%
rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemLoadingMoreIndicator.kt
rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineLoadingMoreIndicator.kt
index 6326897686..847aa2bcef 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemLoadingMoreIndicator.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineLoadingMoreIndicator.kt
@@ -24,7 +24,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
@@ -43,7 +43,7 @@ internal fun TimelineLoadingMoreIndicator(modifier: Modifier = Modifier) {
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TimelineLoadingMoreIndicatorPreview() = ElementPreview {
TimelineLoadingMoreIndicator()
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt
index 06f09d71ab..c93560100c 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt
@@ -33,9 +33,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowDropDown
-import androidx.compose.material.icons.filled.ArrowDropUp
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -46,19 +43,21 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.rotate
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.core.content.getSystemService
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.theme.ElementTheme
@@ -140,7 +139,8 @@ private fun CollapsibleSection(
) {
Text(title, modifier = Modifier.weight(1f))
Icon(
- imageVector = if (isExpanded) Icons.Filled.ArrowDropUp else Icons.Filled.ArrowDropDown,
+ modifier = Modifier.rotate(if (isExpanded) 180f else 0f),
+ resourceId = CommonDrawables.ic_compound_chevron_down,
contentDescription = null
)
}
@@ -172,7 +172,7 @@ private fun CopyableText(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun EventDebugInfoViewPreview() = ElementPreview {
EventDebugInfoView(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt
index 6a74fd11a5..55e889f496 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt
@@ -24,6 +24,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParse
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
+import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
@@ -49,7 +50,10 @@ class TimelineItemContentFactory @Inject constructor(
return when (val itemContent = eventTimelineItem.content) {
is FailedToParseMessageLikeContent -> failedToParseMessageFactory.create(itemContent)
is FailedToParseStateContent -> failedToParseStateFactory.create(itemContent)
- is MessageContent -> messageFactory.create(itemContent)
+ is MessageContent -> {
+ val senderDisplayName = (eventTimelineItem.senderProfile as? ProfileTimelineDetails.Ready)?.displayName ?: eventTimelineItem.sender.value
+ messageFactory.create(itemContent, senderDisplayName)
+ }
is ProfileChangeContent -> profileChangeFactory.create(eventTimelineItem)
is RedactedContent -> redactedMessageFactory.create(itemContent)
is RoomMembershipContent -> roomMembershipFactory.create(eventTimelineItem)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt
index 040969e092..ae2ea4f350 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt
@@ -25,7 +25,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemNoticeContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
-import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractor
import io.element.android.features.messages.impl.timeline.util.toHtmlDocument
@@ -39,6 +38,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessa
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
+import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import javax.inject.Inject
@@ -47,11 +47,11 @@ class TimelineItemContentMessageFactory @Inject constructor(
private val fileExtensionExtractor: FileExtensionExtractor,
) {
- fun create(content: MessageContent): TimelineItemEventContent {
+ fun create(content: MessageContent, senderDisplayName: String): TimelineItemEventContent {
return when (val messageType = content.type) {
is EmoteMessageType -> TimelineItemEmoteContent(
- body = messageType.body,
- htmlDocument = messageType.formatted?.toHtmlDocument(),
+ body = "* $senderDisplayName ${messageType.body}",
+ htmlDocument = messageType.formatted?.toHtmlDocument(prefix = "* senderDisplayName"),
isEdited = content.isEdited,
)
is ImageMessageType -> {
@@ -109,14 +109,17 @@ class TimelineItemContentMessageFactory @Inject constructor(
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
fileExtension = fileExtensionExtractor.extractFromName(messageType.body)
)
- is FileMessageType -> TimelineItemFileContent(
- body = messageType.body,
- thumbnailSource = messageType.info?.thumbnailSource,
- fileSource = messageType.source,
- mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
- formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
- fileExtension = fileExtensionExtractor.extractFromName(messageType.body)
- )
+ is FileMessageType -> {
+ val fileExtension = fileExtensionExtractor.extractFromName(messageType.body)
+ TimelineItemFileContent(
+ body = messageType.body,
+ thumbnailSource = messageType.info?.thumbnailSource,
+ fileSource = messageType.source,
+ mimeType = messageType.info?.mimetype ?: MimeTypes.fromFileExtension(fileExtension),
+ formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
+ fileExtension = fileExtension
+ )
+ }
is NoticeMessageType -> TimelineItemNoticeContent(
body = messageType.body,
htmlDocument = messageType.formatted?.toHtmlDocument(),
@@ -127,7 +130,12 @@ class TimelineItemContentMessageFactory @Inject constructor(
htmlDocument = messageType.formatted?.toHtmlDocument(),
isEdited = content.isEdited,
)
- else -> TimelineItemUnknownContent
+ UnknownMessageType -> TimelineItemTextContent(
+ // Display the body as a fallback
+ body = content.body,
+ htmlDocument = null,
+ isEdited = content.isEdited,
+ )
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt
index 8f00dfb0dd..bd3090e390 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt
@@ -54,6 +54,7 @@ sealed interface TimelineItem {
@Immutable
data class Event(
val id: String,
+ // Note: eventId can be null when the event is a local echo
val eventId: EventId? = null,
val transactionId: TransactionId? = null,
val senderId: UserId,
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRedactedContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRedactedContent.kt
index 7a8edae953..853ab3f030 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRedactedContent.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRedactedContent.kt
@@ -16,6 +16,6 @@
package io.element.android.features.messages.impl.timeline.model.event
-object TimelineItemRedactedContent : TimelineItemEventContent{
+data object TimelineItemRedactedContent : TimelineItemEventContent {
override val type: String = "TimelineItemRedactedContent"
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt
index 8031e77a4d..a38b631eb2 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt
@@ -21,8 +21,12 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
-fun FormattedBody.toHtmlDocument(): Document? {
+fun FormattedBody.toHtmlDocument(prefix: String? = null): Document? {
return takeIf { it.format == MessageFormat.HTML }?.body?.let { formattedBody ->
- Jsoup.parse(formattedBody)
+ if (prefix != null) {
+ Jsoup.parse("$prefix $formattedBody")
+ } else {
+ Jsoup.parse(formattedBody)
+ }
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerEvents.kt
new file mode 100644
index 0000000000..7d6803fc41
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerEvents.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.messages.impl.voicemessages
+
+import io.element.android.libraries.textcomposer.model.PressEvent
+
+sealed interface VoiceMessageComposerEvents {
+ data class RecordButtonEvent(
+ val pressEvent: PressEvent
+ ): VoiceMessageComposerEvents
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerPresenter.kt
new file mode 100644
index 0000000000..106125934b
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerPresenter.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.messages.impl.voicemessages
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import io.element.android.libraries.architecture.Presenter
+import io.element.android.libraries.di.RoomScope
+import io.element.android.libraries.di.SingleIn
+import io.element.android.libraries.textcomposer.model.PressEvent
+import io.element.android.libraries.textcomposer.model.VoiceMessageState
+import javax.inject.Inject
+
+@SingleIn(RoomScope::class)
+class VoiceMessageComposerPresenter @Inject constructor() : Presenter {
+ @Composable
+ override fun present(): VoiceMessageComposerState {
+ var voiceMessageState by remember { mutableStateOf(VoiceMessageState.Idle) }
+
+ fun onRecordButtonPress(event: VoiceMessageComposerEvents.RecordButtonEvent) = when(event.pressEvent) {
+ PressEvent.PressStart -> {
+ // TODO start the recording
+ voiceMessageState = VoiceMessageState.Recording
+ }
+ PressEvent.LongPressEnd -> {
+ // TODO finish the recording
+ voiceMessageState = VoiceMessageState.Idle
+ }
+ PressEvent.Tapped -> {
+ // TODO discard the recording and show the 'hold to record' tooltip
+ voiceMessageState = VoiceMessageState.Idle
+ }
+ }
+
+
+ fun handleEvents(event: VoiceMessageComposerEvents) {
+ when (event) {
+ is VoiceMessageComposerEvents.RecordButtonEvent -> onRecordButtonPress(event)
+ }
+ }
+
+ return VoiceMessageComposerState(
+ voiceMessageState = voiceMessageState,
+ eventSink = { handleEvents(it) }
+ )
+ }
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerState.kt
new file mode 100644
index 0000000000..bacbe76324
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerState.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.messages.impl.voicemessages
+
+import androidx.compose.runtime.Stable
+import io.element.android.libraries.textcomposer.model.VoiceMessageState
+
+@Stable
+data class VoiceMessageComposerState(
+ val voiceMessageState: VoiceMessageState,
+ val eventSink: (VoiceMessageComposerEvents) -> Unit,
+)
+
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerStateProvider.kt
new file mode 100644
index 0000000000..63b59596c0
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageComposerStateProvider.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.messages.impl.voicemessages
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import io.element.android.libraries.textcomposer.model.VoiceMessageState
+
+internal open class VoiceMessageComposerStateProvider : PreviewParameterProvider {
+ override val values: Sequence
+ get() = sequenceOf(
+ aVoiceMessageComposerState(voiceMessageState = VoiceMessageState.Recording),
+ )
+}
+
+internal fun aVoiceMessageComposerState(
+ voiceMessageState: VoiceMessageState = VoiceMessageState.Idle,
+) = VoiceMessageComposerState(
+ voiceMessageState = voiceMessageState,
+ eventSink = {},
+)
diff --git a/features/messages/impl/src/main/res/drawable/ic_apk_install.xml b/features/messages/impl/src/main/res/drawable/ic_apk_install.xml
new file mode 100644
index 0000000000..b39fc4c5d5
--- /dev/null
+++ b/features/messages/impl/src/main/res/drawable/ic_apk_install.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/features/messages/impl/src/main/res/values-cs/translations.xml b/features/messages/impl/src/main/res/values-cs/translations.xml
index 1543101f6d..7129031292 100644
--- a/features/messages/impl/src/main/res/values-cs/translations.xml
+++ b/features/messages/impl/src/main/res/values-cs/translations.xml
@@ -12,6 +12,7 @@
"Knihovna fotografií a videí"
"Poloha"
"Hlasování"
+ "Formátování textu"
"Historie zpráv je momentálně v této místnosti nedostupná"
"Nepodařilo se načíst údaje o uživateli"
"Chtěli byste je pozvat zpět?"
@@ -37,6 +38,6 @@
"Vaši zprávu se nepodařilo odeslat"
"Přidat emoji"
"Zobrazit méně"
+ "Držte pro nahrávání"
"Nahrání média se nezdařilo, zkuste to prosím znovu."
- "Odstranit"
diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml
index 0249df4a9e..d08c04a785 100644
--- a/features/messages/impl/src/main/res/values-de/translations.xml
+++ b/features/messages/impl/src/main/res/values-de/translations.xml
@@ -38,5 +38,4 @@
"Emoji hinzufügen"
"Weniger anzeigen"
"Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut."
- "Entfernen"
diff --git a/features/messages/impl/src/main/res/values-es/translations.xml b/features/messages/impl/src/main/res/values-es/translations.xml
index 5d41b319bd..fe186df358 100644
--- a/features/messages/impl/src/main/res/values-es/translations.xml
+++ b/features/messages/impl/src/main/res/values-es/translations.xml
@@ -4,5 +4,4 @@
- "%1$d cambio en la sala"
- "%1$d cambios en la sala"
- "Eliminar"
diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml
index 5ae43b98d7..ea6f387d1a 100644
--- a/features/messages/impl/src/main/res/values-fr/translations.xml
+++ b/features/messages/impl/src/main/res/values-fr/translations.xml
@@ -38,5 +38,4 @@
"Ajouter un émoji"
"Afficher moins"
"Échec du traitement des médias à télécharger, veuillez réessayer."
- "Supprimer"
diff --git a/features/messages/impl/src/main/res/values-it/translations.xml b/features/messages/impl/src/main/res/values-it/translations.xml
index 694de002fe..1b0a2c99a3 100644
--- a/features/messages/impl/src/main/res/values-it/translations.xml
+++ b/features/messages/impl/src/main/res/values-it/translations.xml
@@ -4,5 +4,4 @@
- "%1$d modifica alla stanza"
- "%1$d modifiche alla stanza"
- "Rimuovi"
diff --git a/features/messages/impl/src/main/res/values-ro/translations.xml b/features/messages/impl/src/main/res/values-ro/translations.xml
index c351eb29cb..20bec7b1b3 100644
--- a/features/messages/impl/src/main/res/values-ro/translations.xml
+++ b/features/messages/impl/src/main/res/values-ro/translations.xml
@@ -39,5 +39,4 @@
"Adăugați emoji"
"Afișați mai puțin"
"Procesarea datelor media a eșuat, vă rugăm să încercați din nou."
- "Ștergeți"
diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml
index 8c96074f1b..e54b92ff08 100644
--- a/features/messages/impl/src/main/res/values-ru/translations.xml
+++ b/features/messages/impl/src/main/res/values-ru/translations.xml
@@ -12,6 +12,7 @@
"Фото и видео"
"Местоположение"
"Опрос"
+ "Форматирование текста"
"В настоящее время история сообщений недоступна в этой комнате"
"Не удалось получить данные о пользователе"
"Хотите пригласить их снова?"
@@ -38,5 +39,4 @@
"Добавить эмодзи"
"Показать меньше"
"Не удалось обработать медиафайл для загрузки, попробуйте еще раз."
- "Удалить"
diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml
index 2c98aab148..c9129faf08 100644
--- a/features/messages/impl/src/main/res/values-sk/translations.xml
+++ b/features/messages/impl/src/main/res/values-sk/translations.xml
@@ -38,6 +38,6 @@
"Vašu správu sa nepodarilo odoslať"
"Pridať emoji"
"Zobraziť menej"
+ "Podržaním nahrajte"
"Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova."
- "Odstrániť"
diff --git a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml
index ac4725896e..078d71bd9f 100644
--- a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml
@@ -7,8 +7,10 @@
"拍照"
"錄影"
"附件"
+ "照片與影片庫"
"位置"
"投票"
+ "格式化文字"
"此聊天室只有您一個人"
"訊息已複製"
"您沒有權限在此聊天室傳送訊息"
@@ -17,8 +19,11 @@
"無法重設為預設模式,請再試一次。"
"無法設定模式,請再試一次。"
"所有訊息"
- "只限提及與關鍵字"
+ "僅限提及與關鍵字"
+ "較少"
+ "更多"
"重傳"
"無法傳送您的訊息"
- "移除"
+ "新增表情符號"
+ "較少"
diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml
index 81cd4933e4..bb285968c6 100644
--- a/features/messages/impl/src/main/res/values/localazy.xml
+++ b/features/messages/impl/src/main/res/values/localazy.xml
@@ -6,7 +6,7 @@
"Camera"
"Take photo"
- "Record a video"
+ "Record video"
"Attachment"
"Photo & Video Library"
"Location"
@@ -37,6 +37,6 @@
"Your message failed to send"
"Add emoji"
"Show less"
+ "Hold to record"
"Failed processing media to upload, please try again."
- "Remove"
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
index db2abcc1dd..b02afd90fb 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
@@ -40,6 +40,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
+import io.element.android.features.messages.impl.voicemessages.VoiceMessageComposerPresenter
import io.element.android.features.messages.media.FakeLocalMediaFactory
import io.element.android.features.messages.textcomposer.TestRichTextEditorStateFactory
import io.element.android.features.messages.timeline.components.customreaction.FakeEmojibaseProvider
@@ -51,7 +52,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore
@@ -70,7 +71,10 @@ import io.element.android.libraries.matrix.test.room.aRoomMember
import io.element.android.libraries.mediapickers.test.FakePickerProvider
import io.element.android.libraries.mediaupload.api.MediaSender
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
-import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.libraries.permissions.api.PermissionsPresenter
+import io.element.android.libraries.permissions.test.FakePermissionsPresenter
+import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.consumeItemsUntilPredicate
@@ -93,7 +97,7 @@ class MessagesPresenterTest {
@Test
fun `present - initial state`() = runTest {
- val presenter = createMessagePresenter()
+ val presenter = createMessagesPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -114,7 +118,7 @@ class MessagesPresenterTest {
fun `present - handle toggling a reaction`() = runTest {
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
val room = FakeMatrixRoom()
- val presenter = createMessagePresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
+ val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -134,7 +138,7 @@ class MessagesPresenterTest {
fun `present - handle toggling a reaction twice`() = runTest {
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
val room = FakeMatrixRoom()
- val presenter = createMessagePresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
+ val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -151,7 +155,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle action forward`() = runTest {
val navigator = FakeMessagesNavigator()
- val presenter = createMessagePresenter(navigator = navigator)
+ val presenter = createMessagesPresenter(navigator = navigator)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -166,7 +170,7 @@ class MessagesPresenterTest {
fun `present - handle action copy`() = runTest {
val clipboardHelper = FakeClipboardHelper()
val event = aMessageEvent()
- val presenter = createMessagePresenter(clipboardHelper = clipboardHelper)
+ val presenter = createMessagesPresenter(clipboardHelper = clipboardHelper)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -180,7 +184,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle action reply`() = runTest {
- val presenter = createMessagePresenter()
+ val presenter = createMessagesPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -195,7 +199,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle action reply to an event with no id does nothing`() = runTest {
- val presenter = createMessagePresenter()
+ val presenter = createMessagesPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -209,7 +213,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle action reply to an image media message`() = runTest {
- val presenter = createMessagePresenter()
+ val presenter = createMessagesPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -240,7 +244,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle action reply to a video media message`() = runTest {
- val presenter = createMessagePresenter()
+ val presenter = createMessagesPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -272,7 +276,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle action reply to a file media message`() = runTest {
- val presenter = createMessagePresenter()
+ val presenter = createMessagesPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -299,7 +303,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle action edit`() = runTest {
- val presenter = createMessagePresenter()
+ val presenter = createMessagesPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -316,7 +320,7 @@ class MessagesPresenterTest {
fun `present - handle action redact`() = runTest {
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
val matrixRoom = FakeMatrixRoom()
- val presenter = createMessagePresenter(matrixRoom = matrixRoom, coroutineDispatchers = coroutineDispatchers)
+ val presenter = createMessagesPresenter(matrixRoom = matrixRoom, coroutineDispatchers = coroutineDispatchers)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -332,7 +336,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle action report content`() = runTest {
val navigator = FakeMessagesNavigator()
- val presenter = createMessagePresenter(navigator = navigator)
+ val presenter = createMessagesPresenter(navigator = navigator)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -345,7 +349,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle dismiss action`() = runTest {
- val presenter = createMessagePresenter()
+ val presenter = createMessagesPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -359,7 +363,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle action show developer info`() = runTest {
val navigator = FakeMessagesNavigator()
- val presenter = createMessagePresenter(navigator = navigator)
+ val presenter = createMessagesPresenter(navigator = navigator)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -373,7 +377,7 @@ class MessagesPresenterTest {
@Test
fun `present - shows prompt to reinvite users in DM`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = true, activeMemberCount = 1L)
- val presenter = createMessagePresenter(matrixRoom = room)
+ val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -399,7 +403,7 @@ class MessagesPresenterTest {
@Test
fun `present - doesn't show reinvite prompt in non-direct room`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = false, activeMemberCount = 1L)
- val presenter = createMessagePresenter(matrixRoom = room)
+ val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -415,7 +419,7 @@ class MessagesPresenterTest {
@Test
fun `present - doesn't show reinvite prompt if other party is present`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = true, activeMemberCount = 2L)
- val presenter = createMessagePresenter(matrixRoom = room)
+ val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -439,7 +443,7 @@ class MessagesPresenterTest {
)
)
)
- val presenter = createMessagePresenter(matrixRoom = room)
+ val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -466,7 +470,7 @@ class MessagesPresenterTest {
)
)
)
- val presenter = createMessagePresenter(matrixRoom = room)
+ val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -487,7 +491,7 @@ class MessagesPresenterTest {
fun `present - handle reinviting other user when memberlist is not ready`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID)
room.givenRoomMembersState(MatrixRoomMembersState.Unknown)
- val presenter = createMessagePresenter(matrixRoom = room)
+ val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -513,7 +517,7 @@ class MessagesPresenterTest {
)
)
room.givenInviteUserResult(Result.failure(Throwable("Oops!")))
- val presenter = createMessagePresenter(matrixRoom = room)
+ val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -534,7 +538,7 @@ class MessagesPresenterTest {
fun `present - permission to post`() = runTest {
val matrixRoom = FakeMatrixRoom()
matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(true))
- val presenter = createMessagePresenter(matrixRoom = matrixRoom)
+ val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -547,7 +551,7 @@ class MessagesPresenterTest {
fun `present - no permission to post`() = runTest {
val matrixRoom = FakeMatrixRoom()
matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(false))
- val presenter = createMessagePresenter(matrixRoom = matrixRoom)
+ val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -562,7 +566,7 @@ class MessagesPresenterTest {
@Test
fun `present - permission to redact`() = runTest {
val matrixRoom = FakeMatrixRoom(canRedact = true)
- val presenter = createMessagePresenter(matrixRoom = matrixRoom)
+ val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -576,7 +580,7 @@ class MessagesPresenterTest {
fun `present - handle poll end`() = runTest {
val room = FakeMatrixRoom()
val analyticsService = FakeAnalyticsService()
- val presenter = createMessagePresenter(
+ val presenter = createMessagesPresenter(
matrixRoom = room,
analyticsService = analyticsService,
)
@@ -595,12 +599,13 @@ class MessagesPresenterTest {
}
}
- private fun TestScope.createMessagePresenter(
+ private fun TestScope.createMessagesPresenter(
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
matrixRoom: MatrixRoom = FakeMatrixRoom(),
navigator: FakeMessagesNavigator = FakeMessagesNavigator(),
clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(),
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
+ permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(),
): MessagesPresenter {
val messageComposerPresenter = MessageComposerPresenter(
appCoroutineScope = this,
@@ -613,8 +618,9 @@ class MessagesPresenterTest {
analyticsService = analyticsService,
messageComposerContext = MessageComposerContextImpl(),
richTextEditorStateFactory = TestRichTextEditorStateFactory(),
-
- )
+ permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter),
+ )
+ val voiceMessageComposerPresenter = VoiceMessageComposerPresenter()
val timelinePresenter = TimelinePresenter(
timelineItemsFactory = aTimelineItemsFactory(),
room = matrixRoom,
@@ -630,6 +636,7 @@ class MessagesPresenterTest {
return MessagesPresenter(
room = matrixRoom,
composerPresenter = messageComposerPresenter,
+ voiceMessageComposerPresenter = voiceMessageComposerPresenter,
timelinePresenter = timelinePresenter,
actionListPresenter = actionListPresenter,
customReactionPresenter = customReactionPresenter,
@@ -640,8 +647,8 @@ class MessagesPresenterTest {
messageSummaryFormatter = FakeMessageSummaryFormatter(),
navigator = navigator,
clipboardHelper = clipboardHelper,
- analyticsService = analyticsService,
preferencesStore = preferencesStore,
+ featureFlagsService = FakeFeatureFlagService(),
dispatchers = coroutineDispatchers,
)
}
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt
index 316e39317e..f94e79f0cb 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt
@@ -46,7 +46,7 @@ class ActionListPresenterTest {
@Test
fun `present - initial state`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = true)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -57,7 +57,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for message from me redacted`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = true)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -82,7 +82,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for message from others redacted`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = true)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -107,7 +107,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for others message`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = true)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -139,7 +139,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for others message cannot sent message`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = true)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -170,7 +170,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for others message and can redact`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = true)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -201,7 +201,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for my message`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = true)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -234,7 +234,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for a media item`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = true)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -265,7 +265,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for a state item in debug build`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = true)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -294,7 +294,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for a state item in non-debuggable build`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = false)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -322,7 +322,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute message in non-debuggable build`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = false)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -354,7 +354,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute message with no actions`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = false)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -381,7 +381,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute not sent message`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = false)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -410,7 +410,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for poll message`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = false)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -436,7 +436,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for ended poll message`() = runTest {
- val presenter = anActionListPresenter(isDeveloperModeEnabled = false)
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = false)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -460,7 +460,7 @@ class ActionListPresenterTest {
}
}
-private fun anActionListPresenter(isDeveloperModeEnabled: Boolean): ActionListPresenter {
+private fun createActionListPresenter(isDeveloperModeEnabled: Boolean): ActionListPresenter {
val preferencesStore = InMemoryPreferencesStore(isDeveloperModeEnabled = isDeveloperModeEnabled)
return ActionListPresenter(preferencesStore = preferencesStore)
}
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt
index 30c8d761b3..22f28f21d0 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt
@@ -59,7 +59,7 @@ class AttachmentsPreviewPresenterTest {
Pair(10, 10)
)
)
- val presenter = anAttachmentsPreviewPresenter(room = room)
+ val presenter = createAttachmentsPreviewPresenter(room = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -81,7 +81,7 @@ class AttachmentsPreviewPresenterTest {
val room = FakeMatrixRoom()
val failure = MediaPreProcessor.Failure(null)
room.givenSendMediaResult(Result.failure(failure))
- val presenter = anAttachmentsPreviewPresenter(room = room)
+ val presenter = createAttachmentsPreviewPresenter(room = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -101,7 +101,7 @@ class AttachmentsPreviewPresenterTest {
@Test
fun `present - dismissing the progress dialog stops media upload`() = runTest {
- val presenter = anAttachmentsPreviewPresenter()
+ val presenter = createAttachmentsPreviewPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -114,7 +114,7 @@ class AttachmentsPreviewPresenterTest {
}
}
- private fun anAttachmentsPreviewPresenter(
+ private fun createAttachmentsPreviewPresenter(
localMedia: LocalMedia = aLocalMedia(
uri = mockMediaUrl,
),
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt
index 6401f60f47..79c865ce92 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt
@@ -30,7 +30,7 @@ import io.element.android.features.messages.impl.media.viewer.MediaViewerPresent
import io.element.android.features.messages.media.FakeLocalMediaActions
import io.element.android.features.messages.media.FakeLocalMediaFactory
import io.element.android.libraries.architecture.Async
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.matrix.test.media.FakeMediaLoader
import io.element.android.libraries.matrix.test.media.aMediaSource
import io.element.android.tests.testutils.WarmUpRule
@@ -55,7 +55,7 @@ class MediaViewerPresenterTest {
fun `present - download media success scenario`() = runTest {
val mediaLoader = FakeMediaLoader()
val mediaActions = FakeLocalMediaActions()
- val presenter = aMediaViewerPresenter(mediaLoader, mediaActions)
+ val presenter = createMediaViewerPresenter(mediaLoader, mediaActions)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -76,7 +76,7 @@ class MediaViewerPresenterTest {
val mediaLoader = FakeMediaLoader()
val mediaActions = FakeLocalMediaActions()
val snackbarDispatcher = SnackbarDispatcher()
- val presenter = aMediaViewerPresenter(mediaLoader, mediaActions, snackbarDispatcher)
+ val presenter = createMediaViewerPresenter(mediaLoader, mediaActions, snackbarDispatcher)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -122,7 +122,7 @@ class MediaViewerPresenterTest {
fun `present - download media failure then retry with success scenario`() = runTest {
val mediaLoader = FakeMediaLoader()
val mediaActions = FakeLocalMediaActions()
- val presenter = aMediaViewerPresenter(mediaLoader, mediaActions)
+ val presenter = createMediaViewerPresenter(mediaLoader, mediaActions)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -147,7 +147,7 @@ class MediaViewerPresenterTest {
}
}
- private fun aMediaViewerPresenter(
+ private fun createMediaViewerPresenter(
mediaLoader: FakeMediaLoader,
localMediaActions: FakeLocalMediaActions,
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt
index 8244562622..94b971f300 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt
@@ -23,7 +23,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.messages.impl.report.ReportMessageEvents
import io.element.android.features.messages.impl.report.ReportMessagePresenter
import io.element.android.libraries.architecture.Async
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_USER_ID
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt
index 3f66269fe1..ee951920a7 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt
@@ -19,6 +19,7 @@
package io.element.android.features.messages.textcomposer
import android.net.Uri
+import androidx.compose.runtime.remember
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.ReceiveTurbine
@@ -32,7 +33,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
import io.element.android.features.messages.media.FakeLocalMediaFactory
import io.element.android.libraries.core.mimetype.MimeTypes
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
@@ -54,8 +55,11 @@ import io.element.android.libraries.mediaupload.api.MediaPreProcessor
import io.element.android.libraries.mediaupload.api.MediaSender
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
-import io.element.android.libraries.textcomposer.Message
-import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.libraries.permissions.api.PermissionsPresenter
+import io.element.android.libraries.permissions.test.FakePermissionsPresenter
+import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
+import io.element.android.libraries.textcomposer.model.Message
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.waitForPredicate
@@ -67,6 +71,7 @@ import org.junit.Rule
import org.junit.Test
import java.io.File
+@Suppress("LargeClass")
class MessageComposerPresenterTest {
@get:Rule
@@ -98,7 +103,6 @@ class MessageComposerPresenterTest {
assertThat(initialState.showAttachmentSourcePicker).isFalse()
assertThat(initialState.canShareLocation).isTrue()
assertThat(initialState.attachmentsState).isEqualTo(AttachmentsState.None)
- assertThat(initialState.canSendMessage).isFalse()
}
}
@@ -128,13 +132,9 @@ class MessageComposerPresenterTest {
skipItems(1)
val initialState = awaitItem()
initialState.richTextEditorState.setHtml(A_MESSAGE)
- val withMessageState = awaitItem()
- assertThat(withMessageState.richTextEditorState.messageHtml).isEqualTo(A_MESSAGE)
- assertThat(withMessageState.canSendMessage).isTrue()
- withMessageState.richTextEditorState.setHtml("")
- val withEmptyMessageState = awaitItem()
- assertThat(withEmptyMessageState.richTextEditorState.messageHtml).isEqualTo("")
- assertThat(withEmptyMessageState.canSendMessage).isFalse()
+ assertThat(initialState.richTextEditorState.messageHtml).isEqualTo(A_MESSAGE)
+ initialState.richTextEditorState.setHtml("")
+ assertThat(initialState.richTextEditorState.messageHtml).isEqualTo("")
}
}
@@ -142,7 +142,8 @@ class MessageComposerPresenterTest {
fun `present - change mode to edit`() = runTest {
val presenter = createPresenter(this)
moleculeFlow(RecompositionMode.Immediate) {
- presenter.present()
+ val state = presenter.present()
+ remember(state, state.richTextEditorState.messageHtml) { state }
}.test {
skipItems(1)
var state = awaitItem()
@@ -152,7 +153,6 @@ class MessageComposerPresenterTest {
assertThat(state.mode).isEqualTo(mode)
state = awaitItem()
assertThat(state.richTextEditorState.messageHtml).isEqualTo(A_MESSAGE)
- assertThat(state.canSendMessage).isTrue()
backToNormalMode(state, skipCount = 1)
}
}
@@ -170,7 +170,6 @@ class MessageComposerPresenterTest {
state = awaitItem()
assertThat(state.mode).isEqualTo(mode)
assertThat(state.richTextEditorState.messageHtml).isEqualTo("")
- assertThat(state.canSendMessage).isFalse()
backToNormalMode(state)
}
}
@@ -188,7 +187,6 @@ class MessageComposerPresenterTest {
state = awaitItem()
assertThat(state.mode).isEqualTo(mode)
assertThat(state.richTextEditorState.messageHtml).isEqualTo("")
- assertThat(state.canSendMessage).isFalse()
backToNormalMode(state)
}
}
@@ -197,18 +195,17 @@ class MessageComposerPresenterTest {
fun `present - send message`() = runTest {
val presenter = createPresenter(this)
moleculeFlow(RecompositionMode.Immediate) {
- presenter.present()
+ val state = presenter.present()
+ remember(state, state.richTextEditorState.messageHtml) { state }
}.test {
skipItems(1)
val initialState = awaitItem()
initialState.richTextEditorState.setHtml(A_MESSAGE)
val withMessageState = awaitItem()
assertThat(withMessageState.richTextEditorState.messageHtml).isEqualTo(A_MESSAGE)
- assertThat(withMessageState.canSendMessage).isTrue()
withMessageState.eventSink.invoke(MessageComposerEvents.SendMessage(A_MESSAGE.toMessage()))
val messageSentState = awaitItem()
assertThat(messageSentState.richTextEditorState.messageHtml).isEqualTo("")
- assertThat(messageSentState.canSendMessage).isFalse()
waitForPredicate { analyticsService.capturedEvents.size == 1 }
assertThat(analyticsService.capturedEvents).containsExactly(
Composer(
@@ -229,7 +226,8 @@ class MessageComposerPresenterTest {
fakeMatrixRoom,
)
moleculeFlow(RecompositionMode.Immediate) {
- presenter.present()
+ val state = presenter.present()
+ remember(state, state.richTextEditorState.messageHtml) { state }
}.test {
skipItems(1)
val initialState = awaitItem()
@@ -240,7 +238,6 @@ class MessageComposerPresenterTest {
val withMessageState = awaitItem()
assertThat(withMessageState.mode).isEqualTo(mode)
assertThat(withMessageState.richTextEditorState.messageHtml).isEqualTo(A_MESSAGE)
- assertThat(withMessageState.canSendMessage).isTrue()
withMessageState.richTextEditorState.setHtml(ANOTHER_MESSAGE)
val withEditedMessageState = awaitItem()
assertThat(withEditedMessageState.richTextEditorState.messageHtml).isEqualTo(ANOTHER_MESSAGE)
@@ -248,7 +245,6 @@ class MessageComposerPresenterTest {
skipItems(1)
val messageSentState = awaitItem()
assertThat(messageSentState.richTextEditorState.messageHtml).isEqualTo("")
- assertThat(messageSentState.canSendMessage).isFalse()
assertThat(fakeMatrixRoom.editMessageCalls.first()).isEqualTo(ANOTHER_MESSAGE to ANOTHER_MESSAGE)
assertThat(analyticsService.capturedEvents).containsExactly(
Composer(
@@ -269,7 +265,8 @@ class MessageComposerPresenterTest {
fakeMatrixRoom,
)
moleculeFlow(RecompositionMode.Immediate) {
- presenter.present()
+ val state = presenter.present()
+ remember(state, state.richTextEditorState.messageHtml) { state }
}.test {
skipItems(1)
val initialState = awaitItem()
@@ -280,7 +277,6 @@ class MessageComposerPresenterTest {
val withMessageState = awaitItem()
assertThat(withMessageState.mode).isEqualTo(mode)
assertThat(withMessageState.richTextEditorState.messageHtml).isEqualTo(A_MESSAGE)
- assertThat(withMessageState.canSendMessage).isTrue()
withMessageState.richTextEditorState.setHtml(ANOTHER_MESSAGE)
val withEditedMessageState = awaitItem()
assertThat(withEditedMessageState.richTextEditorState.messageHtml).isEqualTo(ANOTHER_MESSAGE)
@@ -288,7 +284,6 @@ class MessageComposerPresenterTest {
skipItems(1)
val messageSentState = awaitItem()
assertThat(messageSentState.richTextEditorState.messageHtml).isEqualTo("")
- assertThat(messageSentState.canSendMessage).isFalse()
assertThat(fakeMatrixRoom.editMessageCalls.first()).isEqualTo(ANOTHER_MESSAGE to ANOTHER_MESSAGE)
assertThat(analyticsService.capturedEvents).containsExactly(
Composer(
@@ -319,16 +314,11 @@ class MessageComposerPresenterTest {
val state = awaitItem()
assertThat(state.mode).isEqualTo(mode)
assertThat(state.richTextEditorState.messageHtml).isEqualTo("")
- assertThat(state.canSendMessage).isFalse()
state.richTextEditorState.setHtml(A_REPLY)
- val withMessageState = awaitItem()
- assertThat(withMessageState.richTextEditorState.messageHtml).isEqualTo(A_REPLY)
- assertThat(withMessageState.canSendMessage).isTrue()
- withMessageState.eventSink.invoke(MessageComposerEvents.SendMessage(A_REPLY.toMessage()))
- skipItems(1)
+ assertThat(state.richTextEditorState.messageHtml).isEqualTo(A_REPLY)
+ state.eventSink.invoke(MessageComposerEvents.SendMessage(A_REPLY.toMessage()))
val messageSentState = awaitItem()
assertThat(messageSentState.richTextEditorState.messageHtml).isEqualTo("")
- assertThat(messageSentState.canSendMessage).isFalse()
assertThat(fakeMatrixRoom.replyMessageParameter).isEqualTo(A_REPLY to A_REPLY)
assertThat(analyticsService.capturedEvents).containsExactly(
Composer(
@@ -487,18 +477,84 @@ class MessageComposerPresenterTest {
}
@Test
- fun `present - Take photo`() = runTest {
+ fun `present - create poll`() = runTest {
val room = FakeMatrixRoom()
val presenter = createPresenter(this, room = room)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ skipItems(1)
+ val initialState = awaitItem()
+ initialState.eventSink(MessageComposerEvents.AddAttachment)
+ val attachmentOpenState = awaitItem()
+ assertThat(attachmentOpenState.showAttachmentSourcePicker).isTrue()
+ initialState.eventSink(MessageComposerEvents.PickAttachmentSource.Poll)
+ val finalState = awaitItem()
+ assertThat(finalState.showAttachmentSourcePicker).isFalse()
+ }
+ }
+
+ @Test
+ fun `present - share location`() = runTest {
+ val room = FakeMatrixRoom()
+ val presenter = createPresenter(this, room = room)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ skipItems(1)
+ val initialState = awaitItem()
+ initialState.eventSink(MessageComposerEvents.AddAttachment)
+ val attachmentOpenState = awaitItem()
+ assertThat(attachmentOpenState.showAttachmentSourcePicker).isTrue()
+ initialState.eventSink(MessageComposerEvents.PickAttachmentSource.Location)
+ val finalState = awaitItem()
+ assertThat(finalState.showAttachmentSourcePicker).isFalse()
+ }
+ }
+
+ @Test
+ fun `present - Take photo`() = runTest {
+ val room = FakeMatrixRoom()
+ val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() }
+ val presenter = createPresenter(
+ this,
+ room = room,
+ permissionPresenter = permissionPresenter,
+ )
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
val initialState = awaitItem()
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera)
- val previewingState = awaitItem()
- assertThat(previewingState.showAttachmentSourcePicker).isFalse()
- assertThat(previewingState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java)
+ val finalState = awaitItem()
+ assertThat(finalState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java)
+ cancelAndIgnoreRemainingEvents()
+ }
+ }
+
+ @Test
+ fun `present - Take photo with permission request`() = runTest {
+ val room = FakeMatrixRoom()
+ val permissionPresenter = FakePermissionsPresenter()
+ val presenter = createPresenter(
+ this,
+ room = room,
+ permissionPresenter = permissionPresenter,
+ )
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ skipItems(1)
+ val initialState = awaitItem()
+ initialState.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera)
+ val permissionState = awaitItem()
+ assertThat(permissionState.showAttachmentSourcePicker).isFalse()
+ assertThat(permissionState.attachmentsState).isInstanceOf(AttachmentsState.None::class.java)
+ permissionPresenter.setPermissionGranted()
+ skipItems(1)
+ val finalState = awaitItem()
+ assertThat(finalState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java)
cancelAndIgnoreRemainingEvents()
}
}
@@ -506,16 +562,47 @@ class MessageComposerPresenterTest {
@Test
fun `present - Record video`() = runTest {
val room = FakeMatrixRoom()
- val presenter = createPresenter(this, room = room)
+ val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() }
+ val presenter = createPresenter(
+ this,
+ room = room,
+ permissionPresenter = permissionPresenter,
+ )
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
val initialState = awaitItem()
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera)
- val previewingState = awaitItem()
- assertThat(previewingState.showAttachmentSourcePicker).isFalse()
- assertThat(previewingState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java)
+ val finalState = awaitItem()
+ assertThat(finalState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java)
+ cancelAndIgnoreRemainingEvents()
+ }
+ }
+
+ @Test
+ fun `present - Record video with permission request`() = runTest {
+ val room = FakeMatrixRoom()
+ val permissionPresenter = FakePermissionsPresenter()
+ val presenter = createPresenter(
+ this,
+ room = room,
+ permissionPresenter = permissionPresenter,
+ )
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ skipItems(1)
+ val initialState = awaitItem()
+ initialState.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera)
+ val permissionState = awaitItem()
+ assertThat(permissionState.showAttachmentSourcePicker).isFalse()
+ assertThat(permissionState.attachmentsState).isInstanceOf(AttachmentsState.None::class.java)
+ permissionPresenter.setPermissionGranted()
+ skipItems(1)
+ val finalState = awaitItem()
+ assertThat(finalState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java)
+ cancelAndIgnoreRemainingEvents()
}
}
@@ -602,7 +689,6 @@ class MessageComposerPresenterTest {
val normalState = awaitItem()
assertThat(normalState.mode).isEqualTo(MessageComposerMode.Normal(""))
assertThat(normalState.richTextEditorState.messageHtml).isEqualTo("")
- assertThat(normalState.canSendMessage).isFalse()
}
private fun createPresenter(
@@ -612,6 +698,7 @@ class MessageComposerPresenterTest {
featureFlagService: FeatureFlagService = this.featureFlagService,
mediaPreProcessor: MediaPreProcessor = this.mediaPreProcessor,
snackbarDispatcher: SnackbarDispatcher = this.snackbarDispatcher,
+ permissionPresenter: PermissionsPresenter = FakePermissionsPresenter(),
) = MessageComposerPresenter(
coroutineScope,
room,
@@ -623,6 +710,7 @@ class MessageComposerPresenterTest {
analyticsService,
MessageComposerContextImpl(),
TestRichTextEditorStateFactory(),
+ permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionPresenter),
)
}
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt
index 0435be3cb1..ec83b86cd9 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt
@@ -20,7 +20,9 @@ import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
+import im.vector.app.features.analytics.plan.PollEnd
import im.vector.app.features.analytics.plan.PollVote
+import io.element.android.features.messages.fixtures.aMessageEvent
import io.element.android.features.messages.fixtures.aTimelineItemsFactory
import io.element.android.features.messages.impl.timeline.TimelineEvents
import io.element.android.features.messages.impl.timeline.TimelinePresenter
@@ -42,6 +44,7 @@ import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.awaitWithLatch
import io.element.android.tests.testutils.testCoroutineDispatchers
+import io.element.android.tests.testutils.waitForPredicate
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
@@ -280,6 +283,29 @@ class TimelinePresenterTest {
assertThat(analyticsService.capturedEvents.last()).isEqualTo(PollVote())
}
+ @Test
+ fun `present - PollEndClicked event calls into rust room api and analytics`() = runTest {
+ val room = FakeMatrixRoom()
+ val analyticsService = FakeAnalyticsService()
+ val presenter = createTimelinePresenter(
+ room = room,
+ analyticsService = analyticsService,
+ )
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ initialState.eventSink(TimelineEvents.PollEndClicked(aMessageEvent().eventId!!))
+ waitForPredicate { room.endPollInvocations.size == 1 }
+ cancelAndIgnoreRemainingEvents()
+ assertThat(room.endPollInvocations.size).isEqualTo(1)
+ assertThat(room.endPollInvocations.first().pollStartId).isEqualTo(AN_EVENT_ID)
+ assertThat(room.endPollInvocations.first().text).isEqualTo("The poll with event id: \$anEventId has ended.")
+ assertThat(analyticsService.capturedEvents.size).isEqualTo(1)
+ assertThat(analyticsService.capturedEvents.last()).isEqualTo(PollEnd())
+ }
+ }
+
private fun TestScope.createTimelinePresenter(
timeline: MatrixTimeline = FakeMatrixTimeline(),
timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory()
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/VoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/VoiceMessageComposerPresenterTest.kt
new file mode 100644
index 0000000000..008226bf05
--- /dev/null
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/VoiceMessageComposerPresenterTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package io.element.android.features.messages.voicemessages
+
+import app.cash.molecule.RecompositionMode
+import app.cash.molecule.moleculeFlow
+import app.cash.turbine.test
+import com.google.common.truth.Truth.assertThat
+import io.element.android.features.messages.impl.voicemessages.VoiceMessageComposerEvents
+import io.element.android.features.messages.impl.voicemessages.VoiceMessageComposerPresenter
+import io.element.android.libraries.textcomposer.model.PressEvent
+import io.element.android.libraries.textcomposer.model.VoiceMessageState
+import io.element.android.tests.testutils.WarmUpRule
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+
+class VoiceMessageComposerPresenterTest {
+
+ @get:Rule
+ val warmUpRule = WarmUpRule()
+
+ @Test
+ fun `present - initial state`() = runTest {
+ val presenter = createPresenter()
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ assertThat(initialState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
+ }
+ }
+
+ @Test
+ fun `present - recording state`() = runTest {
+ val presenter = createPresenter()
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart))
+
+ assertThat(awaitItem().voiceMessageState).isEqualTo(VoiceMessageState.Recording)
+ }
+ }
+
+ @Test
+ fun `present - abort recording`() = runTest {
+ val presenter = createPresenter()
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart))
+ awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.Tapped))
+
+ assertThat(awaitItem().voiceMessageState).isEqualTo(VoiceMessageState.Idle)
+ }
+ }
+
+ @Test
+ fun `present - finish recording`() = runTest {
+ val presenter = createPresenter()
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart))
+ awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd))
+
+ assertThat(awaitItem().voiceMessageState).isEqualTo(VoiceMessageState.Idle)
+ }
+ }
+ private fun createPresenter() = VoiceMessageComposerPresenter()
+}
diff --git a/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/MessageComposerContextFake.kt b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/MessageComposerContextFake.kt
index 75c992f495..0ae604004d 100644
--- a/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/MessageComposerContextFake.kt
+++ b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/MessageComposerContextFake.kt
@@ -17,7 +17,7 @@
package io.element.android.features.messages.test
import io.element.android.features.messages.api.MessageComposerContext
-import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
class MessageComposerContextFake(
override var composerMode: MessageComposerMode = MessageComposerMode.Normal(null)
diff --git a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorContainer.kt b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorContainer.kt
new file mode 100644
index 0000000000..421b0c900f
--- /dev/null
+++ b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorContainer.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.networkmonitor.api.ui
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.expandVertically
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.shrinkVertically
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.asPaddingValues
+import androidx.compose.foundation.layout.statusBars
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalInspectionMode
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+/**
+ * A view that displays a connectivity indicator when the device is offline, passing the padding
+ * needed to make sure the status bar is not overlapped to its content views.
+ */
+@Composable
+fun ConnectivityIndicatorContainer(
+ isOnline: Boolean,
+ modifier: Modifier = Modifier,
+ content: @Composable (topPadding: Dp) -> Unit = {},
+) {
+ val isIndicatorVisible = remember { MutableTransitionState(!isOnline) }.apply { targetState = !isOnline }
+
+ val statusBarTopPadding = if (LocalInspectionMode.current) {
+ // Needed to get valid UI previews
+ 24.dp
+ } else {
+ WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + 6.dp
+ }
+ val target = remember(isIndicatorVisible.targetState, statusBarTopPadding) {
+ if (!isIndicatorVisible.targetState) 0.dp else statusBarTopPadding
+ }
+ val animationStateOffset by animateDpAsState(
+ targetValue = target,
+ animationSpec = spring(
+ stiffness = Spring.StiffnessMediumLow,
+ visibilityThreshold = 1.dp,
+ ),
+ label = "insets-animation",
+ )
+
+ content(animationStateOffset)
+
+ // Display the network indicator with an animation
+ AnimatedVisibility(
+ visibleState = isIndicatorVisible,
+ enter = fadeIn() + expandVertically(),
+ exit = fadeOut() + shrinkVertically(),
+ ) {
+ Indicator(modifier)
+ }
+}
diff --git a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt
index cfa5b7e79b..45f62f94b6 100644
--- a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt
+++ b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt
@@ -18,46 +18,17 @@ package io.element.android.features.networkmonitor.api.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
-import androidx.compose.animation.core.Spring
-import androidx.compose.animation.core.animateDpAsState
-import androidx.compose.animation.core.spring
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.asPaddingValues
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.statusBarsPadding
-import androidx.compose.foundation.layout.width
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.WifiOff
-import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.platform.LocalInspectionMode
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
-import io.element.android.libraries.designsystem.text.toDp
-import io.element.android.libraries.designsystem.theme.components.Text
-import io.element.android.libraries.theme.ElementTheme
-import io.element.android.libraries.ui.strings.CommonStrings
/**
* A view that displays a connectivity indicator when the device is offline, adding a default
@@ -90,81 +61,12 @@ fun ConnectivityIndicatorView(
}
}
-/**
- * A view that displays a connectivity indicator when the device is offline, passing the padding
- * needed to make sure the status bar is not overlapped to its content views.
- */
-@Composable
-fun ConnectivityIndicatorContainer(
- isOnline: Boolean,
- modifier: Modifier = Modifier,
- content: @Composable (topPadding: Dp) -> Unit,
-) {
- val isIndicatorVisible = remember { MutableTransitionState(!isOnline) }.apply { targetState = !isOnline }
-
- val statusBarTopPadding = if (LocalInspectionMode.current) {
- // Needed to get valid UI previews
- 24.dp
- } else {
- WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + 6.dp
- }
- val target = remember(isIndicatorVisible.targetState, statusBarTopPadding) {
- if (!isIndicatorVisible.targetState) 0.dp else statusBarTopPadding
- }
- val animationStateOffset by animateDpAsState(
- targetValue = target,
- animationSpec = spring(
- stiffness = Spring.StiffnessMediumLow,
- visibilityThreshold = 1.dp,
- ),
- label = "insets-animation",
- )
-
- content(animationStateOffset)
-
- // Display the network indicator with an animation
- AnimatedVisibility(
- visibleState = isIndicatorVisible,
- enter = fadeIn() + expandVertically(),
- exit = fadeOut() + shrinkVertically(),
- ) {
- Indicator(modifier)
- }
-}
-
-@Composable
-private fun Indicator(modifier: Modifier = Modifier) {
- Row(
- modifier
- .fillMaxWidth()
- .background(MaterialTheme.colorScheme.secondaryContainer)
- .statusBarsPadding()
- .padding(vertical = 6.dp),
- horizontalArrangement = Arrangement.Center,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- val tint = MaterialTheme.colorScheme.primary
- Image(
- imageVector = Icons.Outlined.WifiOff,
- contentDescription = null,
- colorFilter = ColorFilter.tint(tint),
- modifier = Modifier.size(16.sp.toDp()),
- )
- Spacer(modifier = Modifier.width(8.dp))
- Text(
- text = stringResource(CommonStrings.common_offline),
- style = ElementTheme.typography.fontBodyMdMedium,
- color = tint,
- )
- }
-}
-
@Composable
private fun StatusBarPaddingSpacer(modifier: Modifier = Modifier) {
Spacer(modifier = modifier.statusBarsPadding())
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ConnectivityIndicatorViewPreview() {
ElementPreview {
diff --git a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/Indicator.kt b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/Indicator.kt
new file mode 100644
index 0000000000..0be8557bed
--- /dev/null
+++ b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/Indicator.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.networkmonitor.api.ui
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import io.element.android.libraries.designsystem.text.toDp
+import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
+import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.ui.strings.CommonStrings
+
+@Composable
+internal fun Indicator(
+ modifier: Modifier = Modifier,
+) {
+ Row(
+ modifier
+ .fillMaxWidth()
+ .background(MaterialTheme.colorScheme.secondaryContainer)
+ .statusBarsPadding()
+ .padding(vertical = 6.dp),
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ val tint = MaterialTheme.colorScheme.primary
+ Icon(
+ resourceId = CommonDrawables.ic_compound_offline,
+ contentDescription = null,
+ tint = tint,
+ modifier = Modifier.size(16.sp.toDp()),
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(
+ text = stringResource(CommonStrings.common_offline),
+ style = ElementTheme.typography.fontBodyMdMedium,
+ color = tint,
+ )
+ }
+}
diff --git a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt
index 424c24839a..b6d663d2c5 100644
--- a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt
+++ b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt
@@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.QrCode
-import androidx.compose.material.icons.filled.Settings
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
@@ -40,7 +39,7 @@ import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtom
import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtomSize
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.pages.OnBoardingPage
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Icon
@@ -48,6 +47,7 @@ import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.theme.ElementTheme
@@ -140,7 +140,7 @@ private fun OnBoardingContent(
onClick = onOpenDeveloperSettings,
) {
Icon(
- imageVector = Icons.Filled.Settings,
+ resourceId = CommonDrawables.ic_compound_settings_solid,
contentDescription = stringResource(CommonStrings.common_settings)
)
}
@@ -190,7 +190,7 @@ private fun OnBoardingButtons(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun OnBoardingScreenPreview(
@PreviewParameter(OnBoardingStateProvider::class) state: OnBoardingState
diff --git a/features/onboarding/impl/src/main/res/values-cs/translations.xml b/features/onboarding/impl/src/main/res/values-cs/translations.xml
index 6b8f0eaa91..de8b00cd70 100644
--- a/features/onboarding/impl/src/main/res/values-cs/translations.xml
+++ b/features/onboarding/impl/src/main/res/values-cs/translations.xml
@@ -3,7 +3,6 @@
"Ruční přihlášení"
"Přihlásit se pomocí QR kódu"
"Vytvořit účet"
- "Komunikujte a spolupracujte bezpečně"
"Vítejte u dosud nejrychlejšího Elementu. Vylepšený pro rychlost a jednoduchost."
"Vítejte v %1$s. Vylepšený, pro rychlost a jednoduchost."
"Buďte ve svém živlu"
diff --git a/features/onboarding/impl/src/main/res/values-de/translations.xml b/features/onboarding/impl/src/main/res/values-de/translations.xml
index b7f231a32a..ec802a1ba1 100644
--- a/features/onboarding/impl/src/main/res/values-de/translations.xml
+++ b/features/onboarding/impl/src/main/res/values-de/translations.xml
@@ -3,7 +3,6 @@
"Manuell anmelden"
"Mit QR-Code anmelden"
"Konto erstellen"
- "Sicher kommunizieren und zusammenarbeiten"
"Willkommen beim schnellsten Element aller Zeiten. Optimiert für Geschwindigkeit und Einfachheit."
"Willkommen zu %1$s. Aufgeladen, für Geschwindigkeit und Einfachheit."
"Sei in deinem Element"
diff --git a/features/onboarding/impl/src/main/res/values-fr/translations.xml b/features/onboarding/impl/src/main/res/values-fr/translations.xml
index 789b29c5b8..94b9461a88 100644
--- a/features/onboarding/impl/src/main/res/values-fr/translations.xml
+++ b/features/onboarding/impl/src/main/res/values-fr/translations.xml
@@ -3,7 +3,6 @@
"Se connecter manuellement"
"Se connecter avec un QR code"
"Créer un compte"
- "Communiquez et collaborez en toute sécurité"
"Bienvenue dans l’Element le plus rapide de tous les temps. Boosté pour plus de rapidité et de simplicité."
"Bienvenue sur %1$s. Boosté, pour plus de rapidité et de simplicité."
"Soyez dans votre Element"
diff --git a/features/onboarding/impl/src/main/res/values-ro/translations.xml b/features/onboarding/impl/src/main/res/values-ro/translations.xml
index 3572d3a47f..b6db94d213 100644
--- a/features/onboarding/impl/src/main/res/values-ro/translations.xml
+++ b/features/onboarding/impl/src/main/res/values-ro/translations.xml
@@ -3,7 +3,6 @@
"Conectați-vă manual"
"Conectați-vă cu un cod QR"
"Creați un cont"
- "Comunicați și colaborați în siguranță"
"Bine ați venit la cel mai rapid Element din toate timpurile. Supraalimentat pentru viteză și simplitate."
"Bun venit în %1$s. Supraalimentat, pentru viteză și simplitate."
"Fii în Elementul tău"
diff --git a/features/onboarding/impl/src/main/res/values-ru/translations.xml b/features/onboarding/impl/src/main/res/values-ru/translations.xml
index 5c8c12c2b0..31d09cd396 100644
--- a/features/onboarding/impl/src/main/res/values-ru/translations.xml
+++ b/features/onboarding/impl/src/main/res/values-ru/translations.xml
@@ -3,8 +3,7 @@
"Вход в систему вручную"
"Войти с помощью QR-кода"
"Создать учетную запись"
- "Безопасное общение и совместная работа"
"Добро пожаловать в самый быстрый Element. Преимущество в скорости и простоте."
"Добро пожаловать в %1$s. Supercharged — это скорость и простота."
- "Будь в своей стихии"
+ "Будь c element"
diff --git a/features/onboarding/impl/src/main/res/values-sk/translations.xml b/features/onboarding/impl/src/main/res/values-sk/translations.xml
index b41e671956..9639f7c903 100644
--- a/features/onboarding/impl/src/main/res/values-sk/translations.xml
+++ b/features/onboarding/impl/src/main/res/values-sk/translations.xml
@@ -3,7 +3,6 @@
"Prihlásiť sa manuálne"
"Prihlásiť sa pomocou QR kódu"
"Vytvoriť účet"
- "Komunikujte a spolupracujte bezpečne"
"Vitajte v najrýchlejšom Element vôbec. Nadupaný pre rýchlosť a jednoduchosť."
"Vitajte v %1$s. Nadupaný, pre rýchlosť a jednoduchosť."
"Buďte vo svojom elemente"
diff --git a/features/onboarding/impl/src/main/res/values/localazy.xml b/features/onboarding/impl/src/main/res/values/localazy.xml
index cdb258cdad..2e8d8724d4 100644
--- a/features/onboarding/impl/src/main/res/values/localazy.xml
+++ b/features/onboarding/impl/src/main/res/values/localazy.xml
@@ -3,7 +3,6 @@
"Sign in manually"
"Sign in with QR code"
"Create account"
- "Communicate and collaborate securely"
"Welcome to the fastest Element ever. Supercharged for speed and simplicity."
"Welcome to %1$s. Supercharged, for speed and simplicity."
"Be in your element"
diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt
index 0e4b168866..ac86e64fa6 100644
--- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt
+++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt
@@ -33,7 +33,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator
@@ -108,7 +108,7 @@ internal fun PollAnswerView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PollAnswerDisclosedNotSelectedPreview() = ElementPreview {
PollAnswerView(
@@ -116,7 +116,7 @@ internal fun PollAnswerDisclosedNotSelectedPreview() = ElementPreview {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PollAnswerDisclosedSelectedPreview() = ElementPreview {
PollAnswerView(
@@ -124,7 +124,7 @@ internal fun PollAnswerDisclosedSelectedPreview() = ElementPreview {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PollAnswerUndisclosedNotSelectedPreview() = ElementPreview {
PollAnswerView(
@@ -132,7 +132,7 @@ internal fun PollAnswerUndisclosedNotSelectedPreview() = ElementPreview {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PollAnswerUndisclosedSelectedPreview() = ElementPreview {
PollAnswerView(
@@ -140,7 +140,7 @@ internal fun PollAnswerUndisclosedSelectedPreview() = ElementPreview {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PollAnswerEndedWinnerNotSelectedPreview() = ElementPreview {
PollAnswerView(
@@ -148,7 +148,7 @@ internal fun PollAnswerEndedWinnerNotSelectedPreview() = ElementPreview {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PollAnswerEndedWinnerSelectedPreview() = ElementPreview {
PollAnswerView(
@@ -156,7 +156,7 @@ internal fun PollAnswerEndedWinnerSelectedPreview() = ElementPreview {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PollAnswerEndedSelectedPreview() = ElementPreview {
PollAnswerView(
diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt
index e94b5adeeb..42f0c4f527 100644
--- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt
+++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt
@@ -19,13 +19,17 @@ package io.element.android.features.poll.api
import io.element.android.libraries.matrix.api.poll.PollAnswer
import kotlinx.collections.immutable.persistentListOf
-fun aPollAnswerItemList(isEnded: Boolean = false, isDisclosed: Boolean = true) = persistentListOf(
+fun aPollAnswerItemList(
+ hasVotes: Boolean = true,
+ isEnded: Boolean = false,
+ isDisclosed: Boolean = true,
+) = persistentListOf(
aPollAnswerItem(
answer = PollAnswer("option_1", "Italian \uD83C\uDDEE\uD83C\uDDF9"),
isDisclosed = isDisclosed,
isEnabled = !isEnded,
isWinner = isEnded,
- votesCount = 5,
+ votesCount = if (hasVotes) 5 else 0,
percentage = 0.5f
),
aPollAnswerItem(
@@ -42,7 +46,7 @@ fun aPollAnswerItemList(isEnded: Boolean = false, isDisclosed: Boolean = true) =
isEnabled = !isEnded,
isWinner = false,
isSelected = true,
- votesCount = 1,
+ votesCount = if (hasVotes) 1 else 0,
percentage = 0.1f
),
aPollAnswerItem(isDisclosed = isDisclosed, isEnabled = !isEnded),
diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollContentView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollContentView.kt
index a47d69c780..bfcccf3b89 100644
--- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollContentView.kt
+++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollContentView.kt
@@ -26,17 +26,22 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.VectorIcons
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.poll.PollAnswer
import io.element.android.libraries.matrix.api.poll.PollKind
@@ -51,13 +56,37 @@ fun PollContentView(
answerItems: ImmutableList,
pollKind: PollKind,
isPollEnded: Boolean,
+ isMine: Boolean,
onAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
+ onPollEdit: (pollStartId: EventId) -> Unit,
+ onPollEnd: (pollStartId: EventId) -> Unit,
modifier: Modifier = Modifier,
) {
+ val votesCount = remember(answerItems) { answerItems.sumOf { it.votesCount } }
+
fun onAnswerSelected(pollAnswer: PollAnswer) {
eventId?.let { onAnswerSelected(it, pollAnswer.id) }
}
+ fun onPollEdit() {
+ eventId?.let { onPollEdit(it) }
+ }
+
+ fun onPollEnd() {
+ eventId?.let { onPollEnd(it) }
+ }
+
+ var showConfirmation: Boolean by remember { mutableStateOf(false) }
+
+ if (showConfirmation) ConfirmationDialog(
+ content = stringResource(id = CommonStrings.common_poll_end_confirmation),
+ onSubmitClicked = {
+ onPollEnd()
+ showConfirmation = false
+ },
+ onDismiss = { showConfirmation = false },
+ )
+
Column(
modifier = modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(16.dp),
@@ -67,11 +96,20 @@ fun PollContentView(
PollAnswers(answerItems = answerItems, onAnswerSelected = ::onAnswerSelected)
if (isPollEnded || pollKind == PollKind.Disclosed) {
- val votesCount = remember(answerItems) { answerItems.sumOf { it.votesCount } }
DisclosedPollBottomNotice(votesCount = votesCount)
} else {
UndisclosedPollBottomNotice()
}
+
+ if (isMine) {
+ CreatorView(
+ votesCount = 1, // TODO Polls: set to `votesCount` when edit poll screen is implemented.
+ isPollEnded = isPollEnded,
+ onPollEdit = ::onPollEdit,
+ onPollEnd = { showConfirmation = true },
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
}
}
@@ -87,13 +125,13 @@ private fun PollTitle(
) {
if (isPollEnded) {
Icon(
- resourceId = VectorIcons.PollEnd,
+ resourceId = CommonDrawables.ic_poll_end,
contentDescription = stringResource(id = CommonStrings.a11y_poll_end),
modifier = Modifier.size(22.dp)
)
} else {
Icon(
- resourceId = VectorIcons.Poll,
+ resourceId = CommonDrawables.ic_compound_polls,
contentDescription = stringResource(id = CommonStrings.a11y_poll),
modifier = Modifier.size(22.dp)
)
@@ -157,7 +195,32 @@ private fun ColumnScope.UndisclosedPollBottomNotice(
)
}
-@DayNightPreviews
+@Composable
+private fun CreatorView(
+ @Suppress("SameParameterValue") votesCount: Int, // TODO Polls: remove @Suppress when edit poll screen is implemented.
+ isPollEnded: Boolean,
+ onPollEdit: () -> Unit,
+ onPollEnd: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ if (!isPollEnded) {
+ if (votesCount == 0) {
+ Button(
+ text = stringResource(id = CommonStrings.action_edit_poll),
+ onClick = onPollEdit,
+ modifier = modifier,
+ )
+ } else {
+ Button(
+ text = stringResource(id = CommonStrings.action_end_poll),
+ onClick = onPollEnd,
+ modifier = modifier,
+ )
+ }
+ }
+}
+
+@PreviewsDayNight
@Composable
internal fun PollContentUndisclosedPreview() = ElementPreview {
PollContentView(
@@ -166,11 +229,14 @@ internal fun PollContentUndisclosedPreview() = ElementPreview {
answerItems = aPollAnswerItemList(isDisclosed = false),
pollKind = PollKind.Undisclosed,
isPollEnded = false,
+ isMine = false,
onAnswerSelected = { _, _ -> },
+ onPollEdit = {},
+ onPollEnd = {},
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PollContentDisclosedPreview() = ElementPreview {
PollContentView(
@@ -179,11 +245,14 @@ internal fun PollContentDisclosedPreview() = ElementPreview {
answerItems = aPollAnswerItemList(),
pollKind = PollKind.Disclosed,
isPollEnded = false,
+ isMine = false,
onAnswerSelected = { _, _ -> },
+ onPollEdit = {},
+ onPollEnd = {},
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PollContentEndedPreview() = ElementPreview {
PollContentView(
@@ -192,6 +261,57 @@ internal fun PollContentEndedPreview() = ElementPreview {
answerItems = aPollAnswerItemList(isEnded = true),
pollKind = PollKind.Disclosed,
isPollEnded = true,
+ isMine = false,
onAnswerSelected = { _, _ -> },
+ onPollEdit = {},
+ onPollEnd = {},
+ )
+}
+
+@PreviewsDayNight
+@Composable
+internal fun PollContentCreatorNoVotesPreview() = ElementPreview {
+ PollContentView(
+ eventId = EventId("\$anEventId"),
+ question = "What type of food should we have at the party?",
+ answerItems = aPollAnswerItemList(hasVotes = false, isEnded = false),
+ pollKind = PollKind.Disclosed,
+ isPollEnded = false,
+ isMine = true,
+ onAnswerSelected = { _, _ -> },
+ onPollEdit = {},
+ onPollEnd = {},
+ )
+}
+
+@PreviewsDayNight
+@Composable
+internal fun PollContentCreatorPreview() = ElementPreview {
+ PollContentView(
+ eventId = EventId("\$anEventId"),
+ question = "What type of food should we have at the party?",
+ answerItems = aPollAnswerItemList(isEnded = false),
+ pollKind = PollKind.Disclosed,
+ isPollEnded = false,
+ isMine = true,
+ onAnswerSelected = { _, _ -> },
+ onPollEdit = {},
+ onPollEnd = {},
+ )
+}
+
+@PreviewsDayNight
+@Composable
+internal fun PollContentCreatorEndedPreview() = ElementPreview {
+ PollContentView(
+ eventId = EventId("\$anEventId"),
+ question = "What type of food should we have at the party?",
+ answerItems = aPollAnswerItemList(isEnded = true),
+ pollKind = PollKind.Disclosed,
+ isPollEnded = true,
+ isMine = true,
+ onAnswerSelected = { _, _ -> },
+ onPollEdit = {},
+ onPollEnd = {},
)
}
diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollState.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollState.kt
index eccaea45fc..0a7a9ad618 100644
--- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollState.kt
+++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollState.kt
@@ -26,7 +26,7 @@ data class CreatePollState(
val answers: ImmutableList,
val pollKind: PollKind,
val showConfirmation: Boolean,
- val eventSink: (CreatePollEvents) -> Unit = {},
+ val eventSink: (CreatePollEvents) -> Unit,
)
data class Answer(
diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt
index 29aa1288ad..c1393e0da0 100644
--- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt
+++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollStateProvider.kt
@@ -18,12 +18,13 @@ package io.element.android.features.poll.impl.create
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.matrix.api.poll.PollKind
+import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
class CreatePollStateProvider : PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
- CreatePollState(
+ aCreatePollState(
canCreate = false,
canAddAnswer = true,
question = "",
@@ -34,7 +35,7 @@ class CreatePollStateProvider : PreviewParameterProvider {
pollKind = PollKind.Disclosed,
showConfirmation = false,
),
- CreatePollState(
+ aCreatePollState(
canCreate = true,
canAddAnswer = true,
question = "What type of food should we have?",
@@ -45,7 +46,7 @@ class CreatePollStateProvider : PreviewParameterProvider {
showConfirmation = false,
pollKind = PollKind.Undisclosed,
),
- CreatePollState(
+ aCreatePollState(
canCreate = true,
canAddAnswer = true,
question = "What type of food should we have?",
@@ -56,7 +57,7 @@ class CreatePollStateProvider : PreviewParameterProvider {
showConfirmation = true,
pollKind = PollKind.Undisclosed,
),
- CreatePollState(
+ aCreatePollState(
canCreate = true,
canAddAnswer = true,
question = "What type of food should we have?",
@@ -69,7 +70,7 @@ class CreatePollStateProvider : PreviewParameterProvider {
showConfirmation = false,
pollKind = PollKind.Undisclosed,
),
- CreatePollState(
+ aCreatePollState(
canCreate = true,
canAddAnswer = false,
question = "Should there be more than 20 answers?",
@@ -98,7 +99,7 @@ class CreatePollStateProvider : PreviewParameterProvider {
showConfirmation = false,
pollKind = PollKind.Undisclosed,
),
- CreatePollState(
+ aCreatePollState(
canCreate = true,
canAddAnswer = true,
question = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." +
@@ -122,3 +123,22 @@ class CreatePollStateProvider : PreviewParameterProvider {
)
)
}
+
+private fun aCreatePollState(
+ canCreate: Boolean,
+ canAddAnswer: Boolean,
+ question: String,
+ answers: PersistentList,
+ showConfirmation: Boolean,
+ pollKind: PollKind
+): CreatePollState {
+ return CreatePollState(
+ canCreate = canCreate,
+ canAddAnswer = canAddAnswer,
+ question = question,
+ answers = answers,
+ showConfirmation = showConfirmation,
+ pollKind = pollKind,
+ eventSink = {}
+ )
+}
diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt
index 0355b34375..b79054ba84 100644
--- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt
+++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt
@@ -44,11 +44,10 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.poll.impl.R
-import io.element.android.libraries.designsystem.VectorIcons
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.list.ListItemContent
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
@@ -61,6 +60,7 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -165,7 +165,7 @@ fun CreatePollView(
},
trailingContent = ListItemContent.Custom {
Icon(
- resourceId = VectorIcons.Delete,
+ resourceId = CommonDrawables.ic_compound_delete,
contentDescription = null,
modifier = Modifier.clickable(answer.canDelete) {
state.eventSink(CreatePollEvents.RemoveAnswer(index))
@@ -210,7 +210,7 @@ fun CreatePollView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun CreatePollViewPreview(
@PreviewParameter(CreatePollStateProvider::class) state: CreatePollState
diff --git a/features/poll/impl/src/main/res/values-cs/translations.xml b/features/poll/impl/src/main/res/values-cs/translations.xml
index d199df993f..fa722e9616 100644
--- a/features/poll/impl/src/main/res/values-cs/translations.xml
+++ b/features/poll/impl/src/main/res/values-cs/translations.xml
@@ -4,6 +4,8 @@
"Zobrazit výsledky až po skončení hlasování"
"Anonymní hlasování"
"Volba %1$d"
+ "Opravdu chcete zrušit toto hlasování?"
+ "Zrušit hlasování"
"Otázka nebo téma"
"Čeho se hlasování týká?"
"Vytvořit hlasování"
diff --git a/features/poll/impl/src/main/res/values-ru/translations.xml b/features/poll/impl/src/main/res/values-ru/translations.xml
index f1a518b0e7..c471ff9dd2 100644
--- a/features/poll/impl/src/main/res/values-ru/translations.xml
+++ b/features/poll/impl/src/main/res/values-ru/translations.xml
@@ -4,6 +4,8 @@
"Показывать результаты только после окончания опроса"
"Анонимный опрос"
"Настройка %1$d"
+ "Вы действительно хотите отменить этот опрос?"
+ "Отменить опрос"
"Вопрос или тема"
"Тема опроса?"
"Создать опрос"
diff --git a/features/poll/impl/src/main/res/values-zh-rTW/translations.xml b/features/poll/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..298224c8ad
--- /dev/null
+++ b/features/poll/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,12 @@
+
+
+ "新增選項"
+ "只在投票結束後顯示結果"
+ "隱藏票數"
+ "選項 %1$d"
+ "您確定要捨棄這項投票嗎?"
+ "捨棄投票"
+ "問題或主題"
+ "投什麼?"
+ "建立投票"
+
diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts
index b28a068708..a227d24b8b 100644
--- a/features/preferences/impl/build.gradle.kts
+++ b/features/preferences/impl/build.gradle.kts
@@ -47,6 +47,7 @@ dependencies {
implementation(projects.libraries.matrixui)
implementation(projects.libraries.mediapickers.api)
implementation(projects.libraries.mediaupload.api)
+ implementation(projects.libraries.permissions.api)
implementation(projects.features.rageshake.api)
implementation(projects.features.analytics.api)
implementation(projects.features.ftue.api)
@@ -54,7 +55,6 @@ dependencies {
implementation(projects.services.analytics.api)
implementation(projects.services.toolbox.api)
implementation(libs.datetime)
- implementation(libs.accompanist.placeholder)
implementation(libs.coil.compose)
implementation(libs.androidx.browser)
implementation(libs.androidx.datastore.preferences)
@@ -71,6 +71,7 @@ dependencies {
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.libraries.mediapickers.test)
testImplementation(projects.libraries.mediaupload.test)
+ testImplementation(projects.libraries.permissions.test)
testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.pushstore.test)
testImplementation(projects.features.rageshake.test)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt
index 1665147c31..a16eb72281 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt
@@ -21,8 +21,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
-import io.element.android.libraries.designsystem.components.preferences.PreferenceView
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.components.preferences.PreferencePage
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.ui.strings.CommonStrings
@@ -33,7 +33,7 @@ fun AboutView(
onBackPressed: () -> Unit,
modifier: Modifier = Modifier,
) {
- PreferenceView(
+ PreferencePage(
modifier = modifier,
onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_about)
@@ -47,7 +47,7 @@ fun AboutView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AboutViewPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) = ElementPreview {
AboutView(
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt
index cff1454594..3bbc9a1b71 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt
@@ -20,10 +20,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
+import io.element.android.features.preferences.impl.R
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
-import io.element.android.libraries.designsystem.components.preferences.PreferenceView
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
@@ -32,30 +33,27 @@ fun AdvancedSettingsView(
onBackPressed: () -> Unit,
modifier: Modifier = Modifier,
) {
- PreferenceView(
+ PreferencePage(
modifier = modifier,
onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_advanced_settings)
) {
PreferenceSwitch(
title = stringResource(id = CommonStrings.common_rich_text_editor),
- // TODO i18n
- subtitle = "Disable the rich text editor to type Markdown manually",
+ subtitle = stringResource(id = R.string.screen_advanced_settings_rich_text_editor_description),
isChecked = state.isRichTextEditorEnabled,
onCheckedChange = { state.eventSink(AdvancedSettingsEvents.SetRichTextEditorEnabled(it)) },
)
PreferenceSwitch(
- // TODO i18n
- title = "Developer mode",
- // TODO i18n
- subtitle = "The developer mode activates hidden features. For developers only!",
+ title = stringResource(id = R.string.screen_advanced_settings_developer_mode),
+ subtitle = stringResource(id = R.string.screen_advanced_settings_developer_mode_description),
isChecked = state.isDeveloperModeEnabled,
onCheckedChange = { state.eventSink(AdvancedSettingsEvents.SetDeveloperModeEnabled(it)) },
)
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AdvancedSettingsViewPreview(@PreviewParameter(AdvancedSettingsStateProvider::class) state: AdvancedSettingsState) =
ElementPreview {
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt
index 054feac79d..751d513385 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt
@@ -21,8 +21,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesView
-import io.element.android.libraries.designsystem.components.preferences.PreferenceView
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.components.preferences.PreferencePage
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.ui.strings.CommonStrings
@@ -32,7 +32,7 @@ fun AnalyticsSettingsView(
onBackPressed: () -> Unit,
modifier: Modifier = Modifier,
) {
- PreferenceView(
+ PreferencePage(
modifier = modifier,
onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_analytics)
@@ -43,7 +43,7 @@ fun AnalyticsSettingsView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AnalyticsSettingsViewPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) = ElementPreview {
AnalyticsSettingsView(
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt
index 02492ea9b9..c52106cd82 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt
@@ -23,8 +23,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesView
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
-import io.element.android.libraries.designsystem.components.preferences.PreferenceView
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.components.preferences.PreferencePage
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.featureflag.ui.FeatureListView
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
@@ -38,7 +38,7 @@ fun DeveloperSettingsView(
onBackPressed: () -> Unit,
modifier: Modifier = Modifier,
) {
- PreferenceView(
+ PreferencePage(
modifier = modifier,
onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_developer_options)
@@ -85,7 +85,7 @@ fun DeveloperSettingsView(
}
@Composable
-fun FeatureListContent(
+private fun FeatureListContent(
state: DeveloperSettingsState,
modifier: Modifier = Modifier
) {
@@ -100,7 +100,7 @@ fun FeatureListContent(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun DeveloperSettingsViewPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) = ElementPreview {
DeveloperSettingsView(
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt
index 6b278f3c87..15fd6e46b7 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt
@@ -28,11 +28,6 @@ import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowDropDown
-import androidx.compose.material.icons.filled.ArrowDropUp
-import androidx.compose.material.icons.filled.MoreVert
-import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -40,12 +35,13 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.rotate
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.list.ListItemContent
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
@@ -56,6 +52,7 @@ import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.tracing.LogLevel
import io.element.android.libraries.matrix.api.tracing.Target
import io.element.android.libraries.theme.ElementTheme
@@ -93,7 +90,7 @@ fun ConfigureTracingView(
onClick = { showMenu = !showMenu }
) {
Icon(
- imageVector = Icons.Default.MoreVert,
+ resourceId = CommonDrawables.ic_compound_overflow_vertical,
tint = ElementTheme.materialColors.secondary,
contentDescription = null,
)
@@ -110,7 +107,7 @@ fun ConfigureTracingView(
text = { Text("Reset to default") },
leadingIcon = {
Icon(
- Icons.Outlined.Delete,
+ resourceId = CommonDrawables.ic_compound_delete,
tint = ElementTheme.materialColors.secondary,
contentDescription = null,
)
@@ -142,7 +139,7 @@ fun ConfigureTracingView(
}
@Composable
-fun CrateListContent(
+private fun CrateListContent(
state: ConfigureTracingState,
modifier: Modifier = Modifier
) {
@@ -181,7 +178,7 @@ private fun TargetAndLogLevelListView(
}
@Composable
-fun TargetAndLogLevelView(
+private fun TargetAndLogLevelView(
target: Target,
logLevel: LogLevel,
onLogLevelChange: (LogLevel) -> Unit,
@@ -200,7 +197,7 @@ fun TargetAndLogLevelView(
}
@Composable
-fun LogLevelDropdownMenu(
+private fun LogLevelDropdownMenu(
logLevel: LogLevel,
onLogLevelChange: (LogLevel) -> Unit,
modifier: Modifier = Modifier,
@@ -212,11 +209,11 @@ fun LogLevelDropdownMenu(
text = { Text(text = logLevel.filter) },
onClick = { expanded = !expanded },
trailingIcon = {
- if (expanded) {
- Icon(Icons.Default.ArrowDropUp, contentDescription = null)
- } else {
- Icon(Icons.Default.ArrowDropDown, contentDescription = null)
- }
+ Icon(
+ modifier = Modifier.rotate(if (expanded) 180f else 0f),
+ resourceId = CommonDrawables.ic_compound_chevron_down,
+ contentDescription = null,
+ )
},
)
DropdownMenu(
@@ -238,7 +235,7 @@ fun LogLevelDropdownMenu(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ConfigureTracingViewPreview(
@PreviewParameter(ConfigureTracingStateProvider::class) state: ConfigureTracingState
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt
index c7bcbb573c..c6680ed819 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt
@@ -23,8 +23,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.NotificationsOff
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -42,13 +40,14 @@ import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
-import io.element.android.libraries.designsystem.components.preferences.PreferenceView
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.components.preferences.PreferencePage
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.theme.ElementTheme
@@ -70,7 +69,7 @@ fun NotificationSettingsView(
else -> Unit
}
}
- PreferenceView(
+ PreferencePage(
modifier = modifier,
onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.screen_notification_settings_title)
@@ -82,7 +81,7 @@ fun NotificationSettingsView(
onContinueClicked = { state.eventSink(NotificationSettingsEvents.FixConfigurationMismatch) },
onDismissError = { state.eventSink(NotificationSettingsEvents.ClearConfigurationMismatchError) },
)
- NotificationSettingsState.MatrixSettings.Uninitialized -> return@PreferenceView
+ NotificationSettingsState.MatrixSettings.Uninitialized -> return@PreferencePage
is NotificationSettingsState.MatrixSettings.Valid -> NotificationSettingsContentView(
matrixSettings = state.matrixSettings,
systemSettings = state.appSettings,
@@ -123,7 +122,7 @@ private fun NotificationSettingsContentView(
val context = LocalContext.current
if (systemSettings.appNotificationsEnabled && !systemSettings.systemNotificationsEnabled) {
PreferenceText(
- icon = Icons.Filled.NotificationsOff,
+ iconResourceId = CommonDrawables.ic_compound_notifications_solid_off,
title = stringResource(id = CommonStrings.screen_notification_settings_system_notifications_turned_off),
subtitle = stringResource(
id = CommonStrings.screen_notification_settings_system_notifications_action_required,
@@ -240,7 +239,7 @@ private fun InvalidNotificationSettingsView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun NotificationSettingsViewPreview(@PreviewParameter(NotificationSettingsStateProvider::class) state: NotificationSettingsState) = ElementPreview {
NotificationSettingsView(
@@ -250,7 +249,7 @@ internal fun NotificationSettingsViewPreview(@PreviewParameter(NotificationSetti
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun InvalidNotificationSettingsViewPreview() = ElementPreview {
InvalidNotificationSettingsView(
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt
index ce5db4f96e..c7d2ac507d 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt
@@ -27,7 +27,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.RadioButton
import io.element.android.libraries.designsystem.theme.components.Text
@@ -79,7 +79,7 @@ fun DefaultNotificationSettingOption(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun DefaultNotificationSettingOptionPreview() = ElementPreview {
Column {
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt
index cda19e15bb..bf82de7251 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt
@@ -30,12 +30,12 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
-import io.element.android.libraries.designsystem.components.preferences.PreferenceView
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.core.RoomId
+import io.element.android.libraries.designsystem.components.preferences.PreferencePage
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.ui.strings.CommonStrings
@@ -56,7 +56,7 @@ fun EditDefaultNotificationSettingView(
} else {
CommonStrings.screen_notification_settings_group_chats
}
- PreferenceView(
+ PreferencePage(
modifier = modifier,
onBackPressed = onBackPressed,
title = stringResource(id = title)
@@ -133,8 +133,7 @@ fun EditDefaultNotificationSettingView(
}
}
}
-
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun EditDefaultNotificationSettingViewPreview(
@PreviewParameter(EditDefaultNotificationSettingsStateProvider::class) state: EditDefaultNotificationSettingState
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
index dac7ae3204..200785e03d 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
@@ -27,8 +27,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.features.logout.api.LogoutPreferencePresenter
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.meta.BuildType
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
-import io.element.android.libraries.designsystem.utils.collectSnackbarMessageAsState
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.MatrixClient
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt
index accede5d6d..f61ea5890d 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt
@@ -17,7 +17,7 @@
package io.element.android.features.preferences.impl.root
import io.element.android.features.logout.api.LogoutPreferenceState
-import io.element.android.libraries.designsystem.utils.SnackbarMessage
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.user.MatrixUser
data class PreferencesRootState(
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt
index 860c738687..467ca4c6d6 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt
@@ -17,7 +17,7 @@
package io.element.android.features.preferences.impl.root
import io.element.android.features.logout.api.aLogoutPreferenceState
-import io.element.android.libraries.designsystem.utils.SnackbarMessage
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.ui.strings.CommonStrings
fun aPreferencesRootState() = PreferencesRootState(
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt
index beda70059f..2999ddea94 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt
@@ -20,13 +20,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.BugReport
-import androidx.compose.material.icons.outlined.DeveloperMode
-import androidx.compose.material.icons.outlined.Help
import androidx.compose.material.icons.outlined.InsertChart
-import androidx.compose.material.icons.outlined.Notifications
-import androidx.compose.material.icons.outlined.OpenInNew
-import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.VerifiedUser
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -37,14 +31,15 @@ import androidx.compose.ui.unit.dp
import io.element.android.features.logout.api.LogoutPreferenceView
import io.element.android.features.preferences.impl.user.UserPreferences
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
-import io.element.android.libraries.designsystem.components.preferences.PreferenceView
+import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.preview.LargeHeightPreview
+import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.Text
-import io.element.android.libraries.designsystem.utils.SnackbarHost
-import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
+import io.element.android.libraries.designsystem.utils.CommonDrawables
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
+import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.MatrixUserProvider
import io.element.android.libraries.theme.ElementTheme
@@ -69,7 +64,7 @@ fun PreferencesRootView(
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
// Include pref from other modules
- PreferenceView(
+ PreferencePage(
modifier = modifier,
onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_settings),
@@ -92,7 +87,7 @@ fun PreferencesRootView(
if (state.accountManagementUrl != null) {
PreferenceText(
title = stringResource(id = CommonStrings.action_manage_account),
- icon = Icons.Outlined.OpenInNew,
+ iconResourceId = CommonDrawables.ic_compound_pop_out,
onClick = { onManageAccountClicked(state.accountManagementUrl) },
)
HorizontalDivider()
@@ -107,32 +102,32 @@ fun PreferencesRootView(
if (state.showNotificationSettings) {
PreferenceText(
title = stringResource(id = CommonStrings.screen_notification_settings_title),
- icon = Icons.Outlined.Notifications,
+ iconResourceId = CommonDrawables.ic_compound_notifications,
onClick = onOpenNotificationSettings,
)
}
PreferenceText(
title = stringResource(id = CommonStrings.action_report_bug),
- icon = Icons.Outlined.BugReport,
+ iconResourceId = CommonDrawables.ic_compound_chat_problem,
onClick = onOpenRageShake
)
PreferenceText(
title = stringResource(id = CommonStrings.common_about),
- icon = Icons.Outlined.Help,
+ iconResourceId = CommonDrawables.ic_compound_info,
onClick = onOpenAbout,
)
HorizontalDivider()
if (state.devicesManagementUrl != null) {
PreferenceText(
title = stringResource(id = CommonStrings.action_manage_devices),
- icon = Icons.Outlined.OpenInNew,
+ iconResourceId = CommonDrawables.ic_compound_pop_out,
onClick = { onManageAccountClicked(state.devicesManagementUrl) },
)
HorizontalDivider()
}
PreferenceText(
title = stringResource(id = CommonStrings.common_advanced_settings),
- icon = Icons.Outlined.Settings,
+ iconResourceId = CommonDrawables.ic_compound_settings,
onClick = onOpenAdvancedSettings,
)
if (state.showDeveloperSettings) {
@@ -156,20 +151,20 @@ fun PreferencesRootView(
}
@Composable
-fun DeveloperPreferencesView(onOpenDeveloperSettings: () -> Unit) {
+private fun DeveloperPreferencesView(onOpenDeveloperSettings: () -> Unit) {
PreferenceText(
title = stringResource(id = CommonStrings.common_developer_options),
- icon = Icons.Outlined.DeveloperMode,
+ iconResourceId = CommonDrawables.ic_developer_mode,
onClick = onOpenDeveloperSettings
)
}
-@LargeHeightPreview
+@PreviewWithLargeHeight
@Composable
internal fun PreferencesRootViewLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
ElementPreviewLight { ContentToPreview(matrixUser) }
-@LargeHeightPreview
+@PreviewWithLargeHeight
@Composable
internal fun PreferencesRootViewDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
ElementPreviewDark { ContentToPreview(matrixUser) }
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/UserPreferences.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/UserPreferences.kt
index a56dc36f9a..6020b83231 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/UserPreferences.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/UserPreferences.kt
@@ -19,7 +19,7 @@ package io.element.android.features.preferences.impl.user
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.PreviewParameter
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.MatrixUserHeader
@@ -36,7 +36,7 @@ fun UserPreferences(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun UserPreferencesPreview(@PreviewParameter(MatrixUserWithNullProvider::class) matrixUser: MatrixUser?) = ElementPreview {
UserPreferences(matrixUser)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt
index 793c4840d7..be0a5f4cca 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt
@@ -18,6 +18,7 @@ package io.element.android.features.preferences.impl.user.editprofile
import android.net.Uri
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
@@ -39,6 +40,8 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.media.AvatarAction
import io.element.android.libraries.mediapickers.api.PickerProvider
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
+import io.element.android.libraries.permissions.api.PermissionsEvents
+import io.element.android.libraries.permissions.api.PermissionsPresenter
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -49,8 +52,12 @@ class EditUserProfilePresenter @AssistedInject constructor(
private val matrixClient: MatrixClient,
private val mediaPickerProvider: PickerProvider,
private val mediaPreProcessor: MediaPreProcessor,
+ permissionsPresenterFactory: PermissionsPresenter.Factory,
) : Presenter {
+ private val cameraPermissionPresenter: PermissionsPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA)
+ private var pendingPermissionRequest = false
+
@AssistedFactory
interface Factory {
fun create(matrixUser: MatrixUser): EditUserProfilePresenter
@@ -58,6 +65,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
@Composable
override fun present(): EditUserProfileState {
+ val cameraPermissionState = cameraPermissionPresenter.present()
var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl?.let { Uri.parse(it) }) }
var userDisplayName by rememberSaveable { mutableStateOf(matrixUser.displayName) }
val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(
@@ -77,6 +85,13 @@ class EditUserProfilePresenter @AssistedInject constructor(
}
}
+ LaunchedEffect(cameraPermissionState.permissionGranted) {
+ if (cameraPermissionState.permissionGranted && pendingPermissionRequest) {
+ pendingPermissionRequest = false
+ cameraPhotoPicker.launch()
+ }
+ }
+
val saveAction: MutableState> = remember { mutableStateOf(Async.Uninitialized) }
val localCoroutineScope = rememberCoroutineScope()
fun handleEvents(event: EditUserProfileEvents) {
@@ -85,7 +100,12 @@ class EditUserProfilePresenter @AssistedInject constructor(
is EditUserProfileEvents.HandleAvatarAction -> {
when (event.action) {
AvatarAction.ChoosePhoto -> galleryImagePicker.launch()
- AvatarAction.TakePhoto -> cameraPhotoPicker.launch()
+ AvatarAction.TakePhoto -> if (cameraPermissionState.permissionGranted) {
+ cameraPhotoPicker.launch()
+ } else {
+ pendingPermissionRequest = true
+ cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions)
+ }
AvatarAction.Remove -> userAvatarUri = null
}
}
@@ -108,6 +128,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
avatarActions = avatarActions,
saveButtonEnabled = canSave && saveAction.value !is Async.Loading,
saveAction = saveAction.value,
+ cameraPermissionState = cameraPermissionState,
eventSink = { handleEvents(it) },
)
}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt
index 87668e6f45..9561c8609b 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileState.kt
@@ -20,6 +20,7 @@ import android.net.Uri
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.ui.media.AvatarAction
+import io.element.android.libraries.permissions.api.PermissionsState
import kotlinx.collections.immutable.ImmutableList
data class EditUserProfileState(
@@ -29,5 +30,6 @@ data class EditUserProfileState(
val avatarActions: ImmutableList,
val saveButtonEnabled: Boolean,
val saveAction: Async,
+ val cameraPermissionState: PermissionsState,
val eventSink: (EditUserProfileEvents) -> Unit
)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt
index 5e4ccb95cb..c4a8431f4f 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt
@@ -19,6 +19,7 @@ package io.element.android.features.preferences.impl.user.editprofile
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.matrix.api.core.UserId
+import io.element.android.libraries.permissions.api.aPermissionsState
import kotlinx.collections.immutable.persistentListOf
open class EditUserProfileStateProvider : PreviewParameterProvider {
@@ -36,5 +37,6 @@ fun aEditUserProfileState() = EditUserProfileState(
avatarActions = persistentListOf(),
saveAction = Async.Uninitialized,
saveButtonEnabled = true,
+ cameraPermissionState = aPermissionsState(showDialog = false),
eventSink = {}
)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt
index 5b921c047c..760e08261d 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt
@@ -49,7 +49,7 @@ import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Scaffold
@@ -58,6 +58,7 @@ import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet
import io.element.android.libraries.matrix.ui.components.EditableAvatarView
+import io.element.android.libraries.permissions.api.PermissionsView
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.launch
@@ -168,6 +169,9 @@ fun EditUserProfileView(
else -> Unit
}
}
+ PermissionsView(
+ state = state.cameraPermissionState,
+ )
}
private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier =
@@ -177,7 +181,7 @@ private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier =
})
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun EditUserProfileViewPreview(@PreviewParameter(EditUserProfileStateProvider::class) state: EditUserProfileState) =
ElementPreview {
diff --git a/features/preferences/impl/src/main/res/values-cs/translations.xml b/features/preferences/impl/src/main/res/values-cs/translations.xml
new file mode 100644
index 0000000000..b26a3d9113
--- /dev/null
+++ b/features/preferences/impl/src/main/res/values-cs/translations.xml
@@ -0,0 +1,12 @@
+
+
+ "Vývojářský režim"
+ "Povolením získáte přístup k funkcím a funkcím pro vývojáře."
+ "Vypněte editor formátovaného textu pro ruční zadání Markdown."
+ "Zobrazované jméno"
+ "Vaše zobrazované jméno"
+ "Došlo k neznámé chybě a informace nelze změnit."
+ "Nelze aktualizovat profil"
+ "Upravit profil"
+ "Aktualizace profilu…"
+
diff --git a/features/preferences/impl/src/main/res/values-de/translations.xml b/features/preferences/impl/src/main/res/values-de/translations.xml
index 6e26c5ddf1..3992b93ab7 100644
--- a/features/preferences/impl/src/main/res/values-de/translations.xml
+++ b/features/preferences/impl/src/main/res/values-de/translations.xml
@@ -1,5 +1,7 @@
+ "Entwickler-Modus"
+ "Deaktiviere den Rich-Text-Editor, um Markdown manuell einzugeben."
"Anzeigename"
"Dein Anzeigename"
"Ein unbekannter Fehler ist aufgetreten und die Informationen konnten nicht geändert werden."
diff --git a/features/preferences/impl/src/main/res/values-fr/translations.xml b/features/preferences/impl/src/main/res/values-fr/translations.xml
index 392b28c785..3466ecd630 100644
--- a/features/preferences/impl/src/main/res/values-fr/translations.xml
+++ b/features/preferences/impl/src/main/res/values-fr/translations.xml
@@ -1,5 +1,8 @@
+ "Mode développeur"
+ "Activer pour pouvoir accéder aux fonctionnalités destinées aux développeurs."
+ "Désactivez l’éditeur de texte enrichi pour saisir manuellement du Markdown."
"Pseudonyme"
"Votre pseudonyme"
"Une erreur inconnue s’est produite et les informations n’ont pas pu être modifiées."
diff --git a/features/preferences/impl/src/main/res/values-ru/translations.xml b/features/preferences/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..efac864365
--- /dev/null
+++ b/features/preferences/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,12 @@
+
+
+ "Режим разработчика"
+ "Предоставьте разработчикам доступ к функциям и функциональным возможностям."
+ "Отключить редактор форматированного текста и включить Markdown."
+ "Отображаемое имя"
+ "Ваше отображаемое имя"
+ "Произошла неизвестная ошибка, изменить информацию не удалось."
+ "Невозможно обновить профиль"
+ "Редактировать профиль"
+ "Обновление профиля…"
+
diff --git a/features/preferences/impl/src/main/res/values-sk/translations.xml b/features/preferences/impl/src/main/res/values-sk/translations.xml
index 46631c4eb3..392999558e 100644
--- a/features/preferences/impl/src/main/res/values-sk/translations.xml
+++ b/features/preferences/impl/src/main/res/values-sk/translations.xml
@@ -1,5 +1,8 @@
+ "Vývojársky režim"
+ "Umožniť prístup k možnostiam a funkciám pre vývojárov."
+ "Vypnite rozšírený textový editor na ručné písanie Markdown."
"Zobrazované meno"
"Vaše zobrazované meno"
"Vyskytla sa neznáma chyba a informácie nebolo možné zmeniť."
diff --git a/features/preferences/impl/src/main/res/values-zh-rTW/translations.xml b/features/preferences/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..046e820a51
--- /dev/null
+++ b/features/preferences/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,9 @@
+
+
+ "開發者模式"
+ "顯示名稱"
+ "您的顯示名稱"
+ "無法更新個人檔案"
+ "編輯個人檔案"
+ "正在更新個人檔案…"
+
diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml
index f01ae2b5e1..b94db7a565 100644
--- a/features/preferences/impl/src/main/res/values/localazy.xml
+++ b/features/preferences/impl/src/main/res/values/localazy.xml
@@ -1,5 +1,8 @@
+ "Developer mode"
+ "Enable to have access to features and functionality for developers."
+ "Disable the rich text editor to type Markdown manually."
"Display name"
"Your display name"
"An unknown error was encountered and the information couldn\'t be changed."
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt
index 70d15c7a71..cc2c0a7035 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt
@@ -32,7 +32,7 @@ import kotlin.time.Duration.Companion.milliseconds
class NotificationSettingsPresenterTests {
@Test
fun `present - ensures initial state is correct`() = runTest {
- val presenter = aNotificationPresenter()
+ val presenter = createNotificationSettingsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -58,7 +58,7 @@ class NotificationSettingsPresenterTests {
@Test
fun `present - default group notification mode changed`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService()
- val presenter = aNotificationPresenter(notificationSettingsService)
+ val presenter = createNotificationSettingsPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -77,7 +77,7 @@ class NotificationSettingsPresenterTests {
@Test
fun `present - notification settings mismatched`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService()
- val presenter = aNotificationPresenter(notificationSettingsService)
+ val presenter = createNotificationSettingsPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -108,7 +108,7 @@ class NotificationSettingsPresenterTests {
initialEncryptedOneToOneDefaultMode = RoomNotificationMode.ALL_MESSAGES,
initialOneToOneDefaultMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
)
- val presenter = aNotificationPresenter(notificationSettingsService)
+ val presenter = createNotificationSettingsPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -125,7 +125,7 @@ class NotificationSettingsPresenterTests {
@Test
fun `present - set notifications enabled`() = runTest {
- val presenter = aNotificationPresenter()
+ val presenter = createNotificationSettingsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -145,7 +145,7 @@ class NotificationSettingsPresenterTests {
@Test
fun `present - set call notifications enabled`() = runTest {
- val presenter = aNotificationPresenter()
+ val presenter = createNotificationSettingsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -167,7 +167,7 @@ class NotificationSettingsPresenterTests {
@Test
fun `present - set atRoom notifications enabled`() = runTest {
- val presenter = aNotificationPresenter()
+ val presenter = createNotificationSettingsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -187,7 +187,7 @@ class NotificationSettingsPresenterTests {
}
}
- private fun aNotificationPresenter(
+ private fun createNotificationSettingsPresenter(
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService()
) : NotificationSettingsPresenter {
val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService)
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt
index 2237f717bd..e9eaf5ef13 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt
@@ -23,7 +23,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.logout.impl.DefaultLogoutPreferencePresenter
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.core.meta.BuildType
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt
index beece60c9a..8746670e03 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenterTest.kt
@@ -32,6 +32,9 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction
import io.element.android.libraries.mediapickers.test.FakePickerProvider
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
+import io.element.android.libraries.permissions.api.PermissionsPresenter
+import io.element.android.libraries.permissions.test.FakePermissionsPresenter
+import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.consumeItemsUntilPredicate
import io.mockk.every
@@ -78,12 +81,14 @@ class EditUserProfilePresenterTest {
private fun createEditUserProfilePresenter(
matrixClient: MatrixClient = FakeMatrixClient(),
matrixUser: MatrixUser = aMatrixUser(),
+ permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(),
): EditUserProfilePresenter {
return EditUserProfilePresenter(
matrixClient = matrixClient,
matrixUser = matrixUser,
mediaPickerProvider = fakePickerProvider,
mediaPreProcessor = fakeMediaPreProcessor,
+ permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter),
)
}
@@ -157,16 +162,30 @@ class EditUserProfilePresenterTest {
fun `present - obtains avatar uris from camera`() = runTest {
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
fakePickerProvider.givenResult(anotherAvatarUri)
- val presenter = createEditUserProfilePresenter(matrixUser = user)
+ val fakePermissionsPresenter = FakePermissionsPresenter()
+ val presenter = createEditUserProfilePresenter(
+ matrixUser = user,
+ permissionsPresenter = fakePermissionsPresenter,
+ )
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri)
+ assertThat(initialState.cameraPermissionState.permissionGranted).isFalse()
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto))
- awaitItem().apply {
- assertThat(userAvatarUrl).isEqualTo(anotherAvatarUri)
- }
+ val stateWithAskingPermission = awaitItem()
+ assertThat(stateWithAskingPermission.cameraPermissionState.showDialog).isTrue()
+ fakePermissionsPresenter.setPermissionGranted()
+ val stateWithPermission = awaitItem()
+ assertThat(stateWithPermission.cameraPermissionState.permissionGranted).isTrue()
+ val stateWithNewAvatar = awaitItem()
+ assertThat(stateWithNewAvatar.userAvatarUrl).isEqualTo(anotherAvatarUri)
+ // Do it again, no permission is requested
+ fakePickerProvider.givenResult(userAvatarUri)
+ stateWithNewAvatar.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto))
+ val stateWithNewAvatar2 = awaitItem()
+ assertThat(stateWithNewAvatar2.userAvatarUrl).isEqualTo(userAvatarUri)
}
}
diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt
index 99dfcd6d19..3d49d637e5 100644
--- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt
+++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt
@@ -20,7 +20,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import io.element.android.features.rageshake.api.R
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.ui.strings.CommonStrings
@@ -49,7 +49,7 @@ fun CrashDetectionView(
}
@Composable
-fun CrashDetectionContent(
+private fun CrashDetectionContent(
onNoClicked: () -> Unit = { },
onYesClicked: () -> Unit = { },
onDismiss: () -> Unit = { },
@@ -65,7 +65,7 @@ fun CrashDetectionContent(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun CrashDetectionViewPreview() = ElementPreview {
CrashDetectionView(
diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt
index d34ebde40b..88fe5dceb2 100644
--- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt
+++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt
@@ -27,7 +27,7 @@ import io.element.android.features.rageshake.api.screenshot.ImageResult
import io.element.android.features.rageshake.api.screenshot.screenshot
import io.element.android.libraries.androidutils.hardware.vibrate
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
@@ -81,7 +81,7 @@ private fun TakeScreenshot(
}
@Composable
-fun RageshakeDialogContent(
+private fun RageshakeDialogContent(
onNoClicked: () -> Unit = { },
onDisableClicked: () -> Unit = { },
onYesClicked: () -> Unit = { },
@@ -99,7 +99,7 @@ fun RageshakeDialogContent(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RageshakeDialogContentPreview() = ElementPreview {
RageshakeDialogContent()
diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt
index 8f4cb47404..b9b8566d4a 100644
--- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt
+++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt
@@ -25,7 +25,7 @@ import io.element.android.libraries.designsystem.components.preferences.Preferen
import io.element.android.libraries.designsystem.components.preferences.PreferenceSlide
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.ui.strings.CommonStrings
@@ -65,7 +65,7 @@ fun RageshakePreferencesView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RageshakePreferencesViewPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) = ElementPreview {
RageshakePreferencesView(state)
diff --git a/features/rageshake/api/src/main/res/values-cs/translations.xml b/features/rageshake/api/src/main/res/values-cs/translations.xml
index 20d6f31ed0..13769d6d59 100644
--- a/features/rageshake/api/src/main/res/values-cs/translations.xml
+++ b/features/rageshake/api/src/main/res/values-cs/translations.xml
@@ -1,5 +1,4 @@
"%1$s havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?"
- "Zdá se, že jste frustrovaně třásli telefonem. Chcete otevřít obrazovku pro nahlášení chyby?"
diff --git a/features/rageshake/api/src/main/res/values-de/translations.xml b/features/rageshake/api/src/main/res/values-de/translations.xml
index 468ac44491..6cfb4608dc 100644
--- a/features/rageshake/api/src/main/res/values-de/translations.xml
+++ b/features/rageshake/api/src/main/res/values-de/translations.xml
@@ -1,5 +1,4 @@
"%1$s ist bei der letzten Nutzung abgestürzt. Möchtest du einen Absturzbericht mit uns teilen?"
- "Du scheinst das Telefon aus Frustration zu schütteln. Möchtest du den Bildschirm für den Fehlerbericht öffnen?"
diff --git a/features/rageshake/api/src/main/res/values-fr/translations.xml b/features/rageshake/api/src/main/res/values-fr/translations.xml
index 5c3571e443..2031490c1f 100644
--- a/features/rageshake/api/src/main/res/values-fr/translations.xml
+++ b/features/rageshake/api/src/main/res/values-fr/translations.xml
@@ -1,5 +1,4 @@
"%1$s s’est arrêté la dernière fois qu’il a été utilisé. Souhaitez-vous partager un rapport d’incident avec nous ?"
- "Vous semblez secouer le téléphone avec frustration. Voulez-vous ouvrir le formulaire de rapport de problème ?"
diff --git a/features/rageshake/api/src/main/res/values-ru/translations.xml b/features/rageshake/api/src/main/res/values-ru/translations.xml
index 6cb17a3401..d45ecb192d 100644
--- a/features/rageshake/api/src/main/res/values-ru/translations.xml
+++ b/features/rageshake/api/src/main/res/values-ru/translations.xml
@@ -1,5 +1,5 @@
"При последнем использовании %1$s произошел сбой. Хотите поделиться отчетом о сбое?"
- "Кажется, вы трясли телефон. Хотите открыть экран отчета об ошибке?"
+ "Похоже, что вы трясете телефон. Хотите открыть экран сообщения об ошибке?"
diff --git a/features/rageshake/api/src/main/res/values-sk/translations.xml b/features/rageshake/api/src/main/res/values-sk/translations.xml
index 753ead0b97..c1d55eab34 100644
--- a/features/rageshake/api/src/main/res/values-sk/translations.xml
+++ b/features/rageshake/api/src/main/res/values-sk/translations.xml
@@ -1,5 +1,5 @@
"%1$s zlyhal pri poslednom použití. Chcete zdieľať správu o páde s našim tímom?"
- "Zdá sa, že zúrivo trasiete telefónom. Chcete otvoriť obrazovku s nahlásením chýb?"
+ "Zdá sa, že zúrivo trasiete telefónom. Chcete otvoriť obrazovku s hlásením chýb?"
diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt
index 5fd95e79bd..9258909201 100644
--- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt
+++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenter.kt
@@ -17,9 +17,11 @@
package io.element.android.features.rageshake.impl.bugreport
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableFloatState
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
@@ -43,27 +45,27 @@ class BugReportPresenter @Inject constructor(
) : Presenter {
private class BugReporterUploadListener(
- private val sendingProgress: MutableState,
+ private val sendingProgress: MutableFloatState,
private val sendingAction: MutableState>
) : BugReporterListener {
override fun onUploadCancelled() {
- sendingProgress.value = 0f
+ sendingProgress.floatValue = 0f
sendingAction.value = Async.Uninitialized
}
override fun onUploadFailed(reason: String?) {
- sendingProgress.value = 0f
+ sendingProgress.floatValue = 0f
sendingAction.value = Async.Failure(Exception(reason))
}
override fun onProgress(progress: Int) {
- sendingProgress.value = progress.toFloat() / 100
+ sendingProgress.floatValue = progress.toFloat() / 100
sendingAction.value = Async.Loading()
}
override fun onUploadSucceed(reportUrl: String?) {
- sendingProgress.value = 0f
+ sendingProgress.floatValue = 0f
sendingAction.value = Async.Success(Unit)
}
}
@@ -80,7 +82,7 @@ class BugReportPresenter @Inject constructor(
.collectAsState(initial = "")
val sendingProgress = remember {
- mutableStateOf(0f)
+ mutableFloatStateOf(0f)
}
val sendingAction: MutableState> = remember {
mutableStateOf(Async.Uninitialized)
@@ -107,7 +109,7 @@ class BugReportPresenter @Inject constructor(
copy(sendScreenshot = event.sendScreenshot)
}
BugReportEvents.ClearError -> {
- sendingProgress.value = 0f
+ sendingProgress.floatValue = 0f
sendingAction.value = Async.Uninitialized
}
}
@@ -115,7 +117,7 @@ class BugReportPresenter @Inject constructor(
return BugReportState(
hasCrashLogs = crashInfo.isNotEmpty(),
- sendingProgress = sendingProgress.value,
+ sendingProgress = sendingProgress.floatValue,
sending = sendingAction.value,
formState = formState.value,
screenshotUri = screenshotUri.value,
diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt
index af2c8c4dcc..d886819690 100644
--- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt
+++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt
@@ -43,8 +43,8 @@ import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.components.preferences.PreferenceRow
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
-import io.element.android.libraries.designsystem.components.preferences.PreferenceView
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.components.preferences.PreferencePage
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground
import io.element.android.libraries.designsystem.theme.components.Button
@@ -72,7 +72,7 @@ fun BugReportView(
}
Box(modifier = modifier) {
- PreferenceView(
+ PreferencePage(
title = stringResource(id = CommonStrings.common_report_a_bug),
onBackPressed = onBackPressed
) {
@@ -174,7 +174,7 @@ fun BugReportView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun BugReportViewPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreview {
BugReportView(
diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/LogFormatter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/LogFormatter.kt
index e0dc1a5fbb..45c2d0a65b 100644
--- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/LogFormatter.kt
+++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/LogFormatter.kt
@@ -28,9 +28,9 @@ import java.util.logging.LogRecord
internal class LogFormatter : Formatter() {
override fun format(r: LogRecord): String {
- if (!mIsTimeZoneSet) {
+ if (!isTimeZoneSet) {
DATE_FORMAT.timeZone = TimeZone.getTimeZone("UTC")
- mIsTimeZoneSet = true
+ isTimeZoneSet = true
}
val thrown = r.thrown
@@ -59,6 +59,6 @@ internal class LogFormatter : Formatter() {
// private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
private val DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss*SSSZZZZ", Locale.US)
- private var mIsTimeZoneSet = false
+ private var isTimeZoneSet = false
}
}
diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt
index 46ccc7fc56..a693189d3c 100755
--- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt
+++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt
@@ -101,10 +101,10 @@ class DefaultBugReporter @Inject constructor(
}
// the pending bug report call
- private var mBugReportCall: Call? = null
+ private var bugReportCall: Call? = null
// boolean to cancel the bug report
- private val mIsCancelled = false
+ private val isCancelled = false
/*
val adapter = MatrixJsonParser.getMoshi()
@@ -151,7 +151,7 @@ class DefaultBugReporter @Inject constructor(
listener: BugReporterListener?
) {
// enumerate files to delete
- val mBugReportFiles: MutableList = ArrayList()
+ val bugReportFiles: MutableList = ArrayList()
try {
@@ -172,7 +172,7 @@ class DefaultBugReporter @Inject constructor(
val files = getLogFiles()
files.mapNotNullTo(gzippedFiles) { f ->
when {
- mIsCancelled -> null
+ isCancelled -> null
f.extension == "gz" -> f
else -> compressFile(f)
}
@@ -180,7 +180,7 @@ class DefaultBugReporter @Inject constructor(
files.deleteAllExceptMostRecent()
}
- if (!mIsCancelled && (withCrashLogs || withDevicesLogs)) {
+ if (!isCancelled && (withCrashLogs || withDevicesLogs)) {
val gzippedLogcat = saveLogCat(false)
if (null != gzippedLogcat) {
@@ -215,7 +215,7 @@ class DefaultBugReporter @Inject constructor(
val userId = sessionData?.userId ?: "undefined"
var olmVersion = "undefined"
- if (!mIsCancelled) {
+ if (!isCancelled) {
val text = when (reportType) {
ReportType.BUG_REPORT -> bugDescription
ReportType.SUGGESTION -> "[Suggestion] $bugDescription"
@@ -268,7 +268,7 @@ class DefaultBugReporter @Inject constructor(
}
}
- mBugReportFiles.addAll(gzippedFiles)
+ bugReportFiles.addAll(gzippedFiles)
if (gzippedFiles.isNotEmpty() && !uploadedSomeLogs) {
serverError = "Couldn't upload any logs, please retry."
@@ -336,8 +336,8 @@ class DefaultBugReporter @Inject constructor(
0
}
- if (mIsCancelled && null != mBugReportCall) {
- mBugReportCall!!.cancel()
+ if (isCancelled && null != bugReportCall) {
+ bugReportCall!!.cancel()
}
Timber.v("## onWrite() : $percentage%")
@@ -360,8 +360,8 @@ class DefaultBugReporter @Inject constructor(
// trigger the request
try {
- mBugReportCall = okHttpClient.get().newCall(request)
- response = mBugReportCall!!.execute()
+ bugReportCall = okHttpClient.get().newCall(request)
+ response = bugReportCall!!.execute()
responseCode = response.code
} catch (e: CancellationException) {
throw e
@@ -423,11 +423,11 @@ class DefaultBugReporter @Inject constructor(
}
withContext(coroutineDispatchers.main) {
- mBugReportCall = null
+ bugReportCall = null
if (null != listener) {
try {
- if (mIsCancelled) {
+ if (isCancelled) {
listener.onUploadCancelled()
} else if (null == serverError) {
listener.onUploadSucceed(reportURL)
@@ -443,7 +443,7 @@ class DefaultBugReporter @Inject constructor(
}
} finally {
// delete the generated files when the bug report process has finished
- for (file in mBugReportFiles) {
+ for (file in bugReportFiles) {
file.safeDelete()
}
}
diff --git a/features/rageshake/impl/src/main/res/values-zh-rTW/translations.xml b/features/rageshake/impl/src/main/res/values-zh-rTW/translations.xml
index 6e9eaabed3..bd4d1cb665 100644
--- a/features/rageshake/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/rageshake/impl/src/main/res/values-zh-rTW/translations.xml
@@ -1,6 +1,7 @@
"附上螢幕截圖"
+ "如果有其他問題,你可以聯絡我。"
"聯絡我"
"編輯螢幕截圖"
"傳送螢幕截圖"
diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts
index 2137b1401d..3bf38d4fa7 100644
--- a/features/roomdetails/impl/build.gradle.kts
+++ b/features/roomdetails/impl/build.gradle.kts
@@ -43,6 +43,7 @@ dependencies {
implementation(projects.libraries.mediapickers.api)
implementation(projects.libraries.mediaupload.api)
implementation(projects.libraries.featureflag.api)
+ implementation(projects.libraries.permissions.api)
api(projects.features.roomdetails.api)
api(projects.libraries.usersearch.api)
api(projects.services.apperror.api)
@@ -59,10 +60,11 @@ dependencies {
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.mediaupload.test)
testImplementation(projects.libraries.mediapickers.test)
+ testImplementation(projects.libraries.permissions.test)
testImplementation(projects.libraries.usersearch.test)
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.tests.testutils)
- testImplementation(projects.features.leaveroom.fake)
+ testImplementation(projects.features.leaveroom.test)
ksp(libs.showkase.processor)
}
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt
index c580e6d677..72ef265749 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt
@@ -17,7 +17,7 @@
package io.element.android.features.roomdetails.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
-import io.element.android.features.leaveroom.api.LeaveRoomState
+import io.element.android.features.leaveroom.api.aLeaveRoomState
import io.element.android.features.roomdetails.impl.members.details.aRoomMemberDetailsState
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.RoomMember
@@ -80,7 +80,7 @@ fun aRoomDetailsState() = RoomDetailsState(
canShowNotificationSettings = true,
roomType = RoomDetailsType.Room,
roomMemberDetailsState = null,
- leaveRoomState = LeaveRoomState(),
+ leaveRoomState = aLeaveRoomState(),
roomNotificationSettings = RoomNotificationSettings(mode = RoomNotificationMode.MUTE, isDefault = false),
eventSink = {}
)
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
index bb4496a0b2..c6b90e5cc6 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
@@ -33,12 +33,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.outlined.Add
-import androidx.compose.material.icons.outlined.Lock
-import androidx.compose.material.icons.outlined.Notifications
-import androidx.compose.material.icons.outlined.NotificationsOff
import androidx.compose.material.icons.outlined.Person
-import androidx.compose.material.icons.outlined.PersonAddAlt
-import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -48,9 +43,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@@ -69,7 +62,7 @@ import io.element.android.libraries.designsystem.components.preferences.Preferen
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.preview.LargeHeightPreview
+import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
import io.element.android.libraries.designsystem.theme.components.Icon
@@ -77,6 +70,7 @@ import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.theme.ElementTheme
@@ -153,7 +147,8 @@ fun RoomDetailsView(
if (state.canShowNotificationSettings && state.roomNotificationSettings != null) {
NotificationSection(
isDefaultMode = state.roomNotificationSettings.isDefault,
- openRoomNotificationSettings = openRoomNotificationSettings)
+ openRoomNotificationSettings = openRoomNotificationSettings
+ )
}
if (state.roomType is RoomDetailsType.Room) {
@@ -188,7 +183,7 @@ fun RoomDetailsView(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-internal fun RoomDetailsTopBar(
+private fun RoomDetailsTopBar(
goBack: () -> Unit,
onActionClicked: (RoomDetailsAction) -> Unit,
showEdit: Boolean,
@@ -225,27 +220,39 @@ internal fun RoomDetailsTopBar(
}
@Composable
-internal fun MainActionsSection(state: RoomDetailsState, onShareRoom: () -> Unit, modifier: Modifier = Modifier) {
+private fun MainActionsSection(state: RoomDetailsState, onShareRoom: () -> Unit, modifier: Modifier = Modifier) {
Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
val roomNotificationSettings = state.roomNotificationSettings
if (state.canShowNotificationSettings && roomNotificationSettings != null) {
if (roomNotificationSettings.mode == RoomNotificationMode.MUTE) {
- MainActionButton(title = stringResource(CommonStrings.common_unmute), icon = Icons.Outlined.NotificationsOff, onClick = {
- state.eventSink(RoomDetailsEvent.UnmuteNotification)
- })
+ MainActionButton(
+ title = stringResource(CommonStrings.common_unmute),
+ iconResourceId = CommonDrawables.ic_compound_notifications_off,
+ onClick = {
+ state.eventSink(RoomDetailsEvent.UnmuteNotification)
+ },
+ )
} else {
- MainActionButton(title = stringResource(CommonStrings.common_mute), icon = Icons.Outlined.Notifications, onClick = {
- state.eventSink(RoomDetailsEvent.MuteNotification)
- })
+ MainActionButton(
+ title = stringResource(CommonStrings.common_mute),
+ iconResourceId = CommonDrawables.ic_compound_notifications,
+ onClick = {
+ state.eventSink(RoomDetailsEvent.MuteNotification)
+ },
+ )
}
}
Spacer(modifier = Modifier.width(20.dp))
- MainActionButton(title = stringResource(R.string.screen_room_details_share_room_title), icon = Icons.Outlined.Share, onClick = onShareRoom)
+ MainActionButton(
+ title = stringResource(R.string.screen_room_details_share_room_title),
+ iconResourceId = CommonDrawables.ic_compound_share_android,
+ onClick = onShareRoom
+ )
}
}
@Composable
-internal fun RoomHeaderSection(
+private fun RoomHeaderSection(
avatarUrl: String?,
roomId: String,
roomName: String,
@@ -282,7 +289,7 @@ internal fun RoomHeaderSection(
}
@Composable
-internal fun TopicSection(
+private fun TopicSection(
roomTopic: RoomTopicState,
onActionClicked: (RoomDetailsAction) -> Unit,
modifier: Modifier = Modifier
@@ -308,7 +315,7 @@ internal fun TopicSection(
}
@Composable
-internal fun NotificationSection(
+private fun NotificationSection(
isDefaultMode: Boolean,
openRoomNotificationSettings: () -> Unit,
modifier: Modifier = Modifier
@@ -322,21 +329,21 @@ internal fun NotificationSection(
PreferenceText(
title = stringResource(R.string.screen_room_details_notification_title),
subtitle = subtitle,
- icon = Icons.Outlined.Notifications,
+ iconResourceId = CommonDrawables.ic_compound_notifications,
onClick = openRoomNotificationSettings,
)
}
}
@Composable
-internal fun MembersSection(
+private fun MembersSection(
memberCount: Long,
openRoomMemberList: () -> Unit,
modifier: Modifier = Modifier,
) {
PreferenceCategory(modifier = modifier) {
PreferenceText(
- title = stringResource(R.string.screen_room_details_people_title),
+ title = stringResource(CommonStrings.common_people),
icon = Icons.Outlined.Person,
currentValue = memberCount.toString(),
onClick = openRoomMemberList,
@@ -345,48 +352,48 @@ internal fun MembersSection(
}
@Composable
-internal fun InviteSection(
+private fun InviteSection(
invitePeople: () -> Unit,
modifier: Modifier = Modifier,
) {
PreferenceCategory(modifier = modifier) {
PreferenceText(
title = stringResource(R.string.screen_room_details_invite_people_title),
- icon = Icons.Outlined.PersonAddAlt,
+ iconResourceId = CommonDrawables.ic_compound_user_add,
onClick = invitePeople,
)
}
}
@Composable
-internal fun SecuritySection(modifier: Modifier = Modifier) {
+private fun SecuritySection(modifier: Modifier = Modifier) {
PreferenceCategory(title = stringResource(R.string.screen_room_details_security_title), modifier = modifier) {
PreferenceText(
title = stringResource(R.string.screen_room_details_encryption_enabled_title),
subtitle = stringResource(R.string.screen_room_details_encryption_enabled_subtitle),
- icon = Icons.Outlined.Lock,
+ iconResourceId = CommonDrawables.ic_compound_lock,
)
}
}
@Composable
-internal fun OtherActionsSection(onLeaveRoom: () -> Unit, modifier: Modifier = Modifier) {
+private fun OtherActionsSection(onLeaveRoom: () -> Unit, modifier: Modifier = Modifier) {
PreferenceCategory(showDivider = false, modifier = modifier) {
PreferenceText(
title = stringResource(R.string.screen_room_details_leave_room_title),
- icon = ImageVector.vectorResource(R.drawable.ic_door_open),
+ iconResourceId = CommonDrawables.ic_compound_leave,
tintColor = MaterialTheme.colorScheme.error,
onClick = onLeaveRoom,
)
}
}
-@LargeHeightPreview
+@PreviewWithLargeHeight
@Composable
internal fun RoomDetailsPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) =
ElementPreviewLight { ContentToPreview(state) }
-@LargeHeightPreview
+@PreviewWithLargeHeight
@Composable
internal fun RoomDetailsDarkPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) =
ElementPreviewDark { ContentToPreview(state) }
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserDialogs.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserDialogs.kt
new file mode 100644
index 0000000000..ddbff5b6a9
--- /dev/null
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserDialogs.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.roomdetails.impl.blockuser
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import io.element.android.features.roomdetails.impl.R
+import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents
+import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
+import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
+
+@Composable
+fun BlockUserDialogs(state: RoomMemberDetailsState) {
+ when (state.displayConfirmationDialog) {
+ null -> Unit
+ RoomMemberDetailsState.ConfirmationDialog.Block -> {
+ BlockConfirmationDialog(
+ onBlockAction = {
+ state.eventSink(
+ RoomMemberDetailsEvents.BlockUser(
+ needsConfirmation = false
+ )
+ )
+ },
+ onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) }
+ )
+ }
+ RoomMemberDetailsState.ConfirmationDialog.Unblock -> {
+ UnblockConfirmationDialog(
+ onUnblockAction = {
+ state.eventSink(
+ RoomMemberDetailsEvents.UnblockUser(
+ needsConfirmation = false
+ )
+ )
+ },
+ onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) }
+ )
+ }
+ }
+}
+
+@Composable
+private fun BlockConfirmationDialog(onBlockAction: () -> Unit, onDismiss: () -> Unit) {
+ ConfirmationDialog(
+ title = stringResource(R.string.screen_dm_details_block_user),
+ content = stringResource(R.string.screen_dm_details_block_alert_description),
+ submitText = stringResource(R.string.screen_dm_details_block_alert_action),
+ onSubmitClicked = onBlockAction,
+ onDismiss = onDismiss
+ )
+}
+
+@Composable
+private fun UnblockConfirmationDialog(onUnblockAction: () -> Unit, onDismiss: () -> Unit) {
+ ConfirmationDialog(
+ title = stringResource(R.string.screen_dm_details_unblock_user),
+ content = stringResource(R.string.screen_dm_details_unblock_alert_description),
+ submitText = stringResource(R.string.screen_dm_details_unblock_alert_action),
+ onSubmitClicked = onUnblockAction,
+ onDismiss = onDismiss
+ )
+}
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt
index cccf682c9c..c21e8520b3 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt
@@ -27,7 +27,6 @@ import io.element.android.features.roomdetails.impl.members.details.RoomMemberDe
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.core.bool.orFalse
-import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
@@ -85,44 +84,3 @@ private fun PreferenceBlockUser(
)
}
}
-
-@Composable
-internal fun BlockUserDialogs(state: RoomMemberDetailsState) {
- when (state.displayConfirmationDialog) {
- null -> Unit
- RoomMemberDetailsState.ConfirmationDialog.Block -> {
- BlockConfirmationDialog(
- onBlockAction = { state.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = false)) },
- onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) }
- )
- }
- RoomMemberDetailsState.ConfirmationDialog.Unblock -> {
- UnblockConfirmationDialog(
- onUnblockAction = { state.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = false)) },
- onDismiss = { state.eventSink(RoomMemberDetailsEvents.ClearConfirmationDialog) }
- )
- }
- }
-}
-
-@Composable
-internal fun BlockConfirmationDialog(onBlockAction: () -> Unit, onDismiss: () -> Unit) {
- ConfirmationDialog(
- title = stringResource(R.string.screen_dm_details_block_user),
- content = stringResource(R.string.screen_dm_details_block_alert_description),
- submitText = stringResource(R.string.screen_dm_details_block_alert_action),
- onSubmitClicked = onBlockAction,
- onDismiss = onDismiss
- )
-}
-
-@Composable
-internal fun UnblockConfirmationDialog(onUnblockAction: () -> Unit, onDismiss: () -> Unit) {
- ConfirmationDialog(
- title = stringResource(R.string.screen_dm_details_unblock_user),
- content = stringResource(R.string.screen_dm_details_unblock_alert_description),
- submitText = stringResource(R.string.screen_dm_details_unblock_alert_action),
- onSubmitClicked = onUnblockAction,
- onDismiss = onDismiss
- )
-}
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt
index 0024c64268..40df3791d4 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt
@@ -39,6 +39,8 @@ import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
import io.element.android.libraries.matrix.ui.media.AvatarAction
import io.element.android.libraries.mediapickers.api.PickerProvider
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
+import io.element.android.libraries.permissions.api.PermissionsEvents
+import io.element.android.libraries.permissions.api.PermissionsPresenter
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -49,10 +51,15 @@ class RoomDetailsEditPresenter @Inject constructor(
private val room: MatrixRoom,
private val mediaPickerProvider: PickerProvider,
private val mediaPreProcessor: MediaPreProcessor,
+ permissionsPresenterFactory: PermissionsPresenter.Factory,
) : Presenter {
+ private val cameraPermissionPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA)
+ private var pendingPermissionRequest = false
+
@Composable
override fun present(): RoomDetailsEditState {
+ val cameraPermissionState = cameraPermissionPresenter.present()
val roomSyncUpdateFlow = room.syncUpdateFlow.collectAsState()
// Since there is no way to obtain the new avatar uri after uploading a new avatar,
@@ -92,6 +99,13 @@ class RoomDetailsEditPresenter @Inject constructor(
onResult = { uri -> if (uri != null) roomAvatarUri = uri }
)
+ LaunchedEffect(cameraPermissionState.permissionGranted) {
+ if (cameraPermissionState.permissionGranted && pendingPermissionRequest) {
+ pendingPermissionRequest = false
+ cameraPhotoPicker.launch()
+ }
+ }
+
val avatarActions by remember(roomAvatarUri) {
derivedStateOf {
listOfNotNull(
@@ -110,7 +124,12 @@ class RoomDetailsEditPresenter @Inject constructor(
is RoomDetailsEditEvents.HandleAvatarAction -> {
when (event.action) {
AvatarAction.ChoosePhoto -> galleryImagePicker.launch()
- AvatarAction.TakePhoto -> cameraPhotoPicker.launch()
+ AvatarAction.TakePhoto -> if (cameraPermissionState.permissionGranted) {
+ cameraPhotoPicker.launch()
+ } else {
+ pendingPermissionRequest = true
+ cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions)
+ }
AvatarAction.Remove -> roomAvatarUri = null
}
}
@@ -132,6 +151,7 @@ class RoomDetailsEditPresenter @Inject constructor(
avatarActions = avatarActions,
saveButtonEnabled = saveButtonEnabled,
saveAction = saveAction.value,
+ cameraPermissionState = cameraPermissionState,
eventSink = ::handleEvents,
)
}
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt
index ceb87b6f27..9258d882fc 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt
@@ -17,8 +17,9 @@
package io.element.android.features.roomdetails.impl.edit
import android.net.Uri
-import io.element.android.libraries.matrix.ui.media.AvatarAction
import io.element.android.libraries.architecture.Async
+import io.element.android.libraries.matrix.ui.media.AvatarAction
+import io.element.android.libraries.permissions.api.PermissionsState
import kotlinx.collections.immutable.ImmutableList
data class RoomDetailsEditState(
@@ -32,5 +33,6 @@ data class RoomDetailsEditState(
val avatarActions: ImmutableList,
val saveButtonEnabled: Boolean,
val saveAction: Async,
+ val cameraPermissionState: PermissionsState,
val eventSink: (RoomDetailsEditEvents) -> Unit
)
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt
index 96fd47c381..730acaa1c5 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt
@@ -19,6 +19,7 @@ package io.element.android.features.roomdetails.impl.edit
import android.net.Uri
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.Async
+import io.element.android.libraries.permissions.api.aPermissionsState
import kotlinx.collections.immutable.persistentListOf
open class RoomDetailsEditStateProvider : PreviewParameterProvider {
@@ -45,5 +46,6 @@ fun aRoomDetailsEditState() = RoomDetailsEditState(
avatarActions = persistentListOf(),
saveButtonEnabled = true,
saveAction = Async.Uninitialized,
+ cameraPermissionState = aPermissionsState(showDialog = false),
eventSink = {}
)
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt
index 8775a079b1..3d0de05d34 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt
@@ -53,7 +53,7 @@ import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Scaffold
@@ -62,6 +62,7 @@ import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet
import io.element.android.libraries.matrix.ui.components.EditableAvatarView
+import io.element.android.libraries.permissions.api.PermissionsView
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.launch
@@ -193,6 +194,10 @@ fun RoomDetailsEditView(
else -> Unit
}
+
+ PermissionsView(
+ state = state.cameraPermissionState,
+ )
}
@Composable
@@ -228,7 +233,7 @@ private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier =
})
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoomDetailsEditViewPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = ElementPreview {
RoomDetailsEditView(
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersState.kt
index 9a2ceb7c4b..16436debf0 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersState.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersState.kt
@@ -19,15 +19,14 @@ package io.element.android.features.roomdetails.impl.invite
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList
-import kotlinx.collections.immutable.persistentListOf
data class RoomInviteMembersState(
- val canInvite: Boolean = false,
- val searchQuery: String = "",
- val searchResults: SearchBarResultState> = SearchBarResultState.NotSearching(),
- val selectedUsers: ImmutableList = persistentListOf(),
- val isSearchActive: Boolean = false,
- val eventSink: (RoomInviteMembersEvents) -> Unit = {},
+ val canInvite: Boolean,
+ val searchQuery: String,
+ val searchResults: SearchBarResultState>,
+ val selectedUsers: ImmutableList,
+ val isSearchActive: Boolean,
+ val eventSink: (RoomInviteMembersEvents) -> Unit,
)
data class InvitableUser(
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersStateProvider.kt
index 00e9496c2a..f44d518fb5 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersStateProvider.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersStateProvider.kt
@@ -18,20 +18,22 @@ package io.element.android.features.roomdetails.impl.invite
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
+import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
+import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
internal class RoomInviteMembersStateProvider : PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
- RoomInviteMembersState(),
- RoomInviteMembersState(canInvite = true, selectedUsers = aMatrixUserList().toImmutableList()),
- RoomInviteMembersState(isSearchActive = true, searchQuery = "some query"),
- RoomInviteMembersState(isSearchActive = true, searchQuery = "some query", selectedUsers = aMatrixUserList().toImmutableList()),
- RoomInviteMembersState(isSearchActive = true, searchQuery = "some query", searchResults = SearchBarResultState.NoResults()),
- RoomInviteMembersState(
+ aRoomInviteMembersState(),
+ aRoomInviteMembersState(canInvite = true, selectedUsers = aMatrixUserList().toImmutableList()),
+ aRoomInviteMembersState(isSearchActive = true, searchQuery = "some query"),
+ aRoomInviteMembersState(isSearchActive = true, searchQuery = "some query", selectedUsers = aMatrixUserList().toImmutableList()),
+ aRoomInviteMembersState(isSearchActive = true, searchQuery = "some query", searchResults = SearchBarResultState.NoResults()),
+ aRoomInviteMembersState(
isSearchActive = true,
canInvite = true,
searchQuery = "some query",
@@ -48,7 +50,7 @@ internal class RoomInviteMembersStateProvider : PreviewParameterProvider> = SearchBarResultState.NotSearching(),
+ selectedUsers: ImmutableList = persistentListOf(),
+ isSearchActive: Boolean = false,
+): RoomInviteMembersState {
+ return RoomInviteMembersState(
+ canInvite = canInvite,
+ searchQuery = searchQuery,
+ searchResults = searchResults,
+ selectedUsers = selectedUsers,
+ isSearchActive = isSearchActive,
+ eventSink = {},
+ )
+}
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt
index 00b218c947..ef37ee26bb 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt
@@ -34,7 +34,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.features.roomdetails.impl.R
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
@@ -111,7 +111,7 @@ fun RoomInviteMembersView(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun RoomInviteMembersTopBar(
+private fun RoomInviteMembersTopBar(
canSend: Boolean,
modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {},
@@ -216,7 +216,7 @@ private fun RoomInviteMembersSearchBar(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoomInviteMembersPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) = ElementPreview {
RoomInviteMembersView(state)
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt
index 58b2ab4523..991ac60054 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt
@@ -42,7 +42,7 @@ import io.element.android.features.roomdetails.impl.R
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
@@ -203,7 +203,7 @@ private fun RoomMemberListTopBar(
modifier = modifier,
title = {
Text(
- text = stringResource(R.string.screen_room_details_people_title),
+ text = stringResource(CommonStrings.common_people),
style = ElementTheme.typography.aliasScreenTitle,
)
},
@@ -249,7 +249,7 @@ private fun RoomMemberSearchBar(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoomMemberListPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) = ElementPreview {
RoomMemberListView(
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsState.kt
index 0d3423e179..957db2233e 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsState.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsState.kt
@@ -23,7 +23,7 @@ data class RoomMemberDetailsState(
val userName: String?,
val avatarUrl: String?,
val isBlocked: Async,
- val displayConfirmationDialog: ConfirmationDialog? = null,
+ val displayConfirmationDialog: ConfirmationDialog?,
val isCurrentUser: Boolean,
val eventSink: (RoomMemberDetailsEvents) -> Unit
) {
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsStateProvider.kt
index 6883b20898..b14b0e3634 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsStateProvider.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsStateProvider.kt
@@ -37,6 +37,7 @@ fun aRoomMemberDetailsState() = RoomMemberDetailsState(
userName = "Daniel",
avatarUrl = null,
isBlocked = Async.Success(false),
+ displayConfirmationDialog = null,
isCurrentUser = false,
eventSink = {},
)
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt
index 84dd9319cf..bd65e706b4 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt
@@ -16,49 +16,27 @@
package io.element.android.features.roomdetails.impl.members.details
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.ChatBubbleOutline
-import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.roomdetails.impl.blockuser.BlockUserDialogs
import io.element.android.features.roomdetails.impl.blockuser.BlockUserSection
-import io.element.android.libraries.designsystem.components.avatar.Avatar
-import io.element.android.libraries.designsystem.components.avatar.AvatarData
-import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.components.button.MainActionButton
-import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
-import io.element.android.libraries.designsystem.components.preferences.PreferenceText
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.preview.LargeHeightPreview
+import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
import io.element.android.libraries.designsystem.theme.components.Scaffold
-import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
-import io.element.android.libraries.theme.ElementTheme
-import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
@@ -103,45 +81,9 @@ fun RoomMemberDetailsView(
}
}
+/*
@Composable
-internal fun RoomMemberHeaderSection(
- avatarUrl: String?,
- userId: String,
- userName: String?,
- modifier: Modifier = Modifier
-) {
- Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
- Box(modifier = Modifier.size(70.dp)) {
- Avatar(
- avatarData = AvatarData(userId, userName, avatarUrl, AvatarSize.UserHeader),
- modifier = Modifier.fillMaxSize()
- )
- }
- Spacer(modifier = Modifier.height(24.dp))
- if (userName != null) {
- Text(text = userName, style = ElementTheme.typography.fontHeadingLgBold)
- Spacer(modifier = Modifier.height(6.dp))
- }
- Text(
- text = userId,
- style = ElementTheme.typography.fontBodyLgRegular,
- color = MaterialTheme.colorScheme.secondary,
- modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
- textAlign = TextAlign.Center,
- )
- Spacer(Modifier.height(40.dp))
- }
-}
-
-@Composable
-internal fun RoomMemberMainActionsSection(onShareUser: () -> Unit, modifier: Modifier = Modifier) {
- Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
- MainActionButton(title = stringResource(CommonStrings.action_share), icon = Icons.Outlined.Share, onClick = onShareUser)
- }
-}
-
-@Composable
-internal fun SendMessageSection(onSendMessage: () -> Unit, modifier: Modifier = Modifier) {
+private fun SendMessageSection(onSendMessage: () -> Unit, modifier: Modifier = Modifier) {
PreferenceCategory(modifier = modifier) {
PreferenceText(
title = stringResource(CommonStrings.action_send_message),
@@ -150,13 +92,14 @@ internal fun SendMessageSection(onSendMessage: () -> Unit, modifier: Modifier =
)
}
}
+ */
-@LargeHeightPreview
+@PreviewWithLargeHeight
@Composable
internal fun RoomMemberDetailsViewLightPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) =
ElementPreviewLight { ContentToPreview(state) }
-@LargeHeightPreview
+@PreviewWithLargeHeight
@Composable
internal fun RoomMemberDetailsViewDarkPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) =
ElementPreviewDark { ContentToPreview(state) }
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt
new file mode 100644
index 0000000000..26412577b8
--- /dev/null
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.roomdetails.impl.members.details
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.components.avatar.Avatar
+import io.element.android.libraries.designsystem.components.avatar.AvatarData
+import io.element.android.libraries.designsystem.components.avatar.AvatarSize
+import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.theme.ElementTheme
+
+@Composable
+fun RoomMemberHeaderSection(
+ avatarUrl: String?,
+ userId: String,
+ userName: String?,
+ modifier: Modifier = Modifier
+) {
+ Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
+ Box(modifier = Modifier.size(70.dp)) {
+ Avatar(
+ avatarData = AvatarData(userId, userName, avatarUrl, AvatarSize.UserHeader),
+ modifier = Modifier.fillMaxSize()
+ )
+ }
+ Spacer(modifier = Modifier.height(24.dp))
+ if (userName != null) {
+ Text(text = userName, style = ElementTheme.typography.fontHeadingLgBold)
+ Spacer(modifier = Modifier.height(6.dp))
+ }
+ Text(
+ text = userId,
+ style = ElementTheme.typography.fontBodyLgRegular,
+ color = MaterialTheme.colorScheme.secondary,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ textAlign = TextAlign.Center,
+ )
+ Spacer(Modifier.height(40.dp))
+ }
+}
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberMainActionsSection.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberMainActionsSection.kt
new file mode 100644
index 0000000000..edd352f228
--- /dev/null
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberMainActionsSection.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.roomdetails.impl.members.details
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import io.element.android.libraries.designsystem.components.button.MainActionButton
+import io.element.android.libraries.designsystem.utils.CommonDrawables
+import io.element.android.libraries.ui.strings.CommonStrings
+
+@Composable
+fun RoomMemberMainActionsSection(onShareUser: () -> Unit, modifier: Modifier = Modifier) {
+ Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
+ MainActionButton(
+ title = stringResource(CommonStrings.action_share),
+ iconResourceId = CommonDrawables.ic_compound_share_android,
+ onClick = onShareUser
+ )
+ }
+}
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOption.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOption.kt
index 1de1683d65..11f50b763e 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOption.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsOption.kt
@@ -27,7 +27,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.RadioButton
import io.element.android.libraries.designsystem.theme.components.Text
@@ -77,7 +77,7 @@ fun RoomNotificationSettingsOption(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoomPrivacyOptionPreview() = ElementPreview {
Column {
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt
index 6f440958b2..b0c82d291c 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt
@@ -37,7 +37,7 @@ import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Scaffold
@@ -130,7 +130,7 @@ fun RoomNotificationSettingsView(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun RoomNotificationSettingsTopBar(
+private fun RoomNotificationSettingsTopBar(
modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {},
) {
@@ -175,7 +175,7 @@ fun ShowChangeNotificationSettingError(state: RoomNotificationSettingsState, eve
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoomNotificationSettingsPreview(
@PreviewParameter(RoomNotificationSettingsStateProvider::class) state: RoomNotificationSettingsState
diff --git a/features/roomdetails/impl/src/main/res/drawable/ic_door_open.xml b/features/roomdetails/impl/src/main/res/drawable/ic_door_open.xml
deleted file mode 100644
index 5247201807..0000000000
--- a/features/roomdetails/impl/src/main/res/drawable/ic_door_open.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/features/roomdetails/impl/src/main/res/values-cs/translations.xml b/features/roomdetails/impl/src/main/res/values-cs/translations.xml
index defedd3bfb..7c80ba3066 100644
--- a/features/roomdetails/impl/src/main/res/values-cs/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-cs/translations.xml
@@ -45,7 +45,6 @@
"Znovu uvidíte všechny zprávy od nich."
"Odblokovat uživatele"
"Opustit místnost"
- "Lidé"
"Zabezpečení"
"Téma"
diff --git a/features/roomdetails/impl/src/main/res/values-de/translations.xml b/features/roomdetails/impl/src/main/res/values-de/translations.xml
index 1495840415..9549709ef0 100644
--- a/features/roomdetails/impl/src/main/res/values-de/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-de/translations.xml
@@ -44,7 +44,6 @@
"Du kannst dann wieder alle Nachrichten von ihnen sehen."
"Benutzer entsperren"
"Raum verlassen"
- "Personen"
"Sicherheit"
"Thema"
diff --git a/features/roomdetails/impl/src/main/res/values-es/translations.xml b/features/roomdetails/impl/src/main/res/values-es/translations.xml
index 42bce4b756..620f974340 100644
--- a/features/roomdetails/impl/src/main/res/values-es/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-es/translations.xml
@@ -15,7 +15,6 @@
"Al desbloquear al usuario, podrás volver a ver todos sus mensajes."
"Desbloquear usuario"
"Salir de la sala"
- "Personas"
"Seguridad"
"Tema"
diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml
index 7efbe4c1f7..7cf727dfb5 100644
--- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml
@@ -44,7 +44,6 @@
"Vous pourrez à nouveau voir tous ses messages."
"Débloquer l’utilisateur"
"Quitter le salon"
- "Personnes"
"Sécurité"
"Sujet"
diff --git a/features/roomdetails/impl/src/main/res/values-it/translations.xml b/features/roomdetails/impl/src/main/res/values-it/translations.xml
index 190eda82ee..c00fb14bc1 100644
--- a/features/roomdetails/impl/src/main/res/values-it/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-it/translations.xml
@@ -15,7 +15,6 @@
"Dopo aver sbloccato l\'utente, potrai vedere nuovamente tutti i suoi messaggi."
"Sblocca utente"
"Esci dalla stanza"
- "Persone"
"Sicurezza"
"Oggetto"
diff --git a/features/roomdetails/impl/src/main/res/values-ro/translations.xml b/features/roomdetails/impl/src/main/res/values-ro/translations.xml
index f86fbb8bb5..7ec53722f4 100644
--- a/features/roomdetails/impl/src/main/res/values-ro/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-ro/translations.xml
@@ -44,7 +44,6 @@
"La deblocarea utilizatorului, veți putea vedea din nou toate mesajele de la acesta."
"Deblocați utilizatorul"
"Părăsiți camera"
- "Persoane"
"Securitate"
"Subiect"
diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml
index cff08460c1..1b2a305d3a 100644
--- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml
@@ -45,7 +45,6 @@
"Вы снова сможете увидеть все сообщения."
"Разблокировать пользователя"
"Покинуть комнату"
- "Пользователи"
"Безопасность"
"Тема"
diff --git a/features/roomdetails/impl/src/main/res/values-sk/translations.xml b/features/roomdetails/impl/src/main/res/values-sk/translations.xml
index 01e0f717e1..8d28fe5fe5 100644
--- a/features/roomdetails/impl/src/main/res/values-sk/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-sk/translations.xml
@@ -45,7 +45,6 @@
"Všetky správy od nich budete môcť opäť vidieť."
"Odblokovať používateľa"
"Opustiť miestnosť"
- "Ľudia"
"Bezpečnosť"
"Téma"
diff --git a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml
index 7b4eb895ea..4b82111269 100644
--- a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml
@@ -9,6 +9,9 @@
"編輯聊天室"
"無法更新聊天室"
"訊息已加密"
+ "載入通知設定時發生錯誤。"
+ "無法關閉聊天室通知,請再試一次。"
+ "無法開啟聊天室通知,請再試一次。"
"邀請夥伴"
"自訂"
"預設"
@@ -23,12 +26,12 @@
"無法重設為預設模式,請再試一次。"
"無法設定模式,請再試一次。"
"所有訊息"
- "只限提及與關鍵字"
+ "僅限提及與關鍵字"
"封鎖"
"封鎖使用者"
"解除封鎖"
"解除封鎖使用者"
"離開聊天室"
- "夥伴"
+ "安全性"
"主題"
diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml
index 7f0f4ddc30..c88e2e43fa 100644
--- a/features/roomdetails/impl/src/main/res/values/localazy.xml
+++ b/features/roomdetails/impl/src/main/res/values/localazy.xml
@@ -44,7 +44,6 @@
"You\'ll be able to see all messages from them again."
"Unblock user"
"Leave room"
- "People"
"Security"
"Topic"
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt
index a6ffa0a289..5ab79d2cee 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt
@@ -22,7 +22,7 @@ import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.leaveroom.api.LeaveRoomEvent
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
-import io.element.android.features.leaveroom.fake.LeaveRoomPresenterFake
+import io.element.android.features.leaveroom.fake.FakeLeaveRoomPresenter
import io.element.android.features.roomdetails.impl.RoomDetailsEvent
import io.element.android.features.roomdetails.impl.RoomDetailsPresenter
import io.element.android.features.roomdetails.impl.RoomDetailsType
@@ -59,9 +59,10 @@ class RoomDetailsPresenterTests {
@get:Rule
val warmUpRule = WarmUpRule()
- private fun aRoomDetailsPresenter(
+
+ private fun createRoomDetailsPresenter(
room: MatrixRoom,
- leaveRoomPresenter: LeaveRoomPresenter = LeaveRoomPresenterFake(),
+ leaveRoomPresenter: LeaveRoomPresenter = FakeLeaveRoomPresenter(),
dispatchers: CoroutineDispatchers,
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService()
): RoomDetailsPresenter {
@@ -88,7 +89,7 @@ class RoomDetailsPresenterTests {
@Test
fun `present - initial state is created from room info`() = runTest {
val room = aMatrixRoom()
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -107,7 +108,7 @@ class RoomDetailsPresenterTests {
@Test
fun `present - initial state with no room name`() = runTest {
val room = aMatrixRoom(name = null)
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -129,7 +130,7 @@ class RoomDetailsPresenterTests {
val roomMembers = listOf(myRoomMember, otherRoomMember)
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
}
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -145,7 +146,7 @@ class RoomDetailsPresenterTests {
val room = aMatrixRoom().apply {
givenCanInviteResult(Result.success(true))
}
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -163,7 +164,7 @@ class RoomDetailsPresenterTests {
val room = aMatrixRoom().apply {
givenCanInviteResult(Result.success(false))
}
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -178,7 +179,7 @@ class RoomDetailsPresenterTests {
val room = aMatrixRoom().apply {
givenCanInviteResult(Result.failure(Throwable("Whoops")))
}
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -196,7 +197,7 @@ class RoomDetailsPresenterTests {
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Whelp")))
givenCanInviteResult(Result.success(false))
}
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -225,7 +226,7 @@ class RoomDetailsPresenterTests {
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true))
givenCanInviteResult(Result.success(false))
}
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -254,7 +255,7 @@ class RoomDetailsPresenterTests {
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
}
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -275,7 +276,7 @@ class RoomDetailsPresenterTests {
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true))
givenCanInviteResult(Result.success(false))
}
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -296,7 +297,7 @@ class RoomDetailsPresenterTests {
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false))
givenCanInviteResult(Result.success(false))
}
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -314,7 +315,7 @@ class RoomDetailsPresenterTests {
givenCanInviteResult(Result.success(false))
}
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -332,7 +333,7 @@ class RoomDetailsPresenterTests {
givenCanInviteResult(Result.success(false))
}
- val presenter = aRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -348,9 +349,9 @@ class RoomDetailsPresenterTests {
@Test
fun `present - leave room event is passed on to leave room presenter`() = runTest {
- val leaveRoomPresenter = LeaveRoomPresenterFake()
+ val leaveRoomPresenter = FakeLeaveRoomPresenter()
val room = aMatrixRoom()
- val presenter = aRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers())
+ val presenter = createRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers())
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -364,10 +365,10 @@ class RoomDetailsPresenterTests {
@Test
fun `present - notification mode changes`() = runTest {
- val leaveRoomPresenter = LeaveRoomPresenterFake()
+ val leaveRoomPresenter = FakeLeaveRoomPresenter()
val notificationSettingsService = FakeNotificationSettingsService()
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
- val presenter = aRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService)
+ val presenter = createRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -383,10 +384,10 @@ class RoomDetailsPresenterTests {
@Test
fun `present - mute room notifications`() = runTest {
- val leaveRoomPresenter = LeaveRoomPresenterFake()
+ val leaveRoomPresenter = FakeLeaveRoomPresenter()
val notificationSettingsService = FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
- val presenter = aRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService)
+ val presenter = createRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -401,13 +402,13 @@ class RoomDetailsPresenterTests {
@Test
fun `present - unmute room notifications`() = runTest {
- val leaveRoomPresenter = LeaveRoomPresenterFake()
+ val leaveRoomPresenter = FakeLeaveRoomPresenter()
val notificationSettingsService = FakeNotificationSettingsService(
initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES
)
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
- val presenter = aRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService)
+ val presenter = createRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
index aeaefeab6e..eb89c40a24 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
@@ -32,6 +32,9 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction
import io.element.android.libraries.mediapickers.test.FakePickerProvider
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
+import io.element.android.libraries.permissions.api.PermissionsPresenter
+import io.element.android.libraries.permissions.test.FakePermissionsPresenter
+import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
import io.element.android.tests.testutils.WarmUpRule
import io.mockk.every
import io.mockk.mockk
@@ -74,18 +77,22 @@ class RoomDetailsEditPresenterTest {
unmockkAll()
}
- private fun aRoomDetailsEditPresenter(room: MatrixRoom): RoomDetailsEditPresenter {
+ private fun createRoomDetailsEditPresenter(
+ room: MatrixRoom,
+ permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(),
+ ): RoomDetailsEditPresenter {
return RoomDetailsEditPresenter(
room = room,
mediaPickerProvider = fakePickerProvider,
mediaPreProcessor = fakeMediaPreProcessor,
+ permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter),
)
}
@Test
fun `present - initial state is created from room info`() = runTest {
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL)
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -112,7 +119,7 @@ class RoomDetailsEditPresenterTest {
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false))
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops")))
}
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -138,7 +145,7 @@ class RoomDetailsEditPresenterTest {
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true))
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops")))
}
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -164,7 +171,7 @@ class RoomDetailsEditPresenterTest {
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Oops")))
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
}
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -186,7 +193,7 @@ class RoomDetailsEditPresenterTest {
@Test
fun `present - updates state in response to changes`() = runTest {
val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL)
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -232,7 +239,7 @@ class RoomDetailsEditPresenterTest {
fakePickerProvider.givenResult(anotherAvatarUri)
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -252,19 +259,31 @@ class RoomDetailsEditPresenterTest {
val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL)
fakePickerProvider.givenResult(anotherAvatarUri)
-
- val presenter = aRoomDetailsEditPresenter(room)
+ val fakePermissionsPresenter = FakePermissionsPresenter()
+ val presenter = createRoomDetailsEditPresenter(
+ room = room,
+ permissionsPresenter = fakePermissionsPresenter,
+ )
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri)
-
+ assertThat(initialState.cameraPermissionState.permissionGranted).isFalse()
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto))
- awaitItem().apply {
- assertThat(roomAvatarUrl).isEqualTo(anotherAvatarUri)
- }
+ val stateWithAskingPermission = awaitItem()
+ assertThat(stateWithAskingPermission.cameraPermissionState.showDialog).isTrue()
+ fakePermissionsPresenter.setPermissionGranted()
+ val stateWithPermission = awaitItem()
+ assertThat(stateWithPermission.cameraPermissionState.permissionGranted).isTrue()
+ val stateWithNewAvatar = awaitItem()
+ assertThat(stateWithNewAvatar.roomAvatarUrl).isEqualTo(anotherAvatarUri)
+ // Do it again, no permission is requested
+ fakePickerProvider.givenResult(roomAvatarUri)
+ stateWithNewAvatar.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto))
+ val stateWithNewAvatar2 = awaitItem()
+ assertThat(stateWithNewAvatar2.roomAvatarUrl).isEqualTo(roomAvatarUri)
}
}
@@ -274,7 +293,7 @@ class RoomDetailsEditPresenterTest {
fakePickerProvider.givenResult(roomAvatarUri)
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -326,7 +345,7 @@ class RoomDetailsEditPresenterTest {
fakePickerProvider.givenResult(roomAvatarUri)
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -376,7 +395,7 @@ class RoomDetailsEditPresenterTest {
fun `present - save changes room details if different`() = runTest {
val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL)
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -401,7 +420,7 @@ class RoomDetailsEditPresenterTest {
fun `present - save doesn't change room details if they're the same trimmed`() = runTest {
val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL)
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -425,7 +444,7 @@ class RoomDetailsEditPresenterTest {
fun `present - save doesn't change topic if it was unset and is now blank`() = runTest {
val room = aMatrixRoom(topic = null, name = "Name", avatarUrl = AN_AVATAR_URL)
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -448,7 +467,7 @@ class RoomDetailsEditPresenterTest {
fun `present - save doesn't change name if it's now empty`() = runTest {
val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL)
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -473,7 +492,7 @@ class RoomDetailsEditPresenterTest {
givenPickerReturnsFile()
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -498,7 +517,7 @@ class RoomDetailsEditPresenterTest {
fakePickerProvider.givenResult(anotherAvatarUri)
fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no")))
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -564,7 +583,7 @@ class RoomDetailsEditPresenterTest {
givenSetTopicResult(Result.failure(Throwable("!")))
}
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -583,7 +602,7 @@ class RoomDetailsEditPresenterTest {
}
private suspend fun saveAndAssertFailure(room: MatrixRoom, event: RoomDetailsEditEvents) {
- val presenter = aRoomDetailsEditPresenter(room)
+ val presenter = createRoomDetailsEditPresenter(room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
diff --git a/features/roomlist/impl/build.gradle.kts b/features/roomlist/impl/build.gradle.kts
index f5a08ba860..17817ebc91 100644
--- a/features/roomlist/impl/build.gradle.kts
+++ b/features/roomlist/impl/build.gradle.kts
@@ -53,7 +53,6 @@ dependencies {
implementation(projects.features.networkmonitor.api)
implementation(projects.features.leaveroom.api)
implementation(projects.services.analytics.api)
- implementation(libs.accompanist.placeholder)
api(projects.features.roomlist.api)
ksp(libs.showkase.processor)
@@ -70,5 +69,5 @@ dependencies {
testImplementation(projects.features.invitelist.test)
testImplementation(projects.features.networkmonitor.test)
testImplementation(projects.tests.testutils)
- testImplementation(projects.features.leaveroom.fake)
+ testImplementation(projects.features.leaveroom.test)
}
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/InvitesEntryPointView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/InvitesEntryPointView.kt
index cc10191f08..fda79bc8cf 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/InvitesEntryPointView.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/InvitesEntryPointView.kt
@@ -34,7 +34,7 @@ import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@@ -71,7 +71,7 @@ fun InvitesEntryPointView(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun InvitesEntryPointViewPreview(@PreviewParameter(InvitesStateProvider::class) state: InvitesState) = ElementPreview {
InvitesEntryPointView(
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt
index 32acdeb6f1..24f1a42883 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt
@@ -22,8 +22,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
@@ -31,12 +29,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.VectorIcons
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -92,7 +90,7 @@ private fun RoomListModalBottomSheetContent(
modifier = Modifier.clickable { onRoomSettingsClicked(contextMenu.roomId) },
leadingContent = {
Icon(
- imageVector = Icons.Outlined.Settings,
+ resourceId = CommonDrawables.ic_compound_settings,
contentDescription = stringResource(id = CommonStrings.common_settings),
modifier = Modifier.size(20.dp),
tint = MaterialTheme.colorScheme.onSurface,
@@ -110,7 +108,7 @@ private fun RoomListModalBottomSheetContent(
modifier = Modifier.clickable { onLeaveRoomClicked(contextMenu.roomId) },
leadingContent = {
Icon(
- resourceId = VectorIcons.DoorOpen,
+ resourceId = CommonDrawables.ic_compound_leave,
contentDescription = stringResource(id = CommonStrings.action_leave_room),
modifier = Modifier.size(20.dp),
tint = MaterialTheme.colorScheme.error,
@@ -124,7 +122,7 @@ private fun RoomListModalBottomSheetContent(
// TODO This component should be seen in [RoomListView] @Preview but it doesn't show up.
// see: https://issuetracker.google.com/issues/283843380
// Remove this preview when the issue is fixed.
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoomListModalBottomSheetContentPreview() = ElementPreview {
RoomListModalBottomSheetContent(
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt
index e069634d49..5238144755 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt
@@ -33,8 +33,8 @@ import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
import io.element.android.libraries.architecture.Presenter
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
-import io.element.android.libraries.designsystem.utils.collectSnackbarMessageAsState
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.api.user.getCurrentUser
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt
index c555afeca7..f0dfe8e3a9 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt
@@ -19,7 +19,7 @@ package io.element.android.features.roomlist.impl
import androidx.compose.runtime.Immutable
import io.element.android.features.leaveroom.api.LeaveRoomState
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
-import io.element.android.libraries.designsystem.utils.SnackbarMessage
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt
index 421e243504..7199bfd227 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt
@@ -17,12 +17,12 @@
package io.element.android.features.roomlist.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
-import io.element.android.features.leaveroom.api.LeaveRoomState
+import io.element.android.features.leaveroom.api.aLeaveRoomState
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryPlaceholders
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.utils.SnackbarMessage
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser
@@ -58,7 +58,7 @@ internal fun aRoomListState() = RoomListState(
invitesState = InvitesState.NoInvites,
displaySearchResults = false,
contextMenu = RoomListState.ContextMenu.Hidden,
- leaveRoomState = LeaveRoomState(),
+ leaveRoomState = aLeaveRoomState(),
eventSink = {}
)
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt
index 676127d5ed..b01c5fba4d 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt
@@ -51,17 +51,17 @@ import io.element.android.features.roomlist.impl.components.RoomListTopBar
import io.element.android.features.roomlist.impl.components.RoomSummaryRow
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.search.RoomListSearchResultView
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Scaffold
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.designsystem.utils.LogCompositions
-import io.element.android.libraries.designsystem.utils.SnackbarHost
-import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
+import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.designsystem.R as DrawableR
@Composable
fun RoomListView(
@@ -124,7 +124,7 @@ fun RoomListView(
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
-fun RoomListContent(
+private fun RoomListContent(
state: RoomListState,
onVerifyClicked: () -> Unit,
onRoomClicked: (RoomId) -> Unit,
@@ -231,7 +231,7 @@ fun RoomListContent(
) {
Icon(
// Note cannot use Icons.Outlined.EditSquare, it does not exist :/
- resourceId = DrawableR.drawable.ic_edit_square,
+ resourceId = CommonDrawables.ic_september_compose_button,
contentDescription = stringResource(id = R.string.screen_roomlist_a11y_create_message)
)
}
@@ -242,7 +242,7 @@ fun RoomListContent(
internal fun RoomListRoomSummary.contentType() = isPlaceholder
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoomListViewPreview(@PreviewParameter(RoomListStateProvider::class) state: RoomListState) = ElementPreview {
RoomListView(
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt
index 70111be068..991be692a0 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt
@@ -24,8 +24,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -33,13 +31,14 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import io.element.android.features.roomlist.impl.R
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -70,7 +69,7 @@ internal fun RequestVerificationHeader(
)
Icon(
modifier = Modifier.clickable(onClick = onDismissClicked),
- imageVector = Icons.Default.Close,
+ resourceId = CommonDrawables.ic_compound_close,
contentDescription = stringResource(CommonStrings.action_close)
)
}
@@ -91,7 +90,7 @@ internal fun RequestVerificationHeader(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RequestVerificationHeaderPreview() = ElementPreview {
RequestVerificationHeader(onVerifyClicked = {}, onDismissClicked = {})
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt
index 382c73a489..6c2f07bbac 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt
@@ -22,10 +22,6 @@ import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.statusBarsPadding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.MoreVert
-import androidx.compose.material.icons.outlined.BugReport
-import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
@@ -53,7 +49,7 @@ import io.element.android.features.roomlist.impl.R
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatarBloom
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.text.applyScaleDown
import io.element.android.libraries.designsystem.text.roundToPx
@@ -67,6 +63,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser
@@ -75,7 +72,6 @@ import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
-import io.element.android.libraries.designsystem.R as CommonR
private val avatarBloomSize = 430.dp
@@ -210,7 +206,7 @@ private fun DefaultRoomListTopBar(
onClick = onSearchClicked,
) {
Icon(
- resourceId = CommonR.drawable.ic_search,
+ resourceId = CommonDrawables.ic_compound_search,
contentDescription = stringResource(CommonStrings.action_search),
)
}
@@ -218,7 +214,7 @@ private fun DefaultRoomListTopBar(
onClick = { showMenu = !showMenu }
) {
Icon(
- imageVector = Icons.Default.MoreVert,
+ resourceId = CommonDrawables.ic_compound_overflow_vertical,
contentDescription = null,
)
}
@@ -234,7 +230,7 @@ private fun DefaultRoomListTopBar(
text = { Text(stringResource(id = CommonStrings.action_invite)) },
leadingIcon = {
Icon(
- Icons.Outlined.Share,
+ resourceId = CommonDrawables.ic_compound_share_android,
tint = ElementTheme.materialColors.secondary,
contentDescription = null,
)
@@ -248,7 +244,7 @@ private fun DefaultRoomListTopBar(
text = { Text(stringResource(id = CommonStrings.common_report_a_bug)) },
leadingIcon = {
Icon(
- Icons.Outlined.BugReport,
+ resourceId = CommonDrawables.ic_compound_chat_problem,
tint = ElementTheme.materialColors.secondary,
contentDescription = null,
)
@@ -272,7 +268,7 @@ private fun DefaultRoomListTopBar(
}
@OptIn(ExperimentalMaterial3Api::class)
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun DefaultRoomListTopBarPreview() = ElementPreview {
DefaultRoomListTopBar(
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryPlaceholderRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryPlaceholderRow.kt
index 67e5d892f1..633c2cf45a 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryPlaceholderRow.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryPlaceholderRow.kt
@@ -33,7 +33,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.placeholderBackground
import io.element.android.libraries.theme.ElementTheme
@@ -87,7 +87,7 @@ internal fun RoomSummaryPlaceholderRow(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoomSummaryPlaceholderRowPreview() = ElementPreview {
RoomSummaryPlaceholderRow()
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt
index 0d68666c6f..2016cf5a6b 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt
@@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -44,10 +45,9 @@ import androidx.compose.ui.unit.dp
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryProvider
import io.element.android.libraries.core.extensions.orEmpty
-import io.element.android.libraries.designsystem.VectorIcons
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
import io.element.android.libraries.designsystem.components.avatar.Avatar
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
@@ -55,6 +55,7 @@ import io.element.android.libraries.designsystem.theme.roomListRoomMessage
import io.element.android.libraries.designsystem.theme.roomListRoomMessageDate
import io.element.android.libraries.designsystem.theme.roomListRoomName
import io.element.android.libraries.designsystem.theme.unreadIndicator
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -84,7 +85,7 @@ internal fun RoomSummaryRow(
@OptIn(ExperimentalFoundationApi::class)
@Composable
-internal fun RoomSummaryRealRow(
+private fun RoomSummaryRealRow(
room: RoomListRoomSummary,
onClick: (RoomListRoomSummary) -> Unit,
onLongClick: (RoomListRoomSummary) -> Unit,
@@ -171,12 +172,13 @@ private fun RowScope.LastMessageAndIndicatorRow(room: RoomListRoomSummary) {
// Unread
Row(
- horizontalArrangement = Arrangement.spacedBy(8.dp)
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
) {
NotificationIcon(room)
if (room.hasUnread) {
UnreadIndicatorAtom(
- modifier = Modifier.padding(top = 3.dp),
+ modifier = Modifier.padding(vertical = 3.dp),
)
}
}
@@ -189,20 +191,22 @@ private fun NotificationIcon(room: RoomListRoomSummary) {
null, RoomNotificationMode.ALL_MESSAGES -> return
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY ->
Icon(
+ modifier = Modifier.size(16.dp),
contentDescription = stringResource(CommonStrings.screen_notification_settings_mode_mentions),
- imageVector = ImageVector.vectorResource(VectorIcons.Mention),
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_compound_mention),
tint = tint,
)
RoomNotificationMode.MUTE ->
Icon(
+ modifier = Modifier.size(16.dp),
contentDescription = stringResource(CommonStrings.common_mute),
- imageVector = ImageVector.vectorResource(VectorIcons.Mute),
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_compound_notifications_solid_off),
tint = tint,
)
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoomSummaryRowPreview(@PreviewParameter(RoomListRoomSummaryProvider::class) data: RoomListRoomSummary) = ElementPreview {
RoomSummaryRow(
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearch.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt
similarity index 96%
rename from features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearch.kt
rename to features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt
index 23d8d5efbb..799e94bdbd 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearch.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt
@@ -27,8 +27,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextFieldDefaults
@@ -58,13 +56,14 @@ import io.element.android.features.roomlist.impl.contentType
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.modifiers.applyIf
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.designsystem.utils.copy
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.ui.strings.CommonStrings
@@ -101,7 +100,7 @@ internal fun RoomListSearchResultView(
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
-internal fun RoomListSearchResultContent(
+private fun RoomListSearchResultContent(
state: RoomListState,
onRoomClicked: (RoomId) -> Unit,
onRoomLongClicked: (RoomListRoomSummary) -> Unit,
@@ -154,7 +153,7 @@ internal fun RoomListSearchResultContent(
state.eventSink(RoomListEvents.UpdateFilter(""))
}) {
Icon(
- imageVector = Icons.Default.Close,
+ resourceId = CommonDrawables.ic_compound_close,
contentDescription = stringResource(CommonStrings.action_cancel)
)
}
@@ -218,7 +217,7 @@ internal fun RoomListSearchResultContent(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoomListSearchResultContentPreview() = ElementPreview {
RoomListSearchResultContent(
diff --git a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml
index 795a4e46d0..4ad446a0bf 100644
--- a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml
@@ -2,4 +2,6 @@
"建立新的對話或聊天室"
"所有聊天室"
+ "您似乎正在使用新的裝置。請使用另一個裝置進行驗證,以存取您的加密訊息。"
+ "驗證這是您本人"
diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt
index db2f7027c4..fd5c0160fc 100644
--- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt
+++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt
@@ -22,7 +22,7 @@ import app.cash.turbine.test
import com.google.common.truth.Truth
import io.element.android.features.leaveroom.api.LeaveRoomEvent
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
-import io.element.android.features.leaveroom.fake.LeaveRoomPresenterFake
+import io.element.android.features.leaveroom.fake.FakeLeaveRoomPresenter
import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
import io.element.android.features.roomlist.impl.datasource.FakeInviteDataSource
@@ -34,7 +34,7 @@ import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormat
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
import io.element.android.libraries.matrix.api.MatrixClient
@@ -316,7 +316,7 @@ class RoomListPresenterTests {
@Test
fun `present - leave room calls into leave room presenter`() = runTest {
- val leaveRoomPresenter = LeaveRoomPresenterFake()
+ val leaveRoomPresenter = FakeLeaveRoomPresenter()
val scope = CoroutineScope(coroutineContext + SupervisorJob())
val presenter = createRoomListPresenter(leaveRoomPresenter = leaveRoomPresenter, coroutineScope = scope)
moleculeFlow(RecompositionMode.Immediate) {
@@ -364,7 +364,7 @@ class RoomListPresenterTests {
networkMonitor: NetworkMonitor = FakeNetworkMonitor(),
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
inviteStateDataSource: InviteStateDataSource = FakeInviteDataSource(),
- leaveRoomPresenter: LeaveRoomPresenter = LeaveRoomPresenterFake(),
+ leaveRoomPresenter: LeaveRoomPresenter = FakeLeaveRoomPresenter(),
lastMessageTimestampFormatter: LastMessageTimestampFormatter = FakeLastMessageTimestampFormatter().apply {
givenFormat(A_FORMATTED_DATE)
},
diff --git a/features/signedout/api/build.gradle.kts b/features/signedout/api/build.gradle.kts
new file mode 100644
index 0000000000..d2815315f3
--- /dev/null
+++ b/features/signedout/api/build.gradle.kts
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+plugins {
+ id("io.element.android-library")
+}
+
+android {
+ namespace = "io.element.android.features.signedout.api"
+}
+
+dependencies {
+ implementation(projects.libraries.architecture)
+ implementation(projects.libraries.matrix.api)
+}
diff --git a/features/signedout/api/src/main/kotlin/io/element/android/features/signedout/api/SignedOutEntryPoint.kt b/features/signedout/api/src/main/kotlin/io/element/android/features/signedout/api/SignedOutEntryPoint.kt
new file mode 100644
index 0000000000..7a156998d1
--- /dev/null
+++ b/features/signedout/api/src/main/kotlin/io/element/android/features/signedout/api/SignedOutEntryPoint.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.signedout.api
+
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import io.element.android.libraries.architecture.FeatureEntryPoint
+import io.element.android.libraries.matrix.api.core.SessionId
+
+interface SignedOutEntryPoint : FeatureEntryPoint {
+
+ data class Params(
+ val sessionId: SessionId,
+ )
+
+ fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
+
+ interface NodeBuilder {
+ fun params(params: Params): NodeBuilder
+ fun build(): Node
+ }
+}
+
diff --git a/features/leaveroom/fake/build.gradle.kts b/features/signedout/impl/build.gradle.kts
similarity index 68%
rename from features/leaveroom/fake/build.gradle.kts
rename to features/signedout/impl/build.gradle.kts
index 19a057d5ba..0255ac3ca1 100644
--- a/features/leaveroom/fake/build.gradle.kts
+++ b/features/signedout/impl/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright (c) 2023 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.
@@ -17,10 +17,12 @@
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
+ alias(libs.plugins.ksp)
+ id("kotlin-parcelize")
}
android {
- namespace = "io.element.android.features.leaveroom.fake"
+ namespace = "io.element.android.features.signedout.impl"
}
anvil {
@@ -30,15 +32,22 @@ anvil {
dependencies {
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
+ api(projects.features.signedout.api)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api)
- api(projects.features.leaveroom.api)
+ implementation(projects.libraries.matrixui)
+ implementation(projects.libraries.designsystem)
+ implementation(projects.libraries.uiStrings)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)
- testImplementation(libs.coroutines.core)
testImplementation(libs.molecule.runtime)
testImplementation(libs.test.truth)
testImplementation(libs.test.turbine)
+ testImplementation(projects.libraries.matrix.test)
+ testImplementation(projects.libraries.sessionStorage.implMemory)
+ testImplementation(projects.tests.testutils)
+
+ ksp(libs.showkase.processor)
}
diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPoint.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPoint.kt
new file mode 100644
index 0000000000..59b19f041e
--- /dev/null
+++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPoint.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.signedout.impl
+
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.features.signedout.api.SignedOutEntryPoint
+import io.element.android.libraries.architecture.createNode
+import io.element.android.libraries.di.AppScope
+import javax.inject.Inject
+
+@ContributesBinding(AppScope::class)
+class DefaultSignedOutEntryPoint @Inject constructor() : SignedOutEntryPoint {
+
+ override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): SignedOutEntryPoint.NodeBuilder {
+ val plugins = ArrayList()
+
+ return object : SignedOutEntryPoint.NodeBuilder {
+
+ override fun params(params: SignedOutEntryPoint.Params): SignedOutEntryPoint.NodeBuilder {
+ plugins += SignedOutNode.Inputs(params.sessionId)
+ return this
+ }
+
+ override fun build(): Node {
+ return parentNode.createNode(buildContext, plugins)
+ }
+ }
+ }
+}
diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutEvents.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutEvents.kt
new file mode 100644
index 0000000000..a2057226a4
--- /dev/null
+++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutEvents.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.signedout.impl
+
+sealed interface SignedOutEvents {
+ data object SignInAgain : SignedOutEvents
+}
diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutNode.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutNode.kt
new file mode 100644
index 0000000000..381daa7278
--- /dev/null
+++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutNode.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.signedout.impl
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import io.element.android.anvilannotations.ContributesNode
+import io.element.android.libraries.architecture.NodeInputs
+import io.element.android.libraries.architecture.inputs
+import io.element.android.libraries.di.AppScope
+import io.element.android.libraries.matrix.api.core.SessionId
+
+@ContributesNode(AppScope::class)
+class SignedOutNode @AssistedInject constructor(
+ @Assisted buildContext: BuildContext,
+ @Assisted plugins: List,
+ presenterFactory: SignedOutPresenter.Factory,
+) : Node(buildContext, plugins = plugins) {
+
+ data class Inputs(
+ val sessionId: SessionId,
+ ) : NodeInputs
+
+ private val inputs: Inputs = inputs()
+ private val presenter = presenterFactory.create(inputs.sessionId.value)
+
+ @Composable
+ override fun View(modifier: Modifier) {
+ val state = presenter.present()
+ SignedOutView(
+ state = state,
+ modifier = modifier
+ )
+ }
+}
diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutPresenter.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutPresenter.kt
new file mode 100644
index 0000000000..a93d22253d
--- /dev/null
+++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutPresenter.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.signedout.impl
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import io.element.android.libraries.architecture.Presenter
+import io.element.android.libraries.core.meta.BuildMeta
+import io.element.android.libraries.sessionstorage.api.SessionStore
+import kotlinx.coroutines.launch
+
+class SignedOutPresenter @AssistedInject constructor(
+ @Assisted private val sessionId: String, /* Cannot inject SessionId */
+ private val sessionStore: SessionStore,
+ private val buildMeta: BuildMeta,
+) : Presenter {
+
+ @AssistedFactory
+ interface Factory {
+ fun create(sessionId: String): SignedOutPresenter
+ }
+
+ @Composable
+ override fun present(): SignedOutState {
+ val sessions by sessionStore.sessionsFlow().collectAsState(initial = emptyList())
+ val signedOutSession by remember {
+ derivedStateOf { sessions.firstOrNull { it.userId == sessionId } }
+ }
+ val coroutineScope = rememberCoroutineScope()
+
+ fun handleEvents(event: SignedOutEvents) {
+ when (event) {
+ SignedOutEvents.SignInAgain -> coroutineScope.launch {
+ sessionStore.removeSession(sessionId)
+ }
+ }
+ }
+
+ return SignedOutState(
+ appName = buildMeta.applicationName,
+ signedOutSession = signedOutSession,
+ eventSink = ::handleEvents
+ )
+ }
+}
diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutState.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutState.kt
new file mode 100644
index 0000000000..eff0ab6d5b
--- /dev/null
+++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutState.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.signedout.impl
+
+import io.element.android.libraries.sessionstorage.api.SessionData
+
+// Do not use default value, so no member get forgotten in the presenters.
+data class SignedOutState(
+ val appName: String,
+ val signedOutSession: SessionData?,
+ val eventSink: (SignedOutEvents) -> Unit,
+)
diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt
new file mode 100644
index 0000000000..0182e87cf3
--- /dev/null
+++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.signedout.impl
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import io.element.android.libraries.matrix.api.core.SessionId
+import io.element.android.libraries.sessionstorage.api.LoginType
+import io.element.android.libraries.sessionstorage.api.SessionData
+
+open class SignedOutStateProvider : PreviewParameterProvider {
+ override val values: Sequence
+ get() = sequenceOf(
+ aSignedOutState(),
+ // Add other states here
+ )
+}
+
+fun aSignedOutState() = SignedOutState(
+ appName = "AppName",
+ signedOutSession = aSessionData(),
+ eventSink = {},
+)
+
+fun aSessionData(
+ sessionId: SessionId = SessionId("@alice:server.org"),
+ isTokenValid: Boolean = false,
+): SessionData {
+ return SessionData(
+ userId = sessionId.value,
+ deviceId = "aDeviceId",
+ accessToken = "anAccessToken",
+ refreshToken = "aRefreshToken",
+ homeserverUrl = "aHomeserverUrl",
+ oidcData = null,
+ slidingSyncProxy = null,
+ loginTimestamp = null,
+ isTokenValid = isTokenValid,
+ loginType = LoginType.UNKNOWN,
+ )
+}
diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt
new file mode 100644
index 0000000000..63845ead90
--- /dev/null
+++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutView.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.signedout.impl
+
+import androidx.activity.compose.BackHandler
+import androidx.annotation.DrawableRes
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.imePadding
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.systemBarsPadding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.AccountCircle
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.BiasAlignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
+import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
+import io.element.android.libraries.designsystem.atomic.organisms.InfoListItem
+import io.element.android.libraries.designsystem.atomic.organisms.InfoListOrganism
+import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.theme.components.Button
+import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial
+import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.ui.strings.CommonStrings
+import kotlinx.collections.immutable.persistentListOf
+
+@Composable
+fun SignedOutView(
+ state: SignedOutState,
+ modifier: Modifier = Modifier,
+) {
+ BackHandler(onBack = { state.eventSink(SignedOutEvents.SignInAgain) })
+ HeaderFooterPage(
+ modifier = modifier
+ .fillMaxSize()
+ .systemBarsPadding()
+ .imePadding(),
+ header = { SignedOutHeader(state) },
+ content = { SignedOutContent() },
+ footer = {
+ SignedOutFooter(
+ onSignInAgain = { state.eventSink(SignedOutEvents.SignInAgain) },
+ )
+ }
+ )
+}
+
+@Composable
+private fun SignedOutHeader(state: SignedOutState) {
+ IconTitleSubtitleMolecule(
+ modifier = Modifier.padding(top = 60.dp, bottom = 12.dp),
+ title = stringResource(id = R.string.screen_signed_out_title),
+ subTitle = stringResource(id = R.string.screen_signed_out_subtitle, state.appName),
+ iconImageVector = Icons.Filled.AccountCircle,
+ iconTint = ElementTheme.colors.iconSecondary,
+ )
+}
+
+@Composable
+private fun SignedOutContent(
+ modifier: Modifier = Modifier,
+) {
+ Box(
+ modifier = modifier.fillMaxSize(),
+ contentAlignment = BiasAlignment(
+ horizontalBias = 0f,
+ verticalBias = -0.4f
+ )
+ ) {
+ InfoListOrganism(
+ items = persistentListOf(
+ InfoListItem(
+ message = stringResource(id = R.string.screen_signed_out_reason_1),
+ iconComposable = { Icon(R.drawable.ic_lock_outline) },
+ ),
+ InfoListItem(
+ message = stringResource(id = R.string.screen_signed_out_reason_2),
+ iconComposable = { Icon(R.drawable.ic_devices) },
+ ),
+ InfoListItem(
+ message = stringResource(id = R.string.screen_signed_out_reason_3),
+ iconComposable = { Icon(R.drawable.ic_do_disturb_alt) },
+ ),
+ ),
+ textStyle = ElementTheme.typography.fontBodyMdMedium,
+ iconTint = ElementTheme.colors.textPrimary,
+ backgroundColor = ElementTheme.colors.temporaryColorBgSpecial
+ )
+ }
+}
+
+@Composable
+private fun Icon(
+ @DrawableRes iconResourceId: Int,
+ modifier: Modifier = Modifier,
+) {
+ Icon(
+ modifier = modifier
+ .size(20.dp),
+ resourceId = iconResourceId,
+ contentDescription = null,
+ tint = ElementTheme.colors.iconSecondary,
+ )
+}
+
+@Composable
+private fun SignedOutFooter(
+ modifier: Modifier = Modifier,
+ onSignInAgain: () -> Unit,
+) {
+ ButtonColumnMolecule(
+ modifier = modifier,
+ ) {
+ Button(
+ text = stringResource(id = CommonStrings.action_sign_in_again),
+ onClick = onSignInAgain,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+}
+
+@PreviewsDayNight
+@Composable
+internal fun SignedOutViewPreview(
+ @PreviewParameter(SignedOutStateProvider::class) state: SignedOutState,
+) = ElementPreview {
+ SignedOutView(
+ state = state,
+ )
+}
diff --git a/features/signedout/impl/src/main/res/drawable/ic_devices.xml b/features/signedout/impl/src/main/res/drawable/ic_devices.xml
new file mode 100644
index 0000000000..e01de99a1d
--- /dev/null
+++ b/features/signedout/impl/src/main/res/drawable/ic_devices.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/features/signedout/impl/src/main/res/drawable/ic_do_disturb_alt.xml b/features/signedout/impl/src/main/res/drawable/ic_do_disturb_alt.xml
new file mode 100644
index 0000000000..2ac90bd377
--- /dev/null
+++ b/features/signedout/impl/src/main/res/drawable/ic_do_disturb_alt.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/features/signedout/impl/src/main/res/drawable/ic_lock_outline.xml b/features/signedout/impl/src/main/res/drawable/ic_lock_outline.xml
new file mode 100644
index 0000000000..51831bf3c1
--- /dev/null
+++ b/features/signedout/impl/src/main/res/drawable/ic_lock_outline.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/features/signedout/impl/src/main/res/values-cs/translations.xml b/features/signedout/impl/src/main/res/values-cs/translations.xml
new file mode 100644
index 0000000000..06419f3cce
--- /dev/null
+++ b/features/signedout/impl/src/main/res/values-cs/translations.xml
@@ -0,0 +1,8 @@
+
+
+ "Změnili jste heslo v jiné relaci"
+ "Odstranili jste relaci z jiné relace"
+ "Správce vašeho serveru zrušil váš přístup"
+ "Je možné, že jste byli odhlášeni z některého z níže uvedených důvodů. Chcete-li pokračovat v používání %s, přihlaste se znovu."
+ "Jste odhlášeni"
+
diff --git a/features/signedout/impl/src/main/res/values-ru/translations.xml b/features/signedout/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..d3fd510afd
--- /dev/null
+++ b/features/signedout/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,8 @@
+
+
+ "Вы изменили свой пароль в другой сессии"
+ "Вы удалили сессию из другой сессии"
+ "Администратор вашего сервера аннулировал ваш доступ"
+ "Возможно, вы вышли из системы по одной из причин, перечисленных ниже. Пожалуйста, войдите в систему еще раз, чтобы продолжить использование %s."
+ "Вы вышли из системы"
+
diff --git a/features/signedout/impl/src/main/res/values-sk/translations.xml b/features/signedout/impl/src/main/res/values-sk/translations.xml
new file mode 100644
index 0000000000..1c0720b8ee
--- /dev/null
+++ b/features/signedout/impl/src/main/res/values-sk/translations.xml
@@ -0,0 +1,8 @@
+
+
+ "Zmenili ste heslo pri inej relácii"
+ "Odstránili ste reláciu z inej relácie"
+ "Správca vášho servera vám zrušil váš prístup"
+ "Možno ste boli odhlásení z jedného z nižšie uvedených dôvodov. Ak chcete pokračovať v používaní %s, prihláste sa znova."
+ "Ste odhlásený"
+
diff --git a/features/signedout/impl/src/main/res/values/localazy.xml b/features/signedout/impl/src/main/res/values/localazy.xml
new file mode 100644
index 0000000000..f70b0042d5
--- /dev/null
+++ b/features/signedout/impl/src/main/res/values/localazy.xml
@@ -0,0 +1,8 @@
+
+
+ "You’ve changed your password on another session"
+ "You have deleted the session from another session"
+ "Your server’s administrator has invalidated your access"
+ "You might have been signed out for one of the reasons listed below. Please sign in again to continue using %s."
+ "You’re signed out"
+
diff --git a/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/SignedOutPresenterTest.kt b/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/SignedOutPresenterTest.kt
new file mode 100644
index 0000000000..208d21154e
--- /dev/null
+++ b/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/SignedOutPresenterTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.signedout.impl
+
+import app.cash.molecule.RecompositionMode
+import app.cash.molecule.moleculeFlow
+import app.cash.turbine.test
+import com.google.common.truth.Truth.assertThat
+import io.element.android.libraries.matrix.api.core.SessionId
+import io.element.android.libraries.matrix.test.A_SESSION_ID
+import io.element.android.libraries.matrix.test.core.aBuildMeta
+import io.element.android.libraries.sessionstorage.api.SessionStore
+import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore
+import io.element.android.tests.testutils.WarmUpRule
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+
+class SignedOutPresenterTest {
+ @get:Rule
+ val warmUpRule = WarmUpRule()
+
+ private val appName = "AppName"
+
+ @Test
+ fun `present - initial state`() = runTest {
+ val aSessionData = aSessionData()
+ val sessionStore = InMemorySessionStore().apply {
+ storeData(aSessionData)
+ }
+ val presenter = createSignedOutPresenter(sessionStore = sessionStore)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ skipItems(1)
+ val initialState = awaitItem()
+ assertThat(initialState.appName).isEqualTo(appName)
+ assertThat(initialState.signedOutSession).isEqualTo(aSessionData)
+ }
+ }
+
+ @Test
+ fun `present - sign in again`() = runTest {
+ val aSessionData = aSessionData()
+ val sessionStore = InMemorySessionStore().apply {
+ storeData(aSessionData)
+ }
+ val presenter = createSignedOutPresenter(sessionStore = sessionStore)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ skipItems(1)
+ val initialState = awaitItem()
+ assertThat(initialState.signedOutSession).isEqualTo(aSessionData)
+ assertThat(sessionStore.getAllSessions()).isNotEmpty()
+ initialState.eventSink(SignedOutEvents.SignInAgain)
+ assertThat(awaitItem().signedOutSession).isNull()
+ assertThat(sessionStore.getAllSessions()).isEmpty()
+ }
+ }
+
+ private fun createSignedOutPresenter(
+ sessionId: SessionId = A_SESSION_ID,
+ sessionStore: SessionStore = InMemorySessionStore(),
+ ): SignedOutPresenter {
+ return SignedOutPresenter(
+ sessionId = sessionId.value,
+ sessionStore = sessionStore,
+ buildMeta = aBuildMeta(applicationName = appName),
+ )
+ }
+}
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt
index eeb659c82e..9ee44a790a 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt
@@ -42,8 +42,8 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Text
@@ -51,7 +51,6 @@ import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
-import kotlinx.coroutines.sync.Mutex
import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep as FlowStep
@Composable
@@ -74,7 +73,6 @@ fun VerifySelfSessionView(
val buttonsVisible by remember(verificationFlowStep) {
derivedStateOf { verificationFlowStep != FlowStep.AwaitingOtherDeviceResponse && verificationFlowStep != FlowStep.Completed }
}
- Mutex()
HeaderFooterPage(
modifier = modifier,
header = {
@@ -91,7 +89,7 @@ fun VerifySelfSessionView(
}
@Composable
-internal fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier = Modifier) {
+private fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier = Modifier) {
val iconResourceId = when (verificationFlowStep) {
FlowStep.Initial -> R.drawable.ic_verification_devices
FlowStep.Canceled -> R.drawable.ic_verification_warning
@@ -100,7 +98,7 @@ internal fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier =
}
val titleTextId = when (verificationFlowStep) {
FlowStep.Initial -> R.string.screen_session_verification_open_existing_session_title
- FlowStep.Canceled -> R.string.screen_session_verification_cancelled_title
+ FlowStep.Canceled -> CommonStrings.common_verification_cancelled
FlowStep.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_to_accept_title
FlowStep.Ready, is FlowStep.Verifying, FlowStep.Completed -> R.string.screen_session_verification_compare_emojis_title
}
@@ -120,7 +118,7 @@ internal fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier =
}
@Composable
-internal fun Content(flowState: FlowStep, modifier: Modifier = Modifier) {
+private fun Content(flowState: FlowStep, modifier: Modifier = Modifier) {
Column(modifier.fillMaxHeight(), verticalArrangement = Arrangement.Center) {
when (flowState) {
FlowStep.Initial, FlowStep.Ready, FlowStep.Canceled, FlowStep.Completed -> Unit
@@ -131,14 +129,14 @@ internal fun Content(flowState: FlowStep, modifier: Modifier = Modifier) {
}
@Composable
-internal fun ContentWaiting(modifier: Modifier = Modifier) {
+private fun ContentWaiting(modifier: Modifier = Modifier) {
Row(modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
CircularProgressIndicator()
}
}
@Composable
-internal fun ContentVerifying(verificationFlowStep: FlowStep.Verifying, modifier: Modifier = Modifier) {
+private fun ContentVerifying(verificationFlowStep: FlowStep.Verifying, modifier: Modifier = Modifier) {
// We want each row to have up to 4 emojis
val rows = verificationFlowStep.emojiList.chunked(4)
Column(modifier = modifier.fillMaxWidth()) {
@@ -157,7 +155,7 @@ internal fun ContentVerifying(verificationFlowStep: FlowStep.Verifying, modifier
}
@Composable
-internal fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifier) {
+private fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifier) {
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier) {
Text(
text = emoji.code,
@@ -175,7 +173,7 @@ internal fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifi
}
@Composable
-internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) {
+private fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) {
val verificationViewState = screenState.verificationFlowStep
val eventSink = screenState.eventSink
@@ -190,7 +188,7 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit)
R.string.screen_session_verification_they_match
}
}
- FlowStep.Ready -> R.string.screen_session_verification_positive_button_ready
+ FlowStep.Ready -> CommonStrings.action_start
else -> null
}
val negativeButtonTitle = when (verificationViewState) {
@@ -238,7 +236,7 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit)
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun VerifySelfSessionViewPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = ElementPreview {
VerifySelfSessionView(
diff --git a/features/verifysession/impl/src/main/res/values-cs/translations.xml b/features/verifysession/impl/src/main/res/values-cs/translations.xml
index 6bf8db5ac0..83e2c9316f 100644
--- a/features/verifysession/impl/src/main/res/values-cs/translations.xml
+++ b/features/verifysession/impl/src/main/res/values-cs/translations.xml
@@ -14,6 +14,4 @@
"Shodují se"
"Pro pokračování přijměte požadavek na zahájení ověření v jiné relaci."
"Čekání na přijetí žádosti"
- "Ověření zrušeno"
- "Začít"
diff --git a/features/verifysession/impl/src/main/res/values-de/translations.xml b/features/verifysession/impl/src/main/res/values-de/translations.xml
index 183477a4c1..7ac1ddce6f 100644
--- a/features/verifysession/impl/src/main/res/values-de/translations.xml
+++ b/features/verifysession/impl/src/main/res/values-de/translations.xml
@@ -14,6 +14,4 @@
"Sie stimmen überein"
"Akzeptiere die Anfrage, um den Verifizierungsprozess in deiner anderen Session zu starten, um fortzufahren."
"Warten auf die Annahme der Anfrage"
- "Verifizierung abgebrochen"
- "Start"
diff --git a/features/verifysession/impl/src/main/res/values-es/translations.xml b/features/verifysession/impl/src/main/res/values-es/translations.xml
index 386ecfc37c..e80fcc8682 100644
--- a/features/verifysession/impl/src/main/res/values-es/translations.xml
+++ b/features/verifysession/impl/src/main/res/values-es/translations.xml
@@ -14,6 +14,4 @@
"Coinciden"
"Acepta la solicitud para iniciar el proceso de verificación en tu otra sesión para continuar."
"A la espera de aceptar la solicitud"
- "Verificación cancelada"
- "Comenzar"
diff --git a/features/verifysession/impl/src/main/res/values-fr/translations.xml b/features/verifysession/impl/src/main/res/values-fr/translations.xml
index 9339d6c760..c9d3446f2a 100644
--- a/features/verifysession/impl/src/main/res/values-fr/translations.xml
+++ b/features/verifysession/impl/src/main/res/values-fr/translations.xml
@@ -14,6 +14,4 @@
"Ils correspondent"
"Pour continuer, acceptez la demande de lancement de la procédure de vérification dans votre autre session."
"En attente d’acceptation de la demande"
- "Vérification annulée"
- "Démarrer"
diff --git a/features/verifysession/impl/src/main/res/values-it/translations.xml b/features/verifysession/impl/src/main/res/values-it/translations.xml
index 7a6765adbf..852e94b463 100644
--- a/features/verifysession/impl/src/main/res/values-it/translations.xml
+++ b/features/verifysession/impl/src/main/res/values-it/translations.xml
@@ -14,6 +14,4 @@
"Corrispondono"
"Accetta la richiesta di avviare il processo di verifica nell\'altra sessione per continuare."
"In attesa di accettare la richiesta"
- "Verifica annullata"
- "Inizia"
diff --git a/features/verifysession/impl/src/main/res/values-ro/translations.xml b/features/verifysession/impl/src/main/res/values-ro/translations.xml
index e392438bcd..f772bd9b12 100644
--- a/features/verifysession/impl/src/main/res/values-ro/translations.xml
+++ b/features/verifysession/impl/src/main/res/values-ro/translations.xml
@@ -14,6 +14,4 @@
"Se potrivesc"
"Acceptați solicitarea de a începe procesul de verificare în cealaltă sesiune pentru a continua."
"Se așteptă acceptarea cererii"
- "Verificare anulată"
- "Începeți"
diff --git a/features/verifysession/impl/src/main/res/values-ru/translations.xml b/features/verifysession/impl/src/main/res/values-ru/translations.xml
index 552204fd7a..2de79c6ac2 100644
--- a/features/verifysession/impl/src/main/res/values-ru/translations.xml
+++ b/features/verifysession/impl/src/main/res/values-ru/translations.xml
@@ -14,6 +14,4 @@
"Они совпадают"
"Для продолжения работы примите запрос на запуск процесса проверки в другом сеансе."
"Ожидание принятия запроса"
- "Проверка отменена"
- "Начать"
diff --git a/features/verifysession/impl/src/main/res/values-sk/translations.xml b/features/verifysession/impl/src/main/res/values-sk/translations.xml
index 275924e9ec..c00a995266 100644
--- a/features/verifysession/impl/src/main/res/values-sk/translations.xml
+++ b/features/verifysession/impl/src/main/res/values-sk/translations.xml
@@ -14,6 +14,4 @@
"Zhodujú sa"
"Ak chcete pokračovať, prijmite žiadosť o spustenie procesu overenia vo vašej druhej relácii."
"Čaká sa na prijatie žiadosti"
- "Overovanie zrušené"
- "Spustiť"
diff --git a/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml b/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml
index fc59911a93..39a74d1bb6 100644
--- a/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml
@@ -1,9 +1,17 @@
+ "似乎出了一點問題。有可能是因為等候逾時,或是請求被拒絕。"
+ "確認顯示在其他工作階段上的表情符號是否和下方的相同。"
+ "比對表情符號"
+ "新的工作階段已完成驗證。它能夠存取您的加密訊息,而其他使用者會將它視為可信任的。"
+ "為了存取被加密的歷史訊息,您需要證明這是您本人。"
+ "開啟一個現存的工作階段"
+ "重新嘗試驗證"
"我準備好了"
"等待比對"
- "不相符"
- "相符"
- "驗證已取消"
- "開始"
+ "表情符號是唯一的,請相互比對,確認它們的排列順序是否相同。"
+ "不一樣"
+ "一樣"
+ "準備開始驗證,請到您的其他工作階段接受請求。"
+ "等待接受請求"
diff --git a/features/verifysession/impl/src/main/res/values/localazy.xml b/features/verifysession/impl/src/main/res/values/localazy.xml
index 67dc975128..cff455bcaa 100644
--- a/features/verifysession/impl/src/main/res/values/localazy.xml
+++ b/features/verifysession/impl/src/main/res/values/localazy.xml
@@ -14,6 +14,4 @@
"They match"
"Accept the request to start the verification process in your other session to continue."
"Waiting to accept request"
- "Verification cancelled"
- "Start"
diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt
index eee6c51a07..403324816f 100644
--- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt
+++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt
@@ -40,7 +40,7 @@ class VerifySelfSessionPresenterTests {
@Test
fun `present - Initial state is received`() = runTest {
- val presenter = createPresenter()
+ val presenter = createVerifySelfSessionPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -51,7 +51,7 @@ class VerifySelfSessionPresenterTests {
@Test
fun `present - Handles requestVerification`() = runTest {
val service = FakeSessionVerificationService()
- val presenter = createPresenter(service)
+ val presenter = createVerifySelfSessionPresenter(service)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -62,7 +62,7 @@ class VerifySelfSessionPresenterTests {
@Test
fun `present - Handles startSasVerification`() = runTest {
val service = FakeSessionVerificationService()
- val presenter = createPresenter(service)
+ val presenter = createVerifySelfSessionPresenter(service)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -81,7 +81,7 @@ class VerifySelfSessionPresenterTests {
@Test
fun `present - Cancelation on initial state does nothing`() = runTest {
- val presenter = createPresenter()
+ val presenter = createVerifySelfSessionPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -96,7 +96,7 @@ class VerifySelfSessionPresenterTests {
@Test
fun `present - A fail in the flow cancels it`() = runTest {
val service = FakeSessionVerificationService()
- val presenter = createPresenter(service)
+ val presenter = createVerifySelfSessionPresenter(service)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -113,7 +113,7 @@ class VerifySelfSessionPresenterTests {
@Test
fun `present - Canceling the flow once it's verifying cancels it`() = runTest {
val service = FakeSessionVerificationService()
- val presenter = createPresenter(service)
+ val presenter = createVerifySelfSessionPresenter(service)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -127,7 +127,7 @@ class VerifySelfSessionPresenterTests {
@Test
fun `present - When verifying, if we receive another challenge we ignore it`() = runTest {
val service = FakeSessionVerificationService()
- val presenter = createPresenter(service)
+ val presenter = createVerifySelfSessionPresenter(service)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -140,7 +140,7 @@ class VerifySelfSessionPresenterTests {
@Test
fun `present - Restart after cancelation returns to requesting verification`() = runTest {
val service = FakeSessionVerificationService()
- val presenter = createPresenter(service)
+ val presenter = createVerifySelfSessionPresenter(service)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -162,7 +162,7 @@ class VerifySelfSessionPresenterTests {
val service = FakeSessionVerificationService().apply {
givenEmojiList(emojis)
}
- val presenter = createPresenter(service)
+ val presenter = createVerifySelfSessionPresenter(service)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -176,7 +176,7 @@ class VerifySelfSessionPresenterTests {
@Test
fun `present - When verification is declined, the flow is canceled`() = runTest {
val service = FakeSessionVerificationService()
- val presenter = createPresenter(service)
+ val presenter = createVerifySelfSessionPresenter(service)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -210,7 +210,9 @@ class VerifySelfSessionPresenterTests {
return state
}
- private fun createPresenter(service: FakeSessionVerificationService = FakeSessionVerificationService()): VerifySelfSessionPresenter {
+ private fun createVerifySelfSessionPresenter(
+ service: FakeSessionVerificationService = FakeSessionVerificationService()
+ ): VerifySelfSessionPresenter {
return VerifySelfSessionPresenter(service, VerifySelfSessionStateMachine(service))
}
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 3dffb5b1c0..9a0bfc3a04 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -3,26 +3,25 @@
[versions]
# Project
-android_gradle_plugin = "8.1.1"
+android_gradle_plugin = "8.1.2"
kotlin = "1.9.10"
ksp = "1.9.10-1.0.13"
molecule = "1.2.1"
# AndroidX
-material = "1.9.0"
core = "1.12.0"
datastore = "1.0.0"
constraintlayout = "2.1.4"
constraintlayout_compose = "1.0.1"
recyclerview = "1.3.1"
lifecycle = "2.6.2"
-activity = "1.7.2"
+activity = "1.8.0"
startup = "1.1.1"
media3 = "1.1.1"
browser = "1.6.0"
# Compose
-compose_bom = "2023.09.00"
+compose_bom = "2023.10.00"
composecompiler = "1.5.3"
# Coroutines
@@ -40,24 +39,29 @@ datetime = "0.4.1"
serialization_json = "1.6.0"
showkase = "1.0.0-beta18"
jsoup = "1.16.1"
-appyx = "1.3.0"
+appyx = "1.4.0"
dependencycheck = "8.4.0"
-dependencyanalysis = "1.22.0"
+dependencyanalysis = "1.25.0"
stem = "2.3.0"
-sqldelight = "1.5.5"
-telephoto = "0.6.0"
-wysiwyg = "2.12.0"
+sqldelight = "2.0.0"
+telephoto = "0.6.2"
+wysiwyg = "2.14.1"
# DI
-dagger = "2.48"
+dagger = "2.48.1"
anvil = "2.4.8-1-8"
# Auto service
autoservice = "1.1.1"
# quality
-detekt = "1.23.0"
+detekt = "1.23.1"
dependencygraph = "0.12"
+junit = "4.13.2"
+androidx-test-ext-junit = "1.1.5"
+espresso-core = "3.5.1"
+appcompat = "1.6.1"
+material = "1.9.0"
[libraries]
# Project
@@ -69,7 +73,6 @@ kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", v
google_firebase_bom = "com.google.firebase:firebase-bom:32.3.1"
# AndroidX
-androidx_material = { module = "com.google.android.material:material", version.ref = "material" }
androidx_core = { module = "androidx.core:core", version.ref = "core" }
androidx_corektx = { module = "androidx.core:core-ktx", version.ref = "core" }
androidx_datastore_preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" }
@@ -94,27 +97,21 @@ androidx_preference = "androidx.preference:preference:1.2.1"
androidx_webkit = "androidx.webkit:webkit:1.8.0"
androidx_compose_bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose_bom" }
-# Warning: issue on alpha07, make sure this is working when upgrading
-# Context in https://github.com/vector-im/element-x-android/pull/1239#issuecomment-1711500332
-androidx_compose_material3 = "androidx.compose.material3:material3:1.2.0-alpha06"
+androidx_compose_material3 = "androidx.compose.material3:material3:1.2.0-alpha09"
# Coroutines
coroutines_core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
coroutines_test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
# Accompanist
-accompanist_animation = { module = "com.google.accompanist:accompanist-navigation-animation", version.ref = "accompanist" }
accompanist_permission = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
-accompanist_material = { module = "com.google.accompanist:accompanist-navigation-material", version.ref = "accompanist" }
accompanist_systemui = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" }
-accompanist_placeholder = { module = "com.google.accompanist:accompanist-placeholder-material", version.ref = "accompanist" }
-accompanist_flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist" }
# Libraries
squareup_seismic = "com.squareup:seismic:1.0.3"
# network
-network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:4.11.0"
+network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:4.12.0"
network_okhttp_logging = { module = "com.squareup.okhttp3:logging-interceptor" }
network_okhttp = { module = "com.squareup.okhttp3:okhttp" }
network_retrofit = "com.squareup.retrofit2:retrofit:2.9.0"
@@ -128,13 +125,14 @@ test_junit = "junit:junit:4.13.2"
test_runner = "androidx.test:runner:1.5.2"
test_uiautomator = "androidx.test.uiautomator:uiautomator:2.2.0"
test_junitext = "androidx.test.ext:junit:1.1.5"
-test_mockk = "io.mockk:mockk:1.13.7"
+test_mockk = "io.mockk:mockk:1.13.8"
test_barista = "com.adevinta.android:barista:4.3.0"
+test_konsist = "com.lemonappdev:konsist:0.13.0"
test_hamcrest = "org.hamcrest:hamcrest:2.2"
test_orchestrator = "androidx.test:orchestrator:1.4.2"
test_turbine = "app.cash.turbine:turbine:1.0.0"
test_truth = "com.google.truth:truth:1.1.5"
-test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.12"
+test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.13"
test_robolectric = "org.robolectric:robolectric:4.10.3"
test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" }
@@ -150,14 +148,14 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
timber = "com.jakewharton.timber:timber:5.0.1"
-matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.56"
+matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.62"
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
-sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
-sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
-sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
+sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
+sqldelight-driver-jvm = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" }
+sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
sqlcipher = "net.zetetic:android-database-sqlcipher:4.5.4"
-sqlite = "androidx.sqlite:sqlite:2.3.1"
+sqlite = "androidx.sqlite:sqlite-ktx:2.3.1"
unifiedpush = "com.github.UnifiedPush:android-connector:2.1.1"
otaliastudios_transcoder = "com.otaliastudios:transcoder:0.10.5"
vanniktech_blurhash = "com.vanniktech:blurhash:0.1.0"
@@ -169,7 +167,7 @@ maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.1"
# Analytics
posthog = "com.posthog.android:posthog:2.0.3"
-sentry = "io.sentry:sentry-android:6.29.0"
+sentry = "io.sentry:sentry-android:6.31.0"
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:e9cd9adaf18cec52ed851395eb84358b4f9b8d7f"
# Emojibase
@@ -191,6 +189,11 @@ google_autoservice_annotations = { module = "com.google.auto.service:auto-servic
# value of `composecompiler` (which is used to set composeOptions.kotlinCompilerExtensionVersion.
# See https://github.com/renovatebot/renovate/issues/18354
android_composeCompiler = { module = "androidx.compose.compiler:compiler", version.ref = "composecompiler" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
+espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
+appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
[bundles]
@@ -204,13 +207,13 @@ kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
anvil = { id = "com.squareup.anvil", version.ref = "anvil" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
-ktlint = "org.jlleitschuh.gradle.ktlint:11.6.0"
+ktlint = "org.jlleitschuh.gradle.ktlint:11.6.1"
dependencygraph = { id = "com.savvasdalkitsis.module-dependency-graph", version.ref = "dependencygraph" }
dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "dependencycheck" }
dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyanalysis" }
paparazzi = "app.cash.paparazzi:1.3.1"
kover = "org.jetbrains.kotlinx.kover:0.6.1"
-sqldelight = { id = "com.squareup.sqldelight", version.ref = "sqldelight" }
+sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
# Version '4.3.1.3277' introduced some regressions in CI time (more than 2x slower), so make sure
# this is no longer the case before upgrading.
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 6c7fa4d465..01f330a93e 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionSha256Sum=bb09982fdf52718e4c7b25023d10df6d35a5fff969860bdf5a5bd27a3ab27a9e
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
+distributionSha256Sum=f2b9ed0faf8472cbe469255ae6c86eddb77076c75191741b4a462f33128dd419
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index 0adc8e1a53..1aa94a4269 100755
--- a/gradlew
+++ b/gradlew
@@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC3045
+ # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC3045
+ # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -202,11 +202,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-# Collect all arguments for the java command;
-# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-# shell script including quotes and variable substitutions, so put them in
-# double quotes to make sure that they get re-expanded; and
-# * put everything else in single quotes, so that it's not re-expanded.
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BackstackNode.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BackstackNode.kt
index ec22c5e21f..deb6d0e63b 100644
--- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BackstackNode.kt
+++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BackstackNode.kt
@@ -19,6 +19,8 @@ package io.element.android.libraries.architecture
import androidx.compose.runtime.Stable
import com.bumble.appyx.core.children.ChildEntry
import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.navigation.model.combined.plus
+import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.plugin.Plugin
@@ -33,10 +35,11 @@ import com.bumble.appyx.navmodel.backstack.BackStack
abstract class BackstackNode(
val backstack: BackStack,
buildContext: BuildContext,
+ plugins: List,
+ val permanentNavModel: PermanentNavModel = PermanentNavModel(emptySet(), null),
childKeepMode: ChildEntry.KeepMode = ChildEntry.KeepMode.KEEP,
- plugins: List
) : ParentNode(
- navModel = backstack,
+ navModel = backstack + permanentNavModel,
buildContext = buildContext,
plugins = plugins,
childKeepMode = childKeepMode,
diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt
index c6373fbbf6..af2bca157b 100644
--- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt
+++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/mimetype/MimeTypes.kt
@@ -31,6 +31,7 @@ object MimeTypes {
const val BadJpg = "image/jpg"
const val Jpeg = "image/jpeg"
const val Gif = "image/gif"
+ const val WebP = "image/webp"
const val Videos = "video/*"
const val Mp4 = "video/mp4"
@@ -51,4 +52,12 @@ object MimeTypes {
fun String?.isMimeTypeFile() = this?.startsWith("file/").orFalse()
fun String?.isMimeTypeText() = this?.startsWith("text/").orFalse()
fun String?.isMimeTypeAny() = this?.startsWith("*/").orFalse()
+
+ fun fromFileExtension(fileExtension: String): String {
+ return when (fileExtension.lowercase()) {
+ "apk" -> Apk
+ "pdf" -> Pdf
+ else -> OctetStream
+ }
+ }
}
diff --git a/libraries/core/src/test/kotlin/io/element/android/libraries/core/extensions/ResultTests.kt b/libraries/core/src/test/kotlin/io/element/android/libraries/core/extensions/ResultTest.kt
similarity index 99%
rename from libraries/core/src/test/kotlin/io/element/android/libraries/core/extensions/ResultTests.kt
rename to libraries/core/src/test/kotlin/io/element/android/libraries/core/extensions/ResultTest.kt
index de5703b090..7f83c9de8f 100644
--- a/libraries/core/src/test/kotlin/io/element/android/libraries/core/extensions/ResultTests.kt
+++ b/libraries/core/src/test/kotlin/io/element/android/libraries/core/extensions/ResultTest.kt
@@ -19,7 +19,7 @@ package io.element.android.libraries.core.extensions
import com.google.common.truth.Truth.assertThat
import org.junit.Test
-class ResultTests {
+class ResultTest {
@Test
fun testFlatMap() {
diff --git a/libraries/cryptography/api/build.gradle.kts b/libraries/cryptography/api/build.gradle.kts
new file mode 100644
index 0000000000..e8cee5dbd6
--- /dev/null
+++ b/libraries/cryptography/api/build.gradle.kts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+plugins {
+ id("io.element.android-library")
+}
+
+android {
+ namespace = "io.element.android.libraries.cryptography.api"
+}
diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/AESEncryptionSpecs.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/AESEncryptionSpecs.kt
new file mode 100644
index 0000000000..d4be4a1f4f
--- /dev/null
+++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/AESEncryptionSpecs.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.cryptography.api
+
+import android.security.keystore.KeyProperties
+
+object AESEncryptionSpecs {
+ const val BLOCK_MODE = KeyProperties.BLOCK_MODE_GCM
+ const val PADDINGS = KeyProperties.ENCRYPTION_PADDING_NONE
+ const val ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
+ const val KEY_SIZE = 128
+ const val CIPHER_TRANSFORMATION = "$ALGORITHM/$BLOCK_MODE/$PADDINGS"
+}
diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionDecryptionService.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionDecryptionService.kt
new file mode 100644
index 0000000000..d670f7b1d2
--- /dev/null
+++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionDecryptionService.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.cryptography.api
+
+import javax.crypto.Cipher
+import javax.crypto.SecretKey
+
+/**
+ * Simple service to provide encryption and decryption operations.
+ */
+interface EncryptionDecryptionService {
+ fun createEncryptionCipher(key: SecretKey): Cipher
+ fun createDecryptionCipher(key: SecretKey, initializationVector: ByteArray): Cipher
+ fun encrypt(key: SecretKey, input: ByteArray): EncryptionResult
+ fun decrypt(key: SecretKey, encryptionResult: EncryptionResult): ByteArray
+}
diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionResult.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionResult.kt
new file mode 100644
index 0000000000..5aa3a0cbea
--- /dev/null
+++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/EncryptionResult.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+@file:OptIn(ExperimentalEncodingApi::class)
+
+package io.element.android.libraries.cryptography.api
+
+import java.nio.ByteBuffer
+import kotlin.io.encoding.Base64
+import kotlin.io.encoding.ExperimentalEncodingApi
+
+/**
+ * Holds the result of an encryption operation.
+ */
+class EncryptionResult(
+ val encryptedByteArray: ByteArray,
+ val initializationVector: ByteArray
+) {
+ fun toBase64(): String {
+ val initializationVectorSize = ByteBuffer.allocate(Int.SIZE_BYTES).putInt(initializationVector.size).array()
+ val cipherTextWithIv: ByteArray =
+ ByteBuffer.allocate(Int.SIZE_BYTES + initializationVector.size + encryptedByteArray.size)
+ .put(initializationVectorSize)
+ .put(initializationVector)
+ .put(encryptedByteArray)
+ .array()
+ return Base64.encode(cipherTextWithIv)
+ }
+
+ companion object {
+ /**
+ * @param base64 the base64 representation of the encrypted data.
+ * @return the [EncryptionResult] from the base64 representation.
+ */
+ fun fromBase64(base64: String): EncryptionResult {
+ val cipherTextWithIv = Base64.decode(base64)
+ val buffer = ByteBuffer.wrap(cipherTextWithIv)
+ val initializationVectorSize = buffer.int
+ val initializationVector = ByteArray(initializationVectorSize)
+ buffer.get(initializationVector)
+ val encryptedByteArray = ByteArray(buffer.remaining())
+ buffer.get(encryptedByteArray)
+ return EncryptionResult(encryptedByteArray, initializationVector)
+ }
+ }
+}
diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyProvider.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyProvider.kt
new file mode 100644
index 0000000000..85f57ac07f
--- /dev/null
+++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyProvider.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.cryptography.api
+
+import javax.crypto.SecretKey
+
+/**
+ * Simple interface to get or create a secret key for a given alias.
+ * Implementation should be able to store the generated key securely.
+ */
+interface SecretKeyProvider {
+ fun getOrCreateKey(alias: String): SecretKey
+}
diff --git a/libraries/cryptography/impl/build.gradle.kts b/libraries/cryptography/impl/build.gradle.kts
new file mode 100644
index 0000000000..263fecec27
--- /dev/null
+++ b/libraries/cryptography/impl/build.gradle.kts
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+plugins {
+ id("io.element.android-library")
+ alias(libs.plugins.anvil)
+}
+
+android {
+ namespace = "io.element.android.libraries.cryptography.impl"
+}
+
+anvil {
+ generateDaggerFactories.set(true)
+}
+
+dependencies {
+ anvil(projects.anvilcodegen)
+ implementation(libs.dagger)
+ implementation(projects.anvilannotations)
+ implementation(projects.libraries.di)
+ implementation(projects.libraries.cryptography.api)
+
+ testImplementation(libs.test.junit)
+ testImplementation(libs.test.truth)
+}
diff --git a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionService.kt b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionService.kt
new file mode 100644
index 0000000000..cf1ea93e3a
--- /dev/null
+++ b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionService.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.cryptography.impl
+
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.libraries.cryptography.api.AESEncryptionSpecs
+import io.element.android.libraries.cryptography.api.EncryptionDecryptionService
+import io.element.android.libraries.cryptography.api.EncryptionResult
+import io.element.android.libraries.di.AppScope
+import javax.crypto.Cipher
+import javax.crypto.SecretKey
+import javax.crypto.spec.GCMParameterSpec
+import javax.inject.Inject
+
+/**
+ * Default implementation of [EncryptionDecryptionService] using AES encryption.
+ */
+@ContributesBinding(AppScope::class)
+class AESEncryptionDecryptionService @Inject constructor() : EncryptionDecryptionService {
+
+ override fun createEncryptionCipher(key: SecretKey): Cipher {
+ return Cipher.getInstance(AESEncryptionSpecs.CIPHER_TRANSFORMATION).apply {
+ init(Cipher.ENCRYPT_MODE, key)
+ }
+ }
+
+ override fun createDecryptionCipher(key: SecretKey, initializationVector: ByteArray): Cipher {
+ val spec = GCMParameterSpec(128, initializationVector)
+ return Cipher.getInstance(AESEncryptionSpecs.CIPHER_TRANSFORMATION).apply {
+ init(Cipher.DECRYPT_MODE, key, spec)
+ }
+ }
+
+ override fun encrypt(key: SecretKey, input: ByteArray): EncryptionResult {
+ val cipher = createEncryptionCipher(key)
+ val encryptedData = cipher.doFinal(input)
+ return EncryptionResult(encryptedData, cipher.iv)
+ }
+
+ override fun decrypt(key: SecretKey, encryptionResult: EncryptionResult): ByteArray {
+ val cipher = createDecryptionCipher(key, encryptionResult.initializationVector)
+ return cipher.doFinal(encryptionResult.encryptedByteArray)
+ }
+}
diff --git a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyProvider.kt b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyProvider.kt
new file mode 100644
index 0000000000..2cd09ea8f6
--- /dev/null
+++ b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyProvider.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.cryptography.impl
+
+import android.annotation.SuppressLint
+import android.security.keystore.KeyGenParameterSpec
+import android.security.keystore.KeyProperties
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.libraries.cryptography.api.AESEncryptionSpecs
+import io.element.android.libraries.cryptography.api.SecretKeyProvider
+import io.element.android.libraries.di.AppScope
+import java.security.KeyStore
+import javax.crypto.KeyGenerator
+import javax.crypto.SecretKey
+import javax.inject.Inject
+
+private const val ANDROID_KEYSTORE = "AndroidKeyStore"
+
+/**
+ * Default implementation of [SecretKeyProvider] that uses the Android Keystore to store the keys.
+ * The generated key uses AES algorithm, with a key size of 128 bits, and the GCM block mode.
+ */
+@ContributesBinding(AppScope::class)
+class KeyStoreSecretKeyProvider @Inject constructor() : SecretKeyProvider {
+
+ // False positive lint issue
+ @SuppressLint("WrongConstant")
+ override fun getOrCreateKey(alias: String): SecretKey {
+ val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE)
+ val secretKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.SecretKeyEntry)
+ ?.secretKey
+ return if (secretKeyEntry == null) {
+ val generator = KeyGenerator.getInstance(AESEncryptionSpecs.ALGORITHM, ANDROID_KEYSTORE)
+ val keyGenSpec = KeyGenParameterSpec.Builder(
+ alias,
+ KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
+ )
+ .setBlockModes(AESEncryptionSpecs.BLOCK_MODE)
+ .setEncryptionPaddings(AESEncryptionSpecs.PADDINGS)
+ .setKeySize(AESEncryptionSpecs.KEY_SIZE)
+ .build()
+ generator.init(keyGenSpec)
+ generator.generateKey()
+ } else {
+ secretKeyEntry
+ }
+ }
+}
diff --git a/libraries/cryptography/impl/src/test/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionServiceTest.kt b/libraries/cryptography/impl/src/test/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionServiceTest.kt
new file mode 100644
index 0000000000..38e1c924ca
--- /dev/null
+++ b/libraries/cryptography/impl/src/test/kotlin/io/element/android/libraries/cryptography/impl/AESEncryptionDecryptionServiceTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.cryptography.impl
+
+import android.security.keystore.KeyProperties
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import java.security.GeneralSecurityException
+import javax.crypto.KeyGenerator
+
+class AESEncryptionDecryptionServiceTest {
+
+ private val encryptionDecryptionService = AESEncryptionDecryptionService()
+
+ @Test
+ fun `given a valid key then encrypt decrypt work`() {
+ val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES)
+ keyGenerator.init(128)
+ val key = keyGenerator.generateKey()
+ val input = "Hello World".toByteArray()
+ val encryptionResult = encryptionDecryptionService.encrypt(key, input)
+ val decrypted = encryptionDecryptionService.decrypt(key, encryptionResult)
+ assertThat(decrypted).isEqualTo(input)
+ }
+
+ @Test
+ fun `given a wrong key then decrypt fail`() {
+ val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES)
+ keyGenerator.init(128)
+ val encryptionKey = keyGenerator.generateKey()
+ val input = "Hello World".toByteArray()
+ val encryptionResult = encryptionDecryptionService.encrypt(encryptionKey, input)
+ val decryptionKey = keyGenerator.generateKey()
+ assertThrows(GeneralSecurityException::class.java) {
+ encryptionDecryptionService.decrypt(decryptionKey, encryptionResult)
+ }
+ }
+
+}
diff --git a/libraries/cryptography/test/build.gradle.kts b/libraries/cryptography/test/build.gradle.kts
new file mode 100644
index 0000000000..3b9074c897
--- /dev/null
+++ b/libraries/cryptography/test/build.gradle.kts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+plugins {
+ id("io.element.android-library")
+}
+
+android {
+ namespace = "io.element.android.libraries.cryptography.test"
+
+ dependencies {
+ api(projects.libraries.cryptography.api)
+ }
+}
diff --git a/libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/SimpleSecretKeyProvider.kt b/libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/SimpleSecretKeyProvider.kt
new file mode 100644
index 0000000000..d06a545d78
--- /dev/null
+++ b/libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/SimpleSecretKeyProvider.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.cryptography.test
+
+import io.element.android.libraries.cryptography.api.AESEncryptionSpecs
+import io.element.android.libraries.cryptography.api.SecretKeyProvider
+import javax.crypto.KeyGenerator
+import javax.crypto.SecretKey
+
+class SimpleSecretKeyProvider : SecretKeyProvider {
+
+ private var secretKeyForAlias = HashMap()
+
+ override fun getOrCreateKey(alias: String): SecretKey {
+ return secretKeyForAlias.getOrPut(alias) {
+ generateKey()
+ }
+ }
+
+ private fun generateKey(): SecretKey {
+ val keyGenerator = KeyGenerator.getInstance(AESEncryptionSpecs.ALGORITHM)
+ keyGenerator.init(AESEncryptionSpecs.KEY_SIZE)
+ return keyGenerator.generateKey()
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/VectorIcons.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/VectorIcons.kt
deleted file mode 100644
index 919c505439..0000000000
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/VectorIcons.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2023 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 io.element.android.libraries.designsystem
-
-object VectorIcons {
- val Copy = R.drawable.ic_content_copy
- val Forward = R.drawable.ic_forward
- val Delete = R.drawable.ic_delete
- val Reply = R.drawable.ic_reply
- val Edit = R.drawable.ic_edit
- val DoorOpen = R.drawable.ic_door_open_24
- val DeveloperMode = R.drawable.ic_developer_mode
- val ReportContent = R.drawable.ic_report_content
- val Groups = R.drawable.ic_groups
- val Share = R.drawable.ic_share
- val Poll = R.drawable.ic_poll
- val PollEnd = R.drawable.ic_poll_end
- val Bold = R.drawable.ic_bold
- val BulletList = R.drawable.ic_bullet_list
- val CodeBlock = R.drawable.ic_code_block
- val IndentIncrease = R.drawable.ic_indent_increase
- val IndentDecrease = R.drawable.ic_indent_decrease
- val InlineCode = R.drawable.ic_inline_code
- val Italic = R.drawable.ic_italic
- val Link = R.drawable.ic_link
- val NumberedList = R.drawable.ic_numbered_list
- val Quote = R.drawable.ic_quote
- val Strikethrough = R.drawable.ic_strikethrough
- val Underline = R.drawable.ic_underline
- val Mention = R.drawable.ic_mention
- val Mute = R.drawable.ic_mute
- val ThreadDecoration = R.drawable.ic_thread_decoration
- val Plus = R.drawable.ic_plus
- val Cancel = R.drawable.ic_cancel
-}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/animation/AlphaAnimation.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/animation/AlphaAnimation.kt
new file mode 100644
index 0000000000..bfcfc9ff32
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/animation/AlphaAnimation.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.designsystem.animation
+
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.platform.LocalInspectionMode
+
+@Composable
+fun alphaAnimation(
+ fromAlpha: Float = 0f,
+ toAlpha: Float = 1f,
+ delayMillis: Int = 150,
+ durationMillis: Int = 150,
+ label: String = "AlphaAnimation",
+): State {
+ val firstAlpha = if (LocalInspectionMode.current) 1f else fromAlpha
+ var alpha by remember { mutableFloatStateOf(firstAlpha) }
+ LaunchedEffect(Unit) { alpha = toAlpha }
+ return animateFloatAsState(
+ targetValue = alpha,
+ animationSpec = tween(
+ delayMillis = delayMillis,
+ durationMillis = durationMillis,
+ ),
+ label = label
+ )
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt
index 44553c72cd..512f0c3ed1 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt
@@ -37,7 +37,7 @@ import io.element.android.libraries.designsystem.R
import io.element.android.libraries.designsystem.modifiers.blurCompat
import io.element.android.libraries.designsystem.modifiers.blurredShapeShadow
import io.element.android.libraries.designsystem.modifiers.canUseBlurMaskFilter
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.theme.ElementTheme
@@ -149,25 +149,25 @@ sealed class ElementLogoAtomSize(
}
@Composable
-@DayNightPreviews
+@PreviewsDayNight
internal fun ElementLogoAtomMediumPreview() = ElementPreview {
ContentToPreview(ElementLogoAtomSize.Medium)
}
@Composable
-@DayNightPreviews
+@PreviewsDayNight
internal fun ElementLogoAtomLargePreview() = ElementPreview {
ContentToPreview(ElementLogoAtomSize.Large)
}
@Composable
-@DayNightPreviews
+@PreviewsDayNight
internal fun ElementLogoAtomMediumNoBlurShadowPreview() = ElementPreview {
ContentToPreview(ElementLogoAtomSize.Medium, useBlurredShadow = false)
}
@Composable
-@DayNightPreviews
+@PreviewsDayNight
internal fun ElementLogoAtomLargeNoBlurShadowPreview() = ElementPreview {
ContentToPreview(ElementLogoAtomSize.Large, useBlurredShadow = false)
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/PlaceholderAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/PlaceholderAtom.kt
index a7115be87d..fce7378c1e 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/PlaceholderAtom.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/PlaceholderAtom.kt
@@ -26,7 +26,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.placeholderBackground
import io.element.android.libraries.theme.ElementTheme
@@ -49,7 +49,7 @@ fun PlaceholderAtom(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PlaceholderAtomPreview() = ElementPreview {
// Use a Red background to see the shape
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt
index 6cd119a51c..8c8f009618 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/RoundedIconAtom.kt
@@ -32,7 +32,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial
@@ -96,7 +96,7 @@ private fun RoundedIconAtomSize.toIconSize(): Dp {
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun RoundedIconAtomPreview() = ElementPreview {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt
index a481b7984f..5ee00c5288 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt
@@ -26,7 +26,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.unreadIndicator
import io.element.android.libraries.theme.ElementTheme
@@ -46,7 +46,7 @@ fun UnreadIndicatorAtom(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun UnreadIndicatorAtomPreview() = ElementPreview {
UnreadIndicatorAtom()
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt
index 4631eea55d..bdd5fa4cf3 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt
@@ -24,7 +24,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
@@ -45,7 +45,7 @@ fun ButtonColumnMolecule(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ButtonColumnMoleculePreview() = ElementPreview {
ButtonColumnMolecule {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt
index dd6a04dee5..0d5afbe714 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt
@@ -22,7 +22,7 @@ import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.TextButton
@@ -39,7 +39,7 @@ fun ButtonRowMolecule(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ButtonRowMoleculePreview() = ElementPreview {
ButtonRowMolecule {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt
index 1a10aa2886..d4bf94a487 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt
@@ -31,7 +31,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.placeholderBackground
import io.element.android.libraries.theme.ElementTheme
@@ -61,7 +61,7 @@ fun IconTitlePlaceholdersRowMolecule(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun IconTitlePlaceholdersRowMoleculePreview() = ElementPreview {
IconTitlePlaceholdersRowMolecule(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt
index 43b7c235f3..52d8eb5baf 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitleSubtitleMolecule.kt
@@ -31,8 +31,8 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.R
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@@ -49,7 +49,7 @@ import io.element.android.libraries.theme.ElementTheme
@Composable
fun IconTitleSubtitleMolecule(
title: String,
- subTitle: String,
+ subTitle: String?,
modifier: Modifier = Modifier,
iconResourceId: Int? = null,
iconImageVector: ImageVector? = null,
@@ -73,23 +73,25 @@ fun IconTitleSubtitleMolecule(
style = ElementTheme.typography.fontHeadingMdBold,
color = MaterialTheme.colorScheme.primary,
)
- Spacer(Modifier.height(8.dp))
- Text(
- text = subTitle,
- modifier = Modifier.fillMaxWidth(),
- textAlign = TextAlign.Center,
- style = ElementTheme.typography.fontBodyMdRegular,
- color = MaterialTheme.colorScheme.secondary,
- )
+ if (subTitle != null) {
+ Spacer(Modifier.height(8.dp))
+ Text(
+ text = subTitle,
+ modifier = Modifier.fillMaxWidth(),
+ textAlign = TextAlign.Center,
+ style = ElementTheme.typography.fontBodyMdRegular,
+ color = MaterialTheme.colorScheme.secondary,
+ )
+ }
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun IconTitleSubtitleMoleculePreview() = ElementPreview {
IconTitleSubtitleMolecule(
- iconResourceId = R.drawable.ic_edit,
+ iconResourceId = R.drawable.ic_compound_chat,
title = "Title",
- subTitle = "Sub iitle",
+ subTitle = "Subtitle",
)
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/InfoListItemMolecule.kt
similarity index 84%
rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt
rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/InfoListItemMolecule.kt
index 2af9e77b99..c5c328a610 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/InfoListItemMolecule.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package io.element.android.libraries.designsystem.atomic.atoms
+package io.element.android.libraries.designsystem.atomic.molecules
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
@@ -24,17 +24,16 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Info
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
@Composable
fun InfoListItemMolecule(
@@ -68,7 +67,7 @@ fun InfoListItemMolecule(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun InfoListItemMoleculePreview() {
ElementPreview {
@@ -79,25 +78,25 @@ internal fun InfoListItemMoleculePreview() {
) {
InfoListItemMolecule(
message = { Text("A single item") },
- icon = { Icon(imageVector = Icons.Default.Info, contentDescription = null) },
+ icon = { Icon(resourceId = CommonDrawables.ic_compound_info, contentDescription = null) },
position = InfoListItemPosition.Single,
backgroundColor = color,
)
InfoListItemMolecule(
message = { Text("A top item") },
- icon = { Icon(imageVector = Icons.Default.Info, contentDescription = null) },
+ icon = { Icon(resourceId = CommonDrawables.ic_compound_info, contentDescription = null) },
position = InfoListItemPosition.Top,
backgroundColor = color,
)
InfoListItemMolecule(
message = { Text("A middle item") },
- icon = { Icon(imageVector = Icons.Default.Info, contentDescription = null) },
+ icon = { Icon(resourceId = CommonDrawables.ic_compound_info, contentDescription = null) },
position = InfoListItemPosition.Middle,
backgroundColor = color,
)
InfoListItemMolecule(
message = { Text("A bottom item") },
- icon = { Icon(imageVector = Icons.Default.Info, contentDescription = null) },
+ icon = { Icon(resourceId = CommonDrawables.ic_compound_info, contentDescription = null) },
position = InfoListItemPosition.Bottom,
backgroundColor = color,
)
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/InfoListOrganism.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/organisms/InfoListOrganism.kt
similarity index 79%
rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/InfoListOrganism.kt
rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/organisms/InfoListOrganism.kt
index 1bc82eb3b2..3a28344fb7 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/InfoListOrganism.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/organisms/InfoListOrganism.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package io.element.android.libraries.designsystem.atomic.molecules
+package io.element.android.libraries.designsystem.atomic.organisms
import androidx.annotation.DrawableRes
import androidx.compose.foundation.layout.Arrangement
@@ -27,12 +27,15 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.atomic.atoms.InfoListItemMolecule
-import io.element.android.libraries.designsystem.atomic.atoms.InfoListItemPosition
+import io.element.android.libraries.designsystem.atomic.molecules.InfoListItemMolecule
+import io.element.android.libraries.designsystem.atomic.molecules.InfoListItemPosition
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.persistentListOf
@Composable
fun InfoListOrganism(
@@ -84,3 +87,17 @@ data class InfoListItem(
val iconVector: ImageVector? = null,
val iconComposable: @Composable () -> Unit = {},
)
+
+@PreviewsDayNight
+@Composable
+internal fun InfoListOrganismPreview() = ElementPreview {
+ val items = persistentListOf(
+ InfoListItem(message = "A top item"),
+ InfoListItem(message = "A middle item"),
+ InfoListItem(message = "A bottom item"),
+ )
+ InfoListOrganism(
+ items,
+ backgroundColor = ElementTheme.materialColors.surfaceVariant,
+ )
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt
index 8158e8c08e..8d9718283e 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt
@@ -26,7 +26,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@@ -65,7 +65,7 @@ fun HeaderFooterPage(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun HeaderFooterPagePreview() = ElementPreview {
HeaderFooterPage(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt
index c411802d44..26fceb5687 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/OnBoardingPage.kt
@@ -30,7 +30,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.R
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@@ -87,7 +87,7 @@ fun OnBoardingPage(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun OnBoardingPagePreview() = ElementPreview {
OnBoardingPage(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt
index d4b203d4ae..7c9df66498 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/SunsetPage.kt
@@ -38,7 +38,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.R
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.text.withColoredPeriod
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
@@ -134,7 +134,7 @@ private fun SunsetBackground(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun SunsetPagePreview() = ElementPreview {
SunsetPage(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt
index b23c0cc2bb..c44a1140f0 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt
@@ -33,8 +33,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.IconButton
import androidx.compose.material3.TopAppBarDefaults
@@ -100,20 +98,20 @@ import androidx.compose.ui.unit.toSize
import coil.imageLoader
import coil.request.DefaultRequestOptions
import coil.request.ImageRequest
-import coil.size.Size
import com.airbnb.android.showkase.annotation.ShowkaseComposable
import com.vanniktech.blurhash.BlurHash
import io.element.android.libraries.designsystem.R
import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.components.avatar.AvatarData
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.toDp
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@@ -130,7 +128,9 @@ object BloomDefaults {
* Number of components to use with BlurHash to generate the blur effect.
* Larger values mean more detailed blurs.
*/
- const val HASH_COMPONENTS = 5
+ const val HASH_COMPONENTS = 4
+ const val ENCODE_SIZE_PX = 20
+ const val DECODE_SIZE_PX = 5
/** Default bloom layers. */
@Composable
@@ -190,7 +190,11 @@ fun Modifier.bloom(
if (hash == null) return@composed this
val hashedBitmap = remember(hash) {
- BlurHash.decode(hash, BloomDefaults.HASH_COMPONENTS, BloomDefaults.HASH_COMPONENTS)?.asImageBitmap()
+ BlurHash.decode(
+ blurHash = hash,
+ width = BloomDefaults.DECODE_SIZE_PX,
+ height = BloomDefaults.DECODE_SIZE_PX,
+ )?.asImageBitmap()
} ?: return@composed this
val density = LocalDensity.current
val pixelSize = remember(blurSize, density) { blurSize.toIntSize(density) }
@@ -328,7 +332,6 @@ fun Modifier.avatarBloom(
// Request the avatar contents to use as the bloom source
val context = LocalContext.current
- val density = LocalDensity.current
if (avatarData.url != null) {
val painterRequest = remember(avatarData) {
ImageRequest.Builder(context)
@@ -338,7 +341,7 @@ fun Modifier.avatarBloom(
// Needed to be able to read pixels from the Bitmap for the hash
.allowHardware(false)
// Reduce size so it loads faster for large avatars
- .size(with(density) { Size(64.dp.roundToPx(), 64.dp.roundToPx()) })
+ .size(BloomDefaults.ENCODE_SIZE_PX, BloomDefaults.ENCODE_SIZE_PX)
.build()
}
@@ -350,9 +353,9 @@ fun Modifier.avatarBloom(
context.imageLoader.execute(painterRequest).drawable ?: return@withContext
val bitmap = (drawable as? BitmapDrawable)?.bitmap ?: return@withContext
blurHash = BlurHash.encode(
- bitmap,
- BloomDefaults.HASH_COMPONENTS,
- BloomDefaults.HASH_COMPONENTS
+ bitmap = bitmap,
+ componentX = BloomDefaults.HASH_COMPONENTS,
+ componentY = BloomDefaults.HASH_COMPONENTS,
)
}
}
@@ -372,14 +375,18 @@ fun Modifier.avatarBloom(
// There is no URL so we'll generate an avatar with the initials and use that as the bloom source
val avatarColors = AvatarColorsProvider.provide(avatarData.id, ElementTheme.isLightTheme)
val initialsBitmap = initialsBitmap(
- width = avatarData.size.dp,
- height = avatarData.size.dp,
+ width = BloomDefaults.ENCODE_SIZE_PX.toDp(),
+ height = BloomDefaults.ENCODE_SIZE_PX.toDp(),
text = avatarData.initial,
- textColor = avatarColors.foreground,
+ textColor = avatarColors.foreground,
backgroundColor = avatarColors.background,
)
val hash = remember(avatarData, avatarColors) {
- BlurHash.encode(initialsBitmap.asAndroidBitmap(), BloomDefaults.HASH_COMPONENTS, BloomDefaults.HASH_COMPONENTS)
+ BlurHash.encode(
+ bitmap = initialsBitmap.asAndroidBitmap(),
+ componentX = BloomDefaults.HASH_COMPONENTS,
+ componentY = BloomDefaults.HASH_COMPONENTS,
+ )
}
bloom(
hash = hash,
@@ -430,7 +437,7 @@ private fun initialsBitmap(
val bitmap = Bitmap.createBitmap(width.roundToPx(), height.roundToPx(), Bitmap.Config.ARGB_8888).asImageBitmap()
androidx.compose.ui.graphics.Canvas(bitmap).also { canvas ->
canvas.drawCircle(centerPx.toOffset(), width.toPx() / 2, backgroundPaint)
- canvas.nativeCanvas.drawText(text, centerPx.x.toFloat() - result.size.width/2, centerPx.y * 2f - result.size.height/2 - 4, textPaint)
+ canvas.nativeCanvas.drawText(text, centerPx.x.toFloat() - result.size.width / 2, centerPx.y * 2f - result.size.height / 2 - 4, textPaint)
}
bitmap
}
@@ -457,7 +464,7 @@ fun DrawScope.drawWithLayer(block: DrawScope.() -> Unit) {
}
@OptIn(ExperimentalMaterial3Api::class)
-@DayNightPreviews
+@PreviewsDayNight
@ShowkaseComposable(group = PreviewGroup.Bloom)
@Composable
internal fun BloomPreview() {
@@ -467,7 +474,9 @@ internal fun BloomPreview() {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(topAppBarState)
ElementPreview {
Scaffold(
- modifier = Modifier.fillMaxSize().nestedScroll(scrollBehavior.nestedScrollConnection),
+ modifier = Modifier
+ .fillMaxSize()
+ .nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
Box {
MediumTopAppBar(
@@ -499,7 +508,10 @@ internal fun BloomPreview() {
},
actions = {
IconButton(onClick = {}) {
- Icon(imageVector = Icons.Default.Share, contentDescription = null)
+ Icon(
+ resourceId = CommonDrawables.ic_compound_share_android,
+ contentDescription = null,
+ )
}
},
title = {
@@ -525,21 +537,26 @@ internal fun BloomPreview() {
}
}
-class InitialsColorStateProvider : PreviewParameterProvider {
+class InitialsColorIntProvider : PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(0, 1, 2, 3, 4, 5, 6, 7)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
@ShowkaseComposable(group = PreviewGroup.Bloom)
-internal fun BloomInitialsPreview(@PreviewParameter(InitialsColorStateProvider::class) color: Int) {
+internal fun BloomInitialsPreview(@PreviewParameter(InitialsColorIntProvider::class) color: Int) {
ElementPreview {
val avatarColors = AvatarColorsProvider.provide("$color", ElementTheme.isLightTheme)
val bitmap = initialsBitmap(text = "F", backgroundColor = avatarColors.background, textColor = avatarColors.foreground)
- val hash = BlurHash.encode(bitmap.asAndroidBitmap(), BloomDefaults.HASH_COMPONENTS, BloomDefaults.HASH_COMPONENTS)
+ val hash = BlurHash.encode(
+ bitmap = bitmap.asAndroidBitmap(),
+ componentX = BloomDefaults.HASH_COMPONENTS,
+ componentY = BloomDefaults.HASH_COMPONENTS,
+ )
Box(
- modifier = Modifier.size(256.dp)
+ modifier = Modifier
+ .size(256.dp)
.bloom(
hash = hash,
background = if (ElementTheme.isLightTheme) {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BlurHashAsyncImage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BlurHashAsyncImage.kt
index 0ddd0b7346..dcfce743c1 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BlurHashAsyncImage.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/BlurHashAsyncImage.kt
@@ -71,7 +71,7 @@ fun BlurHashAsyncImage(
}
@Composable
-fun BlurHashImage(
+private fun BlurHashImage(
blurHash: String?,
modifier: Modifier = Modifier,
contentDescription: String? = null,
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledOutlinedTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledOutlinedTextField.kt
index 4939cac38d..3658db5206 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledOutlinedTextField.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledOutlinedTextField.kt
@@ -25,7 +25,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Text
@@ -65,7 +65,7 @@ fun LabelledOutlinedTextField(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun LabelledOutlinedTextFieldPreview() = ElementPreview {
Column {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt
index 63d6ef76d1..64edf11c81 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt
@@ -25,7 +25,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextField
@@ -65,7 +65,7 @@ fun LabelledTextField(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun LabelledTextFieldPreview() = ElementPreview {
Column {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt
index 93a0c5d436..79ff622301 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt
@@ -25,7 +25,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.R
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.theme.ElementTheme
@@ -49,7 +49,7 @@ fun PinIcon(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PinIconPreview() = ElementPreview {
PinIcon()
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt
index 2ff35c7f7a..e3186f5600 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt
@@ -26,7 +26,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Text
@@ -55,7 +55,7 @@ fun AsyncFailure(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AsyncFailurePreview() = ElementPreview {
AsyncFailure(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncLoading.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncLoading.kt
index abc60aa97a..7f6385658d 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncLoading.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncLoading.kt
@@ -23,7 +23,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
@@ -39,7 +39,7 @@ fun AsyncLoading(modifier: Modifier = Modifier) {
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AsyncLoadingPreview() = ElementPreview {
AsyncLoading()
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/UserAvatarPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/UserAvatarPreview.kt
index ab1ec70db4..f3681bbb52 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/UserAvatarPreview.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/UserAvatarPreview.kt
@@ -24,12 +24,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.colors.avatarColorsLight
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun UserAvatarPreview() = ElementPreview {
Column(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/BackButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/BackButton.kt
index b16013775c..30c8b95d74 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/BackButton.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/BackButton.kt
@@ -16,25 +16,25 @@
package io.element.android.libraries.designsystem.components.button
+import androidx.annotation.DrawableRes
import androidx.compose.foundation.layout.Column
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun BackButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
- imageVector: ImageVector = Icons.Default.ArrowBack,
+ // TODO Handle RTL languages
+ @DrawableRes resourceId: Int = CommonDrawables.ic_compound_arrow_left,
contentDescription: String = stringResource(CommonStrings.action_back),
enabled: Boolean = true,
) {
@@ -43,7 +43,7 @@ fun BackButton(
onClick = onClick,
enabled = enabled,
) {
- Icon(imageVector = imageVector, contentDescription = contentDescription)
+ Icon(resourceId = resourceId, contentDescription = contentDescription)
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt
index 76c256d967..2ab6b56808 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt
@@ -16,6 +16,7 @@
package io.element.android.libraries.designsystem.components.button
+import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
@@ -24,8 +25,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Share
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
@@ -33,19 +32,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@Composable
fun MainActionButton(
title: String,
- icon: ImageVector,
+ @DrawableRes iconResourceId: Int,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
@@ -64,7 +63,7 @@ fun MainActionButton(
) {
val tintColor = if (enabled) LocalContentColor.current else MaterialTheme.colorScheme.secondary
Icon(
- icon,
+ resourceId = iconResourceId,
contentDescription = contentDescription,
tint = tintColor,
)
@@ -88,8 +87,17 @@ internal fun MainActionButtonPreview() {
@Composable
private fun ContentsToPreview() {
Row(Modifier.padding(10.dp)) {
- MainActionButton(title = "Share", icon = Icons.Outlined.Share, onClick = { })
+ MainActionButton(
+ title = "Share",
+ iconResourceId = CommonDrawables.ic_compound_share_android,
+ onClick = { },
+ )
Spacer(modifier = Modifier.width(20.dp))
- MainActionButton(title = "Share", icon = Icons.Outlined.Share, onClick = { }, enabled = false)
+ MainActionButton(
+ title = "Share",
+ iconResourceId = CommonDrawables.ic_compound_share_android,
+ onClick = { },
+ enabled = false,
+ )
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt
index 979faf5b9a..e82ebfe532 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt
@@ -27,7 +27,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.airbnb.android.showkase.annotation.ShowkaseComposable
import io.element.android.libraries.designsystem.components.list.TextFieldListItem
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.DialogPreview
@@ -72,7 +72,7 @@ fun ListDialog(
}
@Composable
-internal fun ListDialogContent(
+private fun ListDialogContent(
listItems: LazyListScope.() -> Unit,
onDismissRequest: () -> Unit,
onSubmitClicked: () -> Unit,
@@ -98,7 +98,7 @@ internal fun ListDialogContent(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@ShowkaseComposable(group = PreviewGroup.Dialogs)
@Composable
internal fun ListDialogContentPreview() {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt
index af0bb4e1d1..5ed79f2c35 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt
@@ -29,7 +29,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.airbnb.android.showkase.annotation.ShowkaseComposable
import io.element.android.libraries.designsystem.components.list.CheckboxListItem
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.DialogPreview
@@ -78,7 +78,7 @@ fun MultipleSelectionDialog(
}
@Composable
-internal fun MultipleSelectionDialogContent(
+private fun MultipleSelectionDialogContent(
options: ImmutableList,
confirmButtonTitle: String,
onConfirmClicked: (List) -> Unit,
@@ -126,7 +126,7 @@ internal fun MultipleSelectionDialogContent(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@ShowkaseComposable(group = PreviewGroup.Dialogs)
@Composable
internal fun MultipleSelectionDialogContentPreview() {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt
index 5b75318136..db9e7faef2 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt
@@ -27,7 +27,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.airbnb.android.showkase.annotation.ShowkaseComposable
import io.element.android.libraries.designsystem.components.list.RadioButtonListItem
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.DialogPreview
@@ -74,7 +74,7 @@ fun SingleSelectionDialog(
}
@Composable
-internal fun SingleSelectionDialogContent(
+private fun SingleSelectionDialogContent(
options: ImmutableList,
onOptionSelected: (Int) -> Unit,
onDismissRequest: () -> Unit,
@@ -107,7 +107,7 @@ internal fun SingleSelectionDialogContent(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@ShowkaseComposable(group = PreviewGroup.Dialogs)
@Composable
internal fun SingleSelectionDialogContentPreview() {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt
index 4196edb0d5..e3036194a4 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCategory.kt
@@ -20,9 +20,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Announcement
-import androidx.compose.material.icons.filled.BugReport
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
@@ -30,6 +27,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@Composable
@@ -54,7 +52,7 @@ fun PreferenceCategory(
}
@Composable
-fun PreferenceCategoryTitle(title: String, modifier: Modifier = Modifier) {
+private fun PreferenceCategoryTitle(title: String, modifier: Modifier = Modifier) {
Text(
modifier = modifier.padding(
top = 20.dp,
@@ -79,11 +77,11 @@ private fun ContentToPreview() {
) {
PreferenceText(
title = "Title",
- icon = Icons.Default.BugReport,
+ iconResourceId = CommonDrawables.ic_compound_chat_problem,
)
PreferenceSwitch(
title = "Switch",
- icon = Icons.Default.Announcement,
+ iconResourceId = CommonDrawables.ic_compound_threads,
isChecked = true
)
PreferenceSlide(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCheckbox.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCheckbox.kt
index 50b5ab2fe4..b44b3ef2ad 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCheckbox.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceCheckbox.kt
@@ -16,6 +16,7 @@
package io.element.android.libraries.designsystem.components.preferences
+import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -23,8 +24,6 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Announcement
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -38,6 +37,7 @@ import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor
import io.element.android.libraries.designsystem.toSecondaryEnabledColor
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@Composable
@@ -48,6 +48,7 @@ fun PreferenceCheckbox(
supportingText: String? = null,
enabled: Boolean = true,
icon: ImageVector? = null,
+ @DrawableRes iconResourceId: Int? = null,
showIconAreaIfNoIcon: Boolean = false,
onCheckedChange: (Boolean) -> Unit = {},
) {
@@ -61,6 +62,7 @@ fun PreferenceCheckbox(
) {
PreferenceIcon(
icon = icon,
+ iconResourceId = iconResourceId,
enabled = enabled,
isVisible = showIconAreaIfNoIcon
)
@@ -100,14 +102,14 @@ private fun ContentToPreview() {
Column {
PreferenceCheckbox(
title = "Checkbox",
- icon = Icons.Default.Announcement,
+ iconResourceId = CommonDrawables.ic_compound_threads,
enabled = true,
isChecked = true
)
PreferenceCheckbox(
title = "Checkbox with supporting text",
supportingText = "Supporting text",
- icon = Icons.Default.Announcement,
+ iconResourceId = CommonDrawables.ic_compound_threads,
enabled = true,
isChecked = true
)
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceScreen.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferencePage.kt
similarity index 90%
rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceScreen.kt
rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferencePage.kt
index cc7e300641..653df4a74d 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceScreen.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferencePage.kt
@@ -28,25 +28,23 @@ import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Announcement
-import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@OptIn(ExperimentalLayoutApi::class)
@Composable
-fun PreferenceView(
+fun PreferencePage(
title: String,
modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {},
@@ -81,7 +79,7 @@ fun PreferenceView(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun PreferenceTopAppBar(
+private fun PreferenceTopAppBar(
title: String,
modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {},
@@ -103,10 +101,10 @@ fun PreferenceTopAppBar(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun PreferenceViewPreview() = ElementPreview {
- PreferenceView(
+ PreferencePage(
title = "Preference screen"
) {
PreferenceCategory(
@@ -115,18 +113,18 @@ internal fun PreferenceViewPreview() = ElementPreview {
PreferenceText(
title = "Title",
subtitle = "Some other text",
- icon = Icons.Default.BugReport,
+ iconResourceId = CommonDrawables.ic_compound_chat_problem,
)
PreferenceDivider()
PreferenceSwitch(
title = "Switch",
- icon = Icons.Default.Announcement,
+ iconResourceId = CommonDrawables.ic_compound_threads,
isChecked = true,
)
PreferenceDivider()
PreferenceCheckbox(
title = "Checkbox",
- icon = Icons.Default.Announcement,
+ iconResourceId = CommonDrawables.ic_compound_notifications,
isChecked = true,
)
PreferenceDivider()
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSlide.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSlide.kt
index 91a9852ed4..154722d440 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSlide.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSlide.kt
@@ -16,14 +16,13 @@
package io.element.android.libraries.designsystem.components.preferences
+import androidx.annotation.DrawableRes
import androidx.annotation.FloatRange
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Person
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
@@ -35,6 +34,7 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Slider
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@Composable
@@ -44,6 +44,7 @@ fun PreferenceSlide(
value: Float,
modifier: Modifier = Modifier,
icon: ImageVector? = null,
+ @DrawableRes iconResourceId: Int? = null,
showIconAreaIfNoIcon: Boolean = false,
enabled: Boolean = true,
summary: String? = null,
@@ -56,7 +57,11 @@ fun PreferenceSlide(
.defaultMinSize(minHeight = preferenceMinHeight)
.padding(vertical = 4.dp, horizontal = preferencePaddingHorizontal),
) {
- PreferenceIcon(icon = icon, isVisible = showIconAreaIfNoIcon)
+ PreferenceIcon(
+ icon = icon,
+ iconResourceId = iconResourceId,
+ isVisible = showIconAreaIfNoIcon,
+ )
Column(
modifier = Modifier
.weight(1f),
@@ -90,7 +95,7 @@ internal fun PreferenceSlidePreview() = ElementThemedPreview { ContentToPreview(
@Composable
private fun ContentToPreview() {
PreferenceSlide(
- icon = Icons.Default.Person,
+ iconResourceId = CommonDrawables.ic_compound_user_profile,
title = "Slide",
summary = "Summary",
value = 0.75F
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt
index b9a8d267f5..07c3f67270 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt
@@ -16,6 +16,7 @@
package io.element.android.libraries.designsystem.components.preferences
+import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -25,8 +26,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Announcement
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -40,6 +39,7 @@ import io.element.android.libraries.designsystem.theme.components.Switch
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor
import io.element.android.libraries.designsystem.toSecondaryEnabledColor
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@Composable
@@ -50,6 +50,7 @@ fun PreferenceSwitch(
subtitle: String? = null,
enabled: Boolean = true,
icon: ImageVector? = null,
+ @DrawableRes iconResourceId: Int? = null,
showIconAreaIfNoIcon: Boolean = false,
onCheckedChange: (Boolean) -> Unit = {},
switchAlignment: Alignment.Vertical = Alignment.CenterVertically
@@ -64,6 +65,7 @@ fun PreferenceSwitch(
) {
PreferenceIcon(
icon = icon,
+ iconResourceId = iconResourceId,
enabled = enabled,
isVisible = showIconAreaIfNoIcon
)
@@ -107,7 +109,7 @@ private fun ContentToPreview() {
PreferenceSwitch(
title = "Switch",
subtitle = "Subtitle Switch",
- icon = Icons.Default.Announcement,
+ iconResourceId = CommonDrawables.ic_compound_threads,
enabled = true,
isChecked = true
)
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceText.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceText.kt
index 9a5f6b9a41..d4bc627802 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceText.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceText.kt
@@ -16,6 +16,7 @@
package io.element.android.libraries.designsystem.components.preferences
+import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -25,8 +26,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.progressSemantics
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.BugReport
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -41,6 +40,7 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor
import io.element.android.libraries.designsystem.toSecondaryEnabledColor
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
/**
@@ -55,6 +55,7 @@ fun PreferenceText(
currentValue: String? = null,
loadingCurrentValue: Boolean = false,
icon: ImageVector? = null,
+ @DrawableRes iconResourceId: Int? = null,
showIconAreaIfNoIcon: Boolean = false,
tintColor: Color? = null,
onClick: () -> Unit = {},
@@ -71,6 +72,7 @@ fun PreferenceText(
) {
PreferenceIcon(
icon = icon,
+ iconResourceId = iconResourceId,
enabled = enabled,
isVisible = showIconAreaIfNoIcon,
tintColor = tintColor ?: enabled.toSecondaryEnabledColor(),
@@ -126,40 +128,40 @@ private fun ContentToPreview() {
) {
PreferenceText(
title = "Title",
- icon = Icons.Default.BugReport,
+ iconResourceId = CommonDrawables.ic_compound_chat_problem,
)
PreferenceText(
title = "Title",
subtitle = "Some content",
- icon = Icons.Default.BugReport,
+ iconResourceId = CommonDrawables.ic_compound_chat_problem,
)
PreferenceText(
title = "Title",
subtitle = "Some content",
- icon = Icons.Default.BugReport,
+ iconResourceId = CommonDrawables.ic_compound_chat_problem,
currentValue = "123",
)
PreferenceText(
title = "Title",
subtitle = "Some content",
- icon = Icons.Default.BugReport,
+ iconResourceId = CommonDrawables.ic_compound_chat_problem,
currentValue = "123",
enabled = false,
)
PreferenceText(
title = "Title",
subtitle = "Some content",
- icon = Icons.Default.BugReport,
+ iconResourceId = CommonDrawables.ic_compound_chat_problem,
loadingCurrentValue = true,
)
PreferenceText(
title = "Title",
- icon = Icons.Default.BugReport,
+ iconResourceId = CommonDrawables.ic_compound_chat_problem,
currentValue = "123",
)
PreferenceText(
title = "Title",
- icon = Icons.Default.BugReport,
+ iconResourceId = CommonDrawables.ic_compound_chat_problem,
loadingCurrentValue = true,
)
PreferenceText(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt
index ec44a7846a..d1c1281ee9 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/components/PreferenceIcon.kt
@@ -16,6 +16,7 @@
package io.element.android.libraries.designsystem.components.preferences.components
+import androidx.annotation.DrawableRes
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -34,15 +35,17 @@ import io.element.android.libraries.designsystem.toSecondaryEnabledColor
@Composable
fun PreferenceIcon(
- icon: ImageVector?,
modifier: Modifier = Modifier,
+ icon: ImageVector? = null,
+ @DrawableRes iconResourceId: Int? = null,
tintColor: Color? = null,
enabled: Boolean = true,
isVisible: Boolean = true,
) {
- if (icon != null) {
+ if (icon != null || iconResourceId != null) {
Icon(
imageVector = icon,
+ resourceId = iconResourceId,
contentDescription = "",
tint = tintColor ?: enabled.toSecondaryEnabledColor(),
modifier = modifier
@@ -61,5 +64,5 @@ internal fun PreferenceIconPreview(@PreviewParameter(ImageVectorProvider::class)
@Composable
private fun ContentToPreview(content: ImageVector?) {
- PreferenceIcon(content)
+ PreferenceIcon(icon = content)
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt
new file mode 100644
index 0000000000..52773dc0ab
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.designsystem.icons
+
+import io.element.android.libraries.designsystem.R
+
+internal val iconsCompound = listOf(
+ R.drawable.ic_compound_arrow_left,
+ R.drawable.ic_compound_arrow_right,
+ R.drawable.ic_compound_arrow_up_right,
+ R.drawable.ic_compound_block,
+ R.drawable.ic_compound_chat,
+ R.drawable.ic_compound_chat_new,
+ R.drawable.ic_compound_chat_problem,
+ R.drawable.ic_compound_check,
+ R.drawable.ic_compound_check_circle,
+ R.drawable.ic_compound_chevron_down,
+ R.drawable.ic_compound_chevron_left,
+ R.drawable.ic_compound_chevron_right,
+ R.drawable.ic_compound_chevron_up_down,
+ R.drawable.ic_compound_close,
+ R.drawable.ic_compound_computer,
+ R.drawable.ic_compound_delete,
+ R.drawable.ic_compound_download,
+ R.drawable.ic_compound_drag_grid,
+ R.drawable.ic_compound_drag_list,
+ R.drawable.ic_compound_end_call,
+ R.drawable.ic_compound_error,
+ R.drawable.ic_compound_export_archive,
+ R.drawable.ic_compound_extensions,
+ R.drawable.ic_compound_favourite_off,
+ R.drawable.ic_compound_favourite_on,
+ R.drawable.ic_compound_files,
+ R.drawable.ic_compound_filter,
+ R.drawable.ic_compound_grid_view,
+ R.drawable.ic_compound_info,
+ R.drawable.ic_compound_leave,
+ R.drawable.ic_compound_link,
+ R.drawable.ic_compound_lock,
+ R.drawable.ic_compound_lock_off,
+ R.drawable.ic_compound_marker_read_receipts,
+ R.drawable.ic_compound_mention,
+ R.drawable.ic_compound_mic_off_outline,
+ R.drawable.ic_compound_mic_off_solid,
+ R.drawable.ic_compound_mic_on_outline,
+ R.drawable.ic_compound_mic_on_solid,
+ R.drawable.ic_compound_mobile,
+ R.drawable.ic_compound_notifications,
+ R.drawable.ic_compound_notifications_off,
+ R.drawable.ic_compound_notifications_solid,
+ R.drawable.ic_compound_notifications_solid_off,
+ R.drawable.ic_compound_offline,
+ R.drawable.ic_compound_overflow_horizontal,
+ R.drawable.ic_compound_overflow_vertical,
+ R.drawable.ic_compound_polls,
+ R.drawable.ic_compound_pop_out,
+ R.drawable.ic_compound_public,
+ R.drawable.ic_compound_search,
+ R.drawable.ic_compound_settings,
+ R.drawable.ic_compound_settings_solid,
+ R.drawable.ic_compound_share,
+ R.drawable.ic_compound_share_android,
+ R.drawable.ic_compound_share_screen_outline,
+ R.drawable.ic_compound_share_screen_solid,
+ R.drawable.ic_compound_spotlight_view,
+ R.drawable.ic_compound_threads,
+ R.drawable.ic_compound_threads_solid,
+ R.drawable.ic_compound_user_add,
+ R.drawable.ic_compound_user_add_solid,
+ R.drawable.ic_compound_user_profile,
+ R.drawable.ic_compound_verified,
+ R.drawable.ic_compound_video_call,
+ R.drawable.ic_compound_video_call_declined,
+ R.drawable.ic_compound_video_call_missed,
+ R.drawable.ic_compound_video_call_off,
+ R.drawable.ic_compound_visibility_off,
+ R.drawable.ic_compound_visibility_on,
+ R.drawable.ic_compound_voice_call,
+ R.drawable.ic_compound_web_browser,
+)
+
+internal val iconsSeptember = listOf(
+ R.drawable.ic_september_add_reaction,
+ R.drawable.ic_september_attachment,
+ R.drawable.ic_september_compose_button,
+ R.drawable.ic_september_copy,
+ R.drawable.ic_september_decryption_error,
+ R.drawable.ic_september_edit_outline,
+ R.drawable.ic_september_edit_solid_16,
+ R.drawable.ic_september_forward,
+ R.drawable.ic_september_location,
+ R.drawable.ic_september_photo_camera,
+ R.drawable.ic_september_photo_video_library,
+ R.drawable.ic_september_reply,
+ R.drawable.ic_september_send,
+ R.drawable.ic_september_take_photo_camera,
+ R.drawable.ic_september_text_formatting,
+ R.drawable.ic_september_video_call,
+ R.drawable.ic_september_view_source,
+)
+
+// This list and all the drawable it contains should be removed at some point.
+// All the icons should be defined in Compound.
+internal val iconsOther = listOf(
+ R.drawable.ic_developer_mode,
+ R.drawable.ic_groups,
+ R.drawable.ic_indent_decrease,
+ R.drawable.ic_indent_increase,
+ R.drawable.ic_inline_code,
+ R.drawable.ic_italic,
+ R.drawable.ic_link,
+ R.drawable.ic_numbered_list,
+ R.drawable.ic_plus,
+ R.drawable.ic_poll_end,
+ R.drawable.ic_quote,
+ R.drawable.ic_strikethrough,
+ R.drawable.ic_thread_decoration,
+ R.drawable.ic_underline,
+)
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt
new file mode 100644
index 0000000000..446b62bc9c
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.designsystem.icons
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.theme.ElementTheme
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.toPersistentList
+
+@PreviewsDayNight
+@Composable
+internal fun IconsCompoundPart1Preview() = ElementPreview {
+ IconsPreview(
+ title = "R.drawable.ic_compound_* 1 / 2",
+ iconsList = iconsCompound.take(36).toPersistentList(),
+ iconNameTransform = { name ->
+ name.removePrefix("ic_compound_")
+ .replace("_", " ")
+ })
+}
+
+@PreviewsDayNight
+@Composable
+internal fun IconsCompoundPart2Preview() = ElementPreview {
+ IconsPreview(
+ title = "R.drawable.ic_compound_* 2 / 2",
+ iconsList = iconsCompound.drop(36).toPersistentList(),
+ iconNameTransform = { name ->
+ name.removePrefix("ic_compound_")
+ .replace("_", " ")
+ })
+}
+
+@PreviewsDayNight
+@Composable
+internal fun IconsSeptemberPreview() = ElementPreview {
+ IconsPreview(
+ title = "R.drawable.ic_september_*",
+ iconsList = iconsSeptember.toPersistentList(),
+ iconNameTransform = { name ->
+ name.removePrefix("ic_september_")
+ .replace("_", " ")
+ })
+}
+
+@PreviewsDayNight
+@Composable
+internal fun IconsOtherPreview() = ElementPreview {
+ IconsPreview(
+ title = "R.drawable.ic_*",
+ iconsList = iconsOther.toPersistentList(),
+ iconNameTransform = { name ->
+ name.removePrefix("ic_")
+ .replace("_", " ")
+ })
+}
+
+@Composable
+private fun IconsPreview(
+ title: String,
+ iconsList: ImmutableList,
+ iconNameTransform: (String) -> String,
+ modifier: Modifier = Modifier,
+) = ElementPreview {
+ val context = LocalContext.current
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(2.dp),
+ ) {
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ style = ElementTheme.typography.fontHeadingSmMedium,
+ text = title,
+ textAlign = TextAlign.Center,
+ )
+ iconsList.chunked(6).forEach { iconsRow ->
+ Row(horizontalArrangement = Arrangement.spacedBy(1.dp)) {
+ iconsRow.forEach { icon ->
+ Column(
+ modifier = Modifier.width(48.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Icon(
+ modifier = Modifier.padding(2.dp),
+ resourceId = icon,
+ contentDescription = null,
+ )
+ Text(
+ text = iconNameTransform(
+ context.resources
+ .getResourceEntryName(icon)
+ ),
+ modifier = Modifier.fillMaxWidth(),
+ textAlign = TextAlign.Center,
+ style = ElementTheme.typography.fontBodyXsMedium,
+ color = ElementTheme.colors.textSecondary,
+ )
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Blur.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Blur.kt
index fb3eb86c96..bb3dbec175 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Blur.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Blur.kt
@@ -21,7 +21,6 @@ import android.os.Build
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
import androidx.compose.ui.draw.BlurredEdgeTreatment
import androidx.compose.ui.draw.blur
import androidx.compose.ui.draw.drawBehind
@@ -94,8 +93,8 @@ fun Modifier.blurredShapeShadow(
fun Modifier.blurCompat(
radius: Dp,
edgeTreatment: BlurredEdgeTreatment = BlurredEdgeTreatment.Rectangle
-): Modifier = composed {
- when {
+): Modifier {
+ return when {
radius.value == 0f -> this
canUseBlur() -> blur(radius, edgeTreatment)
else -> this // Added in case we find a way to make this work on older devices
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt
index 3c2d61a76e..70777e0184 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreview.kt
@@ -16,90 +16,11 @@
package io.element.android.libraries.designsystem.preview
-import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.theme.ElementTheme
-@Composable
-fun ElementPreviewLight(
- showBackground: Boolean = true,
- content: @Composable () -> Unit
-) {
- ElementPreview(
- darkTheme = false,
- showBackground = showBackground,
- content = content
- )
-}
-
-@Composable
-fun ElementPreviewDark(
- showBackground: Boolean = true,
- content: @Composable () -> Unit
-) {
- ElementPreview(
- darkTheme = true,
- showBackground = showBackground,
- content = content
- )
-}
-
-@Composable
-@Suppress("ModifierMissing")
-fun ElementThemedPreview(
- showBackground: Boolean = true,
- vertical: Boolean = true,
- content: @Composable () -> Unit,
-) {
- Box(
- modifier = Modifier
- .background(Color.Gray)
- .padding(4.dp)
- ) {
- if (vertical) {
- Column {
- ElementPreview(
- darkTheme = false,
- showBackground = showBackground,
- content = content,
- )
- Spacer(modifier = Modifier.height(4.dp))
- ElementPreview(
- darkTheme = true,
- showBackground = showBackground,
- content = content
- )
- }
- } else {
- Row {
- ElementPreview(
- darkTheme = false,
- showBackground = showBackground,
- content = content,
- )
- Spacer(modifier = Modifier.width(4.dp))
- ElementPreview(
- darkTheme = true,
- showBackground = showBackground,
- content = content
- )
- }
- }
- }
-}
-
@Composable
@Suppress("ModifierMissing")
fun ElementPreview(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreviewDark.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreviewDark.kt
new file mode 100644
index 0000000000..595b82214d
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreviewDark.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.designsystem.preview
+
+import androidx.compose.runtime.Composable
+
+@Composable
+fun ElementPreviewDark(
+ showBackground: Boolean = true,
+ content: @Composable () -> Unit
+) {
+ ElementPreview(
+ darkTheme = true,
+ showBackground = showBackground,
+ content = content
+ )
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreviewLight.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreviewLight.kt
new file mode 100644
index 0000000000..fe1c2aa45a
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementPreviewLight.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.designsystem.preview
+
+import androidx.compose.runtime.Composable
+
+@Composable
+fun ElementPreviewLight(
+ showBackground: Boolean = true,
+ content: @Composable () -> Unit
+) {
+ ElementPreview(
+ darkTheme = false,
+ showBackground = showBackground,
+ content = content
+ )
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementThemedPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementThemedPreview.kt
new file mode 100644
index 0000000000..0a6b9fd46e
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/ElementThemedPreview.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.designsystem.preview
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+
+@Composable
+@Suppress("ModifierMissing")
+fun ElementThemedPreview(
+ showBackground: Boolean = true,
+ vertical: Boolean = true,
+ content: @Composable () -> Unit,
+) {
+ Box(
+ modifier = Modifier
+ .background(Color.Gray)
+ .padding(4.dp)
+ ) {
+ if (vertical) {
+ Column {
+ ElementPreview(
+ darkTheme = false,
+ showBackground = showBackground,
+ content = content,
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ ElementPreview(
+ darkTheme = true,
+ showBackground = showBackground,
+ content = content
+ )
+ }
+ } else {
+ Row {
+ ElementPreview(
+ darkTheme = false,
+ showBackground = showBackground,
+ content = content,
+ )
+ Spacer(modifier = Modifier.width(4.dp))
+ ElementPreview(
+ darkTheme = true,
+ showBackground = showBackground,
+ content = content
+ )
+ }
+ }
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/LargeHeightPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewWithLargeHeight.kt
similarity index 96%
rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/LargeHeightPreview.kt
rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewWithLargeHeight.kt
index 7078c9a2c4..a2551ab417 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/LargeHeightPreview.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewWithLargeHeight.kt
@@ -23,4 +23,4 @@ import androidx.compose.ui.tooling.preview.Preview
* adding extra vertical space so long scrolling components can be displayed. This is a helper for that functionality.
*/
@Preview(heightDp = 1000)
-annotation class LargeHeightPreview
+annotation class PreviewWithLargeHeight
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewsDayNight.kt
similarity index 97%
rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt
rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewsDayNight.kt
index b91e6a1024..28e7bd9576 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewsDayNight.kt
@@ -51,4 +51,4 @@ const val DAY_MODE_NAME = "D"
uiMode = Configuration.UI_MODE_NIGHT_YES,
fontScale = 1f,
)
-annotation class DayNightPreviews
+annotation class PreviewsDayNight
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/HorizontalRuler.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/HorizontalRuler.kt
index 56f97a2f84..f5636aaf10 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/HorizontalRuler.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/HorizontalRuler.kt
@@ -26,7 +26,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
/**
@@ -64,7 +64,7 @@ private fun HorizontalRulerItem(height: Dp, color: Color) {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun HorizontalRulerPreview() = ElementPreview {
HorizontalRuler()
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/VerticalRuler.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/VerticalRuler.kt
index 872ab7d149..057cb543b3 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/VerticalRuler.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/VerticalRuler.kt
@@ -26,7 +26,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
/**
@@ -64,7 +64,7 @@ private fun VerticalRulerItem(width: Dp, color: Color) {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun VerticalRulerPreview() = ElementPreview {
VerticalRuler()
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt
index fc793a8604..fd8f6ba37a 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt
@@ -21,7 +21,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
@@ -62,7 +62,7 @@ fun WithRulers(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun WithRulerPreview() = ElementPreview {
WithRulers(xRulersOffset = 20.dp, yRulersOffset = 15.dp) {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/swipe/SwipeableActionsState.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/swipe/SwipeableActionsState.kt
index 77d80cdde5..542c46fae1 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/swipe/SwipeableActionsState.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/swipe/SwipeableActionsState.kt
@@ -21,9 +21,10 @@ import androidx.compose.animation.core.tween
import androidx.compose.foundation.MutatePriority
import androidx.compose.foundation.gestures.DraggableState
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.FloatState
import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -41,8 +42,8 @@ class SwipeableActionsState {
/**
* The current position (in pixels) of the content.
*/
- val offset: State get() = offsetState
- private var offsetState = mutableStateOf(0f)
+ val offset: FloatState get() = offsetState
+ private var offsetState = mutableFloatStateOf(0f)
/**
* Whether the content is currently animating to reset its offset after it was swiped.
@@ -51,21 +52,21 @@ class SwipeableActionsState {
private set
val draggableState = DraggableState { delta ->
- val targetOffset = offsetState.value + delta
+ val targetOffset = offsetState.floatValue + delta
val isAllowed = isResettingOnRelease || targetOffset > 0f
- offsetState.value += if (isAllowed) delta else 0f
+ offsetState.floatValue += if (isAllowed) delta else 0f
}
suspend fun resetOffset() {
draggableState.drag(MutatePriority.PreventUserInput) {
isResettingOnRelease = true
try {
- Animatable(offsetState.value).animateTo(
+ Animatable(offsetState.floatValue).animateTo(
targetValue = 0f,
animationSpec = tween(durationMillis = 300),
) {
- dragBy(value - offsetState.value)
+ dragBy(value - offsetState.floatValue)
}
} finally {
isResettingOnRelease = false
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt
index c6408b662e..95cf050cc3 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt
@@ -52,7 +52,7 @@ fun Dp.applyScaleUp(): Dp = with(LocalDensity.current) {
@Preview
@Composable
-internal fun DpScalePreview_0_75f() = WithFontScale(0.75f) {
+internal fun DpScale_0_75f_Preview() = WithFontScale(0.75f) {
ElementPreviewLight {
val fontSizeInDp = 16.dp
Column(
@@ -77,7 +77,7 @@ internal fun DpScalePreview_0_75f() = WithFontScale(0.75f) {
@Preview
@Composable
-internal fun DpScalePreview_1_0f() = WithFontScale(1f) {
+internal fun DpScale_1_0f_Preview() = WithFontScale(1f) {
ElementPreviewLight {
val fontSizeInDp = 16.dp
Column(
@@ -102,7 +102,7 @@ internal fun DpScalePreview_1_0f() = WithFontScale(1f) {
@Preview
@Composable
-internal fun DpScalePreview_1_5f() = WithFontScale(1.5f) {
+internal fun DpScale_1_5f_Preview() = WithFontScale(1.5f) {
ElementPreviewLight {
val fontSizeInDp = 16.dp
Column(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt
index 474a64e0ed..b347402b41 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt
@@ -19,7 +19,7 @@ package io.element.android.libraries.designsystem.theme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.theme.compound.generated.SemanticColors
@@ -98,7 +98,7 @@ val SemanticColors.bgSubtleTertiary
val SemanticColors.temporaryColorBgSpecial
get() = if (isLight) Color(0xFFE4E8F0) else Color(0xFF3A4048)
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun ColorAliasesPreview() = ElementPreview {
ColorListPreview(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt
index c9bb2d9dd5..abe744bdbc 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt
@@ -24,8 +24,6 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
@@ -44,6 +42,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
import kotlin.math.max
@@ -417,7 +416,10 @@ internal fun DialogWithTitleIconAndOkButtonPreview() {
DialogPreview {
SimpleAlertDialogContent(
icon = {
- Icon(imageVector = Icons.Default.Notifications, contentDescription = null)
+ Icon(
+ resourceId = CommonDrawables.ic_compound_notifications_solid,
+ contentDescription = null
+ )
},
title = "Dialog Title",
content = "A dialog is a type of modal window that appears in front of app content to provide critical information, or prompt for a decision to be made. Learn more",
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/BottomSheetDragHandle.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/BottomSheetDragHandle.kt
new file mode 100644
index 0000000000..63dcf1b653
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/BottomSheetDragHandle.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.designsystem.theme.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.theme.ElementTheme
+
+@Composable
+fun BottomSheetDragHandle(
+ modifier: Modifier = Modifier
+) {
+ Box(
+ modifier = modifier
+ .height(36.dp)
+ .background(Color.Transparent)
+ .fillMaxWidth()
+ .clip(RectangleShape),
+ contentAlignment = Alignment.Center
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .requiredHeight(72.dp)
+ .offset(y = 18.dp)
+ .clip(MaterialTheme.shapes.extraLarge)
+ .background(MaterialTheme.colorScheme.background)
+ .border(0.5.dp, ElementTheme.colors.borderDisabled, MaterialTheme.shapes.extraLarge)
+ )
+
+ Box(
+ modifier = Modifier
+ .width(32.dp)
+ .height(4.dp)
+ .background(ElementTheme.colors.iconQuaternary, RoundedCornerShape(2.dp))
+ )
+ }
+}
+
+
+@PreviewsDayNight
+@Composable
+internal fun BottomSheetDragHandlePreview() = ElementPreview {
+ BottomSheetDragHandle()
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt
index ff613226e2..fce2cb3eff 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt
@@ -31,8 +31,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.progressSemantics
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.LocalContentColor
@@ -53,6 +51,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
// Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&mode=design&t=U03tOFZz5FSLVUMa-1
@@ -118,7 +117,7 @@ fun TextButton(
)
@Composable
-internal fun ButtonInternal(
+private fun ButtonInternal(
text: String,
onClick: () -> Unit,
style: ButtonStyle,
@@ -346,7 +345,7 @@ private fun ButtonCombinationPreview(
// With icon
ButtonRowPreview(
modifier = Modifier.then(modifier),
- leadingIcon = IconSource.Vector(Icons.Outlined.Share),
+ leadingIcon = IconSource.Resource(CommonDrawables.ic_compound_share_android),
style = style,
size = size,
)
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt
index 1e1ccf8d30..7dc48e40fd 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt
@@ -19,9 +19,6 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowRight
-import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MenuDefaults
@@ -33,6 +30,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
// Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=1032%3A44063&mode=design&t=rsNegTbEVLYAXL76-1
@@ -88,32 +86,32 @@ private fun ContentToPreview() {
DropdownMenuItem(
text = { Text(text = "Item") },
onClick = {},
- trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) },
+ trailingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chevron_right, contentDescription = null) },
)
HorizontalDivider()
DropdownMenuItem(
text = { Text(text = "Item") },
onClick = {},
- leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) },
+ leadingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chat_problem, contentDescription = null) },
)
DropdownMenuItem(
text = { Text(text = "Item") },
onClick = {},
- leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) },
- trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) },
+ leadingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chat_problem, contentDescription = null) },
+ trailingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chevron_right, contentDescription = null) },
)
DropdownMenuItem(
text = { Text(text = "Item") },
onClick = {},
enabled = false,
- leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) },
- trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) },
+ leadingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chat_problem, contentDescription = null) },
+ trailingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chevron_right, contentDescription = null) },
)
HorizontalDivider()
DropdownMenuItem(
text = { Text(text = "Multiline\nItem") },
onClick = {},
- trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) },
+ trailingIcon = { Icon(resourceId = CommonDrawables.ic_compound_chevron_right, contentDescription = null) },
)
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FloatingActionButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FloatingActionButton.kt
index 16af6203ca..ed32018f84 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FloatingActionButton.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/FloatingActionButton.kt
@@ -20,8 +20,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.material3.FloatingActionButtonElevation
import androidx.compose.material3.contentColorFor
@@ -34,6 +32,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
@Composable
fun FloatingActionButton(
@@ -67,7 +66,7 @@ internal fun FloatingActionButtonPreview() =
private fun ContentToPreview() {
Box(modifier = Modifier.padding(8.dp)) {
FloatingActionButton(onClick = {}) {
- Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "")
}
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt
index af63763113..1813db36f2 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Icon.kt
@@ -17,8 +17,6 @@
package io.element.android.libraries.designsystem.theme.components
import androidx.annotation.DrawableRes
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -30,6 +28,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
/**
* Icon is a wrapper around [androidx.compose.material3.Icon] which allows to use
@@ -146,5 +145,5 @@ internal fun IconImageVectorPreview() =
@Composable
private fun ContentToPreview() {
- Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "")
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt
index 7063ab1eb1..95132b11e2 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt
@@ -19,8 +19,6 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
@@ -30,6 +28,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
// Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=1182%3A48861&mode=design&t=Shlcvznm1oUyqGC2-1
@@ -67,20 +66,20 @@ private fun ContentToPreview() {
CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconPrimary) {
Row {
IconButton(onClick = {}) {
- Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "")
}
IconButton(enabled = false, onClick = {}) {
- Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "")
}
}
}
CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconSecondary) {
Row {
IconButton(onClick = {}) {
- Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "")
}
IconButton(enabled = false, onClick = {}) {
- Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ Icon(resourceId = CommonDrawables.ic_compound_close, contentDescription = "")
}
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt
index 321dc812a9..528aa3cfec 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt
@@ -17,8 +17,6 @@
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.clickable
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.ListItemColors
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.LocalContentColor
@@ -37,6 +35,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
// Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24208&mode=design&t=G5hCfkLB6GgXDuWe-1
@@ -119,7 +118,9 @@ fun ListItem(
androidx.compose.material3.ListItem(
headlineContent = decoratedHeadlineContent,
- modifier = if (onClick != null) Modifier.clickable(enabled = enabled, onClick = onClick).then(modifier) else modifier,
+ modifier = if (onClick != null) Modifier
+ .clickable(enabled = enabled, onClick = onClick)
+ .then(modifier) else modifier,
overlineContent = null,
supportingContent = decoratedSupportingContent,
leadingContent = decoratedLeadingContent,
@@ -135,27 +136,31 @@ fun ListItem(
*/
sealed interface ListItemStyle {
data object Default : ListItemStyle
- data object Primary: ListItemStyle
+ data object Primary : ListItemStyle
data object Destructive : ListItemStyle
- @Composable fun headlineColor() = when (this) {
+ @Composable
+ fun headlineColor() = when (this) {
Default, Primary -> ListItemDefaultColors.headline
Destructive -> ElementTheme.colors.textCriticalPrimary
}
- @Composable fun supportingTextColor() = when (this) {
+ @Composable
+ fun supportingTextColor() = when (this) {
Default, Primary -> ListItemDefaultColors.supportingText
// FIXME once we have a defined color for this value
Destructive -> ElementTheme.colors.textCriticalPrimary.copy(alpha = 0.8f)
}
- @Composable fun leadingIconColor() = when (this) {
+ @Composable
+ fun leadingIconColor() = when (this) {
Default -> ListItemDefaultColors.icon
Primary -> ElementTheme.colors.iconPrimary
Destructive -> ElementTheme.colors.iconCriticalPrimary
}
- @Composable fun trailingIconColor() = when (this) {
+ @Composable
+ fun trailingIconColor() = when (this) {
Default -> ListItemDefaultColors.icon
Primary -> ElementTheme.colors.iconPrimary
Destructive -> ElementTheme.colors.iconCriticalPrimary
@@ -169,15 +174,16 @@ object ListItemDefaultColors {
val icon: Color @Composable get() = ElementTheme.colors.iconTertiary
val iconDisabled: Color @Composable get() = ElementTheme.colors.iconDisabled
- val colors: ListItemColors @Composable get() = ListItemDefaults.colors(
- headlineColor = headline,
- supportingColor = supportingText,
- leadingIconColor = icon,
- trailingIconColor = icon,
- disabledHeadlineColor = headlineDisabled,
- disabledLeadingIconColor = iconDisabled,
- disabledTrailingIconColor = iconDisabled,
- )
+ val colors: ListItemColors
+ @Composable get() = ListItemDefaults.colors(
+ headlineColor = headline,
+ supportingColor = supportingText,
+ leadingIconColor = icon,
+ trailingIconColor = icon,
+ disabledHeadlineColor = headlineDisabled,
+ disabledLeadingIconColor = iconDisabled,
+ disabledTrailingIconColor = iconDisabled,
+ )
}
// region: Simple list item
@@ -191,7 +197,7 @@ internal fun ListItemTwoLinesSimplePreview() = PreviewItems.TwoLinesListItemPrev
@Preview(name = "List item (1 line) - Simple", group = PreviewGroup.ListItems)
@Composable
-internal fun ListItemSingleLineSimplePreview() = PreviewItems.OneLineListItemPreview()
+internal fun ListItemSingleLineSimplePreview() = PreviewItems.OneLineListItemPreview()
// endregion
// region: Trailing Checkbox
@@ -453,10 +459,12 @@ private object PreviewItems {
}
@Composable
- fun switch() : ListItemContent {
+ fun switch(): ListItemContent {
var checked by remember { mutableStateOf(false) }
return ListItemContent.Switch(checked = checked, onChange = { checked = !checked })
}
- fun icon() = ListItemContent.Icon(iconSource = IconSource.Vector(Icons.Outlined.Share))
+ fun icon() = ListItemContent.Icon(
+ iconSource = IconSource.Resource(CommonDrawables.ic_compound_share_android)
+ )
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSectionHeader.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSectionHeader.kt
new file mode 100644
index 0000000000..646c7cc6dd
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSectionHeader.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.designsystem.theme.components
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.LocalTextStyle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.ElementThemedPreview
+import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.theme.ElementTheme
+
+// Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24208&mode=design&t=G5hCfkLB6GgXDuWe-1
+
+/**
+ * List section header.
+ * @param title The title of the section.
+ * @param modifier The modifier to be applied to the section.
+ * @param hasDivider Whether to show a divider above the section or not. Default is `true`.
+ * @param description A description for the section. It's empty by default.
+ */
+@Composable
+fun ListSectionHeader(
+ title: String,
+ modifier: Modifier = Modifier,
+ hasDivider: Boolean = true,
+ description: @Composable () -> Unit = {},
+) {
+ Column(modifier.fillMaxWidth()) {
+ if (hasDivider) {
+ HorizontalDivider(modifier = Modifier.padding(top = 16.dp))
+ }
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Text(
+ text = title,
+ style = ElementTheme.typography.fontBodyLgMedium,
+ color = ElementTheme.colors.textPrimary,
+ )
+ CompositionLocalProvider(
+ LocalTextStyle provides ElementTheme.typography.fontBodySmRegular,
+ LocalContentColor provides ElementTheme.colors.textSecondary,
+ ) {
+ description()
+ }
+ }
+ }
+}
+
+@Preview(group = PreviewGroup.ListSections, name = "List section header")
+@Composable
+internal fun ListSectionHeaderPreview() {
+ ElementThemedPreview {
+ ListSectionHeader(
+ title = "List section",
+ hasDivider = false,
+ )
+ }
+}
+
+@Preview(group = PreviewGroup.ListSections, name = "List section header with divider")
+@Composable
+internal fun ListSectionHeaderWithDividerPreview() {
+ ElementThemedPreview {
+ ListSectionHeader(
+ title = "List section",
+ hasDivider = true,
+ )
+ }
+}
+
+@Preview(group = PreviewGroup.ListSections, name = "List section header with description")
+@Composable
+internal fun ListSectionHeaderWithDescriptionPreview() {
+ ElementThemedPreview {
+ ListSectionHeader(
+ title = "List section",
+ description = {
+ ListSupportingText(
+ text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
+ contentPadding = ListSupportingTextDefaults.Padding.None,
+ )
+ },
+ hasDivider = false,
+ )
+ }
+}
+
+@Preview(group = PreviewGroup.ListSections, name = "List section header with description and divider")
+@Composable
+internal fun ListSectionHeaderWithDescriptionAndDividerPreview() {
+ ElementThemedPreview {
+ ListSectionHeader(
+ title = "List section",
+ description = {
+ ListSupportingText(
+ text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
+ contentPadding = ListSupportingTextDefaults.Padding.None,
+ )
+ },
+ hasDivider = true,
+ )
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSection.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSupportingText.kt
similarity index 66%
rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSection.kt
rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSupportingText.kt
index cab1d493fc..f76562f0cd 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSection.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSupportingText.kt
@@ -16,17 +16,10 @@
package io.element.android.libraries.designsystem.theme.components
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Share
-import androidx.compose.material3.LocalContentColor
-import androidx.compose.material3.LocalTextStyle
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ExperimentalTextApi
@@ -37,47 +30,11 @@ import io.element.android.libraries.designsystem.components.ClickableLinkText
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
// Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24208&mode=design&t=G5hCfkLB6GgXDuWe-1
-/**
- * List section header.
- * @param title The title of the section.
- * @param modifier The modifier to be applied to the section.
- * @param hasDivider Whether to show a divider above the section or not. Default is `true`.
- * @param description A description for the section. It's empty by default.
- */
-@Composable
-fun ListSectionHeader(
- title: String,
- modifier: Modifier = Modifier,
- hasDivider: Boolean = true,
- description: @Composable () -> Unit = {},
-) {
- Column(modifier.fillMaxWidth()) {
- if (hasDivider) {
- HorizontalDivider(modifier = Modifier.padding(top = 16.dp))
- }
- Column(
- modifier = Modifier.padding(16.dp),
- verticalArrangement = Arrangement.spacedBy(8.dp)
- ) {
- Text(
- text = title,
- style = ElementTheme.typography.fontBodyLgMedium,
- color = ElementTheme.colors.textPrimary,
- )
- CompositionLocalProvider(
- LocalTextStyle provides ElementTheme.typography.fontBodySmRegular,
- LocalContentColor provides ElementTheme.colors.textSecondary,
- ) {
- description()
- }
- }
- }
-}
-
/**
* List supporting text item. Used to display an explanation in the list with a pre-formatted style.
* @param text The text to display.
@@ -128,12 +85,16 @@ object ListSupportingTextDefaults {
sealed interface Padding {
/** No padding. */
data object None : Padding
+
/** Default padding, it will align fine with a [ListItem] with no leading content. */
data object Default : Padding
+
/** It will align to a [ListItem] with an [Icon] or [Checkbox] as leading content. */
data object SmallLeadingContent : Padding
+
/** It will align to with a [ListItem] with a [Switch] as leading content. */
data object LargeLeadingContent : Padding
+
/** It will align to with a [ListItem] with a custom start [padding]. */
data class Custom(val padding: Dp) : Padding
@@ -164,68 +125,6 @@ object ListSupportingTextDefaults {
}
}
-// region: List header previews
-
-@Preview(group = PreviewGroup.ListSections, name = "List section header")
-@Composable
-internal fun ListSectionHeaderPreview() {
- ElementThemedPreview {
- ListSectionHeader(
- title = "List section",
- hasDivider = false,
- )
- }
-}
-
-@Preview(group = PreviewGroup.ListSections, name = "List section header with divider")
-@Composable
-internal fun ListSectionHeaderWithDividerPreview() {
- ElementThemedPreview {
- ListSectionHeader(
- title = "List section",
- hasDivider = true,
- )
- }
-}
-
-@Preview(group = PreviewGroup.ListSections, name = "List section header with description")
-@Composable
-internal fun ListSectionHeaderWithDescriptionPreview() {
- ElementThemedPreview {
- ListSectionHeader(
- title = "List section",
- description = {
- ListSupportingText(
- text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
- contentPadding = ListSupportingTextDefaults.Padding.None,
- )
- },
- hasDivider = false,
- )
- }
-}
-
-@Preview(group = PreviewGroup.ListSections, name = "List section header with description and divider")
-@Composable
-internal fun ListSectionHeaderWithDescriptionAndDividerPreview() {
- ElementThemedPreview {
- ListSectionHeader(
- title = "List section",
- description = {
- ListSupportingText(
- text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
- contentPadding = ListSupportingTextDefaults.Padding.None,
- )
- },
- hasDivider = true,
- )
- }
-}
-
-// endregion
-
-// region: List supporting text previews
-
@Preview(group = PreviewGroup.ListSections, name = "List supporting text - no padding")
@Composable
internal fun ListSupportingTextNoPaddingPreview() {
@@ -256,7 +155,10 @@ internal fun ListSupportingTextDefaultPaddingPreview() {
internal fun ListSupportingTextSmallPaddingPreview() {
ElementThemedPreview {
Column {
- ListItem(headlineContent = { Text("A title") }, leadingContent = ListItemContent.Icon(IconSource.Vector(Icons.Outlined.Share)))
+ ListItem(
+ headlineContent = { Text("A title") },
+ leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_share_android))
+ )
ListSupportingText(
text = "Supporting line text lorem ipsum dolor sit amet, consectetur. Read more",
contentPadding = ListSupportingTextDefaults.Padding.SmallLeadingContent,
@@ -292,5 +194,3 @@ internal fun ListSupportingTextCustomPaddingPreview() {
}
}
}
-
-// endregion
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt
index d868f49965..017e6ed3e7 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt
@@ -18,8 +18,6 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.TopAppBarColors
@@ -32,6 +30,7 @@ import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@OptIn(ExperimentalMaterial3Api::class)
@@ -74,7 +73,10 @@ private fun ContentToPreview() {
actions = {
TextButton(text = "Action", onClick = {})
IconButton(onClick = {}) {
- Icon(imageVector = Icons.Default.Share, contentDescription = null)
+ Icon(
+ resourceId = CommonDrawables.ic_compound_share_android,
+ contentDescription = null,
+ )
}
}
)
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt
index 1eb73532d6..65d156fbbc 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt
@@ -24,9 +24,6 @@ import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
-import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SearchBarColors
@@ -46,6 +43,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -99,7 +97,7 @@ fun SearchBar(
{
IconButton(onClick = { onQueryChange("") }) {
Icon(
- imageVector = Icons.Default.Close,
+ resourceId = CommonDrawables.ic_compound_close,
contentDescription = stringResource(CommonStrings.action_clear),
)
}
@@ -109,7 +107,7 @@ fun SearchBar(
!active -> {
{
Icon(
- imageVector = Icons.Default.Search,
+ resourceId = CommonDrawables.ic_compound_search,
contentDescription = stringResource(CommonStrings.action_search),
tint = MaterialTheme.colorScheme.tertiary,
)
@@ -195,11 +193,11 @@ sealed interface SearchBarResultState {
@Preview(group = PreviewGroup.Search)
@Composable
-internal fun SearchBarPreviewInactive() = ElementThemedPreview { ContentToPreview() }
+internal fun SearchBarInactivePreview() = ElementThemedPreview { ContentToPreview() }
@Preview(group = PreviewGroup.Search)
@Composable
-internal fun SearchBarPreviewActiveEmptyQuery() = ElementThemedPreview {
+internal fun SearchBarActiveEmptyQueryPreview() = ElementThemedPreview {
ContentToPreview(
query = "",
active = true,
@@ -208,7 +206,7 @@ internal fun SearchBarPreviewActiveEmptyQuery() = ElementThemedPreview {
@Preview(group = PreviewGroup.Search)
@Composable
-internal fun SearchBarPreviewActiveWithQuery() = ElementThemedPreview {
+internal fun SearchBarActiveWithQueryPreview() = ElementThemedPreview {
ContentToPreview(
query = "search term",
active = true,
@@ -217,7 +215,7 @@ internal fun SearchBarPreviewActiveWithQuery() = ElementThemedPreview {
@Preview(group = PreviewGroup.Search)
@Composable
-internal fun SearchBarPreviewActiveWithQueryNoBackButton() = ElementThemedPreview {
+internal fun SearchBarActiveWithQueryNoBackButtonPreview() = ElementThemedPreview {
ContentToPreview(
query = "search term",
active = true,
@@ -227,7 +225,7 @@ internal fun SearchBarPreviewActiveWithQueryNoBackButton() = ElementThemedPrevie
@Preview(group = PreviewGroup.Search)
@Composable
-internal fun SearchBarPreviewActiveWithNoResults() = ElementThemedPreview {
+internal fun SearchBarActiveWithNoResultsPreview() = ElementThemedPreview {
ContentToPreview(
query = "search term",
active = true,
@@ -237,7 +235,7 @@ internal fun SearchBarPreviewActiveWithNoResults() = ElementThemedPreview {
@Preview(group = PreviewGroup.Search)
@Composable
-internal fun SearchBarPreviewActiveWithContent() = ElementThemedPreview {
+internal fun SearchBarActiveWithContentPreview() = ElementThemedPreview {
ContentToPreview(
query = "search term",
active = true,
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Slider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Slider.kt
index 872376583d..2041d7998d 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Slider.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Slider.kt
@@ -22,7 +22,7 @@ import androidx.compose.material3.SliderColors
import androidx.compose.material3.SliderDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
@@ -62,7 +62,7 @@ internal fun SlidersPreview() = ElementThemedPreview { ContentToPreview() }
@Composable
private fun ContentToPreview() {
- var value by remember { mutableStateOf(0.33f) }
+ var value by remember { mutableFloatStateOf(0.33f) }
Column {
Slider(onValueChange = { value = it }, value = value, enabled = true)
Slider(steps = 10, onValueChange = { value = it }, value = value, enabled = true)
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt
index d2969fc3e9..d610144198 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt
@@ -17,8 +17,6 @@
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.SnackbarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -29,6 +27,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.button.ButtonVisuals
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.theme.SnackBarLabelColorDark
import io.element.android.libraries.theme.SnackBarLabelColorLight
@@ -119,8 +118,10 @@ internal fun SnackbarWithActionAndCloseButtonPreview() {
ElementThemedPreview {
Snackbar(
message = "Snackbar supporting text",
- action = ButtonVisuals.Text("Action", {}),
- dismissAction = ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), {})
+ action = ButtonVisuals.Text("Action") {},
+ dismissAction = ButtonVisuals.Icon(
+ IconSource.Resource(CommonDrawables.ic_compound_close)
+ ) {}
)
}
}
@@ -140,7 +141,9 @@ internal fun SnackbarWithActionOnNewLineAndCloseButtonPreview() {
Snackbar(
message = "Snackbar supporting text",
action = ButtonVisuals.Text("Action", {}),
- dismissAction = ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), {}),
+ dismissAction = ButtonVisuals.Icon(
+ IconSource.Resource(CommonDrawables.ic_compound_close)
+ ) {},
actionOnNewLine = true
)
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt
index 93d11e8c9a..6fd38653f8 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt
@@ -18,8 +18,6 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.TopAppBarColors
@@ -32,6 +30,7 @@ import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@OptIn(ExperimentalMaterial3Api::class)
@@ -74,7 +73,10 @@ private fun ContentToPreview() {
actions = {
TextButton(text = "Action", onClick = {})
IconButton(onClick = {}) {
- Icon(imageVector = Icons.Default.Share, contentDescription = null)
+ Icon(
+ resourceId = CommonDrawables.ic_compound_share_android,
+ contentDescription = null,
+ )
}
}
)
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt
index e0435ee30e..1b2bfd2188 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt
@@ -30,13 +30,13 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup
@Preview(group = PreviewGroup.DateTimePickers)
@Composable
-internal fun DatePickerPreviewLight() {
+internal fun DatePickerLightPreview() {
ElementPreviewLight { ContentToPreview() }
}
@Preview(group = PreviewGroup.DateTimePickers)
@Composable
-internal fun DatePickerPreviewDark() {
+internal fun DatePickerDarkPreview() {
ElementPreviewDark { ContentToPreview() }
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt
index 2ca04f1008..4bbb20b6a2 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt
@@ -17,7 +17,6 @@
package io.element.android.libraries.designsystem.theme.components.previews
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowRight
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -32,6 +31,7 @@ import io.element.android.libraries.designsystem.theme.components.DropdownMenu
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
@Preview(group = PreviewGroup.Menus)
@Composable
@@ -51,7 +51,10 @@ internal fun MenuPreview() {
val trailingIcon: @Composable (() -> Unit)? = if (i in 3..4) {
@Composable {
- Icon(Icons.Filled.ArrowRight, contentDescription = "Favorite")
+ Icon(
+ resourceId = CommonDrawables.ic_compound_chevron_right,
+ contentDescription = "Favorite",
+ )
}
} else {
null
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt
index d900dd6d8b..9e56a38137 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt
@@ -55,7 +55,7 @@ internal fun TimePickerHorizontalPreview() {
@OptIn(ExperimentalMaterial3Api::class)
@Preview(group = PreviewGroup.DateTimePickers)
@Composable
-internal fun TimePickerVerticalPreviewLight() {
+internal fun TimePickerVerticalLightPreview() {
ElementPreviewLight {
AlertDialogContent(
buttons = { /*TODO*/ },
@@ -77,7 +77,7 @@ internal fun TimePickerVerticalPreviewLight() {
@OptIn(ExperimentalMaterial3Api::class)
@Preview(group = PreviewGroup.DateTimePickers)
@Composable
-internal fun TimePickerVerticalPreviewDark() {
+internal fun TimePickerVerticalDarkPreview() {
val pickerState = rememberTimePickerState(
initialHour = 12,
initialMinute = 0,
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/BooleanProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/BooleanProvider.kt
index 43d0f9e797..1bc35aed89 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/BooleanProvider.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/BooleanProvider.kt
@@ -20,5 +20,5 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
open class BooleanProvider : PreviewParameterProvider {
override val values: Sequence
- get() = sequenceOf(false, true)
+ get() = sequenceOf(true, false)
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/PairCombinedProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/KeepScreenOn.kt
similarity index 61%
rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/PairCombinedProvider.kt
rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/KeepScreenOn.kt
index 0f53d44e44..a9cf913e2a 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/PairCombinedProvider.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/KeepScreenOn.kt
@@ -16,15 +16,21 @@
package io.element.android.libraries.designsystem.utils
-import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.ui.platform.LocalView
-open class PairCombinedProvider(
- private val provider: Pair, PreviewParameterProvider>
-) : PreviewParameterProvider> {
- override val values: Sequence>
- get() = provider.first.values.flatMap { first ->
- provider.second.values.map { second ->
- first to second
+@Composable
+fun KeepScreenOn(
+ keepScreenOn: Boolean = true
+) {
+ if (keepScreenOn) {
+ val currentView = LocalView.current
+ DisposableEffect(Unit) {
+ currentView.keepScreenOn = true
+ onDispose {
+ currentView.keepScreenOn = false
}
}
+ }
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarDispatcher.kt
similarity index 67%
rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt
rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarDispatcher.kt
index 9458bf748a..9275ea1a76 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarDispatcher.kt
@@ -14,13 +14,8 @@
* limitations under the License.
*/
-package io.element.android.libraries.designsystem.utils
+package io.element.android.libraries.designsystem.utils.snackbar
-import androidx.annotation.StringRes
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
-import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -28,11 +23,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.components.button.ButtonVisuals
-import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.Snackbar
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.currentCoroutineContext
@@ -40,7 +31,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.sync.Mutex
-import java.util.concurrent.atomic.AtomicBoolean
/**
* A global dispatcher of [SnackbarMessage] to be displayed in [Snackbar] via a [SnackbarHostState].
@@ -80,20 +70,6 @@ fun SnackbarDispatcher.collectSnackbarMessageAsState(): State
return snackbarMessage.collectAsState(initial = null)
}
-@Composable
-fun SnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) {
- androidx.compose.material3.SnackbarHost(hostState, modifier) { data ->
- Snackbar(
- modifier = Modifier.padding(12.dp), // Add default padding
- message = data.visuals.message,
- action = data.visuals.actionLabel?.let { ButtonVisuals.Text(it, data::performAction) },
- dismissAction = if (data.visuals.withDismissAction) {
- ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), data::dismiss)
- } else null,
- )
- }
-}
-
/**
* Helper method to display a [SnackbarMessage] in a [SnackbarHostState] handling cancellations.
*/
@@ -126,19 +102,3 @@ fun rememberSnackbarHostState(snackbarMessage: SnackbarMessage?): SnackbarHostSt
}
return snackbarHostState
}
-
-/**
- * A message to be displayed in a [Snackbar].
- * @param messageResId The message to be displayed.
- * @param duration The duration of the message. The default value is [SnackbarDuration.Short].
- * @param actionResId The action text to be displayed. The default value is `null`.
- * @param isDisplayed Used to track if the current message is already displayed or not.
- * @param action The action to be performed when the action is clicked.
- */
-data class SnackbarMessage(
- @StringRes val messageResId: Int,
- val duration: SnackbarDuration = SnackbarDuration.Short,
- @StringRes val actionResId: Int? = null,
- val isDisplayed: AtomicBoolean = AtomicBoolean(false),
- val action: () -> Unit = {},
-)
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarHost.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarHost.kt
new file mode 100644
index 0000000000..257c23a2b7
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarHost.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.designsystem.utils.snackbar
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.components.button.ButtonVisuals
+import io.element.android.libraries.designsystem.theme.components.IconSource
+import io.element.android.libraries.designsystem.theme.components.Snackbar
+import io.element.android.libraries.designsystem.utils.CommonDrawables
+
+@Composable
+fun SnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) {
+ androidx.compose.material3.SnackbarHost(hostState, modifier) { data ->
+ Snackbar(
+ modifier = Modifier.padding(12.dp), // Add default padding
+ message = data.visuals.message,
+ action = data.visuals.actionLabel?.let { ButtonVisuals.Text(it, data::performAction) },
+ dismissAction = if (data.visuals.withDismissAction) {
+ ButtonVisuals.Icon(
+ IconSource.Resource(CommonDrawables.ic_compound_close),
+ data::dismiss
+ )
+ } else null,
+ )
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarMessage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarMessage.kt
new file mode 100644
index 0000000000..4c254f9027
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarMessage.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.designsystem.utils.snackbar
+
+import androidx.annotation.StringRes
+import androidx.compose.material3.SnackbarDuration
+import java.util.concurrent.atomic.AtomicBoolean
+
+/**
+ * A message to be displayed in a [Snackbar].
+ * @param messageResId The message to be displayed.
+ * @param duration The duration of the message. The default value is [SnackbarDuration.Short].
+ * @param actionResId The action text to be displayed. The default value is `null`.
+ * @param isDisplayed Used to track if the current message is already displayed or not.
+ * @param action The action to be performed when the action is clicked.
+ */
+data class SnackbarMessage(
+ @StringRes val messageResId: Int,
+ val duration: SnackbarDuration = SnackbarDuration.Short,
+ @StringRes val actionResId: Int? = null,
+ val isDisplayed: AtomicBoolean = AtomicBoolean(false),
+ val action: () -> Unit = {},
+)
diff --git a/libraries/designsystem/src/main/res/drawable/ic_bold.xml b/libraries/designsystem/src/main/res/drawable/ic_bold.xml
index 5a08fee2f3..fd53812873 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_bold.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_bold.xml
@@ -5,5 +5,5 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_bullet_list.xml b/libraries/designsystem/src/main/res/drawable/ic_bullet_list.xml
index 103d0b380d..dd7ebfc59f 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_bullet_list.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_bullet_list.xml
@@ -5,5 +5,5 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_code_block.xml b/libraries/designsystem/src/main/res/drawable/ic_code_block.xml
index 18279bd8b5..ed08196804 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_code_block.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_code_block.xml
@@ -5,5 +5,5 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_send.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_left.xml
similarity index 65%
rename from libraries/textcomposer/impl/src/main/res/drawable/ic_send.xml
rename to libraries/designsystem/src/main/res/drawable/ic_compound_arrow_left.xml
index 2ed6e6e53e..0f4a60e5a3 100644
--- a/libraries/textcomposer/impl/src/main/res/drawable/ic_send.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_left.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
+ android:pathData="M12.207,5.293C12.598,5.683 12.598,6.317 12.207,6.707L7.914,11H18.5C19.052,11 19.5,11.448 19.5,12C19.5,12.552 19.052,13 18.5,13H7.914L12.207,17.293C12.598,17.683 12.598,18.317 12.207,18.707C11.817,19.098 11.183,19.098 10.793,18.707L4.793,12.707C4.402,12.317 4.402,11.683 4.793,11.293L10.793,5.293C11.183,4.902 11.817,4.902 12.207,5.293Z"
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_right.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_right.xml
new file mode 100644
index 0000000000..03062d4dea
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_right.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_up_right.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_up_right.xml
new file mode 100644
index 0000000000..31213ed168
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_arrow_up_right.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_block.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_block.xml
new file mode 100644
index 0000000000..1139ad2291
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_block.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chat.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chat.xml
new file mode 100644
index 0000000000..55e7efe70d
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_chat.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chat_new.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chat_new.xml
new file mode 100644
index 0000000000..a1481a57d3
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_chat_new.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chat_problem.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chat_problem.xml
new file mode 100644
index 0000000000..6bfcc9331c
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_chat_problem.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_check.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_check.xml
new file mode 100644
index 0000000000..879d9a28d3
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_check.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_check_circle.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_check_circle.xml
new file mode 100644
index 0000000000..652e4458ea
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_check_circle.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_down.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_down.xml
new file mode 100644
index 0000000000..c3624052df
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_down.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_left.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_left.xml
new file mode 100644
index 0000000000..fd6f26a330
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_left.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_right.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_right.xml
new file mode 100644
index 0000000000..c45219234c
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_right.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_up_down.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_up_down.xml
new file mode 100644
index 0000000000..eca37146ab
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_chevron_up_down.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_close.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_close.xml
new file mode 100644
index 0000000000..1256db427c
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_close.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_computer.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_computer.xml
new file mode 100644
index 0000000000..c8b4009abc
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_computer.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_delete.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_delete.xml
new file mode 100644
index 0000000000..1170f16553
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_delete.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_download.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_download.xml
new file mode 100644
index 0000000000..38518f2404
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_download.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_drag_grid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_drag_grid.xml
new file mode 100644
index 0000000000..60a5b8a7cd
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_drag_grid.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_drag_list.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_drag_list.xml
new file mode 100644
index 0000000000..fb80dafae2
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_drag_list.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_end_call.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_end_call.xml
new file mode 100644
index 0000000000..93cd5c54fb
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_end_call.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_error.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_error.xml
new file mode 100644
index 0000000000..0b892cce9d
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_error.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_export_archive.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_export_archive.xml
new file mode 100644
index 0000000000..34796f0ce1
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_export_archive.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_extensions.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_extensions.xml
new file mode 100644
index 0000000000..18d6feeb6d
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_extensions.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_off.xml
new file mode 100644
index 0000000000..2ec4dc6559
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_off.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_on.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_on.xml
new file mode 100644
index 0000000000..8347b8671f
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_favourite_on.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_files.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_files.xml
new file mode 100644
index 0000000000..0b99de6492
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_files.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_filter.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_filter.xml
new file mode 100644
index 0000000000..6a63f1c1d7
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_filter.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_grid_view.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_grid_view.xml
new file mode 100644
index 0000000000..8361a792d1
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_grid_view.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_info.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_info.xml
new file mode 100644
index 0000000000..fdb53d2db9
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_info.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_leave.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_leave.xml
new file mode 100644
index 0000000000..43d240246c
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_leave.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_link.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_link.xml
new file mode 100644
index 0000000000..15c9ab6889
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_link.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_lock.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_lock.xml
new file mode 100644
index 0000000000..748dcadf32
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_lock.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_lock_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_lock_off.xml
new file mode 100644
index 0000000000..a7f6096d33
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_lock_off.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_marker_read_receipts.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_marker_read_receipts.xml
new file mode 100644
index 0000000000..3f5b4e7bfa
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_marker_read_receipts.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_mention.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mention.xml
new file mode 100644
index 0000000000..f5771354e1
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_mention.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_outline.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_outline.xml
new file mode 100644
index 0000000000..6dc333ce8f
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_outline.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_solid.xml
new file mode 100644
index 0000000000..27cf5bb1e3
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_off_solid.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_outline.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_outline.xml
new file mode 100644
index 0000000000..6a17adee8d
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_outline.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_solid.xml
new file mode 100644
index 0000000000..4fe8b3cb57
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_mic_on_solid.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_search.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_mobile.xml
similarity index 68%
rename from libraries/designsystem/src/main/res/drawable/ic_search.xml
rename to libraries/designsystem/src/main/res/drawable/ic_compound_mobile.xml
index 440aef72f6..f5648fb87b 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_search.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_mobile.xml
@@ -19,7 +19,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
-
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications.xml
new file mode 100644
index 0000000000..fae20f074c
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_off.xml
new file mode 100644
index 0000000000..e43e4ba904
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_off.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid.xml
new file mode 100644
index 0000000000..d3dd76cea9
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid_off.xml
new file mode 100644
index 0000000000..daf60279e3
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_notifications_solid_off.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_offline.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_offline.xml
new file mode 100644
index 0000000000..4e73fe617c
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_offline.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_horizontal.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_horizontal.xml
new file mode 100644
index 0000000000..4b8c1476fb
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_horizontal.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_vertical.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_vertical.xml
new file mode 100644
index 0000000000..242846cee8
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_overflow_vertical.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_polls.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_polls.xml
new file mode 100644
index 0000000000..1fda4c4dea
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_polls.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_pop_out.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_pop_out.xml
new file mode 100644
index 0000000000..fef7a15d5c
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_pop_out.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_public.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_public.xml
new file mode 100644
index 0000000000..79831bb75e
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_public.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_search.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_search.xml
new file mode 100644
index 0000000000..31f8a6a5ef
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_search.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_settings.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_settings.xml
new file mode 100644
index 0000000000..b15d9990c0
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_settings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_settings_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_settings_solid.xml
new file mode 100644
index 0000000000..0e6071126b
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_settings_solid.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_share.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_share.xml
new file mode 100644
index 0000000000..cf17225881
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_share.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_share_android.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_share_android.xml
new file mode 100644
index 0000000000..8ea3ee523a
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_share_android.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_outline.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_outline.xml
new file mode 100644
index 0000000000..af750bf6fd
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_outline.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_solid.xml
new file mode 100644
index 0000000000..a5608dde2f
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_share_screen_solid.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_spotlight_view.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_spotlight_view.xml
new file mode 100644
index 0000000000..db8133f1b6
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_spotlight_view.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_threads.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_threads.xml
new file mode 100644
index 0000000000..a1de9e0560
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_threads.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_threads_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_threads_solid.xml
new file mode 100644
index 0000000000..496ca795aa
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_threads_solid.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_user_add.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_user_add.xml
new file mode 100644
index 0000000000..5e8d7aa2c2
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_user_add.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_user_add_solid.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_user_add_solid.xml
new file mode 100644
index 0000000000..98fbd006de
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_user_add_solid.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_user_profile.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_user_profile.xml
new file mode 100644
index 0000000000..8027856173
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_user_profile.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_verified.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_verified.xml
new file mode 100644
index 0000000000..9066bf0240
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_verified.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_content_copy.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call.xml
similarity index 58%
rename from libraries/designsystem/src/main/res/drawable/ic_content_copy.xml
rename to libraries/designsystem/src/main/res/drawable/ic_compound_video_call.xml
index 6910b0421a..6000e9e8dc 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_content_copy.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call.xml
@@ -1,5 +1,5 @@
-
-
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_declined.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_declined.xml
new file mode 100644
index 0000000000..1a9cfb0542
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_declined.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_missed.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_missed.xml
new file mode 100644
index 0000000000..eb5f1b5607
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_missed.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_off.xml
new file mode 100644
index 0000000000..69f256c365
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_video_call_off.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_off.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_off.xml
new file mode 100644
index 0000000000..739f147e09
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_off.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_on.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_on.xml
new file mode 100644
index 0000000000..ec24b596af
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_visibility_on.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_voice_call.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_voice_call.xml
new file mode 100644
index 0000000000..091eca5df4
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_voice_call.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_web_browser.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_web_browser.xml
new file mode 100644
index 0000000000..ae31c62172
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_compound_web_browser.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_delete.xml b/libraries/designsystem/src/main/res/drawable/ic_delete.xml
deleted file mode 100644
index d724c2e05f..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_delete.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_door_open_24.xml b/libraries/designsystem/src/main/res/drawable/ic_door_open_24.xml
deleted file mode 100644
index 7d2eec40f5..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_door_open_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_edit.xml b/libraries/designsystem/src/main/res/drawable/ic_edit.xml
deleted file mode 100644
index f64fa2f5fb..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_edit.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_edit_square.xml b/libraries/designsystem/src/main/res/drawable/ic_edit_square.xml
deleted file mode 100644
index 121486a4a2..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_edit_square.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_forward.xml b/libraries/designsystem/src/main/res/drawable/ic_forward.xml
deleted file mode 100644
index 9608767c8d..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_forward.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_groups.xml b/libraries/designsystem/src/main/res/drawable/ic_groups.xml
index 9e87f1d533..4f75cbdb78 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_groups.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_groups.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/libraries/designsystem/src/main/res/drawable/ic_indent_decrease.xml b/libraries/designsystem/src/main/res/drawable/ic_indent_decrease.xml
index 181f94c012..98d59858c5 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_indent_decrease.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_indent_decrease.xml
@@ -5,5 +5,5 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_indent_increase.xml b/libraries/designsystem/src/main/res/drawable/ic_indent_increase.xml
index 06a9ede8d5..ab0eb7d422 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_indent_increase.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_indent_increase.xml
@@ -5,5 +5,5 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_inline_code.xml b/libraries/designsystem/src/main/res/drawable/ic_inline_code.xml
index c15248f8ea..117b78bb23 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_inline_code.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_inline_code.xml
@@ -5,11 +5,11 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_italic.xml b/libraries/designsystem/src/main/res/drawable/ic_italic.xml
index 0a389dbf15..dd31d8ea38 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_italic.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_italic.xml
@@ -5,5 +5,5 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_link.xml b/libraries/designsystem/src/main/res/drawable/ic_link.xml
index c8a37cdda2..ff4f8b16e8 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_link.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_link.xml
@@ -5,5 +5,5 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_mention.xml b/libraries/designsystem/src/main/res/drawable/ic_mention.xml
deleted file mode 100644
index 37f70481e5..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_mention.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_mute.xml b/libraries/designsystem/src/main/res/drawable/ic_mute.xml
deleted file mode 100644
index ea6f842696..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_mute.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_numbered_list.xml b/libraries/designsystem/src/main/res/drawable/ic_numbered_list.xml
index 63e7269508..56502e47e6 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_numbered_list.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_numbered_list.xml
@@ -5,5 +5,5 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_poll.xml b/libraries/designsystem/src/main/res/drawable/ic_poll.xml
deleted file mode 100644
index 6167653c82..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_poll.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_poll_end.xml b/libraries/designsystem/src/main/res/drawable/ic_poll_end.xml
index 86c169cf41..1299f4078f 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_poll_end.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_poll_end.xml
@@ -5,17 +5,17 @@
android:viewportHeight="22">
+ android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_quote.xml b/libraries/designsystem/src/main/res/drawable/ic_quote.xml
index 8f4768f818..2beda28a02 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_quote.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_quote.xml
@@ -5,14 +5,14 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_reply.xml b/libraries/designsystem/src/main/res/drawable/ic_reply.xml
deleted file mode 100644
index ac41dfaa55..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_reply.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_report_content.xml b/libraries/designsystem/src/main/res/drawable/ic_report_content.xml
deleted file mode 100644
index 18c9c2f95e..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_report_content.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_add_reaction.xml b/libraries/designsystem/src/main/res/drawable/ic_september_add_reaction.xml
new file mode 100644
index 0000000000..159c95f253
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_add_reaction.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_attachment.xml b/libraries/designsystem/src/main/res/drawable/ic_september_attachment.xml
new file mode 100644
index 0000000000..6ab5991334
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_attachment.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_compose_button.xml b/libraries/designsystem/src/main/res/drawable/ic_september_compose_button.xml
new file mode 100644
index 0000000000..fa706f56c8
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_compose_button.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_copy.xml b/libraries/designsystem/src/main/res/drawable/ic_september_copy.xml
new file mode 100644
index 0000000000..51da8c16ca
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_copy.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_decryption_error.xml b/libraries/designsystem/src/main/res/drawable/ic_september_decryption_error.xml
new file mode 100644
index 0000000000..4751e55ff0
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_decryption_error.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_edit_outline.xml b/libraries/designsystem/src/main/res/drawable/ic_september_edit_outline.xml
new file mode 100644
index 0000000000..28e0247114
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_edit_outline.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_edit_solid_16.xml b/libraries/designsystem/src/main/res/drawable/ic_september_edit_solid_16.xml
new file mode 100644
index 0000000000..7c7b628201
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_edit_solid_16.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_forward.xml b/libraries/designsystem/src/main/res/drawable/ic_september_forward.xml
new file mode 100644
index 0000000000..0d0e6f38b8
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_forward.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_location.xml b/libraries/designsystem/src/main/res/drawable/ic_september_location.xml
new file mode 100644
index 0000000000..7843eb22cc
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_location.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_photo_camera.xml b/libraries/designsystem/src/main/res/drawable/ic_september_photo_camera.xml
new file mode 100644
index 0000000000..38d7f8af44
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_photo_camera.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_photo_video_library.xml b/libraries/designsystem/src/main/res/drawable/ic_september_photo_video_library.xml
new file mode 100644
index 0000000000..49d06f1a40
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_photo_video_library.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_reply.xml b/libraries/designsystem/src/main/res/drawable/ic_september_reply.xml
new file mode 100644
index 0000000000..dff916345f
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_reply.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_send.xml b/libraries/designsystem/src/main/res/drawable/ic_september_send.xml
new file mode 100644
index 0000000000..8b90be336e
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_send.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_take_photo_camera.xml b/libraries/designsystem/src/main/res/drawable/ic_september_take_photo_camera.xml
new file mode 100644
index 0000000000..970463e374
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_take_photo_camera.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_text_formatting.xml b/libraries/designsystem/src/main/res/drawable/ic_september_text_formatting.xml
new file mode 100644
index 0000000000..fc156b9fda
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_text_formatting.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_video_call.xml b/libraries/designsystem/src/main/res/drawable/ic_september_video_call.xml
new file mode 100644
index 0000000000..cd184f9448
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_video_call.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_view_source.xml b/libraries/designsystem/src/main/res/drawable/ic_september_view_source.xml
new file mode 100644
index 0000000000..3ad615a748
--- /dev/null
+++ b/libraries/designsystem/src/main/res/drawable/ic_september_view_source.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libraries/designsystem/src/main/res/drawable/ic_share.xml b/libraries/designsystem/src/main/res/drawable/ic_share.xml
deleted file mode 100644
index d38f7ae5f7..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_share.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_strikethrough.xml b/libraries/designsystem/src/main/res/drawable/ic_strikethrough.xml
index 4469c5572d..81e4330f03 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_strikethrough.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_strikethrough.xml
@@ -5,5 +5,5 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_thread_decoration.xml b/libraries/designsystem/src/main/res/drawable/ic_thread_decoration.xml
index 09d4ad4ace..b90c22f6b5 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_thread_decoration.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_thread_decoration.xml
@@ -5,11 +5,11 @@
android:viewportHeight="13">
+ android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/main/res/drawable/ic_underline.xml b/libraries/designsystem/src/main/res/drawable/ic_underline.xml
index 9da2f2e0b4..f7a5d7008e 100644
--- a/libraries/designsystem/src/main/res/drawable/ic_underline.xml
+++ b/libraries/designsystem/src/main/res/drawable/ic_underline.xml
@@ -5,5 +5,5 @@
android:viewportHeight="20">
+ android:fillColor="@android:color/white"/>
diff --git a/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/utils/SnackbarDispatcherTests.kt b/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarDispatcherTests.kt
similarity index 97%
rename from libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/utils/SnackbarDispatcherTests.kt
rename to libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarDispatcherTests.kt
index 3eb644d800..fbccaa4e75 100644
--- a/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/utils/SnackbarDispatcherTests.kt
+++ b/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/utils/snackbar/SnackbarDispatcherTests.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package io.element.android.libraries.designsystem.utils
+package io.element.android.libraries.designsystem.utils.snackbar
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt
index 5258c388c5..cabc41b4ed 100644
--- a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt
+++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt
@@ -17,8 +17,10 @@
package io.element.encrypteddb
import android.content.Context
-import com.squareup.sqldelight.android.AndroidSqliteDriver
-import com.squareup.sqldelight.db.SqlDriver
+import app.cash.sqldelight.db.QueryResult
+import app.cash.sqldelight.db.SqlDriver
+import app.cash.sqldelight.db.SqlSchema
+import app.cash.sqldelight.driver.android.AndroidSqliteDriver
import io.element.encrypteddb.passphrase.PassphraseProvider
import net.sqlcipher.database.SupportFactory
@@ -35,7 +37,7 @@ class SqlCipherDriverFactory(
* @param name The name of the database to create.
* @param context Android [Context], used to instantiate the driver.
*/
- fun create(schema: SqlDriver.Schema, name: String, context: Context): SqlDriver {
+ fun create(schema: SqlSchema>, name: String, context: Context): SqlDriver {
val passphrase = passphraseProvider.getPassphrase()
val factory = SupportFactory(passphrase)
return AndroidSqliteDriver(schema = schema, context = context, name = name, factory = factory)
diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt
index a0a0525fb5..d041078051 100644
--- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt
+++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt
@@ -95,7 +95,10 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
is StateContent -> {
stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList)
}
- is PollContent, // TODO Polls: handle last message
+ is PollContent -> {
+ val message = sp.getString(CommonStrings.common_poll_summary, content.question)
+ prefixIfNeeded(message, senderDisplayName, isDmRoom)
+ }
is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> {
prefixIfNeeded(sp.getString(CommonStrings.common_unsupported_event), senderDisplayName, isDmRoom)
}
@@ -103,12 +106,10 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
}
private fun processMessageContents(messageContent: MessageContent, senderDisplayName: String, isDmRoom: Boolean): CharSequence? {
- val messageType: MessageType = messageContent.type ?: return null
-
- val internalMessage = when (messageType) {
+ val internalMessage = when (val messageType: MessageType = messageContent.type) {
// Doesn't need a prefix
is EmoteMessageType -> {
- return "- $senderDisplayName ${messageType.body}"
+ return "* $senderDisplayName ${messageType.body}"
}
is TextMessageType -> {
messageType.body
@@ -129,7 +130,8 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
sp.getString(CommonStrings.common_audio)
}
UnknownMessageType -> {
- sp.getString(CommonStrings.common_unsupported_event)
+ // Display the body as a fallback
+ messageContent.body
}
is NoticeMessageType -> {
messageType.body
diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt
similarity index 95%
rename from libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt
rename to libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt
index 0f22105635..4c26fcb3c7 100644
--- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt
+++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTest.kt
@@ -48,6 +48,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessag
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
+import io.element.android.libraries.matrix.test.room.aPollContent
import io.element.android.libraries.matrix.test.room.aProfileChangeMessageContent
import io.element.android.libraries.matrix.test.room.anEventTimelineItem
import io.element.android.services.toolbox.impl.strings.AndroidStringProvider
@@ -58,8 +59,9 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
+@Suppress("LargeClass")
@RunWith(RobolectricTestRunner::class)
-class DefaultRoomLastMessageFormatterTests {
+class DefaultRoomLastMessageFormatterTest {
private lateinit var context: Context
private lateinit var fakeMatrixClient: FakeMatrixClient
@@ -153,7 +155,7 @@ class DefaultRoomLastMessageFormatterTests {
fun `Message contents`() {
val body = "Shared body"
fun createMessageContent(type: MessageType): MessageContent {
- return MessageContent(body, null, false, false,type)
+ return MessageContent(body, null, false, false, type)
}
val sharedContentMessagesTypes = arrayOf(
@@ -199,11 +201,12 @@ class DefaultRoomLastMessageFormatterTests {
is ImageMessageType -> "Image"
is FileMessageType -> "File"
is LocationMessageType -> "Shared location"
- is EmoteMessageType -> "- $senderName ${type.body}"
- is TextMessageType, is NoticeMessageType -> body
- UnknownMessageType -> "Unsupported event"
+ is EmoteMessageType -> "* $senderName ${type.body}"
+ is TextMessageType,
+ is NoticeMessageType,
+ UnknownMessageType -> body
}
- Truth.assertWithMessage("$type was not properly handled").that(result).isEqualTo(expectedResult)
+ Truth.assertWithMessage("$type was not properly handled for DM").that(result).isEqualTo(expectedResult)
}
// Verify results of Room mode
@@ -215,9 +218,10 @@ class DefaultRoomLastMessageFormatterTests {
is ImageMessageType -> "$senderName: Image"
is FileMessageType -> "$senderName: File"
is LocationMessageType -> "$senderName: Shared location"
- is EmoteMessageType -> "- $senderName ${type.body}"
- is TextMessageType, is NoticeMessageType -> "$senderName: $body"
- UnknownMessageType -> "$senderName: Unsupported event"
+ is TextMessageType,
+ is NoticeMessageType,
+ UnknownMessageType -> "$senderName: $body"
+ is EmoteMessageType -> "* $senderName ${type.body}"
}
val shouldCreateAnnotatedString = when (type) {
is VideoMessageType -> true
@@ -234,7 +238,7 @@ class DefaultRoomLastMessageFormatterTests {
.that(result)
.isInstanceOf(AnnotatedString::class.java)
}
- Truth.assertWithMessage("$type was not properly handled").that(string).isEqualTo(expectedResult)
+ Truth.assertWithMessage("$type was not properly handled for room").that(string).isEqualTo(expectedResult)
}
}
@@ -764,6 +768,34 @@ class DefaultRoomLastMessageFormatterTests {
// endregion
+ // region Polls
+
+ @Test
+ @Config(qualifiers = "en")
+ fun `Computes last message for poll in DM`() {
+ val pollContent = aPollContent()
+
+ val mineContentEvent = createRoomEvent(sentByYou = true, senderDisplayName = "Alice", content = pollContent)
+ Truth.assertThat(formatter.format(mineContentEvent, true)).isEqualTo("Poll: Do you like polls?")
+
+ val contentEvent = createRoomEvent(sentByYou = false, senderDisplayName = "Bob", content = pollContent)
+ Truth.assertThat(formatter.format(contentEvent, true)).isEqualTo("Poll: Do you like polls?")
+ }
+
+ @Test
+ @Config(qualifiers = "en")
+ fun `Computes last message for poll in room`() {
+ val pollContent = aPollContent()
+
+ val mineContentEvent = createRoomEvent(sentByYou = true, senderDisplayName = "Alice", content = pollContent)
+ Truth.assertThat(formatter.format(mineContentEvent, false).toString()).isEqualTo("Alice: Poll: Do you like polls?")
+
+ val contentEvent = createRoomEvent(sentByYou = false, senderDisplayName = "Bob", content = pollContent)
+ Truth.assertThat(formatter.format(contentEvent, false).toString()).isEqualTo("Bob: Poll: Do you like polls?")
+ }
+
+ // endregion
+
private fun createRoomEvent(sentByYou: Boolean, senderDisplayName: String?, content: EventContent): EventTimelineItem {
val sender = if (sentByYou) A_USER_ID else UserId("@someone_else:domain")
val profile = ProfileTimelineDetails.Ready(senderDisplayName, false, null)
diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
index 990aee3a93..121cf26271 100644
--- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
+++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
@@ -43,4 +43,16 @@ enum class FeatureFlags(
title = "Show notification settings",
defaultValue = true,
),
+ VoiceMessages(
+ key = "feature.voicemessages",
+ title = "Voice messages",
+ description = "Send and receive voice messages",
+ defaultValue = false,
+ ),
+ PinUnlock(
+ key = "feature.pinunlock",
+ title = "Pin unlock",
+ description = "Allow user to lock/unlock the app with a pin code or biometrics",
+ defaultValue = false,
+ ),
}
diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt
index 82184d510c..48f159de83 100644
--- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt
+++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt
@@ -35,6 +35,8 @@ class StaticFeatureFlagProvider @Inject constructor() :
FeatureFlags.LocationSharing -> true
FeatureFlags.Polls -> true
FeatureFlags.NotificationSettings -> true
+ FeatureFlags.VoiceMessages -> false
+ FeatureFlags.PinUnlock -> false
}
} else {
false
diff --git a/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/FeatureListView.kt b/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/FeatureListView.kt
index dcd556faf5..6bf4467de8 100644
--- a/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/FeatureListView.kt
+++ b/libraries/featureflag/ui/src/main/kotlin/io/element/android/libraries/featureflag/ui/FeatureListView.kt
@@ -20,7 +20,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.element.android.libraries.designsystem.components.preferences.PreferenceCheckbox
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
import io.element.android.libraries.featureflag.ui.model.aFeatureUiModelList
@@ -46,7 +46,7 @@ fun FeatureListView(
}
@Composable
-fun FeaturePreferenceView(
+private fun FeaturePreferenceView(
feature: FeatureUiModel,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier
@@ -60,7 +60,7 @@ fun FeaturePreferenceView(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun FeatureListViewPreview() = ElementPreview {
FeatureListView(
diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/Symbol.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/Symbol.kt
index 36e8cdc34e..bb40c7dfa9 100644
--- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/Symbol.kt
+++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/Symbol.kt
@@ -50,7 +50,7 @@ internal class SymbolNode(
* @param position the initial symbol position
*/
public class SymbolState(
- position: LatLng = LatLng(0.0, 0.0)
+ position: LatLng
) {
/**
* Current position of the symbol.
diff --git a/libraries/matrix/api/build.gradle.kts b/libraries/matrix/api/build.gradle.kts
index 1ffe07eb6f..5a430f7db5 100644
--- a/libraries/matrix/api/build.gradle.kts
+++ b/libraries/matrix/api/build.gradle.kts
@@ -44,4 +44,6 @@ dependencies {
testImplementation(libs.test.junit)
testImplementation(libs.test.truth)
+ testImplementation(libs.test.robolectric)
+ testImplementation(projects.tests.testutils)
}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt
index c15153876c..501b40508e 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt
@@ -18,12 +18,18 @@ package io.element.android.libraries.matrix.api.auth
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.SessionId
+import io.element.android.libraries.sessionstorage.api.LoggedInState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
interface MatrixAuthenticationService {
- fun isLoggedIn(): Flow
+ fun loggedInStateFlow(): Flow
suspend fun getLatestSessionId(): SessionId?
+
+ /**
+ * Restore a session from a [sessionId].
+ * Do not restore anything it the access token is not valid anymore.
+ */
suspend fun restoreSession(sessionId: SessionId): Result
fun getHomeserverDetails(): StateFlow
suspend fun setHomeserver(homeserver: String): Result
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt
index 8565e4c747..1abbf80130 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt
@@ -61,6 +61,10 @@ sealed interface NotificationContent {
) : MessageLike
data object RoomRedaction : MessageLike
data object Sticker : MessageLike
+ data class Poll(
+ val senderId: UserId,
+ val question: String,
+ ) : MessageLike
}
sealed interface StateEvent : NotificationContent {
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
index 1dd6101354..746f8cead8 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
@@ -77,9 +77,9 @@ interface MatrixRoom : Closeable {
fun destroy()
- fun subscribeToSync()
+ suspend fun subscribeToSync()
- fun unsubscribeFromSync()
+ suspend fun unsubscribeFromSync()
suspend fun userDisplayName(userId: UserId): Result
@@ -89,6 +89,8 @@ interface MatrixRoom : Closeable {
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?): Result
+ suspend fun enterSpecialMode(eventId: EventId?): Result
+
suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result
@@ -183,8 +185,12 @@ interface MatrixRoom : Closeable {
*/
suspend fun endPoll(pollStartId: EventId, text: String): Result
+ suspend fun sendVoiceMessage(
+ file: File,
+ audioInfo: AudioInfo,
+ waveform: List,
+ progressCallback: ProgressCallback?
+ ): Result
+
override fun close() = destroy()
-
}
-
-
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomNotificationSettingsState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomNotificationSettingsState.kt
index d98a3a83d2..2121670d6b 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomNotificationSettingsState.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomNotificationSettingsState.kt
@@ -17,7 +17,7 @@
package io.element.android.libraries.matrix.api.room
sealed interface MatrixRoomNotificationSettingsState {
- object Unknown : MatrixRoomNotificationSettingsState
+ data object Unknown : MatrixRoomNotificationSettingsState
data class Pending(val prevRoomNotificationSettings: RoomNotificationSettings? = null) : MatrixRoomNotificationSettingsState
data class Error(val failure: Throwable, val prevRoomNotificationSettings: RoomNotificationSettings? = null) : MatrixRoomNotificationSettingsState
data class Ready(val roomNotificationSettings: RoomNotificationSettings) : MatrixRoomNotificationSettingsState
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt
index a3edf80bee..f63953e260 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt
@@ -16,13 +16,8 @@
package io.element.android.libraries.matrix.api.timeline.item.event
-import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
-import io.element.android.libraries.matrix.api.media.AudioInfo
-import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ImageInfo
-import io.element.android.libraries.matrix.api.media.MediaSource
-import io.element.android.libraries.matrix.api.media.VideoInfo
import io.element.android.libraries.matrix.api.poll.PollAnswer
import io.element.android.libraries.matrix.api.poll.PollKind
@@ -33,37 +28,10 @@ data class MessageContent(
val inReplyTo: InReplyTo?,
val isEdited: Boolean,
val isThreaded: Boolean,
- val type: MessageType?
+ val type: MessageType
) : EventContent
-sealed interface InReplyTo {
- /** The event details are not loaded yet. We can fetch them. */
- data class NotLoaded(val eventId: EventId) : InReplyTo
-
- /** The event details are pending to be fetched. We should **not** fetch them again. */
- data object Pending : InReplyTo
-
- /** The event details are available. */
- data class Ready(
- val eventId: EventId,
- val content: EventContent,
- val senderId: UserId,
- val senderDisplayName: String?,
- val senderAvatarUrl: String?,
- ) : InReplyTo
-
- /**
- * Fetching the event details failed.
- *
- * We can try to fetch them again **with a proper retry strategy**, but not blindly:
- *
- * If the reason for the failure is consistent on the server, we'd enter a loop
- * where we keep trying to fetch the same event.
- * */
- data object Error : InReplyTo
-}
-
-object RedactedContent : EventContent
+data object RedactedContent : EventContent
data class StickerContent(
val body: String,
@@ -124,106 +92,4 @@ data class FailedToParseStateContent(
val error: String
) : EventContent
-object UnknownContent : EventContent
-
-sealed interface MessageType
-
-object UnknownMessageType : MessageType
-
-enum class MessageFormat {
- HTML, UNKNOWN
-}
-
-data class FormattedBody(
- val format: MessageFormat,
- val body: String
-)
-
-data class EmoteMessageType(
- val body: String,
- val formatted: FormattedBody?
-) : MessageType
-
-data class ImageMessageType(
- val body: String,
- val source: MediaSource,
- val info: ImageInfo?
-) : MessageType
-
-data class LocationMessageType(
- val body: String,
- val geoUri: String,
- val description: String?,
-) : MessageType
-
-data class AudioMessageType(
- val body: String,
- val source: MediaSource,
- val info: AudioInfo?
-) : MessageType
-
-data class VideoMessageType(
- val body: String,
- val source: MediaSource,
- val info: VideoInfo?
-) : MessageType
-
-data class FileMessageType(
- val body: String,
- val source: MediaSource,
- val info: FileInfo?
-) : MessageType
-
-data class NoticeMessageType(
- val body: String,
- val formatted: FormattedBody?
-) : MessageType
-
-data class TextMessageType(
- val body: String,
- val formatted: FormattedBody?
-) : MessageType
-
-enum class MembershipChange {
- NONE,
- ERROR,
- JOINED,
- LEFT,
- BANNED,
- UNBANNED,
- KICKED,
- INVITED,
- KICKED_AND_BANNED,
- INVITATION_ACCEPTED,
- INVITATION_REJECTED,
- INVITATION_REVOKED,
- KNOCKED,
- KNOCK_ACCEPTED,
- KNOCK_RETRACTED,
- KNOCK_DENIED,
- NOT_IMPLEMENTED;
-}
-
-sealed interface OtherState {
- data object PolicyRuleRoom : OtherState
- data object PolicyRuleServer : OtherState
- data object PolicyRuleUser : OtherState
- data object RoomAliases : OtherState
- data class RoomAvatar(val url: String?) : OtherState
- data object RoomCanonicalAlias : OtherState
- data object RoomCreate : OtherState
- data object RoomEncryption : OtherState
- data object RoomGuestAccess : OtherState
- data object RoomHistoryVisibility : OtherState
- data object RoomJoinRules : OtherState
- data class RoomName(val name: String?) : OtherState
- data object RoomPinnedEvents : OtherState
- data object RoomPowerLevels : OtherState
- data object RoomServerAcl : OtherState
- data class RoomThirdPartyInvite(val displayName: String?) : OtherState
- data object RoomTombstone : OtherState
- data class RoomTopic(val topic: String?) : OtherState
- data object SpaceChild : OtherState
- data object SpaceParent : OtherState
- data class Custom(val eventType: String) : OtherState
-}
+data object UnknownContent : EventContent
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/FormattedBody.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/FormattedBody.kt
new file mode 100644
index 0000000000..a11043b200
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/FormattedBody.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.timeline.item.event
+
+data class FormattedBody(
+ val format: MessageFormat,
+ val body: String
+)
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/InReplyTo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/InReplyTo.kt
new file mode 100644
index 0000000000..14a84e2a90
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/InReplyTo.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.timeline.item.event
+
+import io.element.android.libraries.matrix.api.core.EventId
+import io.element.android.libraries.matrix.api.core.UserId
+
+sealed interface InReplyTo {
+ /** The event details are not loaded yet. We can fetch them. */
+ data class NotLoaded(val eventId: EventId) : InReplyTo
+
+ /** The event details are pending to be fetched. We should **not** fetch them again. */
+ data object Pending : InReplyTo
+
+ /** The event details are available. */
+ data class Ready(
+ val eventId: EventId,
+ val content: EventContent,
+ val senderId: UserId,
+ val senderDisplayName: String?,
+ val senderAvatarUrl: String?,
+ ) : InReplyTo
+
+ /**
+ * Fetching the event details failed.
+ *
+ * We can try to fetch them again **with a proper retry strategy**, but not blindly:
+ *
+ * If the reason for the failure is consistent on the server, we'd enter a loop
+ * where we keep trying to fetch the same event.
+ * */
+ data object Error : InReplyTo
+}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MembershipChange.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MembershipChange.kt
new file mode 100644
index 0000000000..8aa8845f23
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MembershipChange.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.timeline.item.event
+
+enum class MembershipChange {
+ NONE,
+ ERROR,
+ JOINED,
+ LEFT,
+ BANNED,
+ UNBANNED,
+ KICKED,
+ INVITED,
+ KICKED_AND_BANNED,
+ INVITATION_ACCEPTED,
+ INVITATION_REJECTED,
+ INVITATION_REVOKED,
+ KNOCKED,
+ KNOCK_ACCEPTED,
+ KNOCK_RETRACTED,
+ KNOCK_DENIED,
+ NOT_IMPLEMENTED;
+}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageFormat.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageFormat.kt
new file mode 100644
index 0000000000..4e88113355
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageFormat.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.timeline.item.event
+
+enum class MessageFormat {
+ HTML, UNKNOWN
+}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt
new file mode 100644
index 0000000000..dc06d5c94a
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/MessageType.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.timeline.item.event
+
+import io.element.android.libraries.matrix.api.media.AudioInfo
+import io.element.android.libraries.matrix.api.media.FileInfo
+import io.element.android.libraries.matrix.api.media.ImageInfo
+import io.element.android.libraries.matrix.api.media.MediaSource
+import io.element.android.libraries.matrix.api.media.VideoInfo
+
+sealed interface MessageType
+
+data object UnknownMessageType : MessageType
+
+data class EmoteMessageType(
+ val body: String,
+ val formatted: FormattedBody?
+) : MessageType
+
+data class ImageMessageType(
+ val body: String,
+ val source: MediaSource,
+ val info: ImageInfo?
+) : MessageType
+
+data class LocationMessageType(
+ val body: String,
+ val geoUri: String,
+ val description: String?,
+) : MessageType
+
+data class AudioMessageType(
+ val body: String,
+ val source: MediaSource,
+ val info: AudioInfo?
+) : MessageType
+
+data class VideoMessageType(
+ val body: String,
+ val source: MediaSource,
+ val info: VideoInfo?
+) : MessageType
+
+data class FileMessageType(
+ val body: String,
+ val source: MediaSource,
+ val info: FileInfo?
+) : MessageType
+
+data class NoticeMessageType(
+ val body: String,
+ val formatted: FormattedBody?
+) : MessageType
+
+data class TextMessageType(
+ val body: String,
+ val formatted: FormattedBody?
+) : MessageType
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/OtherState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/OtherState.kt
new file mode 100644
index 0000000000..2cbfaf76b4
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/OtherState.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.timeline.item.event
+
+sealed interface OtherState {
+ data object PolicyRuleRoom : OtherState
+ data object PolicyRuleServer : OtherState
+ data object PolicyRuleUser : OtherState
+ data object RoomAliases : OtherState
+ data class RoomAvatar(val url: String?) : OtherState
+ data object RoomCanonicalAlias : OtherState
+ data object RoomCreate : OtherState
+ data object RoomEncryption : OtherState
+ data object RoomGuestAccess : OtherState
+ data object RoomHistoryVisibility : OtherState
+ data object RoomJoinRules : OtherState
+ data class RoomName(val name: String?) : OtherState
+ data object RoomPinnedEvents : OtherState
+ data object RoomPowerLevels : OtherState
+ data object RoomServerAcl : OtherState
+ data class RoomThirdPartyInvite(val displayName: String?) : OtherState
+ data object RoomTombstone : OtherState
+ data class RoomTopic(val topic: String?) : OtherState
+ data object SpaceChild : OtherState
+ data object SpaceParent : OtherState
+ data class Custom(val eventType: String) : OtherState
+}
diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/AuthErrorCodeTests.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/AuthErrorCodeTest.kt
similarity index 98%
rename from libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/AuthErrorCodeTests.kt
rename to libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/AuthErrorCodeTest.kt
index b3ccf5264d..5f987ad725 100644
--- a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/AuthErrorCodeTests.kt
+++ b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/AuthErrorCodeTest.kt
@@ -19,7 +19,7 @@ package io.element.android.libraries.matrix.api.auth
import com.google.common.truth.Truth.assertThat
import org.junit.Test
-class AuthErrorCodeTests {
+class AuthErrorCodeTest {
@Test
fun `errorCode finds UNKNOWN code`() {
diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/MatrixToConverterTest.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/MatrixToConverterTest.kt
new file mode 100644
index 0000000000..d3d928c189
--- /dev/null
+++ b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/MatrixToConverterTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.permalink
+
+import android.net.Uri
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class MatrixToConverterTest {
+
+ @Test
+ fun `converting a matrix-to url does nothing`() {
+ val url = Uri.parse("https://matrix.to/#/#element-android:matrix.org")
+ assertThat(MatrixToConverter.convert(url)).isEqualTo(url)
+ }
+
+ @Test
+ fun `converting a url with a supported room path returns a matrix-to url`() {
+ val url = Uri.parse("https://riot.im/develop/#/room/#element-android:matrix.org")
+ assertThat(MatrixToConverter.convert(url)).isEqualTo(Uri.parse("https://matrix.to/#/#element-android:matrix.org"))
+ }
+
+ @Test
+ fun `converting a url with a supported user path returns a matrix-to url`() {
+ val url = Uri.parse("https://riot.im/develop/#/user/@test:matrix.org")
+ assertThat(MatrixToConverter.convert(url)).isEqualTo(Uri.parse("https://matrix.to/#/@test:matrix.org"))
+ }
+
+ @Test
+ fun `converting a url with a supported group path returns a matrix-to url`() {
+ val url = Uri.parse("https://riot.im/develop/#/group/+group:matrix.org")
+ assertThat(MatrixToConverter.convert(url)).isEqualTo(Uri.parse("https://matrix.to/#/+group:matrix.org"))
+ }
+
+ @Test
+ fun `converting an unsupported url returns null`() {
+ val url = Uri.parse("https://element.io/")
+ assertThat(MatrixToConverter.convert(url)).isNull()
+ }
+
+}
diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilderTest.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilderTest.kt
new file mode 100644
index 0000000000..282f58ab21
--- /dev/null
+++ b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilderTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.permalink
+
+import com.google.common.truth.Truth.assertThat
+import io.element.android.libraries.matrix.api.core.RoomId
+import io.element.android.libraries.matrix.api.core.UserId
+import io.element.android.tests.testutils.assertThrowsInDebug
+import io.element.android.tests.testutils.isInDebug
+import org.junit.Test
+
+class PermalinkBuilderTest {
+
+ fun `building a permalink for an invalid user id throws when verifying the id`() {
+ assertThrowsInDebug {
+ val userId = UserId("some invalid user id")
+ PermalinkBuilder.permalinkForUser(userId)
+ }
+ }
+
+ fun `building a permalink for an invalid room id throws when verifying the id`() {
+ assertThrowsInDebug {
+ val roomId = RoomId("some invalid room id")
+ PermalinkBuilder.permalinkForRoomId(roomId)
+ }
+ }
+
+ @Test
+ fun `building a permalink for an invalid user id returns failure when not verifying the id`() {
+ if (!isInDebug()) {
+ val userId = UserId("some invalid user id")
+ assertThat(PermalinkBuilder.permalinkForUser(userId).isFailure).isTrue()
+ }
+ }
+
+ @Test
+ fun `building a permalink for an invalid room id returns failure when not verifying the id`() {
+ if (!isInDebug()) {
+ val roomId = RoomId("some invalid room id")
+ assertThat(PermalinkBuilder.permalinkForRoomId(roomId).isFailure).isTrue()
+ }
+ }
+
+ @Test
+ fun `building a permalink for an invalid room alias returns failure`() {
+ val roomAlias = "an invalid room alias"
+ assertThat(PermalinkBuilder.permalinkForRoomAlias(roomAlias).isFailure).isTrue()
+ }
+
+ @Test
+ fun `building a permalink for a valid user id returns a matrix-to url`() {
+ val userId = UserId("@user:matrix.org")
+ assertThat(PermalinkBuilder.permalinkForUser(userId).getOrNull()).isEqualTo("https://matrix.to/#/@user:matrix.org")
+ }
+
+ @Test
+ fun `building a permalink for a valid room id returns a matrix-to url`() {
+ val roomId = RoomId("!aBCdEFG1234:matrix.org")
+ assertThat(PermalinkBuilder.permalinkForRoomId(roomId).getOrNull()).isEqualTo("https://matrix.to/#/!aBCdEFG1234:matrix.org")
+ }
+
+ @Test
+ fun `building a permalink for a valid room alias returns a matrix-to url`() {
+ val roomAlias = "#room:matrix.org"
+ assertThat(PermalinkBuilder.permalinkForRoomAlias(roomAlias).getOrNull()).isEqualTo("https://matrix.to/#/#room:matrix.org")
+ }
+}
diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParserTest.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParserTest.kt
new file mode 100644
index 0000000000..d55b6aebbe
--- /dev/null
+++ b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParserTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.permalink
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class PermalinkParserTest {
+
+ @Test
+ fun `parsing an invalid url returns a fallback link`() {
+ val url = "https://element.io"
+ assertThat(PermalinkParser.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java)
+ }
+
+ @Test
+ fun `parsing an invalid url with the right path but no content returns a fallback link`() {
+ val url = "https://app.element.io/#/user"
+ assertThat(PermalinkParser.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java)
+ }
+
+ @Test
+ fun `parsing an invalid url with the right path but empty content returns a fallback link`() {
+ val url = "https://app.element.io/#/user/"
+ assertThat(PermalinkParser.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java)
+ }
+
+ @Test
+ fun `parsing an invalid url with the right path but invalid content returns a fallback link`() {
+ val url = "https://app.element.io/#/user/some%20user!"
+ assertThat(PermalinkParser.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java)
+ }
+
+ @Test
+ fun `parsing a valid user url returns a user link`() {
+ val url = "https://app.element.io/#/user/@test:matrix.org"
+ assertThat(PermalinkParser.parse(url)).isEqualTo(
+ PermalinkData.UserLink(
+ userId = "@test:matrix.org"
+ )
+ )
+ }
+
+ @Test
+ fun `parsing a valid room id url returns a room link`() {
+ val url = "https://app.element.io/#/room/!aBCD1234:matrix.org"
+ assertThat(PermalinkParser.parse(url)).isEqualTo(
+ PermalinkData.RoomLink(
+ roomIdOrAlias = "!aBCD1234:matrix.org",
+ isRoomAlias = false,
+ eventId = null,
+ viaParameters = emptyList(),
+ )
+ )
+ }
+
+ @Test
+ fun `parsing a valid room id with event id url returns a room link`() {
+ val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/$1234567890abcdef:matrix.org"
+ assertThat(PermalinkParser.parse(url)).isEqualTo(
+ PermalinkData.RoomLink(
+ roomIdOrAlias = "!aBCD1234:matrix.org",
+ isRoomAlias = false,
+ eventId = "\$1234567890abcdef:matrix.org",
+ viaParameters = emptyList(),
+ )
+ )
+ }
+
+ @Test
+ fun `parsing a valid room id with and invalid event id url returns a room link with no event id`() {
+ val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/1234567890abcdef:matrix.org"
+ assertThat(PermalinkParser.parse(url)).isEqualTo(
+ PermalinkData.RoomLink(
+ roomIdOrAlias = "!aBCD1234:matrix.org",
+ isRoomAlias = false,
+ eventId = null,
+ viaParameters = emptyList(),
+ )
+ )
+ }
+
+ @Test
+ fun `parsing a valid room id with event id and via parameters url returns a room link`() {
+ val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/$1234567890abcdef:matrix.org?via=matrix.org&via=matrix.com"
+ assertThat(PermalinkParser.parse(url)).isEqualTo(
+ PermalinkData.RoomLink(
+ roomIdOrAlias = "!aBCD1234:matrix.org",
+ isRoomAlias = false,
+ eventId = "\$1234567890abcdef:matrix.org",
+ viaParameters = listOf("matrix.org", "matrix.com"),
+ )
+ )
+ }
+
+ @Test
+ fun `parsing a valid room alias url returns a room link`() {
+ val url = "https://app.element.io/#/room/#element-android:matrix.org"
+ assertThat(PermalinkParser.parse(url)).isEqualTo(
+ PermalinkData.RoomLink(
+ roomIdOrAlias = "#element-android:matrix.org",
+ isRoomAlias = true,
+ eventId = null,
+ viaParameters = emptyList(),
+ )
+ )
+ }
+
+ @Test
+ fun `parsing a url with an invalid signurl returns a fallback link`() {
+ // This url has no private key
+ val url = "https://app.element.io/#/room/%21aBCDEF12345%3Amatrix.org" +
+ "?email=testuser%40element.io" +
+ "&signurl=https%3A%2F%2Fvector.im%2F_matrix%2Fidentity%2Fapi%2Fv1%2Fsign-ed25519%3Ftoken%3Da_token" +
+ "&room_name=TestRoom" +
+ "&room_avatar_url=" +
+ "&inviter_name=User" +
+ "&guest_access_token=" +
+ "&guest_user_id=" +
+ "&room_type="
+ assertThat(PermalinkParser.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java)
+ }
+
+ @Test
+ fun `parsing a url with signurl returns a room email invite link`() {
+ val url = "https://app.element.io/#/room/%21aBCDEF12345%3Amatrix.org" +
+ "?email=testuser%40element.io" +
+ "&signurl=https%3A%2F%2Fvector.im%2F_matrix%2Fidentity%2Fapi%2Fv1%2Fsign-ed25519%3Ftoken%3Da_token%26private_key%3Da_private_key" +
+ "&room_name=TestRoom" +
+ "&room_avatar_url=" +
+ "&inviter_name=User" +
+ "&guest_access_token=" +
+ "&guest_user_id=" +
+ "&room_type="
+ assertThat(PermalinkParser.parse(url)).isEqualTo(
+ PermalinkData.RoomEmailInviteLink(
+ roomId = "!aBCDEF12345:matrix.org",
+ email = "testuser@element.io",
+ signUrl = "https://vector.im/_matrix/identity/api/v1/sign-ed25519?token=a_token&private_key=a_private_key",
+ roomName = "TestRoom",
+ roomAvatarUrl = "",
+ inviterName = "User",
+ identityServer = "vector.im",
+ token = "a_token",
+ privateKey = "a_private_key",
+ roomType = "",
+ )
+ )
+ }
+}
diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts
index a2b616f989..23a428c38f 100644
--- a/libraries/matrix/impl/build.gradle.kts
+++ b/libraries/matrix/impl/build.gradle.kts
@@ -29,8 +29,13 @@ anvil {
}
dependencies {
- // implementation(projects.libraries.rustsdk)
- implementation(libs.matrix.sdk)
+ releaseImplementation(libs.matrix.sdk)
+ if (file("${rootDir.path}/libraries/rustsdk/matrix-rust-sdk.aar").exists()) {
+ println("\nNote: Using local binary of the Rust SDK.\n")
+ debugImplementation(projects.libraries.rustsdk)
+ } else {
+ debugImplementation(libs.matrix.sdk)
+ }
implementation(projects.libraries.di)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.network)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
index d99c60b286..14cf3cd17e 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
@@ -48,6 +48,7 @@ import io.element.android.libraries.matrix.impl.notificationsettings.RustNotific
import io.element.android.libraries.matrix.impl.oidc.toRustAction
import io.element.android.libraries.matrix.impl.pushers.RustPushersService
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
+import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
@@ -114,6 +115,7 @@ class RustMatrixClient constructor(
private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock)
private val notificationSettingsService = RustNotificationSettingsService(notificationSettings, dispatchers)
+ private val roomSyncSubscriber = RoomSyncSubscriber(innerRoomListService, dispatchers)
private val isLoggingOut = AtomicBoolean(false)
@@ -124,7 +126,16 @@ class RustMatrixClient constructor(
Timber.v("didReceiveAuthError -> do the cleanup")
//TODO handle isSoftLogout parameter.
appCoroutineScope.launch {
- doLogout(doRequest = false)
+ val existingData = sessionStore.getSession(client.userId())
+ if (existingData != null) {
+ // Set isTokenValid to false
+ val newData = client.session().toSessionData(
+ isTokenValid = false,
+ loginType = existingData.loginType,
+ )
+ sessionStore.updateData(newData)
+ }
+ doLogout(doRequest = false, removeSession = false)
}
} else {
Timber.v("didReceiveAuthError -> already cleaning up")
@@ -134,7 +145,12 @@ class RustMatrixClient constructor(
override fun didRefreshTokens() {
Timber.w("didRefreshTokens()")
appCoroutineScope.launch {
- sessionStore.updateData(client.session().toSessionData())
+ val existingData = sessionStore.getSession(client.userId()) ?: return@launch
+ val newData = client.session().toSessionData(
+ isTokenValid = existingData.isTokenValid,
+ loginType = existingData.loginType,
+ )
+ sessionStore.updateData(newData)
}
}
}
@@ -185,13 +201,15 @@ class RustMatrixClient constructor(
systemClock = clock,
roomContentForwarder = roomContentForwarder,
sessionData = sessionStore.getSession(sessionId.value)!!,
+ roomSyncSubscriber = roomSyncSubscriber
)
}
}
private fun pairOfRoom(roomId: RoomId): Pair? {
val cachedRoomListItem = innerRoomListService.roomOrNull(roomId.value)
- val fullRoom = cachedRoomListItem?.fullRoom()
+ // Keep using fullRoomBlocking for now as it's faster.
+ val fullRoom = cachedRoomListItem?.fullRoomBlocking()
return if (cachedRoomListItem == null || fullRoom == null) {
Timber.d("No room cached for $roomId")
null
@@ -281,10 +299,9 @@ class RustMatrixClient constructor(
runCatching { client.setDisplayName(displayName) }
}
- @OptIn(ExperimentalUnsignedTypes::class)
override suspend fun uploadAvatar(mimeType: String, data: ByteArray): Result =
withContext(sessionDispatcher) {
- runCatching { client.uploadAvatar(mimeType, data.toUByteArray().toList()) }
+ runCatching { client.uploadAvatar(mimeType, data) }
}
override suspend fun removeAvatar(): Result =
@@ -292,7 +309,6 @@ class RustMatrixClient constructor(
runCatching { client.removeAvatar() }
}
-
override fun syncService(): SyncService = rustSyncService
override fun sessionVerificationService(): SessionVerificationService = verificationService
@@ -326,9 +342,9 @@ class RustMatrixClient constructor(
baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = false)
}
- override suspend fun logout(): String? = doLogout(doRequest = true)
+ override suspend fun logout(): String? = doLogout(doRequest = true, removeSession = true)
- private suspend fun doLogout(doRequest: Boolean): String? {
+ private suspend fun doLogout(doRequest: Boolean, removeSession: Boolean): String? {
var result: String? = null
withContext(sessionDispatcher) {
if (doRequest) {
@@ -340,7 +356,9 @@ class RustMatrixClient constructor(
}
close()
baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = true)
- sessionStore.removeSession(sessionId.value)
+ if (removeSession) {
+ sessionStore.removeSession(sessionId.value)
+ }
}
return result
}
@@ -364,10 +382,9 @@ class RustMatrixClient constructor(
}
}
- @OptIn(ExperimentalUnsignedTypes::class)
override suspend fun uploadMedia(mimeType: String, data: ByteArray, progressCallback: ProgressCallback?): Result = withContext(sessionDispatcher) {
runCatching {
- client.uploadMedia(mimeType, data.toUByteArray().toList(), progressCallback?.toProgressWatcher())
+ client.uploadMedia(mimeType, data, progressCallback?.toProgressWatcher())
}
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt
index 8f7f63e503..423fe925dc 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt
@@ -20,18 +20,19 @@ import io.element.android.libraries.matrix.api.auth.AuthenticationException
import org.matrix.rustcomponents.sdk.AuthenticationException as RustAuthenticationException
fun Throwable.mapAuthenticationException(): AuthenticationException {
+ val message = this.message ?: "Unknown error"
return when (this) {
- is RustAuthenticationException.ClientMissing -> AuthenticationException.ClientMissing(this.message!!)
- is RustAuthenticationException.Generic -> AuthenticationException.Generic(this.message!!)
- is RustAuthenticationException.InvalidServerName -> AuthenticationException.InvalidServerName(this.message!!)
- is RustAuthenticationException.SessionMissing -> AuthenticationException.SessionMissing(this.message!!)
- is RustAuthenticationException.SlidingSyncNotAvailable -> AuthenticationException.SlidingSyncNotAvailable(this.message!!)
- is RustAuthenticationException.OidcException -> AuthenticationException.OidcError("OidcException", message!!)
- is RustAuthenticationException.OidcMetadataInvalid -> AuthenticationException.OidcError("OidcMetadataInvalid", message!!)
- is RustAuthenticationException.OidcMetadataMissing -> AuthenticationException.OidcError("OidcMetadataMissing", message!!)
- is RustAuthenticationException.OidcNotSupported -> AuthenticationException.OidcError("OidcNotSupported", message!!)
- is RustAuthenticationException.OidcCancelled -> AuthenticationException.OidcError("OidcCancelled", message!!)
- is RustAuthenticationException.OidcCallbackUrlInvalid -> AuthenticationException.OidcError("OidcCallbackUrlInvalid", message!!)
- else -> AuthenticationException.Generic(this.message ?: "Unknown error")
+ is RustAuthenticationException.ClientMissing -> AuthenticationException.ClientMissing(message)
+ is RustAuthenticationException.Generic -> AuthenticationException.Generic(message)
+ is RustAuthenticationException.InvalidServerName -> AuthenticationException.InvalidServerName(message)
+ is RustAuthenticationException.SessionMissing -> AuthenticationException.SessionMissing(message)
+ is RustAuthenticationException.SlidingSyncNotAvailable -> AuthenticationException.SlidingSyncNotAvailable(message)
+ is RustAuthenticationException.OidcException -> AuthenticationException.OidcError("OidcException", message)
+ is RustAuthenticationException.OidcMetadataInvalid -> AuthenticationException.OidcError("OidcMetadataInvalid", message)
+ is RustAuthenticationException.OidcMetadataMissing -> AuthenticationException.OidcError("OidcMetadataMissing", message)
+ is RustAuthenticationException.OidcNotSupported -> AuthenticationException.OidcError("OidcNotSupported", message)
+ is RustAuthenticationException.OidcCancelled -> AuthenticationException.OidcError("OidcCancelled", message)
+ is RustAuthenticationException.OidcCallbackUrlInvalid -> AuthenticationException.OidcError("OidcCallbackUrlInvalid", message)
+ else -> AuthenticationException.Generic(message)
}
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt
index f2acb0b1be..033a5f6073 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt
@@ -30,6 +30,8 @@ import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
import io.element.android.libraries.matrix.impl.exception.mapClientException
import io.element.android.libraries.matrix.impl.mapper.toSessionData
import io.element.android.libraries.network.useragent.UserAgentProvider
+import io.element.android.libraries.sessionstorage.api.LoggedInState
+import io.element.android.libraries.sessionstorage.api.LoginType
import io.element.android.libraries.sessionstorage.api.SessionStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -62,7 +64,7 @@ class RustMatrixAuthenticationService @Inject constructor(
)
private var currentHomeserver = MutableStateFlow(null)
- override fun isLoggedIn(): Flow {
+ override fun loggedInStateFlow(): Flow {
return sessionStore.isLoggedIn()
}
@@ -74,7 +76,11 @@ class RustMatrixAuthenticationService @Inject constructor(
runCatching {
val sessionData = sessionStore.getSession(sessionId.value)
if (sessionData != null) {
- rustMatrixClientFactory.create(sessionData)
+ if (sessionData.isTokenValid) {
+ rustMatrixClientFactory.create(sessionData)
+ } else {
+ error("Token is not valid")
+ }
} else {
error("No session to restore with id $sessionId")
}
@@ -102,7 +108,12 @@ class RustMatrixAuthenticationService @Inject constructor(
withContext(coroutineDispatchers.io) {
runCatching {
val client = authService.login(username, password, "Element X Android", null)
- val sessionData = client.use { it.session().toSessionData() }
+ val sessionData = client.use {
+ it.session().toSessionData(
+ isTokenValid = true,
+ loginType = LoginType.PASSWORD,
+ )
+ }
sessionStore.storeData(sessionData)
SessionId(sessionData.userId)
}.mapFailure { failure ->
@@ -144,7 +155,12 @@ class RustMatrixAuthenticationService @Inject constructor(
runCatching {
val urlForOidcLogin = pendingOidcAuthenticationData ?: error("You need to call `getOidcUrl()` first")
val client = authService.loginWithOidcCallback(urlForOidcLogin, callbackUrl)
- val sessionData = client.use { it.session().toSessionData() }
+ val sessionData = client.use {
+ it.session().toSessionData(
+ isTokenValid = true,
+ loginType = LoginType.OIDC,
+ )
+ }
pendingOidcAuthenticationData?.close()
pendingOidcAuthenticationData = null
sessionStore.storeData(sessionData)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt
index 825c6f4397..fe21a460c8 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt
@@ -16,11 +16,15 @@
package io.element.android.libraries.matrix.impl.mapper
+import io.element.android.libraries.sessionstorage.api.LoginType
import io.element.android.libraries.sessionstorage.api.SessionData
import org.matrix.rustcomponents.sdk.Session
import java.util.Date
-internal fun Session.toSessionData() = SessionData(
+internal fun Session.toSessionData(
+ isTokenValid: Boolean,
+ loginType: LoginType,
+) = SessionData(
userId = userId,
deviceId = deviceId,
accessToken = accessToken,
@@ -29,4 +33,6 @@ internal fun Session.toSessionData() = SessionData(
oidcData = oidcData,
slidingSyncProxy = slidingSyncProxy,
loginTimestamp = Date(),
+ isTokenValid = isTokenValid,
+ loginType = loginType,
)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt
index c958810f5e..97148f7dd4 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt
@@ -17,6 +17,7 @@
package io.element.android.libraries.matrix.impl.media
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
+import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.media.MediaFile
import io.element.android.libraries.matrix.api.media.MediaSource
@@ -77,7 +78,7 @@ class RustMediaLoader(
val mediaFile = innerClient.getMediaFile(
mediaSource = mediaSource,
body = body,
- mimeType = mimeType ?: "application/octet-stream",
+ mimeType = mimeType ?: MimeTypes.OctetStream,
tempDir = cacheDirectory.path,
)
RustMediaFile(mediaFile)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt
index e30e57113d..3d444f9a63 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt
@@ -94,6 +94,7 @@ private fun MessageLikeEventContent.toContent(senderId: UserId): NotificationCon
}
MessageLikeEventContent.RoomRedaction -> NotificationContent.MessageLike.RoomRedaction
MessageLikeEventContent.Sticker -> NotificationContent.MessageLike.Sticker
+ is MessageLikeEventContent.Poll -> NotificationContent.MessageLike.Poll(senderId, question)
}
}
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt
index 8ee0361ace..d539ec6a53 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt
@@ -27,7 +27,6 @@ import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.RoomListService
import org.matrix.rustcomponents.sdk.TimelineDiff
import org.matrix.rustcomponents.sdk.TimelineListener
-import org.matrix.rustcomponents.sdk.genTransactionId
import kotlin.time.Duration.Companion.milliseconds
/**
@@ -61,7 +60,7 @@ class RoomContentForwarder(
// Sending a message requires a registered timeline listener
targetRoom.addTimelineListener(NoOpTimelineListener)
withTimeout(timeoutMs.milliseconds) {
- targetRoom.send(content, genTransactionId())
+ targetRoom.send(content)
}
}
// After sending, we remove the timeline
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSyncSubscriber.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSyncSubscriber.kt
new file mode 100644
index 0000000000..4286fbb1ec
--- /dev/null
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSyncSubscriber.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.impl.room
+
+import io.element.android.libraries.core.coroutine.CoroutineDispatchers
+import io.element.android.libraries.matrix.api.core.RoomId
+import io.element.android.libraries.matrix.api.timeline.item.event.EventType
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import kotlinx.coroutines.withContext
+import org.matrix.rustcomponents.sdk.RequiredState
+import org.matrix.rustcomponents.sdk.RoomListService
+import org.matrix.rustcomponents.sdk.RoomSubscription
+import timber.log.Timber
+
+class RoomSyncSubscriber(
+ private val roomListService: RoomListService,
+ private val dispatchers: CoroutineDispatchers,
+) {
+
+ private val subscriptionCounts = HashMap()
+ private val mutex = Mutex()
+
+ private val settings = RoomSubscription(
+ requiredState = listOf(
+ RequiredState(key = EventType.STATE_ROOM_CANONICAL_ALIAS, value = ""),
+ RequiredState(key = EventType.STATE_ROOM_TOPIC, value = ""),
+ RequiredState(key = EventType.STATE_ROOM_JOIN_RULES, value = ""),
+ RequiredState(key = EventType.STATE_ROOM_POWER_LEVELS, value = ""),
+ ),
+ timelineLimit = null
+ )
+
+ suspend fun subscribe(roomId: RoomId) = mutex.withLock {
+ withContext(dispatchers.io) {
+ try {
+ val currentSubscription = subscriptionCounts.getOrElse(roomId) { 0 }
+ if (currentSubscription == 0) {
+ Timber.d("Subscribing to room $roomId}")
+ roomListService.room(roomId.value).use { roomListItem ->
+ roomListItem.subscribe(settings)
+ }
+ }
+ subscriptionCounts[roomId] = currentSubscription + 1
+ } catch (exception: Exception) {
+ Timber.e("Failed to subscribe to room $roomId")
+ }
+ }
+ }
+
+ suspend fun unsubscribe(roomId: RoomId) = mutex.withLock {
+ withContext(dispatchers.io) {
+ try {
+ val currentSubscription = subscriptionCounts.getOrElse(roomId) { 0 }
+ when (currentSubscription) {
+ 0 -> return@withContext
+ 1 -> {
+ Timber.d("Unsubscribe from room $roomId")
+ roomListService.room(roomId.value).use { roomListItem ->
+ roomListItem.unsubscribe()
+ }
+ }
+ }
+ subscriptionCounts[roomId] = currentSubscription - 1
+ } catch (exception: Exception) {
+ Timber.e("Failed to unsubscribe from room $roomId")
+ }
+ }
+ }
+}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
index fea5058db5..4dcdb6d88c 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
@@ -40,7 +40,6 @@ import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.room.roomMembers
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
-import io.element.android.libraries.matrix.api.timeline.item.event.EventType
import io.element.android.libraries.matrix.impl.core.toProgressWatcher
import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl
import io.element.android.libraries.matrix.impl.media.map
@@ -55,19 +54,17 @@ import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.withContext
-import kotlinx.coroutines.yield
-import org.matrix.rustcomponents.sdk.RequiredState
+import org.matrix.rustcomponents.sdk.EventTimelineItem
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.RoomMember
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
-import org.matrix.rustcomponents.sdk.RoomSubscription
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
-import org.matrix.rustcomponents.sdk.genTransactionId
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
import timber.log.Timber
@@ -84,6 +81,7 @@ class RustMatrixRoom(
private val systemClock: SystemClock,
private val roomContentForwarder: RoomContentForwarder,
private val sessionData: SessionData,
+ private val roomSyncSubscriber: RoomSyncSubscriber,
) : MatrixRoom {
override val roomId = RoomId(innerRoom.id())
@@ -118,27 +116,15 @@ class RustMatrixRoom(
override val timeline: MatrixTimeline = _timeline
- override fun subscribeToSync() {
- val settings = RoomSubscription(
- requiredState = listOf(
- RequiredState(key = EventType.STATE_ROOM_CANONICAL_ALIAS, value = ""),
- RequiredState(key = EventType.STATE_ROOM_TOPIC, value = ""),
- RequiredState(key = EventType.STATE_ROOM_JOIN_RULES, value = ""),
- RequiredState(key = EventType.STATE_ROOM_POWER_LEVELS, value = ""),
- ),
- timelineLimit = null
- )
- roomListItem.subscribe(settings)
- }
+ override suspend fun subscribeToSync() = roomSyncSubscriber.subscribe(roomId)
- override fun unsubscribeFromSync() {
- roomListItem.unsubscribe()
- }
+ override suspend fun unsubscribeFromSync() = roomSyncSubscriber.unsubscribe(roomId)
override fun destroy() {
roomCoroutineScope.cancel()
innerRoom.destroy()
roomListItem.destroy()
+ specialModeEventTimelineItem?.destroy()
}
override val name: String?
@@ -193,7 +179,7 @@ class RustMatrixRoom(
while (true) {
// Loading the whole membersIterator as a stop-gap measure.
// We should probably implement some sort of paging in the future.
- yield()
+ ensureActive()
addAll(membersIterator.nextChunk(1000u) ?: break)
}
}
@@ -241,10 +227,9 @@ class RustMatrixRoom(
}
override suspend fun sendMessage(body: String, htmlBody: String?): Result = withContext(roomDispatcher) {
- val transactionId = genTransactionId()
messageEventContentFromParts(body, htmlBody).use { content ->
runCatching {
- innerRoom.send(content, transactionId)
+ innerRoom.send(content)
}
}
}
@@ -253,26 +238,46 @@ class RustMatrixRoom(
withContext(roomDispatcher) {
if (originalEventId != null) {
runCatching {
- innerRoom.edit(messageEventContentFromParts(body, htmlBody), originalEventId.value, transactionId?.value)
+ val editedEvent = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(originalEventId.value)
+ editedEvent.use {
+ innerRoom.edit(
+ newContent = messageEventContentFromParts(body, htmlBody),
+ editItem = it,
+ )
+ }
+ specialModeEventTimelineItem = null
}
} else {
runCatching {
transactionId?.let { cancelSend(it) }
- innerRoom.send(messageEventContentFromParts(body, htmlBody), genTransactionId())
+ innerRoom.send(messageEventContentFromParts(body, htmlBody))
}
}
}
+ private var specialModeEventTimelineItem: EventTimelineItem? = null
+
+ override suspend fun enterSpecialMode(eventId: EventId?): Result = withContext(roomDispatcher) {
+ runCatching {
+ specialModeEventTimelineItem?.destroy()
+ specialModeEventTimelineItem = null
+ specialModeEventTimelineItem = eventId?.let { innerRoom.getEventTimelineItemByEventId(it.value) }
+ }
+ }
+
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result = withContext(roomDispatcher) {
runCatching {
- innerRoom.sendReply(messageEventContentFromParts(body, htmlBody), eventId.value, genTransactionId())
+ val inReplyTo = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(eventId.value)
+ inReplyTo.use { eventTimelineItem ->
+ innerRoom.sendReply(messageEventContentFromParts(body, htmlBody), eventTimelineItem)
+ }
+ specialModeEventTimelineItem = null
}
}
override suspend fun redactEvent(eventId: EventId, reason: String?) = withContext(roomDispatcher) {
- val transactionId = genTransactionId()
runCatching {
- innerRoom.redact(eventId.value, reason, transactionId)
+ innerRoom.redact(eventId.value, reason)
}
}
@@ -368,10 +373,9 @@ class RustMatrixRoom(
}
}
- @OptIn(ExperimentalUnsignedTypes::class)
override suspend fun updateAvatar(mimeType: String, data: ByteArray): Result = withContext(roomDispatcher) {
runCatching {
- innerRoom.uploadAvatar(mimeType, data.toUByteArray().toList())
+ innerRoom.uploadAvatar(mimeType, data, null)
}
}
@@ -416,7 +420,6 @@ class RustMatrixRoom(
description = description,
zoomLevel = zoomLevel?.toUByte(),
assetType = assetType?.toInner(),
- txnId = genTransactionId(),
)
}
}
@@ -433,7 +436,6 @@ class RustMatrixRoom(
answers = answers,
maxSelections = maxSelections.toUByte(),
pollKind = pollKind.toInner(),
- txnId = genTransactionId(),
)
}
}
@@ -446,7 +448,6 @@ class RustMatrixRoom(
innerRoom.sendPollResponse(
pollStartId = pollStartId.value,
answers = answers,
- txnId = genTransactionId(),
)
}
}
@@ -459,11 +460,24 @@ class RustMatrixRoom(
innerRoom.endPoll(
pollStartId = pollStartId.value,
text = text,
- txnId = genTransactionId(),
)
}
}
+ override suspend fun sendVoiceMessage(
+ file: File,
+ audioInfo: AudioInfo,
+ waveform: List,
+ progressCallback: ProgressCallback?,
+ ): Result = sendAttachment(listOf(file)) {
+ innerRoom.sendVoiceMessage(
+ url = file.path,
+ audioInfo = audioInfo.map(),
+ waveform = waveform.map { it.toUShort() },
+ progressWatcher = progressCallback?.toProgressWatcher(),
+ )
+ }
+
private suspend fun sendAttachment(files: List, handle: () -> SendAttachmentJoinHandle): Result {
return runCatching {
MediaUploadHandlerImpl(files, handle())
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt
index 28297426c4..b95eb95333 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt
@@ -23,14 +23,14 @@ import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.catch
-import org.matrix.rustcomponents.sdk.RoomList
import org.matrix.rustcomponents.sdk.RoomListEntriesListener
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
import org.matrix.rustcomponents.sdk.RoomListEntry
+import org.matrix.rustcomponents.sdk.RoomListInterface
import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.RoomListLoadingState
import org.matrix.rustcomponents.sdk.RoomListLoadingStateListener
-import org.matrix.rustcomponents.sdk.RoomListService
+import org.matrix.rustcomponents.sdk.RoomListServiceInterface
import org.matrix.rustcomponents.sdk.RoomListServiceState
import org.matrix.rustcomponents.sdk.RoomListServiceStateListener
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
@@ -40,7 +40,7 @@ import timber.log.Timber
private const val SYNC_INDICATOR_DELAY_BEFORE_SHOWING = 1000u
private const val SYNC_INDICATOR_DELAY_BEFORE_HIDING = 0u
-fun RoomList.loadingStateFlow(): Flow =
+fun RoomListInterface.loadingStateFlow(): Flow =
mxCallbackFlow {
val listener = object : RoomListLoadingStateListener {
override fun onUpdate(state: RoomListLoadingState) {
@@ -58,7 +58,7 @@ fun RoomList.loadingStateFlow(): Flow =
Timber.d(it, "loadingStateFlow() failed")
}.buffer(Channel.UNLIMITED)
-fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit): Flow> =
+fun RoomListInterface.entriesFlow(onInitialList: suspend (List) -> Unit): Flow> =
mxCallbackFlow {
val listener = object : RoomListEntriesListener {
override fun onUpdate(roomEntriesUpdate: List) {
@@ -76,7 +76,7 @@ fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit):
Timber.d(it, "entriesFlow() failed")
}.buffer(Channel.UNLIMITED)
-fun RoomListService.stateFlow(): Flow =
+fun RoomListServiceInterface.stateFlow(): Flow =
mxCallbackFlow {
val listener = object : RoomListServiceStateListener {
override fun onUpdate(state: RoomListServiceState) {
@@ -88,7 +88,7 @@ fun RoomListService.stateFlow(): Flow =
}
}.buffer(Channel.UNLIMITED)
-fun RoomListService.syncIndicator(): Flow =
+fun RoomListServiceInterface.syncIndicator(): Flow =
mxCallbackFlow {
val listener = object : RoomListServiceSyncIndicatorListener {
override fun onUpdate(syncIndicator: RoomListServiceSyncIndicator) {
@@ -104,7 +104,7 @@ fun RoomListService.syncIndicator(): Flow =
}
}.buffer(Channel.UNLIMITED)
-fun RoomListService.roomOrNull(roomId: String): RoomListItem? {
+fun RoomListServiceInterface.roomOrNull(roomId: String): RoomListItem? {
return try {
room(roomId)
} catch (exception: Exception) {
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt
index 778637bdc7..d0e3d1c8cf 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt
@@ -26,14 +26,14 @@ import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
import org.matrix.rustcomponents.sdk.RoomListEntry
-import org.matrix.rustcomponents.sdk.RoomListService
+import org.matrix.rustcomponents.sdk.RoomListServiceInterface
import org.matrix.rustcomponents.sdk.use
import timber.log.Timber
import java.util.UUID
class RoomSummaryListProcessor(
private val roomSummaries: MutableStateFlow>,
- private val roomListService: RoomListService,
+ private val roomListService: RoomListServiceInterface,
private val dispatcher: CoroutineDispatcher,
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
) {
@@ -73,7 +73,7 @@ class RoomSummaryListProcessor(
}
}
- private fun MutableList.applyUpdate(update: RoomListEntriesUpdate) {
+ private suspend fun MutableList.applyUpdate(update: RoomListEntriesUpdate) {
when (update) {
is RoomListEntriesUpdate.Append -> {
val roomSummaries = update.values.map {
@@ -119,7 +119,7 @@ class RoomSummaryListProcessor(
}
}
- private fun buildSummaryForRoomListEntry(entry: RoomListEntry): RoomSummary {
+ private suspend fun buildSummaryForRoomListEntry(entry: RoomListEntry): RoomSummary {
return when (entry) {
RoomListEntry.Empty -> buildEmptyRoomSummary()
is RoomListEntry.Filled -> buildAndCacheRoomSummaryForIdentifier(entry.roomId)
@@ -133,9 +133,9 @@ class RoomSummaryListProcessor(
return RoomSummary.Empty(UUID.randomUUID().toString())
}
- private fun buildAndCacheRoomSummaryForIdentifier(identifier: String): RoomSummary {
+ private suspend fun buildAndCacheRoomSummaryForIdentifier(identifier: String): RoomSummary {
val builtRoomSummary = roomListService.roomOrNull(identifier)?.use { roomListItem ->
- roomListItem.roomInfoBlocking().use { roomInfo ->
+ roomListItem.roomInfo().use { roomInfo ->
RoomSummary.Filled(
details = roomSummaryDetailsFactory.create(roomInfo)
)
diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt
new file mode 100644
index 0000000000..b0c02c9dcd
--- /dev/null
+++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.impl.auth
+
+import com.google.common.truth.ThrowableSubject
+import com.google.common.truth.Truth.assertThat
+import io.element.android.libraries.matrix.api.auth.AuthenticationException
+import org.junit.Test
+import org.matrix.rustcomponents.sdk.AuthenticationException as RustAuthenticationException
+
+class AuthenticationExceptionMappingTest {
+
+ @Test
+ fun `mapping an exception with no message returns 'Unknown error' message`() {
+ val exception = Exception()
+ val mappedException = exception.mapAuthenticationException()
+ assertThat(mappedException.message).isEqualTo("Unknown error")
+ }
+
+ @Test
+ fun `mapping a generic exception returns a Generic AuthenticationException`() {
+ val exception = Exception("Generic exception")
+ val mappedException = exception.mapAuthenticationException()
+ assertThat(mappedException).isException("Generic exception")
+ }
+
+ @Test
+ fun `mapping specific exceptions map to their kotlin counterparts`() {
+ assertThat(RustAuthenticationException.ClientMissing("Client missing").mapAuthenticationException())
+ .isException("Client missing")
+
+ assertThat(RustAuthenticationException.Generic("Generic").mapAuthenticationException()).isException("Generic")
+
+ assertThat(RustAuthenticationException.InvalidServerName("Invalid server name").mapAuthenticationException())
+ .isException("Invalid server name")
+
+ assertThat(RustAuthenticationException.SessionMissing("Session missing").mapAuthenticationException())
+ .isException("Session missing")
+
+ assertThat(RustAuthenticationException.SlidingSyncNotAvailable("Sliding sync not available").mapAuthenticationException())
+ .isException("Sliding sync not available")
+ }
+
+ @Test
+ fun `mapping Oidc related exceptions creates an 'OidcError' with different types`() {
+ assertIsOidcError(
+ throwable = RustAuthenticationException.OidcException("Oidc exception"),
+ type = "OidcException",
+ message = "Oidc exception"
+ )
+ assertIsOidcError(
+ throwable = RustAuthenticationException.OidcMetadataInvalid("Oidc metadata invalid"),
+ type = "OidcMetadataInvalid",
+ message = "Oidc metadata invalid"
+ )
+ assertIsOidcError(
+ throwable = RustAuthenticationException.OidcMetadataMissing("Oidc metadata missing"),
+ type = "OidcMetadataMissing",
+ message = "Oidc metadata missing"
+ )
+ assertIsOidcError(
+ throwable = RustAuthenticationException.OidcNotSupported("Oidc not supported"),
+ type = "OidcNotSupported",
+ message = "Oidc not supported"
+ )
+ assertIsOidcError(
+ throwable = RustAuthenticationException.OidcCancelled("Oidc cancelled"),
+ type = "OidcCancelled",
+ message = "Oidc cancelled"
+ )
+ assertIsOidcError(
+ throwable = RustAuthenticationException.OidcCallbackUrlInvalid("Oidc callback url invalid"),
+ type = "OidcCallbackUrlInvalid",
+ message = "Oidc callback url invalid"
+ )
+ }
+
+ private inline fun ThrowableSubject.isException(message: String) {
+ isInstanceOf(T::class.java)
+ hasMessageThat().isEqualTo(message)
+ }
+
+ private fun assertIsOidcError(throwable: Throwable, type: String, message: String) {
+ val authenticationException = throwable.mapAuthenticationException()
+ assertThat(authenticationException).isInstanceOf(AuthenticationException.OidcError::class.java)
+ assertThat((authenticationException as? AuthenticationException.OidcError)?.type).isEqualTo(type)
+ assertThat(authenticationException.message).isEqualTo(message)
+ }
+}
diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTests.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTests.kt
new file mode 100644
index 0000000000..5a376ea928
--- /dev/null
+++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTests.kt
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.impl.roomlist
+
+import com.google.common.truth.Truth.assertThat
+import com.sun.jna.Pointer
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
+import io.element.android.libraries.matrix.test.A_ROOM_ID
+import io.element.android.libraries.matrix.test.A_ROOM_ID_2
+import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.withTimeout
+import org.junit.Test
+import org.matrix.rustcomponents.sdk.RoomList
+import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
+import org.matrix.rustcomponents.sdk.RoomListEntry
+import org.matrix.rustcomponents.sdk.RoomListInput
+import org.matrix.rustcomponents.sdk.RoomListItem
+import org.matrix.rustcomponents.sdk.RoomListServiceInterface
+import org.matrix.rustcomponents.sdk.RoomListServiceStateListener
+import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicatorListener
+import org.matrix.rustcomponents.sdk.TaskHandle
+import kotlin.time.Duration.Companion.milliseconds
+
+// NOTE: this class is using a fake implementation of a Rust SDK interface which returns actual Rust objects with pointers.
+// Since we don't access the data in those objects, this is fine for our tests, but that's as far as we can test this class.
+class RoomSummaryListProcessorTests {
+
+ private val summaries = MutableStateFlow>(emptyList())
+
+ @Test
+ fun `postUpdates can't start until postEntries is done`() = runTest {
+ val processor = createProcessor()
+ val update = listOf(RoomListEntriesUpdate.Reset(emptyList()))
+
+ val timeoutError = runCatching {
+ withTimeout(10.milliseconds) { processor.postUpdate(update) }
+ }.exceptionOrNull()
+ assertThat(timeoutError).isInstanceOf(CancellationException::class.java)
+
+ processor.postEntries(listOf(RoomListEntry.Empty))
+ processor.postUpdate(update)
+ }
+
+ @Test
+ fun `postEntries adds all new entries with no diffing`() = runTest {
+ summaries.value = listOf(aRoomSummaryFilled())
+ val processor = createProcessor()
+
+ processor.postEntries(listOf(RoomListEntry.Empty, RoomListEntry.Empty, RoomListEntry.Empty))
+
+ assertThat(summaries.value.count()).isEqualTo(4)
+ }
+
+ @Test
+ fun `Append adds new entries at the end of the list`() = runTest {
+ summaries.value = listOf(aRoomSummaryFilled())
+ val processor = createProcessor()
+
+ // Start processing updates
+ processor.postEntries(listOf())
+ // Process actual update
+ processor.postUpdate(listOf(RoomListEntriesUpdate.Append(listOf(RoomListEntry.Empty, RoomListEntry.Empty, RoomListEntry.Empty))))
+
+ assertThat(summaries.value.count()).isEqualTo(4)
+ assertThat(summaries.value.subList(1, 4).all { it is RoomSummary.Empty }).isTrue()
+ }
+
+ @Test
+ fun `PushBack adds a new entry at the end of the list`() = runTest {
+ summaries.value = listOf(aRoomSummaryFilled())
+ val processor = createProcessor()
+
+ // Start processing updates
+ processor.postEntries(listOf())
+ // Process actual update
+ processor.postUpdate(listOf(RoomListEntriesUpdate.PushBack(RoomListEntry.Empty)))
+
+ assertThat(summaries.value.count()).isEqualTo(2)
+ assertThat(summaries.value.last()).isInstanceOf(RoomSummary.Empty::class.java)
+ }
+
+ @Test
+ fun `PushFront inserts a new entry at the start of the list`() = runTest {
+ summaries.value = listOf(aRoomSummaryFilled())
+ val processor = createProcessor()
+
+ // Start processing updates
+ processor.postEntries(listOf())
+ // Process actual update
+ processor.postUpdate(listOf(RoomListEntriesUpdate.PushFront(RoomListEntry.Empty)))
+
+ assertThat(summaries.value.count()).isEqualTo(2)
+ assertThat(summaries.value.first()).isInstanceOf(RoomSummary.Empty::class.java)
+ }
+
+ @Test
+ fun `Set replaces an entry at some index`() = runTest {
+ summaries.value = listOf(aRoomSummaryFilled())
+ val processor = createProcessor()
+ val index = 0
+
+ // Start processing updates
+ processor.postEntries(listOf())
+ // Process actual update
+ processor.postUpdate(listOf(RoomListEntriesUpdate.Set(index.toUInt(), RoomListEntry.Empty)))
+
+ assertThat(summaries.value.count()).isEqualTo(1)
+ assertThat(summaries.value[index]).isInstanceOf(RoomSummary.Empty::class.java)
+ }
+
+ @Test
+ fun `Insert inserts a new entry at the provided index`() = runTest {
+ summaries.value = listOf(aRoomSummaryFilled())
+ val processor = createProcessor()
+ val index = 0
+
+ // Start processing updates
+ processor.postEntries(listOf())
+ // Process actual update
+ processor.postUpdate(listOf(RoomListEntriesUpdate.Insert(index.toUInt(), RoomListEntry.Empty)))
+
+ assertThat(summaries.value.count()).isEqualTo(2)
+ assertThat(summaries.value[index]).isInstanceOf(RoomSummary.Empty::class.java)
+ }
+
+ @Test
+ fun `Remove removes an entry at some index`() = runTest {
+ summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
+ val processor = createProcessor()
+ val index = 0
+
+ // Start processing updates
+ processor.postEntries(listOf())
+ // Process actual update
+ processor.postUpdate(listOf(RoomListEntriesUpdate.Remove(index.toUInt())))
+
+ assertThat(summaries.value.count()).isEqualTo(1)
+ assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID_2.value)
+ }
+
+ @Test
+ fun `PopBack removes an entry at the end of the list`() = runTest {
+ summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
+ val processor = createProcessor()
+ val index = 0
+
+ // Start processing updates
+ processor.postEntries(listOf())
+ // Process actual update
+ processor.postUpdate(listOf(RoomListEntriesUpdate.PopBack))
+
+ assertThat(summaries.value.count()).isEqualTo(1)
+ assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID.value)
+ }
+
+ @Test
+ fun `PopFront removes an entry at the start of the list`() = runTest {
+ summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
+ val processor = createProcessor()
+ val index = 0
+
+ // Start processing updates
+ processor.postEntries(listOf())
+ // Process actual update
+ processor.postUpdate(listOf(RoomListEntriesUpdate.PopFront))
+
+ assertThat(summaries.value.count()).isEqualTo(1)
+ assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID_2.value)
+ }
+
+ @Test
+ fun `Clear removes all the entries`() = runTest {
+ summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
+ val processor = createProcessor()
+
+ // Start processing updates
+ processor.postEntries(listOf())
+ // Process actual update
+ processor.postUpdate(listOf(RoomListEntriesUpdate.Clear))
+
+ assertThat(summaries.value).isEmpty()
+ }
+
+ @Test
+ fun `Truncate removes all entries after the provided length`() = runTest {
+ summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
+ val processor = createProcessor()
+ val index = 0
+
+ // Start processing updates
+ processor.postEntries(listOf())
+ // Process actual update
+ processor.postUpdate(listOf(RoomListEntriesUpdate.Truncate(1u)))
+
+ assertThat(summaries.value.count()).isEqualTo(1)
+ assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID.value)
+ }
+
+ private fun TestScope.createProcessor() = RoomSummaryListProcessor(
+ summaries,
+ fakeRoomListService,
+ dispatcher = StandardTestDispatcher(testScheduler),
+ roomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
+ )
+
+ // Fake room list service that returns Rust objects with null pointers. Luckily for us, they don't crash for our test cases
+ private val fakeRoomListService = object : RoomListServiceInterface {
+ override suspend fun allRooms(): RoomList {
+ return RoomList(Pointer.NULL)
+ }
+
+ override suspend fun applyInput(input: RoomListInput) = Unit
+
+ override suspend fun invites(): RoomList {
+ return RoomList(Pointer.NULL)
+ }
+
+ override fun room(roomId: String): RoomListItem {
+ return RoomListItem(Pointer.NULL)
+ }
+
+ override fun state(listener: RoomListServiceStateListener): TaskHandle {
+ return TaskHandle(Pointer.NULL)
+ }
+
+ override fun syncIndicator(delayBeforeShowingInMs: UInt, delayBeforeHidingInMs: UInt, listener: RoomListServiceSyncIndicatorListener): TaskHandle {
+ return TaskHandle(Pointer.NULL)
+ }
+ }
+}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt
index 81fa3b677c..2cf6b77a78 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt
@@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.test.A_USER_ID
+import io.element.android.libraries.sessionstorage.api.LoggedInState
import io.element.android.tests.testutils.simulateLongTask
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -36,9 +37,10 @@ class FakeAuthenticationService : MatrixAuthenticationService {
private var oidcCancelError: Throwable? = null
private var loginError: Throwable? = null
private var changeServerError: Throwable? = null
+ private var matrixClient: MatrixClient? = null
- override fun isLoggedIn(): Flow {
- return flowOf(false)
+ override fun loggedInStateFlow(): Flow {
+ return flowOf(LoggedInState.NotLoggedIn)
}
override suspend fun getLatestSessionId(): SessionId? {
@@ -46,7 +48,11 @@ class FakeAuthenticationService : MatrixAuthenticationService {
}
override suspend fun restoreSession(sessionId: SessionId): Result {
- return Result.failure(IllegalStateException())
+ return if (matrixClient != null) {
+ Result.success(matrixClient!!)
+ } else {
+ Result.failure(IllegalStateException())
+ }
}
override fun getHomeserverDetails(): StateFlow {
@@ -92,4 +98,8 @@ class FakeAuthenticationService : MatrixAuthenticationService {
fun givenChangeServerError(throwable: Throwable?) {
changeServerError = throwable
}
+
+ fun givenMatrixClient(matrixClient: MatrixClient) {
+ this.matrixClient = matrixClient
+ }
}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
index 0e8916e87e..7549522c0c 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
@@ -31,8 +31,8 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification
import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
-import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
+import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
@@ -157,9 +157,9 @@ class FakeMatrixRoom(
override val timeline: MatrixTimeline = matrixTimeline
- override fun subscribeToSync() = Unit
+ override suspend fun subscribeToSync() = Unit
- override fun unsubscribeFromSync() = Unit
+ override suspend fun unsubscribeFromSync() = Unit
override fun destroy() = Unit
@@ -208,6 +208,10 @@ class FakeMatrixRoom(
var replyMessageParameter: Pair? = null
private set
+ override suspend fun enterSpecialMode(eventId: EventId?): Result {
+ return Result.success(Unit)
+ }
+
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result {
replyMessageParameter = body to htmlBody
return Result.success(Unit)
@@ -357,6 +361,13 @@ class FakeMatrixRoom(
return endPollResult
}
+ override suspend fun sendVoiceMessage(
+ file: File,
+ audioInfo: AudioInfo,
+ waveform: List,
+ progressCallback: ProgressCallback?
+ ): Result = fakeSendMedia(progressCallback)
+
fun givenLeaveRoomError(throwable: Throwable?) {
this.leaveRoomError = throwable
}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt
index d3b4dbc577..ae473d8da8 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt
@@ -20,10 +20,12 @@ import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.core.UserId
+import io.element.android.libraries.matrix.api.poll.PollAnswer
+import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+import io.element.android.libraries.matrix.api.room.message.RoomMessage
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
-import io.element.android.libraries.matrix.api.room.message.RoomMessage
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction
@@ -32,6 +34,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.MessageType
+import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
@@ -173,3 +176,13 @@ fun aTimelineItemDebugInfo(
model, originalJson, latestEditedJson
)
+fun aPollContent(
+ question: String = "Do you like polls?",
+) = PollContent(
+ question = question,
+ kind = PollKind.Disclosed,
+ maxSelections = 1u,
+ answers = listOf(PollAnswer("1", "Yes"), PollAnswer("2", "No")),
+ votes = mapOf(),
+ endTime = null
+)
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt
index 71883ebc20..cafbdd04c0 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt
@@ -21,7 +21,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Attachment
import androidx.compose.material.icons.outlined.GraphicEq
import androidx.compose.material.icons.outlined.VideoCameraBack
import androidx.compose.material3.MaterialTheme
@@ -34,6 +33,7 @@ import androidx.compose.ui.layout.ContentScale
import io.element.android.libraries.designsystem.components.BlurHashAsyncImage
import io.element.android.libraries.designsystem.components.PinIcon
import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.media.MediaRequestData
import kotlinx.parcelize.Parcelize
@@ -77,7 +77,7 @@ fun AttachmentThumbnail(
}
AttachmentThumbnailType.File -> {
Icon(
- imageVector = Icons.Outlined.Attachment,
+ resourceId = CommonDrawables.ic_september_attachment,
contentDescription = info.textContent,
modifier = Modifier.rotate(-45f)
)
@@ -94,7 +94,7 @@ fun AttachmentThumbnail(
}
@Parcelize
-enum class AttachmentThumbnailType: Parcelable {
+enum class AttachmentThumbnailType : Parcelable {
Image, Video, File, Audio, Location
}
@@ -104,4 +104,4 @@ data class AttachmentThumbnailInfo(
val thumbnailSource: MediaSource? = null,
val textContent: String? = null,
val blurHash: String? = null,
-): Parcelable
+) : Parcelable
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt
index c9933b8160..05287e04e1 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt
@@ -35,7 +35,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheetLayout
@@ -100,7 +100,7 @@ private fun AvatarActionBottomSheetContent(
},
leadingContent = {
Icon(
- imageVector = action.icon,
+ resourceId = action.iconResourceId,
contentDescription = stringResource(action.titleResId),
tint = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.secondary,
)
@@ -110,7 +110,7 @@ private fun AvatarActionBottomSheetContent(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AvatarActionBottomSheetPreview() = ElementPreview {
AvatarActionBottomSheet(
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt
index 2f95082e67..d9bbff4f7d 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableMatrixUserRow.kt
@@ -16,22 +16,13 @@
package io.element.android.libraries.matrix.ui.components
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.PreviewParameter
-import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
-import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.matrix.ui.model.getBestName
@@ -54,42 +45,7 @@ fun CheckableMatrixUserRow(
enabled = enabled,
)
-@Composable
-fun CheckableUserRow(
- checked: Boolean,
- avatarData: AvatarData,
- name: String,
- subtext: String?,
- modifier: Modifier = Modifier,
- onCheckedChange: (Boolean) -> Unit = {},
- enabled: Boolean = true,
-) {
- Row(
- modifier = modifier
- .fillMaxWidth()
- .clickable(role = Role.Checkbox, enabled = enabled) {
- onCheckedChange(!checked)
- },
- verticalAlignment = Alignment.CenterVertically,
- ) {
- UserRow(
- modifier = Modifier.weight(1f),
- avatarData = avatarData,
- name = name,
- subtext = subtext,
- )
-
- Checkbox(
- modifier = Modifier
- .padding(end = 16.dp),
- checked = checked,
- onCheckedChange = null,
- enabled = enabled,
- )
- }
-}
-
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun CheckableMatrixUserRowPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreview {
Column {
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUnresolvedUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUnresolvedUserRow.kt
new file mode 100644
index 0000000000..b829ff8e47
--- /dev/null
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUnresolvedUserRow.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.ui.components
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.components.avatar.AvatarData
+import io.element.android.libraries.designsystem.components.avatar.AvatarSize
+import io.element.android.libraries.designsystem.preview.ElementThemedPreview
+import io.element.android.libraries.designsystem.theme.components.Checkbox
+import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
+import io.element.android.libraries.matrix.ui.model.getAvatarData
+
+@Composable
+fun CheckableUnresolvedUserRow(
+ checked: Boolean,
+ avatarData: AvatarData,
+ id: String,
+ modifier: Modifier = Modifier,
+ onCheckedChange: (Boolean) -> Unit = {},
+ enabled: Boolean = true,
+) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .clickable(role = Role.Checkbox, enabled = enabled) {
+ onCheckedChange(!checked)
+ },
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ UnresolvedUserRow(
+ modifier = Modifier.weight(1f),
+ avatarData = avatarData,
+ id = id,
+ )
+
+ Checkbox(
+ modifier = Modifier.padding(end = 16.dp),
+ checked = checked,
+ onCheckedChange = null,
+ enabled = enabled,
+ )
+ }
+}
+
+@Preview
+@Composable
+internal fun CheckableUnresolvedUserRowPreview() = ElementThemedPreview {
+ val matrixUser = aMatrixUser()
+ Column {
+ CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value)
+ HorizontalDivider()
+ CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value)
+ HorizontalDivider()
+ CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value, enabled = false)
+ HorizontalDivider()
+ CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value, enabled = false)
+ }
+}
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUserRow.kt
new file mode 100644
index 0000000000..f6272b9fda
--- /dev/null
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/CheckableUserRow.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.ui.components
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.components.avatar.AvatarData
+import io.element.android.libraries.designsystem.theme.components.Checkbox
+
+@Composable
+fun CheckableUserRow(
+ checked: Boolean,
+ avatarData: AvatarData,
+ name: String,
+ subtext: String?,
+ modifier: Modifier = Modifier,
+ onCheckedChange: (Boolean) -> Unit = {},
+ enabled: Boolean = true,
+) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .clickable(role = Role.Checkbox, enabled = enabled) {
+ onCheckedChange(!checked)
+ },
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ UserRow(
+ modifier = Modifier.weight(1f),
+ avatarData = avatarData,
+ name = name,
+ subtext = subtext,
+ )
+
+ Checkbox(
+ modifier = Modifier
+ .padding(end = 16.dp),
+ checked = checked,
+ onCheckedChange = null,
+ enabled = enabled,
+ )
+ }
+}
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt
index ef950ca7b5..901b403d0f 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt
@@ -30,7 +30,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.user.MatrixUser
@@ -100,7 +100,7 @@ private fun MatrixUserHeaderContent(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun MatrixUserHeaderPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreview {
MatrixUserHeader(matrixUser)
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt
index 5ebdffb166..ff0e674a79 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt
@@ -33,7 +33,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.placeholderBackground
import io.element.android.libraries.theme.ElementTheme
@@ -65,7 +65,7 @@ fun MatrixUserHeaderPlaceholder(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun MatrixUserHeaderPlaceholderPreview() = ElementPreview {
MatrixUserHeaderPlaceholder()
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt
index c24a6f285a..1380d15e8b 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt
@@ -16,28 +16,15 @@
package io.element.android.libraries.matrix.ui.components
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter
-import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.components.avatar.Avatar
-import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
-import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.matrix.ui.model.getBestName
-import io.element.android.libraries.theme.ElementTheme
@Composable
fun MatrixUserRow(
@@ -51,48 +38,7 @@ fun MatrixUserRow(
modifier = modifier,
)
-@Composable
-fun UserRow(
- avatarData: AvatarData,
- name: String,
- subtext: String?,
- modifier: Modifier = Modifier,
-) {
- Row(
- modifier = modifier
- .fillMaxWidth()
- .heightIn(min = 56.dp)
- .padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Avatar(avatarData)
- Column(
- modifier = Modifier
- .padding(start = 12.dp),
- ) {
- // Name
- Text(
- text = name,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- color = MaterialTheme.colorScheme.primary,
- style = ElementTheme.typography.fontBodyLgRegular,
- )
- // Id
- subtext?.let {
- Text(
- text = subtext,
- color = MaterialTheme.colorScheme.secondary,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- style = ElementTheme.typography.fontBodySmRegular,
- )
- }
- }
- }
-}
-
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun MatrixUserRowPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreview {
MatrixUserRow(matrixUser)
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt
index b235e74ae6..dbf4968ba3 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt
@@ -24,8 +24,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -39,11 +37,12 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
import io.element.android.libraries.ui.strings.CommonStrings
@@ -82,7 +81,7 @@ fun SelectedRoom(
),
) {
Icon(
- imageVector = Icons.Default.Close,
+ resourceId = CommonDrawables.ic_compound_close,
contentDescription = stringResource(id = CommonStrings.action_remove),
tint = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier.padding(2.dp)
@@ -91,7 +90,7 @@ fun SelectedRoom(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun SelectedRoomPreview() = ElementPreview {
SelectedRoom(
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt
index 6208d7260a..536ef783ff 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt
@@ -24,8 +24,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -38,11 +36,12 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.matrix.ui.model.getBestName
@@ -82,7 +81,7 @@ fun SelectedUser(
),
) {
Icon(
- imageVector = Icons.Default.Close,
+ resourceId = CommonDrawables.ic_compound_close,
contentDescription = stringResource(id = CommonStrings.action_remove),
tint = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier.padding(2.dp)
@@ -91,7 +90,7 @@ fun SelectedUser(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun SelectedUserPreview() = ElementPreview {
SelectedUser(aMatrixUser())
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt
index 3bd55fc49c..884f45de02 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt
@@ -29,7 +29,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
@@ -37,7 +37,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.text.toPx
import io.element.android.libraries.matrix.api.user.MatrixUser
@@ -55,7 +55,7 @@ fun SelectedUsersList(
) {
val lazyListState = rememberLazyListState()
if (autoScroll) {
- var currentSize by rememberSaveable { mutableStateOf(selectedUsers.size) }
+ var currentSize by rememberSaveable { mutableIntStateOf(selectedUsers.size) }
LaunchedEffect(selectedUsers.size) {
val isItemAdded = selectedUsers.size > currentSize
if (isItemAdded) {
@@ -128,7 +128,7 @@ fun SelectedUsersList(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun SelectedUsersListPreview() = ElementPreview {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt
index b84cddd531..2916fa7f06 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt
@@ -16,7 +16,6 @@
package io.element.android.libraries.matrix.ui.components
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -24,14 +23,11 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Error
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -40,10 +36,9 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
-import io.element.android.libraries.designsystem.theme.components.Checkbox
-import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -83,7 +78,7 @@ fun UnresolvedUserRow(
.padding(top = 3.dp)
) {
Icon(
- imageVector = Icons.Filled.Error,
+ resourceId = CommonDrawables.ic_compound_error,
contentDescription = "",
modifier = Modifier
.size(18.dp)
@@ -102,58 +97,9 @@ fun UnresolvedUserRow(
}
}
+@Preview
@Composable
-fun CheckableUnresolvedUserRow(
- checked: Boolean,
- avatarData: AvatarData,
- id: String,
- modifier: Modifier = Modifier,
- onCheckedChange: (Boolean) -> Unit = {},
- enabled: Boolean = true,
-) {
- Row(
- modifier = modifier
- .fillMaxWidth()
- .clickable(role = Role.Checkbox, enabled = enabled) {
- onCheckedChange(!checked)
- },
- verticalAlignment = Alignment.CenterVertically,
- ) {
- UnresolvedUserRow(
- modifier = Modifier.weight(1f),
- avatarData = avatarData,
- id = id,
- )
-
- Checkbox(
- modifier = Modifier.padding(end = 16.dp),
- checked = checked,
- onCheckedChange = null,
- enabled = enabled,
- )
- }
+internal fun UnresolvedUserRowPreview() = ElementThemedPreview {
+ val matrixUser = aMatrixUser()
+ UnresolvedUserRow(matrixUser.getAvatarData(size = AvatarSize.UserListItem), matrixUser.userId.value)
}
-
-@Preview
-@Composable
-internal fun UnresolvedUserRowPreview() =
- ElementThemedPreview {
- val matrixUser = aMatrixUser()
- UnresolvedUserRow(matrixUser.getAvatarData(size = AvatarSize.UserListItem), matrixUser.userId.value)
- }
-
-@Preview
-@Composable
-internal fun CheckableUnresolvedUserRowPreview() =
- ElementThemedPreview {
- val matrixUser = aMatrixUser()
- Column {
- CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value)
- HorizontalDivider()
- CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value)
- HorizontalDivider()
- CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value, enabled = false)
- HorizontalDivider()
- CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.UserListItem), matrixUser.userId.value, enabled = false)
- }
- }
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt
index 08bb5548b9..f7852d690e 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt
@@ -35,7 +35,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import coil.request.ImageRequest
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground
import io.element.android.libraries.designsystem.theme.components.Icon
@@ -82,7 +82,7 @@ fun UnsavedAvatar(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun UnsavedAvatarPreview() = ElementPreview {
Row {
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UserRow.kt
new file mode 100644
index 0000000000..5e59ce7aef
--- /dev/null
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UserRow.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.matrix.ui.components
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.components.avatar.Avatar
+import io.element.android.libraries.designsystem.components.avatar.AvatarData
+import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.theme.ElementTheme
+
+@Composable
+internal fun UserRow(
+ avatarData: AvatarData,
+ name: String,
+ subtext: String?,
+ modifier: Modifier = Modifier,
+) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .heightIn(min = 56.dp)
+ .padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Avatar(avatarData)
+ Column(
+ modifier = Modifier
+ .padding(start = 12.dp),
+ ) {
+ // Name
+ Text(
+ text = name,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ color = MaterialTheme.colorScheme.primary,
+ style = ElementTheme.typography.fontBodyLgRegular,
+ )
+ // Id
+ subtext?.let {
+ Text(
+ text = subtext,
+ color = MaterialTheme.colorScheme.secondary,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ style = ElementTheme.typography.fontBodySmRegular,
+ )
+ }
+ }
+ }
+}
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt
index 5fbc1cf44d..99a6083565 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt
@@ -16,22 +16,31 @@
package io.element.android.libraries.matrix.ui.media
+import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Delete
-import androidx.compose.material.icons.outlined.PhotoCamera
-import androidx.compose.material.icons.outlined.PhotoLibrary
import androidx.compose.runtime.Immutable
-import androidx.compose.ui.graphics.vector.ImageVector
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.ui.strings.CommonStrings
@Immutable
sealed class AvatarAction(
@StringRes val titleResId: Int,
- val icon: ImageVector,
+ @DrawableRes val iconResourceId: Int,
val destructive: Boolean = false,
) {
- data object TakePhoto : AvatarAction(titleResId = CommonStrings.action_take_photo, icon = Icons.Outlined.PhotoCamera)
- data object ChoosePhoto : AvatarAction(titleResId = CommonStrings.action_choose_photo, icon = Icons.Outlined.PhotoLibrary)
- data object Remove : AvatarAction(titleResId = CommonStrings.action_remove, icon = Icons.Outlined.Delete, destructive = true)
+ data object TakePhoto : AvatarAction(
+ titleResId = CommonStrings.action_take_photo,
+ iconResourceId = CommonDrawables.ic_september_take_photo_camera,
+ )
+
+ data object ChoosePhoto : AvatarAction(
+ titleResId = CommonStrings.action_choose_photo,
+ iconResourceId = CommonDrawables.ic_september_photo_video_library,
+ )
+
+ data object Remove : AvatarAction(
+ titleResId = CommonStrings.action_remove,
+ iconResourceId = CommonDrawables.ic_compound_delete,
+ destructive = true
+ )
}
diff --git a/libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/PickerTypeTests.kt b/libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/PickerTypeTest.kt
similarity index 99%
rename from libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/PickerTypeTests.kt
rename to libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/PickerTypeTest.kt
index 4f17081f8b..d759dc3020 100644
--- a/libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/PickerTypeTests.kt
+++ b/libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/PickerTypeTest.kt
@@ -26,7 +26,7 @@ import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
-class PickerTypeTests {
+class PickerTypeTest {
@Test
fun `ImageAndVideo - assert types`() {
diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt
index 9fc160252b..cd968530f8 100644
--- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt
+++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessor.kt
@@ -66,6 +66,8 @@ class AndroidMediaPreProcessor @Inject constructor(
* values may surpass this limit. (i.e.: an image of `480x3000px` would have `inSampleSize=1` and be sent as is).
*/
private const val IMAGE_SCALE_REF_SIZE = 640
+
+ private val notCompressibleImageTypes = listOf(MimeTypes.Gif, MimeTypes.WebP)
}
private val contentResolver = context.contentResolver
@@ -78,7 +80,10 @@ class AndroidMediaPreProcessor @Inject constructor(
): Result = withContext(coroutineDispatchers.computation) {
runCatching {
val result = when {
- mimeType.isMimeTypeImage() -> processImage(uri, mimeType, compressIfPossible && mimeType != MimeTypes.Gif)
+ mimeType.isMimeTypeImage() -> {
+ val shouldBeCompressed = compressIfPossible && mimeType !in notCompressibleImageTypes
+ processImage(uri, mimeType, shouldBeCompressed)
+ }
mimeType.isMimeTypeVideo() -> processVideo(uri, mimeType, compressIfPossible)
mimeType.isMimeTypeAudio() -> processAudio(uri, mimeType)
else -> processFile(uri, mimeType)
@@ -125,13 +130,11 @@ class AndroidMediaPreProcessor @Inject constructor(
exifInterface?.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)
} ?: ExifInterface.ORIENTATION_UNDEFINED
- val compressionResult = contentResolver.openInputStream(uri).use { input ->
- imageCompressor.compressToTmpFile(
- inputStream = requireNotNull(input),
- resizeMode = ResizeMode.Approximate(IMAGE_SCALE_REF_SIZE, IMAGE_SCALE_REF_SIZE),
- orientation = orientation,
- ).getOrThrow()
- }
+ val compressionResult = imageCompressor.compressToTmpFile(
+ inputStreamProvider = { contentResolver.openInputStream(uri)!! },
+ resizeMode = ResizeMode.Approximate(IMAGE_SCALE_REF_SIZE, IMAGE_SCALE_REF_SIZE),
+ orientation = orientation,
+ ).getOrThrow()
val thumbnailResult: ThumbnailResult = thumbnailFactory.createImageThumbnail(compressionResult.file)
val imageInfo = compressionResult.toImageInfo(
mimeType = mimeType,
diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt
index a619a27bd9..d936b3a5bf 100644
--- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt
+++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt
@@ -27,7 +27,6 @@ import io.element.android.libraries.androidutils.file.createTmpFile
import io.element.android.libraries.di.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-import java.io.BufferedInputStream
import java.io.File
import java.io.InputStream
import javax.inject.Inject
@@ -42,14 +41,14 @@ class ImageCompressor @Inject constructor(
* @return a [Result] containing the resulting [ImageCompressionResult] with the temporary [File] and some metadata.
*/
suspend fun compressToTmpFile(
- inputStream: InputStream,
+ inputStreamProvider: () -> InputStream,
resizeMode: ResizeMode,
format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
orientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
desiredQuality: Int = 80,
): Result = withContext(Dispatchers.IO) {
runCatching {
- val compressedBitmap = compressToBitmap(inputStream, resizeMode, orientation).getOrThrow()
+ val compressedBitmap = compressToBitmap(inputStreamProvider, resizeMode, orientation).getOrThrow()
// Encode bitmap to the destination temporary file
val tmpFile = context.createTmpFile(extension = "jpeg")
tmpFile.outputStream().use {
@@ -65,17 +64,24 @@ class ImageCompressor @Inject constructor(
}
/**
- * Decodes the [inputStream] into a [Bitmap] and applies the needed transformations (rotation, scale) based on [resizeMode] and [orientation].
+ * Decodes the inputStream from [inputStreamProvider] into a [Bitmap] and applies the needed transformations (rotation, scale)
+ * based on [resizeMode] and [orientation].
* @return a [Result] containing the resulting [Bitmap].
*/
fun compressToBitmap(
- inputStream: InputStream,
+ inputStreamProvider: () -> InputStream,
resizeMode: ResizeMode,
orientation: Int,
): Result = runCatching {
- BufferedInputStream(inputStream).use { input ->
- val options = BitmapFactory.Options()
+ val options = BitmapFactory.Options()
+ // Decode bounds
+ inputStreamProvider().use { input ->
calculateDecodingScale(input, resizeMode, options)
+ }
+ // Decode the actual bitmap
+ inputStreamProvider().use { input ->
+ // Now read the actual image and rotate it to match its metadata
+ options.inJustDecodeBounds = false
val decodedBitmap = BitmapFactory.decodeStream(input, null, options)
?: error("Decoding Bitmap from InputStream failed")
val rotatedBitmap = decodedBitmap.rotateToMetadataOrientation(orientation)
@@ -88,7 +94,7 @@ class ImageCompressor @Inject constructor(
}
private fun calculateDecodingScale(
- inputStream: BufferedInputStream,
+ inputStream: InputStream,
resizeMode: ResizeMode,
options: BitmapFactory.Options
) {
@@ -98,14 +104,10 @@ class ImageCompressor @Inject constructor(
is ResizeMode.None -> return
}
// Read bounds only
- inputStream.mark(inputStream.available())
options.inJustDecodeBounds = true
BitmapFactory.decodeStream(inputStream, null, options)
// Set sample size based on the outWidth and outHeight
options.inSampleSize = options.calculateInSampleSize(width, height)
- // Now read the actual image and rotate it to match its metadata
- inputStream.reset()
- options.inJustDecodeBounds = false
}
}
diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsEvents.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsEvents.kt
index 45232a51db..0f3432b9d9 100644
--- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsEvents.kt
+++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsEvents.kt
@@ -17,6 +17,7 @@
package io.element.android.libraries.permissions.api
sealed interface PermissionsEvents {
- data object OpenSystemDialog : PermissionsEvents
+ data object RequestPermissions : PermissionsEvents
data object CloseDialog : PermissionsEvents
+ data object OpenSystemSettingAndCloseDialog : PermissionsEvents
}
diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsViewStateProvider.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStateProvider.kt
similarity index 60%
rename from libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsViewStateProvider.kt
rename to libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStateProvider.kt
index e93b74d934..cc59d96b44 100644
--- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsViewStateProvider.kt
+++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsStateProvider.kt
@@ -19,20 +19,24 @@ package io.element.android.libraries.permissions.api
import android.Manifest
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
-open class PermissionsViewStateProvider : PreviewParameterProvider {
+open class PermissionsStateProvider : PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
- aPermissionsState(),
- aPermissionsState().copy(shouldShowRationale = true),
- aPermissionsState().copy(permissionAlreadyDenied = true),
+ aPermissionsState(showDialog = true, permission = Manifest.permission.POST_NOTIFICATIONS),
+ aPermissionsState(showDialog = true, permission = Manifest.permission.CAMERA),
+ aPermissionsState(showDialog = true, permission = Manifest.permission.RECORD_AUDIO),
+ aPermissionsState(showDialog = true, permission = Manifest.permission.INTERNET),
)
}
-fun aPermissionsState() = PermissionsState(
- permission = Manifest.permission.INTERNET,
+fun aPermissionsState(
+ showDialog: Boolean,
+ permission: String = Manifest.permission.POST_NOTIFICATIONS
+) = PermissionsState(
+ permission = permission,
permissionGranted = false,
shouldShowRationale = false,
- showDialog = true,
+ showDialog = showDialog,
permissionAlreadyAsked = false,
permissionAlreadyDenied = false,
eventSink = {}
diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt
index 67f1c7e127..9e2413fff9 100644
--- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt
+++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt
@@ -16,73 +16,48 @@
package io.element.android.libraries.permissions.api
+import android.Manifest
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun PermissionsView(
state: PermissionsState,
modifier: Modifier = Modifier,
- openSystemSettings: () -> Unit = {},
) {
if (state.showDialog.not()) return
- when {
- state.permissionGranted -> {
- // Notification Granted, nothing to do
- }
- state.permissionAlreadyDenied -> {
- // In this case, tell the user to go to the settings
- ConfirmationDialog(
- modifier = modifier,
- title = "System",
- content = "In order to let the application display notification, please grant the permission to the system settings",
- submitText = "Open settings",
- onSubmitClicked = {
- state.eventSink.invoke(PermissionsEvents.CloseDialog)
- openSystemSettings()
- },
- onDismiss = { state.eventSink.invoke(PermissionsEvents.CloseDialog) },
- )
- }
- else -> {
- val textToShow = if (state.shouldShowRationale) {
- // TODO Move to state
- // If the user has denied the permission but the rationale can be shown,
- // then gently explain why the app requires this permission
- // permissions_rationale_msg_notification
- "To be able to receive notifications, please grant the permission. Else you will not be able to be alerted if you've got new messages."
- } else {
- // TODO Move to state
- // If it's the first time the user lands on this feature, or the user
- // doesn't want to be asked again for this permission, explain that the
- // permission is required
- "To be able to receive notifications, please grant the permission."
- }
- ConfirmationDialog(
- modifier = modifier,
- title = "Notifications",
- content = textToShow,
- submitText = "Request permission",
- onSubmitClicked = {
- state.eventSink.invoke(PermissionsEvents.OpenSystemDialog)
- },
- onCancelClicked = {
- state.eventSink.invoke(PermissionsEvents.CloseDialog)
- },
- onDismiss = {}
- )
- }
+ ConfirmationDialog(
+ modifier = modifier,
+ title = stringResource(id = CommonStrings.common_permission),
+ content = state.permission.toDialogContent(),
+ submitText = stringResource(id = CommonStrings.action_open_settings),
+ onSubmitClicked = {
+ state.eventSink.invoke(PermissionsEvents.OpenSystemSettingAndCloseDialog)
+ },
+ onDismiss = { state.eventSink.invoke(PermissionsEvents.CloseDialog) },
+ )
+}
+
+@Composable
+private fun String.toDialogContent(): String {
+ return when (this) {
+ Manifest.permission.POST_NOTIFICATIONS -> stringResource(id = R.string.dialog_permission_notification)
+ Manifest.permission.CAMERA -> stringResource(id = R.string.dialog_permission_camera)
+ Manifest.permission.RECORD_AUDIO -> stringResource(id = R.string.dialog_permission_microphone)
+ else -> stringResource(id = R.string.dialog_permission_generic)
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
-internal fun PermissionsViewPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = ElementPreview {
+internal fun PermissionsViewPreview(@PreviewParameter(PermissionsStateProvider::class) state: PermissionsState) = ElementPreview {
PermissionsView(
state = state,
)
diff --git a/libraries/permissions/api/src/main/res/values-cs/translations.xml b/libraries/permissions/api/src/main/res/values-cs/translations.xml
new file mode 100644
index 0000000000..967924280c
--- /dev/null
+++ b/libraries/permissions/api/src/main/res/values-cs/translations.xml
@@ -0,0 +1,7 @@
+
+
+ "Aby mohla aplikace používat fotoaparát, udělte prosím oprávnění v nastavení systému."
+ "Udělte prosím oprávnění v nastavení systému."
+ "Aby aplikace mohla používat mikrofon, udělte prosím oprávnění v nastavení systému."
+ "Aby aplikace mohla zobrazovat upozornění, udělte prosím oprávnění v nastavení systému."
+
diff --git a/libraries/permissions/api/src/main/res/values-de/translations.xml b/libraries/permissions/api/src/main/res/values-de/translations.xml
new file mode 100644
index 0000000000..fb801cc450
--- /dev/null
+++ b/libraries/permissions/api/src/main/res/values-de/translations.xml
@@ -0,0 +1,7 @@
+
+
+ "Damit die Anwendung die Kamera verwenden kann, erteile bitte die Erlaubnis in den Systemeinstellungen."
+ "Bitte erteile die Erlaubnis in den Systemeinstellungen."
+ "Damit die Anwendung das Mikrofon verwenden kann, erteile bitte die Erlaubnis in den Systemeinstellungen."
+ "Damit die Anwendung Benachrichtigungen anzeigen kann, erteile bitte die Erlaubnis in den Systemeinstellungen."
+
diff --git a/libraries/permissions/api/src/main/res/values-fr/translations.xml b/libraries/permissions/api/src/main/res/values-fr/translations.xml
new file mode 100644
index 0000000000..1ddcac97a5
--- /dev/null
+++ b/libraries/permissions/api/src/main/res/values-fr/translations.xml
@@ -0,0 +1,7 @@
+
+
+ "Pour permettre à l’application d’utiliser l’appareil photo, veuillez accorder l’autorisation dans les paramètres du système."
+ "Veuillez accorder l’autorisation dans les paramètres du système."
+ "Pour permettre à l’application d’utiliser le microphone, veuillez accorder l’autorisation dans les paramètres du système."
+ "Pour permettre à l’application d’afficher les notifications, veuillez accorder l’autorisation dans les paramètres du système."
+
diff --git a/libraries/permissions/api/src/main/res/values-ru/translations.xml b/libraries/permissions/api/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..5c7a1b25f1
--- /dev/null
+++ b/libraries/permissions/api/src/main/res/values-ru/translations.xml
@@ -0,0 +1,7 @@
+
+
+ "Чтобы приложение могло использовать камеру, предоставьте разрешение в системных настройках."
+ "Пожалуйста, предоставьте разрешение в системных настройках."
+ "Чтобы приложение могло использовать микрофон, предоставьте разрешение в системных настройках."
+ "Чтобы приложение отображало уведомления, предоставьте разрешение в системных настройках."
+
diff --git a/libraries/permissions/api/src/main/res/values-sk/translations.xml b/libraries/permissions/api/src/main/res/values-sk/translations.xml
new file mode 100644
index 0000000000..0670b59573
--- /dev/null
+++ b/libraries/permissions/api/src/main/res/values-sk/translations.xml
@@ -0,0 +1,7 @@
+
+
+ "Aby aplikácia mohla používať fotoaparát, udeľte povolenie v systémových nastaveniach."
+ "Udeľte prosím povolenie v systémových nastaveniach."
+ "Aby aplikácia mohla používať mikrofón, udeľte povolenie v systémových nastaveniach."
+ "Ak chcete, aby aplikácia zobrazovala oznámenia, udeľte povolenie v nastaveniach systému."
+
diff --git a/libraries/permissions/api/src/main/res/values-zh-rTW/translations.xml b/libraries/permissions/api/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..cfd4762579
--- /dev/null
+++ b/libraries/permissions/api/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,7 @@
+
+
+ "為了讓應用程式使用相機,請到系統設定中開啟權限。"
+ "請到系統設定中開啟權限。"
+ "為了讓應用程式使用麥克風,請到系統設定中開啟權限。"
+ "為了讓應用程式顯示通知,請到系統設定中開啟權限。"
+
diff --git a/libraries/permissions/api/src/main/res/values/localazy.xml b/libraries/permissions/api/src/main/res/values/localazy.xml
new file mode 100644
index 0000000000..b189d59063
--- /dev/null
+++ b/libraries/permissions/api/src/main/res/values/localazy.xml
@@ -0,0 +1,7 @@
+
+
+ "In order to let the application use the camera, please grant the permission in the system settings."
+ "Please grant the permission in the system settings."
+ "In order to let the application use the microphone, please grant the permission in the system settings."
+ "In order to let the application display notifications, please grant the permission in the system settings."
+
diff --git a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt
index 1684833d70..8fccfcc09d 100644
--- a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt
+++ b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt
@@ -39,6 +39,7 @@ import io.element.android.libraries.permissions.api.PermissionsEvents
import io.element.android.libraries.permissions.api.PermissionsPresenter
import io.element.android.libraries.permissions.api.PermissionsState
import io.element.android.libraries.permissions.api.PermissionsStore
+import io.element.android.libraries.permissions.impl.action.PermissionActions
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -48,6 +49,7 @@ class DefaultPermissionsPresenter @AssistedInject constructor(
@Assisted val permission: String,
private val permissionsStore: PermissionsStore,
private val composablePermissionStateProvider: ComposablePermissionStateProvider,
+ private val permissionActions: PermissionActions,
) : PermissionsPresenter {
@AssistedFactory
@@ -98,20 +100,27 @@ class DefaultPermissionsPresenter @AssistedInject constructor(
LaunchedEffect(this) {
if (permissionState.status.isGranted) {
- // User may have granted permission from the settings, to reset the store regarding this permission
+ // User may have granted permission from the settings, so reset the store regarding this permission
permissionsStore.resetPermission(permission)
}
}
- val showDialog = rememberSaveable { mutableStateOf(permissionState.status !is PermissionStatus.Granted) }
+ val showDialog = rememberSaveable { mutableStateOf(false) }
fun handleEvents(event: PermissionsEvents) {
when (event) {
PermissionsEvents.CloseDialog -> {
showDialog.value = false
}
- PermissionsEvents.OpenSystemDialog -> {
- permissionState.launchPermissionRequest()
+ PermissionsEvents.RequestPermissions -> {
+ if (permissionState.status !is PermissionStatus.Granted && isAlreadyDenied) {
+ showDialog.value = true
+ } else {
+ permissionState.launchPermissionRequest()
+ }
+ }
+ PermissionsEvents.OpenSystemSettingAndCloseDialog -> {
+ permissionActions.openSettings()
showDialog.value = false
}
}
diff --git a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/AndroidPermissionActions.kt b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/AndroidPermissionActions.kt
new file mode 100644
index 0000000000..a694c079e3
--- /dev/null
+++ b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/AndroidPermissionActions.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.permissions.impl.action
+
+import android.content.Context
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.libraries.androidutils.system.openAppSettingsPage
+import io.element.android.libraries.di.AppScope
+import io.element.android.libraries.di.ApplicationContext
+import javax.inject.Inject
+
+@ContributesBinding(AppScope::class)
+class AndroidPermissionActions @Inject constructor(
+ @ApplicationContext private val context: Context
+) : PermissionActions {
+
+ override fun openSettings() {
+ context.openAppSettingsPage()
+ }
+}
diff --git a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/PermissionActions.kt b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/PermissionActions.kt
new file mode 100644
index 0000000000..66c37bdb4d
--- /dev/null
+++ b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/PermissionActions.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.permissions.impl.action
+
+interface PermissionActions {
+ fun openSettings()
+}
diff --git a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt
index 316ce7bc67..ca96833d69 100644
--- a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt
+++ b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt
@@ -25,6 +25,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionStatus
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.permissions.api.PermissionsEvents
+import io.element.android.libraries.permissions.impl.action.FakePermissionActions
import io.element.android.libraries.permissions.test.InMemoryPermissionsStore
import io.element.android.tests.testutils.WarmUpRule
import kotlinx.coroutines.test.runTest
@@ -52,7 +53,8 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter(
A_PERMISSION,
permissionsStore,
- permissionStateProvider
+ permissionStateProvider,
+ FakePermissionActions(),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@@ -69,7 +71,10 @@ class DefaultPermissionsPresenterTest {
@Test
fun `present - user closes dialog`() = runTest {
- val permissionsStore = InMemoryPermissionsStore()
+ val permissionsStore = InMemoryPermissionsStore(
+ permissionDenied = true,
+ permissionAsked = true
+ )
val permissionState = FakePermissionState(
A_PERMISSION,
PermissionStatus.Denied(shouldShowRationale = false)
@@ -81,18 +86,58 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter(
A_PERMISSION,
permissionsStore,
- permissionStateProvider
+ permissionStateProvider,
+ FakePermissionActions(),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
- assertThat(initialState.showDialog).isTrue()
- initialState.eventSink.invoke(PermissionsEvents.CloseDialog)
+ initialState.eventSink.invoke(PermissionsEvents.RequestPermissions)
+ val withDialogState = awaitItem()
+ assertThat(withDialogState.showDialog).isTrue()
+ withDialogState.eventSink.invoke(PermissionsEvents.CloseDialog)
assertThat(awaitItem().showDialog).isFalse()
}
}
+ @Test
+ fun `present - user open settings`() = runTest {
+ val permissionsStore = InMemoryPermissionsStore(
+ permissionDenied = true,
+ permissionAsked = true
+ )
+ val permissionState = FakePermissionState(
+ A_PERMISSION,
+ PermissionStatus.Denied(shouldShowRationale = false)
+ )
+ val permissionStateProvider =
+ FakeComposablePermissionStateProvider(
+ permissionState
+ )
+ val permissionActions = FakePermissionActions()
+ val presenter = DefaultPermissionsPresenter(
+ A_PERMISSION,
+ permissionsStore,
+ permissionStateProvider,
+ permissionActions,
+ )
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ skipItems(1)
+ val initialState = awaitItem()
+ initialState.eventSink.invoke(PermissionsEvents.RequestPermissions)
+ val withDialogState = awaitItem()
+ assertThat(withDialogState.showDialog).isTrue()
+ assertThat(permissionActions.openSettingsCalled).isFalse()
+ withDialogState.eventSink.invoke(PermissionsEvents.OpenSystemSettingAndCloseDialog)
+ assertThat(awaitItem().showDialog).isFalse()
+ assertThat(permissionActions.openSettingsCalled).isTrue()
+ }
+ }
+
@Test
fun `present - user does not grant permission`() = runTest {
val permissionsStore = InMemoryPermissionsStore()
@@ -107,16 +152,16 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter(
A_PERMISSION,
permissionsStore,
- permissionStateProvider
+ permissionStateProvider,
+ FakePermissionActions(),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
- assertThat(initialState.showDialog).isTrue()
- initialState.eventSink.invoke(PermissionsEvents.OpenSystemDialog)
+ assertThat(initialState.showDialog).isFalse()
+ initialState.eventSink.invoke(PermissionsEvents.RequestPermissions)
assertThat(permissionState.launchPermissionRequestCalled).isTrue()
- assertThat(awaitItem().showDialog).isFalse()
// User does not grant permission
permissionStateProvider.userGiveAnswer(answer = false, firstTime = true)
skipItems(1)
@@ -142,16 +187,16 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter(
A_PERMISSION,
permissionsStore,
- permissionStateProvider
+ permissionStateProvider,
+ FakePermissionActions(),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
- assertThat(initialState.showDialog).isTrue()
- initialState.eventSink.invoke(PermissionsEvents.OpenSystemDialog)
+ assertThat(initialState.showDialog).isFalse()
+ initialState.eventSink.invoke(PermissionsEvents.RequestPermissions)
assertThat(permissionState.launchPermissionRequestCalled).isTrue()
- assertThat(awaitItem().showDialog).isFalse()
// User does not grant permission
permissionStateProvider.userGiveAnswer(answer = false, firstTime = false)
skipItems(2)
@@ -181,17 +226,20 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter(
A_PERMISSION,
permissionsStore,
- permissionStateProvider
+ permissionStateProvider,
+ FakePermissionActions(),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
val initialState = awaitItem()
- assertThat(initialState.showDialog).isTrue()
- assertThat(initialState.permissionGranted).isFalse()
- assertThat(initialState.permissionAlreadyDenied).isTrue()
- assertThat(initialState.permissionAlreadyAsked).isTrue()
+ initialState.eventSink.invoke(PermissionsEvents.RequestPermissions)
+ val withDialogState = awaitItem()
+ assertThat(withDialogState.showDialog).isTrue()
+ assertThat(withDialogState.permissionGranted).isFalse()
+ assertThat(withDialogState.permissionAlreadyDenied).isTrue()
+ assertThat(withDialogState.permissionAlreadyAsked).isTrue()
}
}
@@ -209,16 +257,16 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter(
A_PERMISSION,
permissionsStore,
- permissionStateProvider
+ permissionStateProvider,
+ FakePermissionActions(),
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
- assertThat(initialState.showDialog).isTrue()
- initialState.eventSink.invoke(PermissionsEvents.OpenSystemDialog)
+ assertThat(initialState.showDialog).isFalse()
+ initialState.eventSink.invoke(PermissionsEvents.RequestPermissions)
assertThat(permissionState.launchPermissionRequestCalled).isTrue()
- assertThat(awaitItem().showDialog).isFalse()
// User grants permission
permissionStateProvider.userGiveAnswer(answer = true, firstTime = true)
skipItems(1)
diff --git a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/action/FakePermissionActions.kt b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/action/FakePermissionActions.kt
new file mode 100644
index 0000000000..fa17329900
--- /dev/null
+++ b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/action/FakePermissionActions.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.permissions.impl.action
+
+class FakePermissionActions : PermissionActions {
+ var openSettingsCalled = false
+ private set
+
+ override fun openSettings() {
+ openSettingsCalled = true
+ }
+}
diff --git a/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt b/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt
index f26f268860..871f562489 100644
--- a/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt
+++ b/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt
@@ -24,13 +24,14 @@ import io.element.android.libraries.permissions.api.PermissionsState
import io.element.android.libraries.permissions.api.aPermissionsState
class FakePermissionsPresenter(
- private val initialState: PermissionsState = aPermissionsState().copy(showDialog = false),
+ private val initialState: PermissionsState = aPermissionsState(showDialog = false),
) : PermissionsPresenter {
private fun eventSink(events: PermissionsEvents) {
when (events) {
- PermissionsEvents.OpenSystemDialog -> state.value = state.value.copy(showDialog = true, permissionAlreadyAsked = true)
+ PermissionsEvents.RequestPermissions -> state.value = state.value.copy(showDialog = true, permissionAlreadyAsked = true)
PermissionsEvents.CloseDialog -> state.value = state.value.copy(showDialog = false)
+ PermissionsEvents.OpenSystemSettingAndCloseDialog -> state.value = state.value.copy(showDialog = false)
}
}
diff --git a/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenterFactory.kt b/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenterFactory.kt
new file mode 100644
index 0000000000..05fcb2beb4
--- /dev/null
+++ b/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenterFactory.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.permissions.test
+
+import io.element.android.libraries.permissions.api.PermissionsPresenter
+
+class FakePermissionsPresenterFactory(
+ private val permissionPresenter: PermissionsPresenter = FakePermissionsPresenter(),
+) : PermissionsPresenter.Factory {
+ override fun create(permission: String): PermissionsPresenter {
+ return permissionPresenter
+ }
+}
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt
index 39ff4323a5..7f4ee63004 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt
@@ -63,20 +63,19 @@ class PushersManager @Inject constructor(
override suspend fun registerPusher(matrixClient: MatrixClient, pushKey: String, gateway: String) {
val userDataStore = userPushStoreFactory.create(matrixClient.sessionId)
if (userDataStore.getCurrentRegisteredPushKey() == pushKey) {
- Timber.tag(loggerTag.value).d("Unnecessary to register again the same pusher")
- } else {
- // Register the pusher to the server
- matrixClient.pushersService().setHttpPusher(
- createHttpPusher(pushKey, gateway, matrixClient.sessionId)
- ).fold(
- {
- userDataStore.setCurrentRegisteredPushKey(pushKey)
- },
- { throwable ->
- Timber.tag(loggerTag.value).e(throwable, "Unable to register the pusher")
- }
- )
+ Timber.tag(loggerTag.value)
+ .d("Unnecessary to register again the same pusher, but do it in case the pusher has been removed from the server")
}
+ matrixClient.pushersService().setHttpPusher(
+ createHttpPusher(pushKey, gateway, matrixClient.sessionId)
+ ).fold(
+ {
+ userDataStore.setCurrentRegisteredPushKey(pushKey)
+ },
+ { throwable ->
+ Timber.tag(loggerTag.value).e(throwable, "Unable to register the pusher")
+ }
+ )
}
private suspend fun createHttpPusher(
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt
index c93d517e89..113a323611 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt
@@ -87,7 +87,7 @@ class NotifiableEventResolver @Inject constructor(
noisy = isNoisy,
timestamp = this.timestamp,
senderName = senderDisplayName,
- body = descriptionFromMessageContent(content),
+ body = descriptionFromMessageContent(content, senderDisplayName ?: content.senderId.value),
imageUriString = this.contentUrl,
roomName = roomDisplayName,
roomIsDirect = isDirect,
@@ -114,10 +114,76 @@ class NotifiableEventResolver @Inject constructor(
title = null, // TODO check if title is needed anymore
)
} else {
- fallbackNotifiableEvent(userId, roomId, eventId)
+ Timber.tag(loggerTag.value).d("Ignoring notification state event for membership ${content.membershipState}")
+ null
}
}
- else -> fallbackNotifiableEvent(userId, roomId, eventId)
+ NotificationContent.MessageLike.CallAnswer,
+ NotificationContent.MessageLike.CallCandidates,
+ NotificationContent.MessageLike.CallHangup,
+ NotificationContent.MessageLike.CallInvite -> null.also {
+ Timber.tag(loggerTag.value).d("Ignoring notification for call ${content.javaClass.simpleName}")
+ }
+ NotificationContent.MessageLike.KeyVerificationAccept,
+ NotificationContent.MessageLike.KeyVerificationCancel,
+ NotificationContent.MessageLike.KeyVerificationDone,
+ NotificationContent.MessageLike.KeyVerificationKey,
+ NotificationContent.MessageLike.KeyVerificationMac,
+ NotificationContent.MessageLike.KeyVerificationReady,
+ NotificationContent.MessageLike.KeyVerificationStart -> null.also {
+ Timber.tag(loggerTag.value).d("Ignoring notification for verification ${content.javaClass.simpleName}")
+ }
+ is NotificationContent.MessageLike.Poll -> {
+ buildNotifiableMessageEvent(
+ sessionId = userId,
+ senderId = content.senderId,
+ roomId = roomId,
+ eventId = eventId,
+ noisy = isNoisy,
+ timestamp = this.timestamp,
+ senderName = senderDisplayName,
+ body = stringProvider.getString(CommonStrings.common_poll_summary, content.question),
+ imageUriString = null,
+ roomName = roomDisplayName,
+ roomIsDirect = isDirect,
+ roomAvatarPath = roomAvatarUrl,
+ senderAvatarPath = senderAvatarUrl,
+ )
+ }
+ is NotificationContent.MessageLike.ReactionContent -> null.also {
+ Timber.tag(loggerTag.value).d("Ignoring notification for reaction")
+ }
+ NotificationContent.MessageLike.RoomEncrypted -> fallbackNotifiableEvent(userId, roomId, eventId).also {
+ Timber.tag(loggerTag.value).w("Notification with encrypted content -> fallback")
+ }
+ NotificationContent.MessageLike.RoomRedaction -> null.also {
+ Timber.tag(loggerTag.value).d("Ignoring notification for redaction")
+ }
+ NotificationContent.MessageLike.Sticker -> null.also {
+ Timber.tag(loggerTag.value).d("Ignoring notification for sticker")
+ }
+ NotificationContent.StateEvent.PolicyRuleRoom,
+ NotificationContent.StateEvent.PolicyRuleServer,
+ NotificationContent.StateEvent.PolicyRuleUser,
+ NotificationContent.StateEvent.RoomAliases,
+ NotificationContent.StateEvent.RoomAvatar,
+ NotificationContent.StateEvent.RoomCanonicalAlias,
+ NotificationContent.StateEvent.RoomCreate,
+ NotificationContent.StateEvent.RoomEncryption,
+ NotificationContent.StateEvent.RoomGuestAccess,
+ NotificationContent.StateEvent.RoomHistoryVisibility,
+ NotificationContent.StateEvent.RoomJoinRules,
+ NotificationContent.StateEvent.RoomName,
+ NotificationContent.StateEvent.RoomPinnedEvents,
+ NotificationContent.StateEvent.RoomPowerLevels,
+ NotificationContent.StateEvent.RoomServerAcl,
+ NotificationContent.StateEvent.RoomThirdPartyInvite,
+ NotificationContent.StateEvent.RoomTombstone,
+ NotificationContent.StateEvent.RoomTopic,
+ NotificationContent.StateEvent.SpaceChild,
+ NotificationContent.StateEvent.SpaceParent -> null.also {
+ Timber.tag(loggerTag.value).d("Ignoring notification for state event ${content.javaClass.simpleName}")
+ }
}
}
@@ -139,10 +205,11 @@ class NotifiableEventResolver @Inject constructor(
private fun descriptionFromMessageContent(
content: NotificationContent.MessageLike.RoomMessage,
+ senderDisplayName: String,
): String {
return when (val messageType = content.messageType) {
is AudioMessageType -> messageType.body
- is EmoteMessageType -> messageType.body
+ is EmoteMessageType -> "* $senderDisplayName ${messageType.body}"
is FileMessageType -> messageType.body
is ImageMessageType -> messageType.body
is NoticeMessageType -> messageType.body
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt
index 6c9138fb15..cf5b13ce9b 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt
@@ -56,7 +56,7 @@ class DefaultPushHandler @Inject constructor(
private val coroutineScope = CoroutineScope(SupervisorJob())
// UI handler
- private val mUIHandler by lazy {
+ private val uiHandler by lazy {
Handler(Looper.getMainLooper())
}
@@ -81,7 +81,7 @@ class DefaultPushHandler @Inject constructor(
return
}
- mUIHandler.post {
+ uiHandler.post {
coroutineScope.launch(Dispatchers.IO) { handleInternal(pushData) }
}
}
diff --git a/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml b/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml
index 248fae8b0b..973981253b 100644
--- a/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml
@@ -2,6 +2,7 @@
"通話"
"無聲通知"
+ "** 無法傳送,請開啟聊天室"
"加入"
"拒絕"
"邀請您聊天"
@@ -10,6 +11,8 @@
"邀請您加入聊天室"
"我"
"您正在查看通知!點我!"
+ "%1$s:%2$s"
+ "%1$s:%2$s %3$s"
- "%1$s:%2$d 則訊息"
diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/LoggedInState.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/LoggedInState.kt
new file mode 100644
index 0000000000..ddf7ae5e8a
--- /dev/null
+++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/LoggedInState.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.sessionstorage.api
+
+sealed interface LoggedInState {
+ data object NotLoggedIn : LoggedInState
+ data class LoggedIn(
+ val sessionId: String,
+ val isTokenValid: Boolean,
+ ) : LoggedInState
+}
diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/LoginType.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/LoginType.kt
new file mode 100644
index 0000000000..ce29e3729a
--- /dev/null
+++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/LoginType.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.sessionstorage.api
+
+// Imported from Element Android, to be able to migrate from EA to EXA.
+enum class LoginType {
+ PASSWORD,
+ OIDC,
+ SSO,
+ UNSUPPORTED,
+ CUSTOM,
+ DIRECT,
+ UNKNOWN,
+ QR;
+
+ companion object {
+
+ fun fromName(name: String) = when (name) {
+ PASSWORD.name -> PASSWORD
+ OIDC.name -> OIDC
+ SSO.name -> SSO
+ UNSUPPORTED.name -> UNSUPPORTED
+ CUSTOM.name -> CUSTOM
+ DIRECT.name -> DIRECT
+ QR.name -> QR
+ else -> UNKNOWN
+ }
+ }
+}
diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt
index e14c3feeab..25a48c0efe 100644
--- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt
+++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt
@@ -27,4 +27,6 @@ data class SessionData(
val oidcData: String?,
val slidingSyncProxy: String?,
val loginTimestamp: Date?,
+ val isTokenValid: Boolean,
+ val loginType: LoginType,
)
diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt
index 2b3398f76c..cc23353a8f 100644
--- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt
+++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt
@@ -20,7 +20,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
interface SessionStore {
- fun isLoggedIn(): Flow
+ fun isLoggedIn(): Flow
fun sessionsFlow(): Flow>
suspend fun storeData(sessionData: SessionData)
diff --git a/libraries/session-storage/impl-memory/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/memory/InMemorySessionStore.kt b/libraries/session-storage/impl-memory/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/memory/InMemorySessionStore.kt
index df78149eef..4b76e82e8b 100644
--- a/libraries/session-storage/impl-memory/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/memory/InMemorySessionStore.kt
+++ b/libraries/session-storage/impl-memory/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/memory/InMemorySessionStore.kt
@@ -16,6 +16,7 @@
package io.element.android.libraries.sessionstorage.impl.memory
+import io.element.android.libraries.sessionstorage.api.LoggedInState
import io.element.android.libraries.sessionstorage.api.SessionData
import io.element.android.libraries.sessionstorage.api.SessionStore
import kotlinx.coroutines.flow.Flow
@@ -26,8 +27,17 @@ class InMemorySessionStore : SessionStore {
private var sessionDataFlow = MutableStateFlow(null)
- override fun isLoggedIn(): Flow {
- return sessionDataFlow.map { it != null }
+ override fun isLoggedIn(): Flow {
+ return sessionDataFlow.map {
+ if (it == null) {
+ LoggedInState.NotLoggedIn
+ } else {
+ LoggedInState.LoggedIn(
+ sessionId = it.userId,
+ isTokenValid = it.isTokenValid,
+ )
+ }
+ }
}
override fun sessionsFlow(): Flow> {
diff --git a/libraries/session-storage/impl/build.gradle.kts b/libraries/session-storage/impl/build.gradle.kts
index 698bfcf230..03de9acf86 100644
--- a/libraries/session-storage/impl/build.gradle.kts
+++ b/libraries/session-storage/impl/build.gradle.kts
@@ -45,10 +45,20 @@ dependencies {
testImplementation(libs.test.turbine)
testImplementation(libs.coroutines.test)
testImplementation(libs.sqldelight.driver.jvm)
+
+ coreLibraryDesugaring(libs.android.desugar)
}
sqldelight {
- database("SessionDatabase") {
- verifyMigrations = true
+ databases {
+ create("SessionDatabase") {
+ // https://cashapp.github.io/sqldelight/2.0.0/android_sqlite/migrations/
+ // To generate a .db file from your latest schema, run this task
+ // ./gradlew generateDebugSessionDatabaseSchema
+ // Test migration by running
+ // ./gradlew verifySqlDelightMigration
+ schemaOutputDirectory = File("src/main/sqldelight/databases")
+ verifyMigrations = true
+ }
}
}
diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt
index eb273411a0..c437a4ef08 100644
--- a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt
+++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt
@@ -16,12 +16,14 @@
package io.element.android.libraries.sessionstorage.impl
+import app.cash.sqldelight.coroutines.asFlow
+import app.cash.sqldelight.coroutines.mapToList
+import app.cash.sqldelight.coroutines.mapToOneOrNull
import com.squareup.anvil.annotations.ContributesBinding
-import com.squareup.sqldelight.runtime.coroutines.asFlow
-import com.squareup.sqldelight.runtime.coroutines.mapToList
-import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull
+import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn
+import io.element.android.libraries.sessionstorage.api.LoggedInState
import io.element.android.libraries.sessionstorage.api.SessionData
import io.element.android.libraries.sessionstorage.api.SessionStore
import kotlinx.coroutines.flow.Flow
@@ -33,13 +35,23 @@ import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DatabaseSessionStore @Inject constructor(
private val database: SessionDatabase,
+ private val dispatchers: CoroutineDispatchers,
) : SessionStore {
- override fun isLoggedIn(): Flow {
+ override fun isLoggedIn(): Flow {
return database.sessionDataQueries.selectFirst()
.asFlow()
- .mapToOneOrNull()
- .map { it != null }
+ .mapToOneOrNull(dispatchers.io)
+ .map {
+ if (it == null) {
+ LoggedInState.NotLoggedIn
+ } else {
+ LoggedInState.LoggedIn(
+ sessionId = it.userId,
+ isTokenValid = it.isTokenValid == 1L
+ )
+ }
+ }
}
override suspend fun storeData(sessionData: SessionData) {
@@ -86,7 +98,7 @@ class DatabaseSessionStore @Inject constructor(
Timber.w("Observing session list!")
return database.sessionDataQueries.selectAll()
.asFlow()
- .mapToList()
+ .mapToList(dispatchers.io)
.map { it.map { sessionData -> sessionData.toApiModel() } }
}
diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt
index d0c89d9896..1a81647f5c 100644
--- a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt
+++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt
@@ -16,6 +16,7 @@
package io.element.android.libraries.sessionstorage.impl
+import io.element.android.libraries.sessionstorage.api.LoginType
import io.element.android.libraries.sessionstorage.api.SessionData
import java.util.Date
import io.element.android.libraries.matrix.session.SessionData as DbSessionData
@@ -30,6 +31,8 @@ internal fun SessionData.toDbModel(): DbSessionData {
oidcData = oidcData,
slidingSyncProxy = slidingSyncProxy,
loginTimestamp = loginTimestamp?.time,
+ isTokenValid = if (isTokenValid) 1L else 0L,
+ loginType = loginType.name,
)
}
@@ -42,6 +45,8 @@ internal fun DbSessionData.toApiModel(): SessionData {
homeserverUrl = homeserverUrl,
oidcData = oidcData,
slidingSyncProxy = slidingSyncProxy,
- loginTimestamp = loginTimestamp?.let { Date(it) }
+ loginTimestamp = loginTimestamp?.let { Date(it) },
+ isTokenValid = isTokenValid == 1L,
+ loginType = LoginType.fromName(loginType ?: LoginType.UNKNOWN.name),
)
}
diff --git a/libraries/session-storage/impl/src/main/sqldelight/databases/1.db b/libraries/session-storage/impl/src/main/sqldelight/databases/1.db
new file mode 100644
index 0000000000..24a9b98f93
Binary files /dev/null and b/libraries/session-storage/impl/src/main/sqldelight/databases/1.db differ
diff --git a/libraries/session-storage/impl/src/main/sqldelight/databases/2.db b/libraries/session-storage/impl/src/main/sqldelight/databases/2.db
new file mode 100644
index 0000000000..8d5a4188bd
Binary files /dev/null and b/libraries/session-storage/impl/src/main/sqldelight/databases/2.db differ
diff --git a/libraries/session-storage/impl/src/main/sqldelight/databases/3.db b/libraries/session-storage/impl/src/main/sqldelight/databases/3.db
new file mode 100644
index 0000000000..58c8e8f09a
Binary files /dev/null and b/libraries/session-storage/impl/src/main/sqldelight/databases/3.db differ
diff --git a/libraries/session-storage/impl/src/main/sqldelight/databases/4.db b/libraries/session-storage/impl/src/main/sqldelight/databases/4.db
new file mode 100644
index 0000000000..f51605ff2a
Binary files /dev/null and b/libraries/session-storage/impl/src/main/sqldelight/databases/4.db differ
diff --git a/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq b/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq
index 05049c5635..d6d16cb6e2 100644
--- a/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq
+++ b/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq
@@ -1,3 +1,13 @@
+--------------------------------------------------------------------
+-- Current version of the DB is the highest value of filename
+-- in the folder `sqldelight/databases`.
+--
+-- When upgrading the schema, you have to create a file .sqm in the
+-- `sqldelight/databases` folder and run the following task to
+-- generate a .db file using the latest schema
+-- > ./gradlew generateDebugSessionDatabaseSchema
+--------------------------------------------------------------------
+
CREATE TABLE SessionData (
userId TEXT NOT NULL PRIMARY KEY,
deviceId TEXT NOT NULL,
@@ -5,8 +15,13 @@ CREATE TABLE SessionData (
refreshToken TEXT,
homeserverUrl TEXT NOT NULL,
slidingSyncProxy TEXT,
+ -- added in version 2
loginTimestamp INTEGER,
- oidcData TEXT
+ -- added in version 3
+ oidcData TEXT,
+ -- added in version 4
+ isTokenValid INTEGER NOT NULL DEFAULT 1,
+ loginType TEXT
);
diff --git a/libraries/session-storage/impl/src/main/sqldelight/migrations/0.sqm b/libraries/session-storage/impl/src/main/sqldelight/migrations/0.sqm
index 396a8f28dd..4577105e3d 100644
--- a/libraries/session-storage/impl/src/main/sqldelight/migrations/0.sqm
+++ b/libraries/session-storage/impl/src/main/sqldelight/migrations/0.sqm
@@ -1,3 +1,6 @@
+-- This file is not striclty necessary, since the first
+-- version of the DB is 1, so we will never migrate from 0
+
CREATE TABLE SessionData (
userId TEXT NOT NULL PRIMARY KEY,
deviceId TEXT NOT NULL,
diff --git a/libraries/session-storage/impl/src/main/sqldelight/migrations/1.sqm b/libraries/session-storage/impl/src/main/sqldelight/migrations/1.sqm
index 3ee7762585..845fabc321 100644
--- a/libraries/session-storage/impl/src/main/sqldelight/migrations/1.sqm
+++ b/libraries/session-storage/impl/src/main/sqldelight/migrations/1.sqm
@@ -1 +1,3 @@
+-- Migrate DB from version 1
+
ALTER TABLE SessionData ADD COLUMN loginTimestamp INTEGER;
diff --git a/libraries/session-storage/impl/src/main/sqldelight/migrations/2.sqm b/libraries/session-storage/impl/src/main/sqldelight/migrations/2.sqm
index 9fc7f2fdaa..0af4cf8d2b 100644
--- a/libraries/session-storage/impl/src/main/sqldelight/migrations/2.sqm
+++ b/libraries/session-storage/impl/src/main/sqldelight/migrations/2.sqm
@@ -1 +1,3 @@
+-- Migrate DB from version 2
+
ALTER TABLE SessionData ADD COLUMN oidcData TEXT;
diff --git a/libraries/session-storage/impl/src/main/sqldelight/migrations/3.sqm b/libraries/session-storage/impl/src/main/sqldelight/migrations/3.sqm
new file mode 100644
index 0000000000..eef6eb5efb
--- /dev/null
+++ b/libraries/session-storage/impl/src/main/sqldelight/migrations/3.sqm
@@ -0,0 +1,4 @@
+-- Migrate DB from version 3
+
+ALTER TABLE SessionData ADD COLUMN isTokenValid INTEGER NOT NULL DEFAULT 1;
+ALTER TABLE SessionData ADD COLUMN loginType TEXT;
diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt
index e035ff9ae1..cb5569fb62 100644
--- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt
+++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt
@@ -16,10 +16,15 @@
package io.element.android.libraries.sessionstorage.impl
+import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
-import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
+import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.session.SessionData
+import io.element.android.libraries.sessionstorage.api.LoggedInState
+import io.element.android.libraries.sessionstorage.api.LoginType
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -38,8 +43,11 @@ class DatabaseSessionStoreTests {
slidingSyncProxy = null,
loginTimestamp = null,
oidcData = "aOidcData",
+ isTokenValid = 1,
+ loginType = LoginType.UNKNOWN.name,
)
+ @OptIn(ExperimentalCoroutinesApi::class)
@Before
fun setup() {
// Initialise in memory SQLite driver
@@ -47,7 +55,14 @@ class DatabaseSessionStoreTests {
SessionDatabase.Schema.create(driver)
database = SessionDatabase(driver)
- databaseSessionStore = DatabaseSessionStore(database)
+ databaseSessionStore = DatabaseSessionStore(
+ database = database,
+ dispatchers = CoroutineDispatchers(
+ io = UnconfinedTestDispatcher(),
+ computation = UnconfinedTestDispatcher(),
+ main = UnconfinedTestDispatcher(),
+ )
+ )
}
@Test
@@ -63,11 +78,11 @@ class DatabaseSessionStoreTests {
@Test
fun `isLoggedIn emits true while there are sessions in the DB`() = runTest {
databaseSessionStore.isLoggedIn().test {
- assertThat(awaitItem()).isFalse()
+ assertThat(awaitItem()).isEqualTo(LoggedInState.NotLoggedIn)
database.sessionDataQueries.insertSessionData(aSessionData)
- assertThat(awaitItem()).isTrue()
+ assertThat(awaitItem()).isEqualTo(LoggedInState.LoggedIn(sessionId = aSessionData.userId, isTokenValid = true))
database.sessionDataQueries.removeSession(aSessionData.userId)
- assertThat(awaitItem()).isFalse()
+ assertThat(awaitItem()).isEqualTo(LoggedInState.NotLoggedIn)
}
}
@@ -121,6 +136,8 @@ class DatabaseSessionStoreTests {
slidingSyncProxy = "slidingSyncProxy",
loginTimestamp = 1,
oidcData = "aOidcData",
+ isTokenValid = 1,
+ loginType = null,
)
val secondSessionData = SessionData(
userId = "userId",
@@ -131,6 +148,8 @@ class DatabaseSessionStoreTests {
slidingSyncProxy = "slidingSyncProxyAltered",
loginTimestamp = 2,
oidcData = "aOidcDataAltered",
+ isTokenValid = 1,
+ loginType = null,
)
assertThat(firstSessionData.userId).isEqualTo(secondSessionData.userId)
assertThat(firstSessionData.loginTimestamp).isNotEqualTo(secondSessionData.loginTimestamp)
diff --git a/libraries/textcomposer/impl/build.gradle.kts b/libraries/textcomposer/impl/build.gradle.kts
index 86e911ca3e..bdc5e2b3c5 100644
--- a/libraries/textcomposer/impl/build.gradle.kts
+++ b/libraries/textcomposer/impl/build.gradle.kts
@@ -32,11 +32,13 @@ dependencies {
implementation(projects.libraries.matrixui)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.testtags)
- implementation(libs.androidx.constraintlayout)
- implementation(libs.androidx.constraintlayout.compose)
implementation(libs.matrix.richtexteditor)
api(libs.matrix.richtexteditor.compose)
ksp(libs.showkase.processor)
+
+ testImplementation(libs.test.junit)
+ testImplementation(libs.test.truth)
+ testImplementation(libs.coroutines.test)
}
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
index b59b7c99c4..18246f1ac4 100644
--- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
@@ -16,58 +16,43 @@
package io.element.android.libraries.textcomposer
-import androidx.compose.animation.core.animateDpAsState
-import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredHeightIn
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
-import androidx.constraintlayout.compose.ConstraintLayout
-import androidx.constraintlayout.compose.Dimension.Companion.fillToConstraints
-import androidx.constraintlayout.compose.Visibility
-import io.element.android.libraries.designsystem.VectorIcons
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.applyScaleUp
import io.element.android.libraries.designsystem.theme.components.Icon
-import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.media.MediaSource
@@ -76,30 +61,40 @@ import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
-import io.element.android.libraries.textcomposer.components.FormattingOption
-import io.element.android.libraries.textcomposer.components.FormattingOptionState
+import io.element.android.libraries.textcomposer.components.ComposerOptionsButton
+import io.element.android.libraries.textcomposer.components.DismissTextFormattingButton
+import io.element.android.libraries.textcomposer.components.RecordButton
+import io.element.android.libraries.textcomposer.components.RecordingProgress
+import io.element.android.libraries.textcomposer.components.SendButton
+import io.element.android.libraries.textcomposer.components.TextFormatting
+import io.element.android.libraries.textcomposer.components.textInputRoundedCornerShape
+import io.element.android.libraries.textcomposer.model.Message
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
+import io.element.android.libraries.textcomposer.model.PressEvent
+import io.element.android.libraries.textcomposer.model.VoiceMessageState
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.wysiwyg.compose.RichTextEditor
import io.element.android.wysiwyg.compose.RichTextEditorState
-import io.element.android.wysiwyg.view.models.InlineFormat
-import io.element.android.wysiwyg.view.models.LinkAction
-import uniffi.wysiwyg_composer.ActionState
-import uniffi.wysiwyg_composer.ComposerAction
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.persistentListOf
@Composable
fun TextComposer(
state: RichTextEditorState,
+ voiceMessageState: VoiceMessageState,
composerMode: MessageComposerMode,
- canSendMessage: Boolean,
enableTextFormatting: Boolean,
+ enableVoiceMessages: Boolean,
modifier: Modifier = Modifier,
showTextFormatting: Boolean = false,
+ subcomposing: Boolean = false,
onRequestFocus: () -> Unit = {},
onSendMessage: (Message) -> Unit = {},
onResetComposerMode: () -> Unit = {},
onAddAttachment: () -> Unit = {},
onDismissTextFormatting: () -> Unit = {},
+ onVoiceRecordButtonEvent: (PressEvent) -> Unit = {},
onError: (Throwable) -> Unit = {},
) {
val onSendClicked = {
@@ -107,330 +102,246 @@ fun TextComposer(
onSendMessage(Message(html = html, markdown = state.messageMarkdown))
}
- Column(
- modifier = modifier
- .padding(
- start = 3.dp,
- end = 6.dp,
- top = 8.dp,
- bottom = 4.dp,
- )
- .fillMaxWidth(),
+ val layoutModifier = modifier
+ .fillMaxSize()
+ .height(IntrinsicSize.Min)
+
+ val composerOptionsButton = @Composable {
+ ComposerOptionsButton(
+ modifier = Modifier
+ .size(48.dp),
+ onClick = onAddAttachment
+ )
+ }
+
+ val textInput = @Composable {
+ TextInput(
+ state = state,
+ subcomposing = subcomposing,
+ placeholder = if (composerMode.inThread) {
+ stringResource(id = CommonStrings.action_reply_in_thread)
+ } else {
+ stringResource(id = R.string.rich_text_editor_composer_placeholder)
+ },
+ composerMode = composerMode,
+ onResetComposerMode = onResetComposerMode,
+ onError = onError,
+ )
+ }
+
+ val canSendMessage by remember { derivedStateOf { state.messageHtml.isNotEmpty() } }
+ val sendButton = @Composable {
+ SendButton(
+ canSendMessage = canSendMessage,
+ onClick = onSendClicked,
+ composerMode = composerMode,
+ )
+ }
+ val recordButton = @Composable {
+ RecordButton(
+ onPressStart = { onVoiceRecordButtonEvent(PressEvent.PressStart) },
+ onLongPressEnd = { onVoiceRecordButtonEvent(PressEvent.LongPressEnd) },
+ onTap = { onVoiceRecordButtonEvent(PressEvent.Tapped) },
+ )
+ }
+
+ val textFormattingOptions = @Composable { TextFormatting(state = state) }
+
+ val sendOrRecordButton = if (canSendMessage || !enableVoiceMessages) {
+ sendButton
+ } else {
+ recordButton
+ }
+
+ val recordingProgress = @Composable {
+ RecordingProgress()
+ }
+
+ if (showTextFormatting) {
+ TextFormattingLayout(
+ modifier = layoutModifier,
+ textInput = textInput,
+ dismissTextFormattingButton = {
+ DismissTextFormattingButton(onClick = onDismissTextFormatting)
+ },
+ textFormatting = textFormattingOptions,
+ sendButton = sendButton,
+ )
+ } else {
+ StandardLayout(
+ voiceMessageState = voiceMessageState,
+ modifier = layoutModifier,
+ composerOptionsButton = composerOptionsButton,
+ textInput = textInput,
+ endButton = sendOrRecordButton,
+ recordingProgress = recordingProgress,
+ )
+ }
+
+ if (!subcomposing) {
+ SoftKeyboardEffect(composerMode, onRequestFocus) {
+ it is MessageComposerMode.Special
+ }
+
+ SoftKeyboardEffect(showTextFormatting, onRequestFocus) { it }
+ }
+}
+
+@Composable
+private fun StandardLayout(
+ voiceMessageState: VoiceMessageState,
+ textInput: @Composable () -> Unit,
+ composerOptionsButton: @Composable () -> Unit,
+ recordingProgress: @Composable () -> Unit,
+ endButton: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ Row(
+ modifier = modifier,
+ verticalAlignment = Alignment.Bottom,
) {
- ConstraintLayout(
- modifier = Modifier.fillMaxWidth(),
+ if (voiceMessageState is VoiceMessageState.Recording) {
+ Box(
+ modifier = Modifier
+ .padding(start = 16.dp, bottom = 8.dp, top = 8.dp)
+ .weight(1f)
+ ) {
+ recordingProgress()
+ }
+ } else {
+ Box(
+ Modifier
+ .padding(bottom = 5.dp, top = 5.dp, start = 3.dp)
+ ) {
+ composerOptionsButton()
+ }
+ Box(
+ modifier = Modifier
+ .padding(bottom = 8.dp, top = 8.dp)
+ .weight(1f)
+ ) {
+ textInput()
+ }
+ }
+ Box(
+ Modifier
+ .padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp)
) {
- val (composeOptions, textInput, sendButton) = createRefs()
- val showComposerOptionsButton by remember(showTextFormatting) {
- derivedStateOf { !showTextFormatting }
- }
- IconButton(
- modifier = Modifier
- .size(48.dp)
- .constrainAs(composeOptions) {
- start.linkTo(parent.start)
- bottom.linkTo(parent.bottom)
- visibility = if (showComposerOptionsButton) Visibility.Visible else Visibility.Gone
- },
- onClick = onAddAttachment
- ) {
- Icon(
- modifier = Modifier.size(30.dp.applyScaleUp()),
- resourceId = VectorIcons.Plus,
- contentDescription = stringResource(R.string.rich_text_editor_a11y_add_attachment),
- tint = ElementTheme.colors.iconPrimary,
- )
- }
- val roundCornerSmall = 20.dp.applyScaleUp()
- val roundCornerLarge = 28.dp.applyScaleUp()
-
- val roundedCornerSize = remember(state.lineCount, composerMode) {
- if (composerMode is MessageComposerMode.Special) {
- roundCornerSmall
- } else {
- roundCornerLarge
- }
- }
- val roundedCornerSizeState = animateDpAsState(
- targetValue = roundedCornerSize,
- animationSpec = tween(
- durationMillis = 100,
- ),
- label = "roundedCornerSizeAnimation"
- )
- val roundedCorners = RoundedCornerShape(roundedCornerSizeState.value)
- val colors = ElementTheme.colors
- val bgColor = colors.bgSubtleSecondary
- val borderColor = colors.borderDisabled
-
- Column(
- modifier = Modifier
- .constrainAs(textInput) {
- start.linkTo(composeOptions.end, margin = 3.dp, goneMargin = 9.dp)
- end.linkTo(sendButton.start, margin = 6.dp, goneMargin = 6.dp)
- bottom.linkTo(parent.bottom)
- width = fillToConstraints
- }
- .padding(vertical = 3.dp)
- .fillMaxWidth()
- .clip(roundedCorners)
- .background(color = bgColor)
- .border(0.5.dp, borderColor, roundedCorners)
- ) {
- if (composerMode is MessageComposerMode.Special) {
- ComposerModeView(composerMode = composerMode, onResetComposerMode = onResetComposerMode)
- }
- TextInput(
- state = state,
- placeholder = if (composerMode.inThread) {
- stringResource(id = CommonStrings.action_reply_in_thread)
- } else {
- stringResource(id = R.string.rich_text_editor_composer_placeholder)
- },
- roundedCorners = roundedCorners,
- bgColor = bgColor,
- onError = onError,
- )
- }
-
- SendButton(
- canSendMessage = canSendMessage,
- onClick = onSendClicked,
- composerMode = composerMode,
- modifier = Modifier
- .constrainAs(sendButton) {
- bottom.linkTo(parent.bottom)
- end.linkTo(parent.end)
- visibility = if (!showTextFormatting) Visibility.Visible else Visibility.Gone
- }
- )
- }
-
- if (showTextFormatting) {
- TextFormatting(
- state = state,
- onDismiss = onDismissTextFormatting,
- sendButton = {
- SendButton(
- canSendMessage = canSendMessage,
- onClick = onSendClicked,
- composerMode = composerMode,
- modifier = it
- )
- },
- )
+ endButton()
}
}
+}
- SoftKeyboardEffect(composerMode, onRequestFocus) {
- it is MessageComposerMode.Special
+@Composable
+private fun TextFormattingLayout(
+ textInput: @Composable () -> Unit,
+ dismissTextFormattingButton: @Composable () -> Unit,
+ textFormatting: @Composable () -> Unit,
+ sendButton: @Composable () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Column(
+ modifier = modifier.padding(vertical = 4.dp),
+ verticalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
+ Box(
+ modifier = Modifier
+ .weight(1f)
+ .padding(horizontal = 12.dp)
+ ) {
+ textInput()
+ }
+ Row(
+ modifier = Modifier
+ .fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Box(
+ modifier = Modifier.padding(start = 3.dp)
+ ) {
+ dismissTextFormattingButton()
+ }
+ Box(modifier = Modifier.weight(1f)) {
+ textFormatting()
+ }
+ Box(
+ modifier = Modifier.padding(
+ start = 14.dp,
+ end = 6.dp
+ )
+ ) {
+ sendButton()
+ }
+ }
}
-
- SoftKeyboardEffect(showTextFormatting, onRequestFocus) { it }
}
@Composable
private fun TextInput(
state: RichTextEditorState,
+ subcomposing: Boolean,
placeholder: String,
- roundedCorners: RoundedCornerShape,
- bgColor: Color,
+ composerMode: MessageComposerMode,
+ onResetComposerMode: () -> Unit,
modifier: Modifier = Modifier,
onError: (Throwable) -> Unit = {},
) {
- val minHeight = 42.dp.applyScaleUp()
- val defaultTypography = ElementTheme.typography.fontBodyLgRegular
- Box(
+ val bgColor = ElementTheme.colors.bgSubtleSecondary
+ val borderColor = ElementTheme.colors.borderDisabled
+ val roundedCorners = textInputRoundedCornerShape(composerMode = composerMode)
+
+ Column(
modifier = modifier
- .heightIn(min = minHeight)
- .background(color = bgColor, shape = roundedCorners)
- .padding(
- PaddingValues(
+ .clip(roundedCorners)
+ .border(0.5.dp, borderColor, roundedCorners)
+ .background(color = bgColor)
+ .requiredHeightIn(min = 42.dp.applyScaleUp())
+ .fillMaxSize(),
+ ) {
+ if (composerMode is MessageComposerMode.Special) {
+ ComposerModeView(composerMode = composerMode, onResetComposerMode = onResetComposerMode)
+ }
+ val defaultTypography = ElementTheme.typography.fontBodyLgRegular
+ Box(
+ modifier = Modifier
+ .padding(
top = 4.dp.applyScaleUp(),
bottom = 4.dp.applyScaleUp(),
start = 12.dp.applyScaleUp(),
end = 42.dp.applyScaleUp()
)
- )
- .testTag(TestTags.richTextEditor),
- contentAlignment = Alignment.CenterStart,
- ) {
-
- // Placeholder
- if (state.messageHtml.isEmpty()) {
- Text(
- placeholder,
- style = defaultTypography.copy(
- color = ElementTheme.colors.textSecondary,
- ),
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- )
- }
-
- RichTextEditor(
- state = state,
- modifier = Modifier
- .padding(top = 6.dp, bottom = 6.dp)
- .fillMaxWidth(),
- style = ElementRichTextEditorStyle.create(
- hasFocus = state.hasFocus
- ),
- onError = onError
- )
- }
-}
-
-@Composable
-private fun TextFormatting(
- state: RichTextEditorState,
- onDismiss: () -> Unit,
- modifier: Modifier = Modifier,
- sendButton: @Composable (modifier: Modifier) -> Unit,
-) {
- ConstraintLayout(
- modifier = modifier
- .fillMaxWidth()
- ) {
- val (close, formatting, send) = createRefs()
-
- IconButton(
- modifier = Modifier
- .size(48.dp)
- .constrainAs(close) {
- start.linkTo(parent.start)
- top.linkTo(parent.top)
- bottom.linkTo(parent.bottom)
- },
- onClick = onDismiss
+ .testTag(TestTags.richTextEditor),
+ contentAlignment = Alignment.CenterStart,
) {
- Icon(
- modifier = Modifier.size(30.dp.applyScaleUp()),
- resourceId = VectorIcons.Cancel,
- contentDescription = stringResource(CommonStrings.action_close),
- tint = ElementTheme.colors.iconPrimary,
- )
- }
-
- val scrollState = rememberScrollState()
- Row(
- modifier = Modifier
- .constrainAs(formatting) {
- top.linkTo(parent.top)
- bottom.linkTo(parent.bottom)
- start.linkTo(close.end, margin = 1.dp)
- end.linkTo(send.start, margin = 14.dp)
- width = fillToConstraints
- }
- .horizontalScroll(scrollState),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.spacedBy(4.dp),
- ) {
- FormattingOption(
- state = state.actions[ComposerAction.BOLD].toButtonState(),
- onClick = { state.toggleInlineFormat(InlineFormat.Bold) },
- imageVector = ImageVector.vectorResource(VectorIcons.Bold),
- contentDescription = stringResource(R.string.rich_text_editor_format_bold)
- )
- FormattingOption(
- state = state.actions[ComposerAction.ITALIC].toButtonState(),
- onClick = { state.toggleInlineFormat(InlineFormat.Italic) },
- imageVector = ImageVector.vectorResource(VectorIcons.Italic),
- contentDescription = stringResource(R.string.rich_text_editor_format_italic)
- )
- FormattingOption(
- state = state.actions[ComposerAction.UNDERLINE].toButtonState(),
- onClick = { state.toggleInlineFormat(InlineFormat.Underline) },
- imageVector = ImageVector.vectorResource(VectorIcons.Underline),
- contentDescription = stringResource(R.string.rich_text_editor_format_underline)
- )
- FormattingOption(
- state = state.actions[ComposerAction.STRIKE_THROUGH].toButtonState(),
- onClick = { state.toggleInlineFormat(InlineFormat.StrikeThrough) },
- imageVector = ImageVector.vectorResource(VectorIcons.Strikethrough),
- contentDescription = stringResource(R.string.rich_text_editor_format_strikethrough)
- )
-
- var linkDialogAction by remember { mutableStateOf(null) }
-
- linkDialogAction?.let {
- TextComposerLinkDialog(
- onDismissRequest = { linkDialogAction = null },
- onCreateLinkRequest = state::insertLink,
- onSaveLinkRequest = state::setLink,
- onRemoveLinkRequest = state::removeLink,
- linkAction = it,
+ // Placeholder
+ if (state.messageHtml.isEmpty()) {
+ Text(
+ placeholder,
+ style = defaultTypography.copy(
+ color = ElementTheme.colors.textSecondary,
+ ),
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
)
}
- FormattingOption(
- state = state.actions[ComposerAction.LINK].toButtonState(),
- onClick = { linkDialogAction = state.linkAction },
- imageVector = ImageVector.vectorResource(VectorIcons.Link),
- contentDescription = stringResource(R.string.rich_text_editor_link)
- )
-
- FormattingOption(
- state = state.actions[ComposerAction.UNORDERED_LIST].toButtonState(),
- onClick = { state.toggleList(ordered = false) },
- imageVector = ImageVector.vectorResource(VectorIcons.BulletList),
- contentDescription = stringResource(R.string.rich_text_editor_bullet_list)
- )
- FormattingOption(
- state = state.actions[ComposerAction.ORDERED_LIST].toButtonState(),
- onClick = { state.toggleList(ordered = true) },
- imageVector = ImageVector.vectorResource(VectorIcons.NumberedList),
- contentDescription = stringResource(R.string.rich_text_editor_numbered_list)
- )
- FormattingOption(
- state = state.actions[ComposerAction.INDENT].toButtonState(),
- onClick = { state.indent() },
- imageVector = ImageVector.vectorResource(VectorIcons.IndentIncrease),
- contentDescription = stringResource(R.string.rich_text_editor_indent)
- )
- FormattingOption(
- state = state.actions[ComposerAction.UNINDENT].toButtonState(),
- onClick = { state.unindent() },
- imageVector = ImageVector.vectorResource(VectorIcons.IndentDecrease),
- contentDescription = stringResource(R.string.rich_text_editor_unindent)
- )
- FormattingOption(
- state = state.actions[ComposerAction.INLINE_CODE].toButtonState(),
- onClick = { state.toggleInlineFormat(InlineFormat.InlineCode) },
- imageVector = ImageVector.vectorResource(VectorIcons.InlineCode),
- contentDescription = stringResource(R.string.rich_text_editor_inline_code)
- )
- FormattingOption(
- state = state.actions[ComposerAction.CODE_BLOCK].toButtonState(),
- onClick = { state.toggleCodeBlock() },
- imageVector = ImageVector.vectorResource(VectorIcons.CodeBlock),
- contentDescription = stringResource(R.string.rich_text_editor_code_block)
- )
- FormattingOption(
- state = state.actions[ComposerAction.QUOTE].toButtonState(),
- onClick = { state.toggleQuote() },
- imageVector = ImageVector.vectorResource(VectorIcons.Quote),
- contentDescription = stringResource(R.string.rich_text_editor_quote)
+ RichTextEditor(
+ state = state,
+ // Disable most of the editor functionality if it's just being measured for a subcomposition.
+ // This prevents it gaining focus and mutating the state.
+ registerStateUpdates = !subcomposing,
+ modifier = Modifier
+ .padding(top = 6.dp, bottom = 6.dp)
+ .fillMaxWidth(),
+ style = ElementRichTextEditorStyle.create(
+ hasFocus = state.hasFocus
+ ),
+ onError = onError
)
}
-
- sendButton(
- Modifier.constrainAs(send) {
- top.linkTo(parent.top)
- bottom.linkTo(parent.bottom)
- end.linkTo(parent.end)
- },
- )
}
}
-private fun ActionState?.toButtonState(): FormattingOptionState =
- when (this) {
- ActionState.ENABLED -> FormattingOptionState.Default
- ActionState.REVERSED -> FormattingOptionState.Selected
- ActionState.DISABLED, null -> FormattingOptionState.Disabled
- }
-
@Composable
private fun ComposerModeView(
composerMode: MessageComposerMode,
@@ -460,14 +371,14 @@ private fun EditingModeView(
modifier: Modifier = Modifier,
) {
Row(
- horizontalArrangement = Arrangement.spacedBy(5.dp),
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.fillMaxWidth()
.padding(start = 12.dp)
) {
Icon(
- resourceId = VectorIcons.Edit,
+ resourceId = CommonDrawables.ic_september_edit_solid_16,
contentDescription = stringResource(CommonStrings.common_editing),
tint = ElementTheme.materialColors.secondary,
modifier = Modifier
@@ -484,7 +395,7 @@ private fun EditingModeView(
.weight(1f)
)
Icon(
- imageVector = Icons.Default.Close,
+ resourceId = CommonDrawables.ic_compound_close,
contentDescription = stringResource(CommonStrings.action_close),
tint = ElementTheme.materialColors.secondary,
modifier = Modifier
@@ -547,7 +458,7 @@ private fun ReplyToModeView(
)
}
Icon(
- imageVector = Icons.Default.Close,
+ resourceId = CommonDrawables.ic_compound_close,
contentDescription = stringResource(CommonStrings.action_close),
tint = MaterialTheme.colorScheme.secondary,
modifier = Modifier
@@ -563,138 +474,113 @@ private fun ReplyToModeView(
}
}
-@Composable
-private fun SendButton(
- canSendMessage: Boolean,
- onClick: () -> Unit,
- composerMode: MessageComposerMode,
- modifier: Modifier = Modifier,
-) {
- IconButton(
- modifier = modifier
- .size(48.dp.applyScaleUp()),
- onClick = onClick,
- enabled = canSendMessage,
- ) {
- val iconId = when (composerMode) {
- is MessageComposerMode.Edit -> R.drawable.ic_tick
- else -> R.drawable.ic_send
- }
- val contentDescription = when (composerMode) {
- is MessageComposerMode.Edit -> stringResource(CommonStrings.action_edit)
- else -> stringResource(CommonStrings.action_send)
- }
- Box(
- modifier = Modifier
- .clip(CircleShape)
- .size(36.dp.applyScaleUp())
- .background(if (canSendMessage) ElementTheme.colors.iconAccentTertiary else Color.Transparent)
- ) {
- Icon(
- modifier = Modifier
- .height(24.dp.applyScaleUp())
- .align(Alignment.Center),
- resourceId = iconId,
- contentDescription = contentDescription,
- // Exception here, we use Color.White instead of ElementTheme.colors.iconOnSolidPrimary
- tint = if (canSendMessage) Color.White else ElementTheme.colors.iconDisabled
- )
- }
- }
-}
-
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TextComposerSimplePreview() = ElementPreview {
- Column {
+ PreviewColumn(items = persistentListOf(
+ {
+ TextComposer(
+ RichTextEditorState("", initialFocus = true),
+ voiceMessageState = VoiceMessageState.Idle,
+ onSendMessage = {},
+ composerMode = MessageComposerMode.Normal(""),
+ onResetComposerMode = {},
+ enableTextFormatting = true,
+ enableVoiceMessages = true,
+ )
+ }, {
TextComposer(
- RichTextEditorState("", fake = true).apply { requestFocus() },
- canSendMessage = false,
- onSendMessage = {},
- composerMode = MessageComposerMode.Normal(""),
- onResetComposerMode = {},
- enableTextFormatting = true,
- )
- TextComposer(
- RichTextEditorState("A message", fake = true).apply { requestFocus() },
- canSendMessage = true,
+ RichTextEditorState("A message", initialFocus = true),
+ voiceMessageState = VoiceMessageState.Idle,
onSendMessage = {},
composerMode = MessageComposerMode.Normal(""),
onResetComposerMode = {},
enableTextFormatting = true,
+ enableVoiceMessages = true,
)
+ }, {
TextComposer(
RichTextEditorState(
"A message\nWith several lines\nTo preview larger textfields and long lines with overflow",
- fake = true
- ).apply {
- requestFocus()
- },
- canSendMessage = true,
+ initialFocus = true
+ ),
+ voiceMessageState = VoiceMessageState.Idle,
onSendMessage = {},
composerMode = MessageComposerMode.Normal(""),
onResetComposerMode = {},
enableTextFormatting = true,
+ enableVoiceMessages = true,
)
+ }, {
TextComposer(
- RichTextEditorState("A message without focus", fake = true),
- canSendMessage = true,
+ RichTextEditorState("A message without focus", initialFocus = false),
+ voiceMessageState = VoiceMessageState.Idle,
onSendMessage = {},
composerMode = MessageComposerMode.Normal(""),
onResetComposerMode = {},
enableTextFormatting = true,
+ enableVoiceMessages = true,
)
- }
-}
-
-@DayNightPreviews
-@Composable
-internal fun TextComposerFormattingPreview() = ElementPreview {
- Column {
- TextComposer(
- RichTextEditorState("", fake = true),
- canSendMessage = false,
- showTextFormatting = true,
- composerMode = MessageComposerMode.Normal(""),
- enableTextFormatting = true,
- )
- TextComposer(
- RichTextEditorState("A message", fake = true),
- canSendMessage = true,
- showTextFormatting = true,
- composerMode = MessageComposerMode.Normal(""),
- enableTextFormatting = true,
- )
- TextComposer(
- RichTextEditorState("A message\nWith several lines\nTo preview larger textfields and long lines with overflow", fake = true),
- canSendMessage = true,
- showTextFormatting = true,
- composerMode = MessageComposerMode.Normal(""),
- enableTextFormatting = true,
- )
- }
-}
-
-@DayNightPreviews
-@Composable
-internal fun TextComposerEditPreview() = ElementPreview {
- TextComposer(
- RichTextEditorState("A message", fake = true).apply { requestFocus() },
- canSendMessage = true,
- onSendMessage = {},
- composerMode = MessageComposerMode.Edit(EventId("$1234"), "Some text", TransactionId("1234")),
- onResetComposerMode = {},
- enableTextFormatting = true,
+ })
)
}
-@DayNightPreviews
+@PreviewsDayNight
+@Composable
+internal fun TextComposerFormattingPreview() = ElementPreview {
+ PreviewColumn(items = persistentListOf({
+ TextComposer(
+ RichTextEditorState("", initialFocus = false),
+ voiceMessageState = VoiceMessageState.Idle,
+ showTextFormatting = true,
+ composerMode = MessageComposerMode.Normal(""),
+ enableTextFormatting = true,
+ enableVoiceMessages = true,
+ )
+ }, {
+ TextComposer(
+ RichTextEditorState("A message", initialFocus = false),
+ voiceMessageState = VoiceMessageState.Idle,
+ showTextFormatting = true,
+ composerMode = MessageComposerMode.Normal(""),
+ enableTextFormatting = true,
+ enableVoiceMessages = true,
+ )
+ }, {
+ TextComposer(
+ RichTextEditorState("A message\nWith several lines\nTo preview larger textfields and long lines with overflow", initialFocus = false),
+ voiceMessageState = VoiceMessageState.Idle,
+ showTextFormatting = true,
+ composerMode = MessageComposerMode.Normal(""),
+ enableTextFormatting = true,
+ enableVoiceMessages = true,
+ )
+ }))
+}
+
+@PreviewsDayNight
+@Composable
+internal fun TextComposerEditPreview() = ElementPreview {
+ PreviewColumn(items = persistentListOf({
+ TextComposer(
+ RichTextEditorState("A message", initialFocus = true),
+ voiceMessageState = VoiceMessageState.Idle,
+ onSendMessage = {},
+ composerMode = MessageComposerMode.Edit(EventId("$1234"), "Some text", TransactionId("1234")),
+ onResetComposerMode = {},
+ enableTextFormatting = true,
+ enableVoiceMessages = true,
+ )
+ }))
+}
+
+@PreviewsDayNight
@Composable
internal fun TextComposerReplyPreview() = ElementPreview {
- Column {
+ PreviewColumn(items = persistentListOf({
TextComposer(
- RichTextEditorState("", fake = true),
- canSendMessage = false,
+ RichTextEditorState(""),
+ voiceMessageState = VoiceMessageState.Idle,
onSendMessage = {},
composerMode = MessageComposerMode.Reply(
isThreaded = false,
@@ -707,26 +593,31 @@ internal fun TextComposerReplyPreview() = ElementPreview {
),
onResetComposerMode = {},
enableTextFormatting = true,
+ enableVoiceMessages = true,
)
+ },
+ {
+ TextComposer(
+ RichTextEditorState(""),
+ voiceMessageState = VoiceMessageState.Idle,
+ onSendMessage = {},
+ composerMode = MessageComposerMode.Reply(
+ isThreaded = true,
+ senderName = "Alice",
+ eventId = EventId("$1234"),
+ attachmentThumbnailInfo = null,
+ defaultContent = "A message\n" +
+ "With several lines\n" +
+ "To preview larger textfields and long lines with overflow"
+ ),
+ onResetComposerMode = {},
+ enableTextFormatting = true,
+ enableVoiceMessages = true,
+ )
+ }, {
TextComposer(
- RichTextEditorState("", fake = true),
- canSendMessage = false,
- onSendMessage = {},
- composerMode = MessageComposerMode.Reply(
- isThreaded = true,
- senderName = "Alice",
- eventId = EventId("$1234"),
- attachmentThumbnailInfo = null,
- defaultContent = "A message\n" +
- "With several lines\n" +
- "To preview larger textfields and long lines with overflow"
- ),
- onResetComposerMode = {},
- enableTextFormatting = true,
- )
- TextComposer(
- RichTextEditorState("A message", fake = true),
- canSendMessage = true,
+ RichTextEditorState("A message"),
+ voiceMessageState = VoiceMessageState.Idle,
onSendMessage = {},
composerMode = MessageComposerMode.Reply(
isThreaded = true,
@@ -742,10 +633,12 @@ internal fun TextComposerReplyPreview() = ElementPreview {
),
onResetComposerMode = {},
enableTextFormatting = true,
+ enableVoiceMessages = true,
)
+ }, {
TextComposer(
- RichTextEditorState("A message", fake = true),
- canSendMessage = true,
+ RichTextEditorState("A message"),
+ voiceMessageState = VoiceMessageState.Idle,
onSendMessage = {},
composerMode = MessageComposerMode.Reply(
isThreaded = false,
@@ -761,10 +654,12 @@ internal fun TextComposerReplyPreview() = ElementPreview {
),
onResetComposerMode = {},
enableTextFormatting = true,
+ enableVoiceMessages = true,
)
+ }, {
TextComposer(
- RichTextEditorState("A message", fake = true),
- canSendMessage = true,
+ RichTextEditorState("A message"),
+ voiceMessageState = VoiceMessageState.Idle,
onSendMessage = {},
composerMode = MessageComposerMode.Reply(
isThreaded = false,
@@ -780,10 +675,12 @@ internal fun TextComposerReplyPreview() = ElementPreview {
),
onResetComposerMode = {},
enableTextFormatting = true,
+ enableVoiceMessages = true,
)
+ }, {
TextComposer(
- RichTextEditorState("A message", fake = true).apply { requestFocus() },
- canSendMessage = true,
+ RichTextEditorState("A message", initialFocus = true),
+ voiceMessageState = VoiceMessageState.Idle,
onSendMessage = {},
composerMode = MessageComposerMode.Reply(
isThreaded = false,
@@ -799,6 +696,26 @@ internal fun TextComposerReplyPreview() = ElementPreview {
),
onResetComposerMode = {},
enableTextFormatting = true,
+ enableVoiceMessages = true,
)
+ })
+ )
+}
+
+@Composable
+private fun PreviewColumn(
+ items: ImmutableList<@Composable () -> Unit>,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier
+ ) {
+ items.forEach { item ->
+ Box(
+ modifier = Modifier.height(IntrinsicSize.Min)
+ ) {
+ item()
+ }
+ }
}
}
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposerLinkDialog.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposerLinkDialog.kt
index 4a5b801774..f36e2ca11c 100644
--- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposerLinkDialog.kt
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposerLinkDialog.kt
@@ -26,7 +26,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import io.element.android.libraries.designsystem.components.dialogs.ListDialog
import io.element.android.libraries.designsystem.components.list.TextFieldListItem
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@@ -198,7 +198,7 @@ private fun EditLinkDialog(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TextComposerLinkDialogCreateLinkPreview() {
TextComposerLinkDialog(
@@ -210,7 +210,7 @@ internal fun TextComposerLinkDialogCreateLinkPreview() {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TextComposerLinkDialogCreateLinkWithoutTextPreview() {
TextComposerLinkDialog(
@@ -222,7 +222,7 @@ internal fun TextComposerLinkDialogCreateLinkWithoutTextPreview() {
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun TextComposerLinkDialogEditLinkPreview() {
TextComposerLinkDialog(
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/ComposerOptionsButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/ComposerOptionsButton.kt
new file mode 100644
index 0000000000..d1c7355861
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/ComposerOptionsButton.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.components
+
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.text.applyScaleUp
+import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.theme.components.IconButton
+import io.element.android.libraries.designsystem.utils.CommonDrawables
+import io.element.android.libraries.textcomposer.R
+import io.element.android.libraries.theme.ElementTheme
+
+@Composable
+internal fun ComposerOptionsButton(
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ IconButton(
+ modifier = modifier
+ .size(48.dp),
+ onClick = onClick
+ ) {
+ Icon(
+ modifier = Modifier.size(30.dp.applyScaleUp()),
+ resourceId = CommonDrawables.ic_plus,
+ contentDescription = stringResource(R.string.rich_text_editor_a11y_add_attachment),
+ tint = ElementTheme.colors.iconPrimary,
+ )
+ }
+}
+
+@PreviewsDayNight
+@Composable
+internal fun ComposerOptionsButtonPreview() = ElementPreview {
+ ComposerOptionsButton(onClick = {})
+}
+
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/DismissTextFormattingButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/DismissTextFormattingButton.kt
new file mode 100644
index 0000000000..c6ebe270bf
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/DismissTextFormattingButton.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.components
+
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.text.applyScaleUp
+import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.theme.components.IconButton
+import io.element.android.libraries.designsystem.utils.CommonDrawables
+import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.ui.strings.CommonStrings
+
+@Composable
+internal fun DismissTextFormattingButton(
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ IconButton(
+ modifier = modifier
+ .size(48.dp),
+ onClick = onClick
+ ) {
+ Icon(
+ modifier = Modifier.size(30.dp.applyScaleUp()),
+ resourceId = CommonDrawables.ic_cancel,
+ contentDescription = stringResource(CommonStrings.action_close),
+ tint = ElementTheme.colors.iconPrimary,
+ )
+ }
+}
+
+@PreviewsDayNight
+@Composable
+internal fun DismissTextFormattingButtonPreview() = ElementPreview {
+ DismissTextFormattingButton(onClick = {})
+}
+
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/FormattingOption.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/FormattingOption.kt
index a3635b28c3..b7874af386 100644
--- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/FormattingOption.kt
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/FormattingOption.kt
@@ -32,12 +32,12 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.VectorIcons
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.text.applyScaleUp
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.iconSuccessPrimaryBackground
+import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.theme.compound.generated.SemanticColors
@@ -89,26 +89,26 @@ internal fun FormattingOption(
}
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun FormattingButtonPreview() = ElementPreview {
Row {
FormattingOption(
state = FormattingOptionState.Default,
onClick = { },
- imageVector = ImageVector.vectorResource(VectorIcons.Bold),
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_bold),
contentDescription = "",
)
FormattingOption(
state = FormattingOptionState.Selected,
onClick = { },
- imageVector = ImageVector.vectorResource(VectorIcons.Italic),
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_italic),
contentDescription = "",
)
FormattingOption(
state = FormattingOptionState.Disabled,
onClick = { },
- imageVector = ImageVector.vectorResource(VectorIcons.Underline),
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_underline),
contentDescription = "",
)
}
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/RecordButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/RecordButton.kt
new file mode 100644
index 0000000000..7c70dd1ef6
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/RecordButton.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.components
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.PointerEventType
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.text.applyScaleUp
+import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.theme.components.IconButton
+import io.element.android.libraries.designsystem.utils.CommonDrawables
+import io.element.android.libraries.textcomposer.utils.PressState
+import io.element.android.libraries.textcomposer.utils.PressStateEffects
+import io.element.android.libraries.textcomposer.utils.rememberPressState
+import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.ui.strings.CommonStrings
+import kotlinx.coroutines.launch
+
+@Composable
+internal fun RecordButton(
+ modifier: Modifier = Modifier,
+ onPressStart: () -> Unit = {},
+ onLongPressEnd: () -> Unit = {},
+ onTap: () -> Unit = {},
+) {
+ val coroutineScope = rememberCoroutineScope()
+ val pressState = rememberPressState()
+
+ PressStateEffects(
+ pressState = pressState.value,
+ onPressStart = onPressStart,
+ onLongPressEnd = onLongPressEnd,
+ onTap = onTap,
+ )
+
+ RecordButtonView(
+ isPressed = pressState.value is PressState.Pressing,
+ modifier = modifier
+ .pointerInput(Unit) {
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ coroutineScope.launch {
+ when (event.type) {
+ PointerEventType.Press -> pressState.press()
+ PointerEventType.Release -> pressState.release()
+ }
+ }
+ }
+ }
+ }
+ )
+}
+
+@Composable
+private fun RecordButtonView(
+ isPressed: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ IconButton(
+ modifier = modifier
+ .size(48.dp),
+ onClick = {},
+ ) {
+ Icon(
+ modifier = Modifier.size(24.dp.applyScaleUp()),
+ resourceId = if (isPressed) {
+ CommonDrawables.ic_compound_mic_on_solid
+ } else {
+ CommonDrawables.ic_compound_mic_on_outline
+ },
+ contentDescription = stringResource(CommonStrings.a11y_voice_message_record),
+ tint = ElementTheme.colors.iconSecondary,
+ )
+ }
+}
+
+@PreviewsDayNight
+@Composable
+internal fun RecordButtonPreview() = ElementPreview {
+ Row {
+ RecordButtonView(isPressed = false)
+ RecordButtonView(isPressed = true)
+ }
+}
+
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/RecordingProgress.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/RecordingProgress.kt
new file mode 100644
index 0000000000..db4f59342c
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/RecordingProgress.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.theme.ElementTheme
+
+@Composable
+internal fun RecordingProgress(
+ modifier: Modifier = Modifier,
+) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .background(
+ color = ElementTheme.colors.bgSubtleSecondary,
+ shape = MaterialTheme.shapes.medium,
+ )
+ .padding(start = 12.dp, end = 20.dp, top = 8.dp, bottom = 8.dp)
+ .heightIn(26.dp)
+
+ ,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Box(
+ modifier = Modifier
+ .size(8.dp)
+ .background(color = ElementTheme.colors.textCriticalPrimary, shape = CircleShape)
+ )
+ Spacer(Modifier.size(8.dp))
+
+ // TODO Replace with timer UI
+ Text(
+ text = "Recording...", // Not localized because it is a placeholder
+ color = ElementTheme.colors.textSecondary,
+ style = ElementTheme.typography.fontBodySmMedium
+ )
+ }
+}
+
+@PreviewsDayNight
+@Composable
+internal fun RecordingProgressPreview() {
+ RecordingProgress()
+}
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt
new file mode 100644
index 0000000000..8dc1a4706b
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.text.applyScaleUp
+import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.theme.components.IconButton
+import io.element.android.libraries.designsystem.utils.CommonDrawables
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
+import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.ui.strings.CommonStrings
+
+@Composable
+internal fun SendButton(
+ canSendMessage: Boolean,
+ onClick: () -> Unit,
+ composerMode: MessageComposerMode,
+ modifier: Modifier = Modifier,
+) {
+ IconButton(
+ modifier = modifier
+ .size(48.dp.applyScaleUp()),
+ onClick = onClick,
+ enabled = canSendMessage,
+ ) {
+ val iconId = when (composerMode) {
+ is MessageComposerMode.Edit -> CommonDrawables.ic_compound_check
+ else -> CommonDrawables.ic_september_send
+ }
+ val iconSize = when (composerMode) {
+ is MessageComposerMode.Edit -> 24.dp
+ // CommonDrawables.ic_september_send is too big... reduce its size.
+ else -> 18.dp
+ }
+ val iconStartPadding = when (composerMode) {
+ is MessageComposerMode.Edit -> 0.dp
+ else -> 2.dp
+ }
+ val contentDescription = when (composerMode) {
+ is MessageComposerMode.Edit -> stringResource(CommonStrings.action_edit)
+ else -> stringResource(CommonStrings.action_send)
+ }
+ Box(
+ modifier = Modifier
+ .clip(CircleShape)
+ .size(36.dp.applyScaleUp())
+ .background(if (canSendMessage) ElementTheme.colors.iconAccentTertiary else Color.Transparent)
+ ) {
+ Icon(
+ modifier = Modifier
+ .height(iconSize.applyScaleUp())
+ .padding(start = iconStartPadding)
+ .align(Alignment.Center),
+ resourceId = iconId,
+ contentDescription = contentDescription,
+ // Exception here, we use Color.White instead of ElementTheme.colors.iconOnSolidPrimary
+ tint = if (canSendMessage) Color.White else ElementTheme.colors.iconDisabled
+ )
+ }
+ }
+}
+
+@PreviewsDayNight
+@Composable
+internal fun SendButtonPreview() = ElementPreview {
+ val normalMode = MessageComposerMode.Normal("")
+ val editMode = MessageComposerMode.Edit(null, "", null)
+ Row {
+ SendButton(canSendMessage = true, onClick = {}, composerMode = normalMode)
+ SendButton(canSendMessage = false, onClick = {}, composerMode = normalMode)
+ SendButton(canSendMessage = true, onClick = {}, composerMode = editMode)
+ SendButton(canSendMessage = false, onClick = {}, composerMode = editMode)
+ }
+}
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextFormatting.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextFormatting.kt
new file mode 100644
index 0000000000..2df01c5f3a
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextFormatting.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.components
+
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.utils.CommonDrawables
+import io.element.android.libraries.textcomposer.R
+import io.element.android.libraries.textcomposer.TextComposerLinkDialog
+import io.element.android.wysiwyg.compose.RichTextEditorState
+import io.element.android.wysiwyg.view.models.InlineFormat
+import io.element.android.wysiwyg.view.models.LinkAction
+import kotlinx.coroutines.launch
+import uniffi.wysiwyg_composer.ActionState
+import uniffi.wysiwyg_composer.ComposerAction
+@Composable
+internal fun TextFormatting(
+ state: RichTextEditorState,
+ modifier: Modifier = Modifier,
+) {
+
+ val scrollState = rememberScrollState()
+ val coroutineScope = rememberCoroutineScope()
+
+ fun onInlineFormatClick(inlineFormat: InlineFormat) {
+ coroutineScope.launch {
+ state.toggleInlineFormat(inlineFormat)
+ }
+ }
+
+ fun onToggleListClick(ordered: Boolean) {
+ coroutineScope.launch {
+ state.toggleList(ordered)
+ }
+ }
+
+ fun onIndentClick() {
+ coroutineScope.launch {
+ state.indent()
+ }
+ }
+
+ fun onUnindentClick() {
+ coroutineScope.launch {
+ state.unindent()
+ }
+ }
+
+ fun onCodeBlockClick() {
+ coroutineScope.launch {
+ state.toggleCodeBlock()
+ }
+ }
+
+ fun onQuoteClick() {
+ coroutineScope.launch {
+ state.toggleQuote()
+ }
+ }
+
+ fun onCreateLinkRequest(url: String, text: String) {
+ coroutineScope.launch {
+ state.insertLink(url, text)
+ }
+ }
+
+ fun onSaveLinkRequest(url: String) {
+ coroutineScope.launch {
+ state.setLink(url)
+ }
+ }
+
+ fun onRemoveLinkRequest() {
+ coroutineScope.launch {
+ state.removeLink()
+ }
+ }
+
+ Row(
+ modifier = modifier
+ .horizontalScroll(scrollState),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
+ FormattingOption(
+ state = state.actions[ComposerAction.BOLD].toButtonState(),
+ onClick = { onInlineFormatClick(InlineFormat.Bold) },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_bold),
+ contentDescription = stringResource(R.string.rich_text_editor_format_bold)
+ )
+ FormattingOption(
+ state = state.actions[ComposerAction.ITALIC].toButtonState(),
+ onClick = { onInlineFormatClick(InlineFormat.Italic) },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_italic),
+ contentDescription = stringResource(R.string.rich_text_editor_format_italic)
+ )
+ FormattingOption(
+ state = state.actions[ComposerAction.UNDERLINE].toButtonState(),
+ onClick = { onInlineFormatClick(InlineFormat.Underline) },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_underline),
+ contentDescription = stringResource(R.string.rich_text_editor_format_underline)
+ )
+ FormattingOption(
+ state = state.actions[ComposerAction.STRIKE_THROUGH].toButtonState(),
+ onClick = { onInlineFormatClick(InlineFormat.StrikeThrough) },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_strikethrough),
+ contentDescription = stringResource(R.string.rich_text_editor_format_strikethrough)
+ )
+
+ var linkDialogAction by remember { mutableStateOf(null) }
+
+ linkDialogAction?.let {
+ TextComposerLinkDialog(
+ onDismissRequest = { linkDialogAction = null },
+ onCreateLinkRequest = ::onCreateLinkRequest,
+ onSaveLinkRequest = ::onSaveLinkRequest,
+ onRemoveLinkRequest = ::onRemoveLinkRequest,
+ linkAction = it,
+ )
+ }
+
+ FormattingOption(
+ state = state.actions[ComposerAction.LINK].toButtonState(),
+ onClick = { linkDialogAction = state.linkAction },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_link),
+ contentDescription = stringResource(R.string.rich_text_editor_link)
+ )
+
+ FormattingOption(
+ state = state.actions[ComposerAction.UNORDERED_LIST].toButtonState(),
+ onClick = { onToggleListClick(ordered = false) },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_bullet_list),
+ contentDescription = stringResource(R.string.rich_text_editor_bullet_list)
+ )
+ FormattingOption(
+ state = state.actions[ComposerAction.ORDERED_LIST].toButtonState(),
+ onClick = { onToggleListClick(ordered = true) },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_numbered_list),
+ contentDescription = stringResource(R.string.rich_text_editor_numbered_list)
+ )
+ FormattingOption(
+ state = state.actions[ComposerAction.INDENT].toButtonState(),
+ onClick = { onIndentClick() },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_indent_increase),
+ contentDescription = stringResource(R.string.rich_text_editor_indent)
+ )
+ FormattingOption(
+ state = state.actions[ComposerAction.UNINDENT].toButtonState(),
+ onClick = { onUnindentClick() },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_indent_decrease),
+ contentDescription = stringResource(R.string.rich_text_editor_unindent)
+ )
+ FormattingOption(
+ state = state.actions[ComposerAction.INLINE_CODE].toButtonState(),
+ onClick = { onInlineFormatClick(InlineFormat.InlineCode) },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_inline_code),
+ contentDescription = stringResource(R.string.rich_text_editor_inline_code)
+ )
+ FormattingOption(
+ state = state.actions[ComposerAction.CODE_BLOCK].toButtonState(),
+ onClick = { onCodeBlockClick() },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_code_block),
+ contentDescription = stringResource(R.string.rich_text_editor_code_block)
+ )
+ FormattingOption(
+ state = state.actions[ComposerAction.QUOTE].toButtonState(),
+ onClick = { onQuoteClick() },
+ imageVector = ImageVector.vectorResource(CommonDrawables.ic_quote),
+ contentDescription = stringResource(R.string.rich_text_editor_quote)
+ )
+ }
+}
+
+private fun ActionState?.toButtonState(): FormattingOptionState =
+ when (this) {
+ ActionState.ENABLED -> FormattingOptionState.Default
+ ActionState.REVERSED -> FormattingOptionState.Selected
+ ActionState.DISABLED, null -> FormattingOptionState.Disabled
+ }
+
+@PreviewsDayNight
+@Composable
+internal fun TextFormattingPreview() = ElementPreview {
+ TextFormatting(state = RichTextEditorState())
+}
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextInputRoundedCornerShape.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextInputRoundedCornerShape.kt
new file mode 100644
index 0000000000..81f64e6ebe
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/TextInputRoundedCornerShape.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.components
+
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.text.applyScaleUp
+import io.element.android.libraries.textcomposer.model.MessageComposerMode
+
+@Composable
+internal fun textInputRoundedCornerShape(
+ composerMode: MessageComposerMode,
+): RoundedCornerShape {
+ val roundCornerSmall = 20.dp.applyScaleUp()
+ val roundCornerLarge = 21.dp.applyScaleUp()
+
+ val roundedCornerSize = if (composerMode is MessageComposerMode.Special) {
+ roundCornerSmall
+ } else {
+ roundCornerLarge
+ }
+
+ val roundedCornerSizeState = animateDpAsState(
+ targetValue = roundedCornerSize,
+ animationSpec = tween(
+ durationMillis = 100,
+ ),
+ label = "roundedCornerSizeAnimation"
+ )
+ return RoundedCornerShape(roundedCornerSizeState.value)
+}
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/Message.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/Message.kt
similarity index 92%
rename from libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/Message.kt
rename to libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/Message.kt
index ebc066188a..226adc5e57 100644
--- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/Message.kt
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/Message.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package io.element.android.libraries.textcomposer
+package io.element.android.libraries.textcomposer.model
data class Message(
val html: String?,
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerMode.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt
similarity index 97%
rename from libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerMode.kt
rename to libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt
index 3dbc652aaf..49ce0ddb6e 100644
--- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/MessageComposerMode.kt
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package io.element.android.libraries.textcomposer
+package io.element.android.libraries.textcomposer.model
import android.os.Parcelable
import io.element.android.libraries.matrix.api.core.EventId
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/PressEvent.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/PressEvent.kt
new file mode 100644
index 0000000000..96dff11cad
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/PressEvent.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.model
+
+sealed class PressEvent {
+ data object PressStart: PressEvent()
+ data object Tapped: PressEvent()
+ data object LongPressEnd: PressEvent()
+}
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageState.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageState.kt
new file mode 100644
index 0000000000..d376c4ee70
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageState.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.model
+
+sealed class VoiceMessageState {
+ data object Idle: VoiceMessageState()
+ data object Recording: VoiceMessageState()
+}
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressState.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressState.kt
new file mode 100644
index 0000000000..714581feb1
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressState.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.utils
+
+/**
+ * State of a press gesture.
+ */
+internal sealed class PressState {
+ data class Idle(
+ val lastPress: Pressing?
+ ) : PressState()
+
+ sealed class Pressing : PressState()
+ data object Tapping : Pressing()
+ data object LongPressing : Pressing()
+}
+
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateEffects.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateEffects.kt
new file mode 100644
index 0000000000..aaee6bae0f
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateEffects.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.utils
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+
+/**
+ * React to [PressState] changes.
+ */
+@Composable
+internal fun PressStateEffects(
+ pressState: PressState,
+ onPressStart: () -> Unit = {},
+ onLongPressStart: () -> Unit = {},
+ onTap: () -> Unit = {},
+ onLongPressEnd: () -> Unit = {},
+) {
+ LaunchedEffect(pressState) {
+ when (pressState) {
+ is PressState.Idle ->
+ when (pressState.lastPress) {
+ PressState.Tapping -> onTap()
+ PressState.LongPressing -> onLongPressEnd()
+ null -> {} // Do nothing
+ }
+ is PressState.LongPressing -> onLongPressStart()
+ PressState.Tapping -> onPressStart()
+ }
+ }
+}
+
+
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolder.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolder.kt
new file mode 100644
index 0000000000..7021b8ac46
--- /dev/null
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolder.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.utils
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.platform.LocalViewConfiguration
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.yield
+import timber.log.Timber
+
+@Composable
+internal fun rememberPressState(
+ longPressTimeoutMillis: Long = LocalViewConfiguration.current.longPressTimeoutMillis,
+): PressStateHolder {
+ return remember(longPressTimeoutMillis) {
+ PressStateHolder(longPressTimeoutMillis = longPressTimeoutMillis)
+ }
+}
+
+/**
+ * State machine that keeps track of the pressed state.
+ *
+ * When a press is started, the state will transition through:
+ * [PressState.Idle] -> [PressState.Tapping] -> ...
+ *
+ * If a press is held for a longer time, the state will continue through:
+ * ... -> [PressState.LongPressing] -> ...
+ *
+ * When the press is released the states will then transition back to idle.
+ * ... -> [PressState.Idle]
+ *
+ * Whether a press should be considered a tap or a long press can be determined by
+ * looking at the last press when in the idle state.
+ *
+ * @see [PressStateEffects]
+ * @see [rememberPressState]
+ */
+internal class PressStateHolder(
+ private val longPressTimeoutMillis: Long,
+) : State {
+ private var state: PressState by mutableStateOf(PressState.Idle(lastPress = null))
+
+ override val value: PressState
+ get() = state
+
+ private var longPressTimer: Job? = null
+
+ suspend fun press() = coroutineScope {
+ when (state) {
+ is PressState.Idle -> {
+ state = PressState.Tapping
+ }
+ is PressState.Pressing ->
+ Timber.e("Pointer pressed but it has not been released")
+ }
+
+ longPressTimer = launch {
+ delay(longPressTimeoutMillis)
+ yield()
+
+ if (isActive && state == PressState.Tapping) {
+ state = PressState.LongPressing
+ }
+ }
+ }
+
+ fun release() {
+ longPressTimer?.cancel()
+ longPressTimer = null
+ when (val lastState = state) {
+ is PressState.Pressing ->
+ state = PressState.Idle(lastPress = lastState)
+ is PressState.Idle ->
+ Timber.e("Pointer pressed but it has not been released")
+ }
+ }
+}
+
diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_tick.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_tick.xml
deleted file mode 100644
index dd7863bab8..0000000000
--- a/libraries/textcomposer/impl/src/main/res/drawable/ic_tick.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/textcomposer/impl/src/main/res/values-cs/translations.xml b/libraries/textcomposer/impl/src/main/res/values-cs/translations.xml
index cae45c8cd2..ea3bda3bae 100644
--- a/libraries/textcomposer/impl/src/main/res/values-cs/translations.xml
+++ b/libraries/textcomposer/impl/src/main/res/values-cs/translations.xml
@@ -1,8 +1,11 @@
"Přepnout seznam s odrážkami"
+ "Zavřít možnosti formátování"
"Přepnout blok kódu"
"Zpráva…"
+ "Vytvořit odkaz"
+ "Upravit odkaz"
"Použít tučný text"
"Použít kurzívu"
"Použít přeškrtnutí"
@@ -12,7 +15,10 @@
"Použít formát inline kódu"
"Nastavit odkaz"
"Přepnout číslovaný seznam"
+ "Otevřít možnosti psaní"
"Přepnout citaci"
+ "Odstranit odkaz"
"Zrušit odsazení"
+ "Odkaz"
"Přidat přílohu"
diff --git a/libraries/textcomposer/impl/src/main/res/values-ru/translations.xml b/libraries/textcomposer/impl/src/main/res/values-ru/translations.xml
index 921c3bc9da..ad5fb64f7a 100644
--- a/libraries/textcomposer/impl/src/main/res/values-ru/translations.xml
+++ b/libraries/textcomposer/impl/src/main/res/values-ru/translations.xml
@@ -1,8 +1,11 @@
"Переключить список маркеров"
+ "Закрыть параметры форматирования"
"Переключить блок кода"
"Сообщение"
+ "Создать ссылку"
+ "Редактировать ссылку"
"Применить жирный шрифт"
"Применить курсивный формат"
"Применить формат зачеркивания"
@@ -12,7 +15,10 @@
"Применить встроенный формат кода"
"Установить ссылку"
"Переключить нумерованный список"
+ "Открыть параметры компоновки"
"Переключить цитату"
+ "Удалить ссылку"
"Без отступа"
+ "Ссылка"
"Прикрепить файл"
diff --git a/libraries/textcomposer/impl/src/main/res/values-zh-rTW/translations.xml b/libraries/textcomposer/impl/src/main/res/values-zh-rTW/translations.xml
index 4342c3478e..c299975f82 100644
--- a/libraries/textcomposer/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/libraries/textcomposer/impl/src/main/res/values-zh-rTW/translations.xml
@@ -14,6 +14,7 @@
"設定連結"
"切換數字編號"
"切換引用"
+ "移除連結"
"減少縮排"
"連結"
"新增附件"
diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolderTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolderTest.kt
new file mode 100644
index 0000000000..615692911e
--- /dev/null
+++ b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolderTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.utils
+
+import com.google.common.truth.Truth.assertThat
+import io.element.android.libraries.textcomposer.utils.PressState.Idle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import kotlin.time.Duration.Companion.milliseconds
+
+@OptIn(ExperimentalCoroutinesApi::class) class PressStateHolderTest {
+ companion object {
+ const val LONG_PRESS_TIMEOUT_MILLIS = 1L
+ }
+ @Test
+ fun `it starts in idle state`() = runTest {
+ val stateHolder = createStateHolder()
+ assertThat(stateHolder.value).isEqualTo(Idle(lastPress = null))
+ }
+
+ @Test
+ fun `when press, it moves to tapping state`() = runTest {
+ val stateHolder = createStateHolder()
+ val press = async { stateHolder.press() }
+ advanceTimeBy(1.milliseconds)
+ assertThat(stateHolder.value).isEqualTo(PressState.Tapping)
+ press.await()
+ }
+
+ @Test
+ fun `when release after short delay, it moves through tap states`() = runTest {
+ val stateHolder = createStateHolder()
+ val press = async { stateHolder.press() }
+ advanceTimeBy(1.milliseconds)
+ assertThat(stateHolder.value).isEqualTo(PressState.Tapping)
+ stateHolder.release()
+ advanceTimeBy(1.milliseconds) // wait for the long press timeout which should not be triggered
+ assertThat(stateHolder.value).isEqualTo(Idle(lastPress = PressState.Tapping))
+ press.await()
+ }
+
+ @Test
+ fun `when hold, it moves through long press states`() = runTest {
+ val stateHolder = createStateHolder()
+ val press = async { stateHolder.press() }
+ advanceTimeBy(1.milliseconds)
+ assertThat(stateHolder.value).isEqualTo(PressState.Tapping)
+ advanceTimeBy(1.milliseconds)
+ assertThat(stateHolder.value).isEqualTo(PressState.LongPressing)
+ stateHolder.release()
+ assertThat(stateHolder.value).isEqualTo(Idle(lastPress = PressState.LongPressing))
+ press.await()
+ }
+
+ @Test
+ fun `when release and repress, it doesn't enter long press states`() = runTest {
+ val stateHolder = createStateHolder()
+ val press1 = async { stateHolder.press() }
+ advanceTimeBy(1.milliseconds)
+ assertThat(stateHolder.value).isEqualTo(PressState.Tapping)
+ stateHolder.release()
+ val press2 = async { stateHolder.press() }
+ advanceTimeBy(1.milliseconds)
+ assertThat(stateHolder.value).isEqualTo(PressState.Tapping)
+ press1.await()
+ press2.await()
+ }
+
+ @Test
+ fun `when press twice without releasing, it doesn't throw an error`() = runTest {
+ val stateHolder = createStateHolder()
+ stateHolder.press()
+ stateHolder.press()
+ }
+
+ @Test
+ fun `when release without first pressing, it doesn't throw an error`() = runTest {
+ val stateHolder = createStateHolder()
+ stateHolder.release()
+ }
+
+ @Test
+ fun `when release twice without pressing, it doesn't throw an error `() = runTest {
+ val stateHolder = createStateHolder()
+ stateHolder.press()
+ stateHolder.release()
+ stateHolder.release()
+ }
+
+ private fun createStateHolder() =
+ PressStateHolder(
+ LONG_PRESS_TIMEOUT_MILLIS,
+ )
+}
diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt
index 81780d1c2d..3b87565271 100644
--- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt
+++ b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt
@@ -26,7 +26,6 @@ import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.remember
@@ -138,27 +137,7 @@ fun ElementTheme(
}
}
-/**
- * Can be used to force a composable in dark theme.
- * It will automatically change the system ui colors back to normal when leaving the composition.
- */
-@Composable
-fun ForcedDarkElementTheme(
- lightStatusBar: Boolean = false,
- content: @Composable () -> Unit,
-) {
- val systemUiController = rememberSystemUiController()
- val colorScheme = MaterialTheme.colorScheme
- val wasDarkTheme = !ElementTheme.colors.isLight
- DisposableEffect(Unit) {
- onDispose {
- systemUiController.applyTheme(colorScheme, wasDarkTheme)
- }
- }
- ElementTheme(darkTheme = true, lightStatusBar = lightStatusBar, content = content)
-}
-
-private fun SystemUiController.applyTheme(
+internal fun SystemUiController.applyTheme(
colorScheme: ColorScheme,
darkTheme: Boolean,
) {
diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ForcedDarkElementTheme.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ForcedDarkElementTheme.kt
new file mode 100644
index 0000000000..c10ea74fc9
--- /dev/null
+++ b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ForcedDarkElementTheme.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.libraries.theme
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import com.google.accompanist.systemuicontroller.rememberSystemUiController
+
+/**
+ * Can be used to force a composable in dark theme.
+ * It will automatically change the system ui colors back to normal when leaving the composition.
+ */
+@Composable
+fun ForcedDarkElementTheme(
+ lightStatusBar: Boolean = false,
+ content: @Composable () -> Unit,
+) {
+ val systemUiController = rememberSystemUiController()
+ val colorScheme = MaterialTheme.colorScheme
+ val wasDarkTheme = !ElementTheme.colors.isLight
+ DisposableEffect(Unit) {
+ onDispose {
+ systemUiController.applyTheme(colorScheme, wasDarkTheme)
+ }
+ }
+ ElementTheme(darkTheme = true, lightStatusBar = lightStatusBar, content = content)
+}
diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt
index 3d359594e1..9df1db3505 100644
--- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt
+++ b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt
@@ -91,7 +91,7 @@ internal val materialColorSchemeDark = darkColorScheme(
@Preview
@Composable
-internal fun ColorsSchemePreviewLight() = ColorsSchemePreview(
+internal fun ColorsSchemeLightPreview() = ColorsSchemePreview(
Color.Black,
Color.White,
materialColorSchemeLight,
@@ -99,7 +99,7 @@ internal fun ColorsSchemePreviewLight() = ColorsSchemePreview(
@Preview
@Composable
-internal fun ColorsSchemePreviewDark() = ColorsSchemePreview(
+internal fun ColorsSchemeDarkPreview() = ColorsSchemePreview(
Color.White,
Color.Black,
materialColorSchemeDark,
diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml
index 683a1e6abd..f28095763c 100644
--- a/libraries/ui-strings/src/main/res/values-cs/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml
@@ -1,10 +1,19 @@
+ "Smazat"
"Skrýt heslo"
+ "Pouze zmínky"
+ "Ztišeno"
+ "Pozastavit"
+ "Přehrát"
+ "Hlasování"
+ "Hlasování ukončeno"
"Odeslat soubory"
"Zobrazit heslo"
"Uživatelské menu"
+ "Nahrajte hlasovou zprávu. Dvojitě klepněte a podržte pro záznam. Uvolněním ukončíte nahrávání."
"Přijmout"
+ "Přidat na časovou osu"
"Zpět"
"Zrušit"
"Vybrat fotku"
@@ -31,19 +40,24 @@
"Pozvat přátele do %1$s"
"Pozvat lidi na %1$s"
"Pozvánky"
+ "Vstoupit"
"Zjistit více"
"Odejít"
"Opustit místnost"
+ "Spravovat účet"
+ "Spravovat zařízení"
"Další"
"Ne"
"Teď ne"
"OK"
+ "Otevřít nastavení"
"Otevřít v aplikaci"
"Rychlá odpověď"
"Citovat"
"Reagovat"
"Odstranit"
"Odpovědět"
+ "Odpovědět ve vlákně"
"Nahlásit chybu"
"Nahlásit obsah"
"Zkusit znovu"
@@ -54,6 +68,7 @@
"Odeslat zprávu"
"Sdílet"
"Sdílet odkaz"
+ "Přihlásit se znovu"
"Přeskočit"
"Začít"
"Zahájit chat"
@@ -62,8 +77,10 @@
"Vyfotit"
"Zobrazit zdroj"
"Ano"
+ "Upravit hlasování"
"O aplikaci"
"Zásady používání"
+ "Pokročilá nastavení"
"Analytika"
"Zvuk"
"Bubliny"
@@ -77,11 +94,14 @@
"* %1$s %2$s"
"Šifrování povoleno"
"Chyba"
+ "Všichni"
"Soubor"
"Soubor byl uložen do složky Stažené soubory"
"Přeposlat zprávu"
"GIF"
"Obrázek"
+ "V odpovědi na %1$s"
+ "Nainstalovat APK"
"Tento Matrix identifikátor nelze najít, takže pozvánka nemusí být přijata."
"Opuštění místnosti"
"Odkaz zkopírován do schránky"
@@ -96,20 +116,23 @@
"Heslo"
"Lidé"
"Trvalý odkaz"
+ "Oprávnění"
"Celkový počet hlasů: %1$s"
"Výsledky se zobrazí po skončení hlasování"
"Zásady ochrany osobních údajů"
+ "Reakce"
"Reakce"
"Obnovování…"
"Odpověď na %1$s"
"Nahlásit chybu"
"Zpráva odeslána"
+ "Editor formátovaného textu"
"Název místnosti"
"např. název vašeho projektu"
"Hledat někoho"
"Výsledky hledání"
+ "Zabezpečená záloha"
"Zabezpečení"
- "Vyberte svůj server"
"Odesílání…"
"Server není podporován"
"URL serveru"
@@ -120,7 +143,9 @@
"Úspěch"
"Návrhy"
"Synchronizace"
+ "Text"
"Oznámení třetích stran"
+ "Vlákno"
"Téma"
"O čem je tato místnost?"
"Nelze dešifrovat"
@@ -132,7 +157,10 @@
"Ověření zrušeno"
"Ověření dokončeno"
"Video"
+ "Hlasová zpráva"
"Čekání…"
+ "Opravdu chcete ukončit toto hlasování?"
+ "Hlasování: %1$s"
"Potvrzení"
"Upozornění"
"Aktivity"
@@ -168,7 +196,6 @@
- "%d hlasů"
"Zatřeste zařízením pro nahlášení chyby"
- "Zdá se, že jste frustrovaně třásli telefonem. Chcete otevřít obrazovku pro nahlášení chyby?"
"Tato zpráva bude nahlášena správci vašeho domovského serveru. Nebude si moci přečíst žádné šifrované zprávy."
"Důvod nahlášení tohoto obsahu"
"Toto je začátek %1$s."
@@ -206,7 +233,6 @@ Pokud budete pokračovat, některá nastavení se mohou změnit."
"Systémová oznámení byla vypnuta"
"Oznámení"
"Zaškrtněte, pokud chcete skrýt všechny aktuální a budoucí zprávy od tohoto uživatele"
- "Účet a zařízení"
"Sdílet polohu"
"Sdílet moji polohu"
"Otevřít v Mapách Apple"
@@ -216,7 +242,6 @@ Pokud budete pokračovat, některá nastavení se mohou změnit."
"Poloha"
"Rageshake"
"Práh detekce"
- "Obecné"
"Verze: %1$s (%2$s)"
"en"
"Chyba"
diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml
index d4cc0a5186..dd84815254 100644
--- a/libraries/ui-strings/src/main/res/values-de/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-de/translations.xml
@@ -9,6 +9,7 @@
"Passwort anzeigen"
"Benutzermenü"
"Akzeptieren"
+ "Zur Zeitleiste hinzufügen"
"Zurück"
"Abbrechen"
"Foto auswählen"
@@ -35,6 +36,7 @@
"Freunde einladen %1$s"
"Lade Personen in %1$s ein"
"Einladungen"
+ "Beitreten"
"Mehr erfahren"
"Verlassen"
"Raum verlassen"
@@ -44,6 +46,7 @@
"Nein"
"Nicht jetzt"
"OK"
+ "Einstellungen öffnen"
"Öffnen mit"
"Schnelle Antwort"
"Zitat"
@@ -69,6 +72,7 @@
"Foto machen"
"Quelle anzeigen"
"Ja"
+ "Umfrage bearbeiten"
"Über"
"Nutzungsrichtlinie"
"Erweiterte Einstellungen"
@@ -91,6 +95,7 @@
"GIF"
"Bild"
"Als Antwort auf %1$s"
+ "APK installieren"
"Diese Matrix-ID kann nicht gefunden werden, daher wird die Einladung möglicherweise nicht empfangen."
"Raum verlassen"
"Link in die Zwischenablage kopiert"
@@ -105,6 +110,7 @@
"Passwort"
"Personen"
"Permalink"
+ "Erlaubnis"
"Stimmen insgesamt: %1$s"
"Die Ergebnisse werden nach Ende der Umfrage angezeigt"
"Datenschutzerklärung"
@@ -120,7 +126,6 @@
"Nach jemandem suchen"
"Suchergebnisse"
"Sicherheit"
- "Wähle deinen Server aus"
"Wird gesendet…"
"Server wird nicht unterstützt"
"Server-URL"
@@ -146,6 +151,8 @@
"Verifizierung abgeschlossen"
"Video"
"Warten…"
+ "Bist du sicher, dass du diese Umfrage beenden möchtest?"
+ "Umfrage: %1$s"
"Bestätigung"
"Warnung"
"Aktivitäten"
@@ -179,7 +186,6 @@
- "%d Stimmen"
"Schüttel heftig zum Melden von Fehlern"
- "Du scheinst das Telefon aus Frustration zu schütteln. Möchtest du den Bildschirm für den Fehlerbericht öffnen?"
"Diese Meldung wird an den Administrator deines Homeservers weitergeleitet. Dieser kann keine verschlüsselten Nachrichten lesen."
"Grund für die Meldung dieses Inhalts"
"Dies ist der Anfang von %1$s."
@@ -213,7 +219,6 @@
"Systembenachrichtigungen deaktiviert"
"Benachrichtigungen"
"Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest"
- "Konto und Geräte"
"Standort teilen"
"Meinen Standort teilen"
"In Apple Maps öffnen"
@@ -223,7 +228,6 @@
"Standort"
"Rageshake"
"Erkennungsschwelle"
- "Allgemein"
"Version: %1$s (%2$s)"
"en"
"Fehler"
diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml
index fa8a80f953..8bc500d7fe 100644
--- a/libraries/ui-strings/src/main/res/values-es/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-es/translations.xml
@@ -77,7 +77,6 @@
"Informe enviado"
"Buscar a alguien"
"Seguridad"
- "Selecciona tu servidor"
"Enviando…"
"Servidor no compatible"
"Dirección del servidor"
@@ -117,7 +116,6 @@
- "%1$d miembros"
"Agitar con fuerza para informar de un error"
- "Parece que sacudes el teléfono con frustración. ¿Quieres abrir la pantalla de informe de errores?"
"Este mensaje se notificará al administrador de su homeserver. No podrán leer ningún mensaje cifrado."
"Motivo para denunciar este contenido"
"Este es el principio de %1$s."
@@ -126,7 +124,6 @@
"Marque si quieres ocultar todos los mensajes actuales y futuros de este usuario"
"Agitar con fuerza"
"Umbral de detección"
- "General"
"Versión: %1$s (%2$s)"
"es"
"Error"
diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml
index ae6e112547..f0de166ff1 100644
--- a/libraries/ui-strings/src/main/res/values-fr/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml
@@ -9,6 +9,7 @@
"Afficher le mot de passe"
"Menu utilisateur"
"Accepter"
+ "Ajouter à la discussion"
"Retour"
"Annuler"
"Choisissez une photo"
@@ -35,6 +36,7 @@
"Inviter des amis à %1$s"
"Invitez des personnes à %1$s"
"Invitations"
+ "Rejoindre"
"En savoir plus"
"Quitter"
"Quitter le salon"
@@ -44,6 +46,7 @@
"Non"
"Pas maintenant"
"OK"
+ "Ouvrir les paramètres"
"Ouvrir avec"
"Réponse rapide"
"Citer"
@@ -69,6 +72,7 @@
"Prendre une photo"
"Afficher la source"
"Oui"
+ "Modifier le sondage"
"À propos"
"Politique d’utilisation acceptable"
"Paramètres avancés"
@@ -91,6 +95,7 @@
"GIF"
"Image"
"En réponse à %1$s"
+ "Installer l’APK"
"Cet identifiant Matrix est introuvable, il est donc possible que l’invitation ne soit pas reçue."
"Quitter le salon…"
"Lien copié dans le presse-papiers"
@@ -105,6 +110,7 @@
"Mot de passe"
"Personnes"
"Permalien"
+ "Autorisation"
"Nombre total de votes : %1$s"
"Les résultats s’afficheront une fois le sondage terminé"
"Politique de confidentialité"
@@ -120,7 +126,6 @@
"Rechercher quelqu’un"
"Résultats de la recherche"
"Sécurité"
- "Sélectionnez votre serveur"
"Envoi en cours…"
"Serveur non pris en charge"
"URL du serveur"
@@ -146,6 +151,8 @@
"Vérification terminée"
"Vidéo"
"En attente…"
+ "Êtes-vous sûr de vouloir mettre fin à ce sondage ?"
+ "Sondage : %1$s"
"Confirmation"
"Attention"
"Activités"
@@ -179,7 +186,6 @@
- "%d votes"
"Rageshake pour signaler un problème"
- "Vous semblez secouer le téléphone avec frustration. Voulez-vous ouvrir le formulaire de rapport de problème ?"
"Ce message sera signalé à l’administrateur de votre serveur d’accueil. Il ne pourra lire aucun message chiffré."
"Raison du signalement de ce contenu"
"Ceci est le début de %1$s."
@@ -217,7 +223,6 @@ Si vous continuez, il est possible que certains de vos paramètres soient modifi
"Les notifications du système sont désactivées"
"Notifications"
"Cochez si vous souhaitez masquer tous les messages actuels et futurs de cet utilisateur."
- "Compte et sessions"
"Partage de position"
"Partager ma position"
"Ouvrir dans Apple Maps"
@@ -227,7 +232,6 @@ Si vous continuez, il est possible que certains de vos paramètres soient modifi
"Position"
"Rageshake"
"Seuil de détection"
- "Général"
"Version : %1$s ( %2$s )"
"Ang."
"Erreur"
diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml
index b15d570dfc..303ab94c50 100644
--- a/libraries/ui-strings/src/main/res/values-it/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-it/translations.xml
@@ -77,7 +77,6 @@
"Segnalazione inviata"
"Cerca qualcuno"
"Sicurezza"
- "Seleziona il tuo server"
"Invio in corso…"
"Server non supportato"
"URL del server"
@@ -117,7 +116,6 @@
- "%1$d membri"
"Scuoti per segnalare un problema"
- "Sembra che tu stia scuotendo il telefono per la frustrazione. Vuoi aprire la schermata di segnalazione dei problemi?"
"Questo messaggio verrà segnalato all\'amministratore dell\'homeserver. Questi non sarà in grado di leggere i messaggi criptati."
"Motivo della segnalazione di questo contenuto"
"Questo è l\'inizio di %1$s."
@@ -126,7 +124,6 @@
"Seleziona se vuoi nascondere tutti i messaggi attuali e futuri di questo utente"
"Rageshake"
"Soglia di rilevamento"
- "Generali"
"Versione: %1$s (%2$s)"
"it"
"Errore"
diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml
index 91de1b2c20..566133a6a5 100644
--- a/libraries/ui-strings/src/main/res/values-ro/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml
@@ -111,7 +111,6 @@
"Căutați pe cineva"
"Rezultatele căutării"
"Securitate"
- "Selectați serverul"
"Se trimite…"
"Serverul nu este compatibil"
"Adresa URL a serverului"
@@ -171,7 +170,6 @@
- "%d voturi"
"Rageshake pentru a raporta erori"
- "Se pare că scuturați telefonul de frustrare. Doriți să deschdeți ecranul de raportare a unei erori?"
"Acest mesaj va fi raportat administratorilor homeserver-ului tau. Ei nu vor putea citi niciun mesaj criptat."
"Motivul raportării acestui conținut"
"Acesta este începutul conversației %1$s."
@@ -209,7 +207,6 @@ Dacă continuați, unele dintre setările dumneavoastră pot fi modificate.""Notificările de sistem sunt dezactivate"
"Notificări"
"Confirmați că doriți să ascundeți toate mesajele curente și viitoare de la acest utilizator"
- "Cont și dispozitive"
"Partajați locația"
"Distribuiți locația mea"
"Deschideți în Apple Maps"
@@ -219,7 +216,6 @@ Dacă continuați, unele dintre setările dumneavoastră pot fi modificate.""Locație"
"Rageshake"
"Prag de detecție"
- "General"
"Versiunea: %1$s (%2$s)"
"ro"
"Eroare"
diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml
index 388e6b8d62..7401685482 100644
--- a/libraries/ui-strings/src/main/res/values-ru/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml
@@ -1,10 +1,15 @@
"Скрыть пароль"
+ "Только упоминания"
+ "Звук отключен"
+ "Опрос"
+ "Опрос завершен"
"Отправить файлы"
"Показать пароль"
"Меню пользователя"
"Разрешить"
+ "Добавить в хронологию"
"Назад"
"Отмена"
"Выбрать фото"
@@ -23,6 +28,7 @@
"Готово"
"Редактировать"
"Включить"
+ "Завершить опрос"
"Забыли пароль?"
"Переслать"
"Пригласить"
@@ -30,19 +36,24 @@
"Пригласить друзей в %1$s"
"Пригласите пользователей в %1$s"
"Приглашения"
+ "Присоединиться"
"Подробнее"
"Выйти"
"Покинуть комнату"
+ "Настройки аккаунта"
+ "Управление устройствами"
"Далее"
"Нет"
"Не сейчас"
"Ок"
+ "Открыть настройки"
"Открыть с помощью"
"Быстрый ответ"
"Цитата"
"Реакция"
"Удалить"
"Ответить"
+ "Ответить в теме"
"Сообщить об ошибке"
"Пожаловаться на содержание"
"Повторить"
@@ -53,6 +64,7 @@
"Отправить сообщение"
"Поделиться"
"Поделиться ссылкой"
+ "Повторите вход"
"Пропустить"
"Начать"
"Начать чат "
@@ -61,8 +73,10 @@
"Сделать фото"
"Показать источник"
"Да"
+ "Редактировать опрос"
"О приложении"
"Политика допустимого использования"
+ "Дополнительные параметры"
"Аналитика"
"Аудио"
"Пузыри"
@@ -81,6 +95,8 @@
"Переслать сообщение"
"GIF"
"Изображения"
+ "В ответ на %1$s"
+ "Установить APK"
"Идентификатор Matrix ID не найден, приглашение может быть не получено."
"Покинуть комнату"
"Ссылка скопирована в буфер обмена"
@@ -95,20 +111,23 @@
"Пароль"
"Пользователи"
"Постоянная ссылка"
+ "Разрешение"
"Всего голосов: %1$s"
"Результаты будут показаны после завершения опроса"
"Политика конфиденциальности"
+ "Реакция"
"Реакции"
"Обновление…"
"Отвечает на %1$s"
"Сообщить об ошибке"
"Отчет отправлен"
+ "Редактор форматированного текста"
"Название комнаты"
"например, название вашего проекта"
"Поиск человека"
"Результаты поиска"
+ "Безопасное резервное копирование"
"Безопасность"
- "Выберите свой сервер"
"Отправка…"
"Сервер не поддерживается"
"Адрес сервера"
@@ -119,7 +138,9 @@
"Успешно"
"Рекомендации"
"Синхронизация"
+ "Текст"
"Уведомление о третьей стороне"
+ "Обсуждение"
"Тема"
"О чем эта комната?"
"Невозможно расшифровать"
@@ -131,7 +152,10 @@
"Проверка отменена"
"Проверка завершена"
"Видео"
+ "Голосовое сообщение"
"Ожидание…"
+ "Вы действительно хотите завершить данный опрос?"
+ "Опрос: %1$s"
"Подтверждение"
"Предупреждение"
"Деятельность"
@@ -167,7 +191,6 @@
- "%d голосов"
"Rageshake сообщит об ошибке"
- "Кажется, вы трясли телефон. Хотите открыть экран отчета об ошибке?"
"Это сообщение будет передано администратору вашего домашнего сервера. Они не смогут прочитать зашифрованные сообщения."
"Причина, по которой вы пожаловались на этот контент"
"Это начало %1$s."
@@ -205,17 +228,15 @@
"Системные уведомления выключены"
"Уведомления"
"Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя"
- "Учетная запись и устройства"
"Поделиться местоположением"
"Поделиться моим местоположением"
"Открыть в Apple Maps"
- "Открыть в Google Картах"
+ "Открыть в Google Maps"
"Открыть в OpenStreetMap"
"Поделиться этим местоположением"
"Местоположение"
"Rageshake"
"Порог обнаружения"
- "Основные"
"Версия: %1$s (%2$s)"
"en"
"Ошибка"
diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml
index c82ce0ef12..536db8c501 100644
--- a/libraries/ui-strings/src/main/res/values-sk/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml
@@ -1,14 +1,19 @@
+ "Vymazať"
"Skryť heslo"
"Iba zmienky"
"Stlmené"
+ "Pozastaviť"
+ "Prehrať"
"Anketa"
"Ukončená anketa"
"Odoslať súbory"
"Zobraziť heslo"
"Používateľské menu"
+ "Nahrávanie hlasovej správy. Nahrávanie spustíte dvojitým ťuknutím a podržaním. Uvoľnením nahrávanie ukončíte."
"Prijať"
+ "Pridať na časovú os"
"Späť"
"Zrušiť"
"Vybrať fotku"
@@ -35,6 +40,7 @@
"Pozvať priateľov do %1$s"
"Pozvať ľudí do %1$s"
"Pozvánky"
+ "Pripojiť sa"
"Zistiť viac"
"Opustiť"
"Opustiť miestnosť"
@@ -44,6 +50,7 @@
"Nie"
"Teraz nie"
"OK"
+ "Otvoriť nastavenia"
"Otvoriť pomocou"
"Rýchla odpoveď"
"Citovať"
@@ -61,6 +68,7 @@
"Odoslať správu"
"Zdieľať"
"Zdieľať odkaz"
+ "Prihláste sa znova"
"Preskočiť"
"Spustiť"
"Začať konverzáciu"
@@ -69,6 +77,7 @@
"Urobiť fotku"
"Zobraziť zdroj"
"Áno"
+ "Upraviť anketu"
"O aplikácii"
"Zásady prijateľného používania"
"Pokročilé nastavenia"
@@ -85,18 +94,20 @@
"* %1$s %2$s"
"Šifrovanie zapnuté"
"Chyba"
+ "Všetci"
"Súbor"
"Súbor bol uložený do priečinka Stiahnuté súbory"
"Preposlať správu"
"GIF"
"Obrázok"
"V odpovedi na %1$s"
+ "Inštalovať APK"
"Toto Matrix ID sa nedá nájsť, takže pozvánka nemusí byť prijatá."
"Opustenie miestnosti"
"Odkaz bol skopírovaný do schránky"
"Načítava sa…"
"Správa"
- "Rozloženie správy"
+ "Štýl správ"
"Správa odstránená"
"Moderné"
"Stlmiť"
@@ -105,6 +116,7 @@
"Heslo"
"Ľudia"
"Trvalý odkaz"
+ "Povolenie"
"Celkový počet hlasov: %1$s"
"Výsledky sa zobrazia po ukončení ankety"
"Zásady ochrany osobných údajov"
@@ -119,8 +131,8 @@
"napr. názov vášho projektu"
"Vyhľadať niekoho"
"Výsledky hľadania"
+ "Bezpečné zálohovanie"
"Bezpečnosť"
- "Vyberte svoj server"
"Odosiela sa…"
"Server nie je podporovaný"
"URL adresa servera"
@@ -145,7 +157,10 @@
"Overovanie zrušené"
"Overovanie je dokončené"
"Video"
+ "Hlasová správa"
"Čaká sa…"
+ "Ste si istí, že chcete ukončiť túto anketu?"
+ "Anketa: %1$s"
"Potvrdenie"
"Upozornenie"
"Aktivity"
@@ -181,7 +196,6 @@
- "%d hlasov"
"Zúrivo potriasť pre nahlásenie chyby"
- "Zdá sa, že zúrivo trasiete telefónom. Chcete otvoriť obrazovku s nahlásením chýb?"
"Táto správa bude nahlásená správcovi vášho domovského servera. Nebude môcť prečítať žiadne šifrované správy."
"Dôvod nahlásenia tohto obsahu"
"Toto je začiatok %1$s."
@@ -219,7 +233,6 @@ Ak budete pokračovať, niektoré z vašich nastavení sa môžu zmeniť.""Systémové oznámenia sú vypnuté"
"Oznámenia"
"Označte, či chcete skryť všetky aktuálne a budúce správy od tohto používateľa"
- "Účet a zariadenia"
"Zdieľať polohu"
"Zdieľať moju polohu"
"Otvoriť v Apple Maps"
@@ -229,7 +242,6 @@ Ak budete pokračovať, niektoré z vašich nastavení sa môžu zmeniť.""Poloha"
"Zúrivé potrasenie"
"Prahová hodnota detekcie"
- "Všeobecné"
"Verzia: %1$s (%2$s)"
"sk"
"Chyba"
diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml
index 0ace8beb22..71aa40e8fb 100644
--- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml
@@ -1,10 +1,18 @@
+ "刪除"
"隱藏密碼"
+ "僅限提及"
+ "已關閉通知"
+ "暫停"
+ "播放"
+ "投票"
+ "投票已結束"
"傳送檔案"
"顯示密碼"
"使用者選單"
"接受"
+ "新增至時間軸"
"返回"
"取消"
"選擇照片"
@@ -21,28 +29,33 @@
"完成"
"編輯"
"啟用"
+ "結束投票"
"忘記密碼?"
"轉寄"
"邀請"
"邀請朋友"
- "邀請朋友使用%1$s"
- "邀請夥伴使用%1$s"
+ "邀請朋友使用 %1$s"
+ "邀請夥伴使用 %1$s"
"邀請"
+ "加入"
"了解更多"
"離開"
"離開聊天室"
"管理帳號"
"管理裝置"
- "下一個"
+ "下一步"
"否"
"以後再說"
"OK"
+ "開啟設定"
"用其他方式開啟"
"快速回覆"
"引用"
"回應"
"移除"
"回覆"
+ "在討論串中回覆"
+ "回報程式錯誤"
"檢舉內容"
"再試一次"
"再次嘗試解密"
@@ -52,7 +65,7 @@
"傳送訊息"
"分享"
"分享連結"
- "跳過"
+ "略過"
"開始"
"開始聊天"
"開始驗證"
@@ -60,7 +73,10 @@
"拍照"
"檢視原始碼"
"是"
+ "編輯投票"
"關於"
+ "可接受使用政策"
+ "進階設定"
"分析"
"音訊"
"著作權"
@@ -70,6 +86,7 @@
"開發者選項"
"(已編輯)"
"編輯中"
+ "* %1$s %2$s"
"已啟用加密"
"錯誤"
"檔案"
@@ -77,6 +94,8 @@
"訊息轉寄"
"GIF"
"圖片"
+ "回覆 %1$s"
+ "安裝 APK"
"找不到此 Matrix ID,因此可能沒有人會收到邀請。"
"正在離開聊天室"
"連結已複製到剪貼簿"
@@ -91,32 +110,44 @@
"密碼"
"夥伴"
"永久連結"
+ "權限"
+ "總票數:%1$s"
"結果將在投票結束後公佈"
"隱私權政策"
+ "回應"
"回應"
- "重新整理…"
+ "重新整理中…"
"正在回覆%1$s"
+ "回報程式錯誤"
+ "格式化文字編輯器"
"聊天室名稱"
"範例:您的計畫名稱"
"搜尋結果"
- "選擇您的伺服器"
+ "安全性"
"傳送中…"
+ "伺服器不支援"
"伺服器 URL"
"設定"
"貼圖"
"成功"
"建議"
"同步中"
+ "文字"
+ "討論串"
"主題"
"無法解密"
"無法發送邀請給一或多個使用者。"
"無法發送邀請"
"開啟通知"
+ "不支援的事件"
"使用者名稱"
"驗證已取消"
"驗證完成"
"影片"
+ "語音訊息"
"等待中…"
+ "您確定要結束這項投票嗎?"
+ "投票:%1$s"
"確認"
"警告"
"活動"
@@ -131,7 +162,10 @@
"%1$s無法載入地圖。請稍後再試。"
"無法載入訊息"
"%1$s無法取得您的位置。請稍後再試。"
+ "%1$s 沒有權限存取您的位置。您可以到設定中開啟權限。"
+ "%1$s 沒有權限存取您的位置。請在下方開啟權限。"
"有些訊息尚未傳送"
+ "嘿,來 %1$s 和我聊天:%2$s"
"您確定要離開聊天室嗎?這裡只有您一個人。如果您離開了,包含您在內的所有人都無法再進入此聊天室。"
"您確定要離開聊天室嗎?此聊天室不是公開的,如果沒有收到邀請,您無法重新加入。"
"您確定要離開聊天室嗎?"
@@ -148,6 +182,9 @@
"無法上傳媒體檔案,請稍後再試。"
"其他設定"
"私訊"
+ "更新通知設定時發生錯誤。"
+ "所有訊息"
+ "僅限提及與關鍵字"
"在這個裝置上開啟通知"
"群組聊天"
"提及"
@@ -162,13 +199,12 @@
"在開放街圖(OpenStreetMap) 中開啟"
"分享這個位置"
"位置"
- "一般"
"版本:%1$s(%2$s)"
"zh-tw"
"錯誤"
"成功"
- "分享匿名的使用數據以協助我們釐清問題"
- "您可以到 %1$s 閱讀我們的條款。"
+ "分享匿名的使用數據以協助我們釐清問題。"
+ "您可以到%1$s閱讀我們的條款。"
"這裡"
"封鎖使用者"
diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml
index 72a31d46b0..b9a6460bec 100644
--- a/libraries/ui-strings/src/main/res/values/localazy.xml
+++ b/libraries/ui-strings/src/main/res/values/localazy.xml
@@ -1,14 +1,19 @@
+ "Delete"
"Hide password"
"Mentions only"
"Muted"
+ "Pause"
+ "Play"
"Poll"
"Ended poll"
"Send files"
"Show password"
"User menu"
+ "Record voice message. Double tap and hold to record. Release to end recording."
"Accept"
+ "Add to timeline"
"Back"
"Cancel"
"Choose photo"
@@ -35,6 +40,7 @@
"Invite friends to %1$s"
"Invite people to %1$s"
"Invites"
+ "Join"
"Learn more"
"Leave"
"Leave room"
@@ -44,6 +50,7 @@
"No"
"Not now"
"OK"
+ "Open settings"
"Open with"
"Quick reply"
"Quote"
@@ -52,7 +59,7 @@
"Reply"
"Reply in thread"
"Report bug"
- "Report Content"
+ "Report content"
"Retry"
"Retry decryption"
"Save"
@@ -61,14 +68,16 @@
"Send message"
"Share"
"Share link"
+ "Sign in again"
"Skip"
"Start"
"Start chat"
"Start verification"
"Tap to load map"
"Take photo"
- "View Source"
+ "View source"
"Yes"
+ "Edit poll"
"About"
"Acceptable use policy"
"Advanced settings"
@@ -85,12 +94,14 @@
"* %1$s %2$s"
"Encryption enabled"
"Error"
+ "Everyone"
"File"
"File saved to Downloads"
"Forward message"
"GIF"
"Image"
"In reply to %1$s"
+ "Install APK"
"This Matrix ID can\'t be found, so the invite might not be received."
"Leaving room"
"Link copied to clipboard"
@@ -105,6 +116,7 @@
"Password"
"People"
"Permalink"
+ "Permission"
"Total votes: %1$s"
"Results will show after the poll has ended"
"Privacy policy"
@@ -119,8 +131,8 @@
"e.g. your project name"
"Search for someone"
"Search results"
+ "Secure backup"
"Security"
- "Select your server"
"Sending…"
"Server not supported"
"Server URL"
@@ -145,7 +157,10 @@
"Verification cancelled"
"Verification complete"
"Video"
+ "Voice message"
"Waiting…"
+ "Are you sure you want to end this poll?"
+ "Poll: %1$s"
"Confirmation"
"Warning"
"Activities"
@@ -179,7 +194,6 @@
- "%d votes"
"Rageshake to report bug"
- "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?"
"This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages."
"Reason for reporting this content"
"This is the beginning of %1$s."
@@ -215,7 +229,6 @@ If you proceed, some of your settings may change."
"System notifications turned off"
"Notifications"
"Check if you want to hide all current and future messages from this user"
- "Account and devices"
"Share location"
"Share my location"
"Open in Apple Maps"
@@ -225,7 +238,6 @@ If you proceed, some of your settings may change."
"Location"
"Rageshake"
"Detection threshold"
- "General"
"Version: %1$s (%2$s)"
"en"
"en"
diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt
index 9f996aacdb..94f40c788a 100644
--- a/plugins/src/main/kotlin/Versions.kt
+++ b/plugins/src/main/kotlin/Versions.kt
@@ -56,7 +56,7 @@ private const val versionMinor = 2
// Note: even values are reserved for regular release, odd values for hotfix release.
// When creating a hotfix, you should decrease the value, since the current value
// is the value for the next regular release.
-private const val versionPatch = 1
+private const val versionPatch = 5
object Versions {
val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch
diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt
index ec4d7cf9f2..7eb24be57d 100644
--- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt
+++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt
@@ -40,7 +40,7 @@ class LoginScreen(private val authenticationService: MatrixAuthenticationService
}
LaunchedEffect(Unit) {
- authenticationService.setHomeserver(defaultAccountProvider.title)
+ authenticationService.setHomeserver(defaultAccountProvider.url)
}
val state = presenter.present()
diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt
index d0309ebec7..c95a44d8ea 100644
--- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt
+++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt
@@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService
import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
+import io.element.android.libraries.sessionstorage.api.LoggedInState
import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore
import io.element.android.libraries.theme.ElementTheme
import io.element.android.services.toolbox.impl.systemclock.DefaultSystemClock
@@ -64,8 +65,8 @@ class MainActivity : ComponentActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
ElementTheme {
- val isLoggedIn by matrixAuthenticationService.isLoggedIn().collectAsState(initial = false)
- Content(isLoggedIn = isLoggedIn, modifier = Modifier.fillMaxSize())
+ val loggedInState by matrixAuthenticationService.loggedInStateFlow().collectAsState(initial = LoggedInState.NotLoggedIn)
+ Content(isLoggedIn = loggedInState is LoggedInState.LoggedIn, modifier = Modifier.fillMaxSize())
}
}
diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt
index 425888f003..0c43619f39 100644
--- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt
+++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt
@@ -31,7 +31,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.dateformatter.impl.DateFormatters
import io.element.android.libraries.dateformatter.impl.DefaultLastMessageTimestampFormatter
import io.element.android.libraries.dateformatter.impl.LocalDateTimeProvider
-import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
+import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.eventformatter.impl.DefaultRoomLastMessageFormatter
import io.element.android.libraries.eventformatter.impl.ProfileChangeContentFormatter
import io.element.android.libraries.eventformatter.impl.RoomMembershipContentFormatter
diff --git a/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt b/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt
index e5ed19491f..80ac8d30fc 100644
--- a/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt
+++ b/services/apperror/impl/src/main/kotlin/io/element/android/services/apperror/impl/AppErrorView.kt
@@ -18,7 +18,7 @@ package io.element.android.services.apperror.impl
import androidx.compose.runtime.Composable
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
-import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.services.apperror.api.AppErrorState
import io.element.android.services.apperror.api.aAppErrorState
@@ -37,7 +37,7 @@ fun AppErrorView(
}
@Composable
-fun AppErrorViewContent(
+private fun AppErrorViewContent(
title: String,
body: String,
onDismiss: () -> Unit = { },
@@ -49,7 +49,7 @@ fun AppErrorViewContent(
)
}
-@DayNightPreviews
+@PreviewsDayNight
@Composable
internal fun AppErrorViewPreview() = ElementPreview {
AppErrorView(
diff --git a/services/appnavstate/impl/src/main/kotlin/io/element/android/services/appnavstate/impl/DefaultAppNavigationStateService.kt b/services/appnavstate/impl/src/main/kotlin/io/element/android/services/appnavstate/impl/DefaultAppNavigationStateService.kt
index 9360ce93ec..b39360c698 100644
--- a/services/appnavstate/impl/src/main/kotlin/io/element/android/services/appnavstate/impl/DefaultAppNavigationStateService.kt
+++ b/services/appnavstate/impl/src/main/kotlin/io/element/android/services/appnavstate/impl/DefaultAppNavigationStateService.kt
@@ -25,9 +25,9 @@ import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.SpaceId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.services.appnavstate.api.AppForegroundStateService
-import io.element.android.services.appnavstate.api.NavigationState
-import io.element.android.services.appnavstate.api.AppNavigationStateService
import io.element.android.services.appnavstate.api.AppNavigationState
+import io.element.android.services.appnavstate.api.AppNavigationStateService
+import io.element.android.services.appnavstate.api.NavigationState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -50,16 +50,15 @@ class DefaultAppNavigationStateService @Inject constructor(
private val state = MutableStateFlow(
AppNavigationState(
- navigationState = NavigationState.Root,
- isInForeground = true,
- )
+ navigationState = NavigationState.Root,
+ isInForeground = true,
+ )
)
override val appNavigationState: StateFlow = state
init {
coroutineScope.launch {
appForegroundStateService.start()
-
appForegroundStateService.isInForeground.collect { isInForeground ->
state.getAndUpdate { it.copy(isInForeground = isInForeground) }
}
@@ -83,7 +82,7 @@ class DefaultAppNavigationStateService @Inject constructor(
val currentValue = state.value.navigationState
Timber.tag(loggerTag.value).d("Navigating to space $spaceId. Current state: $currentValue")
val newValue: NavigationState.Space = when (currentValue) {
- NavigationState.Root -> error("onNavigateToSession() must be called first")
+ NavigationState.Root -> return logError("onNavigateToSession()")
is NavigationState.Session -> NavigationState.Space(owner, spaceId, currentValue)
is NavigationState.Space -> NavigationState.Space(owner, spaceId, currentValue.parentSession)
is NavigationState.Room -> NavigationState.Space(owner, spaceId, currentValue.parentSpace.parentSession)
@@ -96,8 +95,8 @@ class DefaultAppNavigationStateService @Inject constructor(
val currentValue = state.value.navigationState
Timber.tag(loggerTag.value).d("Navigating to room $roomId. Current state: $currentValue")
val newValue: NavigationState.Room = when (currentValue) {
- NavigationState.Root -> error("onNavigateToSession() must be called first")
- is NavigationState.Session -> error("onNavigateToSpace() must be called first")
+ NavigationState.Root -> return logError("onNavigateToSession()")
+ is NavigationState.Session -> return logError("onNavigateToSpace()")
is NavigationState.Space -> NavigationState.Room(owner, roomId, currentValue)
is NavigationState.Room -> NavigationState.Room(owner, roomId, currentValue.parentSpace)
is NavigationState.Thread -> NavigationState.Room(owner, roomId, currentValue.parentRoom.parentSpace)
@@ -109,9 +108,9 @@ class DefaultAppNavigationStateService @Inject constructor(
val currentValue = state.value.navigationState
Timber.tag(loggerTag.value).d("Navigating to thread $threadId. Current state: $currentValue")
val newValue: NavigationState.Thread = when (currentValue) {
- NavigationState.Root -> error("onNavigateToSession() must be called first")
- is NavigationState.Session -> error("onNavigateToSpace() must be called first")
- is NavigationState.Space -> error("onNavigateToRoom() must be called first")
+ NavigationState.Root -> return logError("onNavigateToSession()")
+ is NavigationState.Session -> return logError("onNavigateToSpace()")
+ is NavigationState.Space -> return logError("onNavigateToRoom()")
is NavigationState.Room -> NavigationState.Thread(owner, threadId, currentValue)
is NavigationState.Thread -> NavigationState.Thread(owner, threadId, currentValue.parentRoom)
}
@@ -123,10 +122,10 @@ class DefaultAppNavigationStateService @Inject constructor(
Timber.tag(loggerTag.value).d("Leaving thread. Current state: $currentValue")
if (!currentValue.assertOwner(owner)) return
val newValue: NavigationState.Room = when (currentValue) {
- NavigationState.Root -> error("onNavigateToSession() must be called first")
- is NavigationState.Session -> error("onNavigateToSpace() must be called first")
- is NavigationState.Space -> error("onNavigateToRoom() must be called first")
- is NavigationState.Room -> error("onNavigateToThread() must be called first")
+ NavigationState.Root -> return logError("onNavigateToSession()")
+ is NavigationState.Session -> return logError("onNavigateToSpace()")
+ is NavigationState.Space -> return logError("onNavigateToRoom()")
+ is NavigationState.Room -> return logError("onNavigateToThread()")
is NavigationState.Thread -> currentValue.parentRoom
}
state.getAndUpdate { it.copy(navigationState = newValue) }
@@ -137,9 +136,9 @@ class DefaultAppNavigationStateService @Inject constructor(
Timber.tag(loggerTag.value).d("Leaving room. Current state: $currentValue")
if (!currentValue.assertOwner(owner)) return
val newValue: NavigationState.Space = when (currentValue) {
- NavigationState.Root -> error("onNavigateToSession() must be called first")
- is NavigationState.Session -> error("onNavigateToSpace() must be called first")
- is NavigationState.Space -> error("onNavigateToRoom() must be called first")
+ NavigationState.Root -> return logError("onNavigateToSession()")
+ is NavigationState.Session -> return logError("onNavigateToSpace()")
+ is NavigationState.Space -> return logError("onNavigateToRoom()")
is NavigationState.Room -> currentValue.parentSpace
is NavigationState.Thread -> currentValue.parentRoom.parentSpace
}
@@ -151,8 +150,8 @@ class DefaultAppNavigationStateService @Inject constructor(
Timber.tag(loggerTag.value).d("Leaving space. Current state: $currentValue")
if (!currentValue.assertOwner(owner)) return
val newValue: NavigationState.Session = when (currentValue) {
- NavigationState.Root -> error("onNavigateToSession() must be called first")
- is NavigationState.Session -> error("onNavigateToSpace() must be called first")
+ NavigationState.Root -> return logError("onNavigateToSession()")
+ is NavigationState.Session -> return logError("onNavigateToSpace()")
is NavigationState.Space -> currentValue.parentSession
is NavigationState.Room -> currentValue.parentSpace.parentSession
is NavigationState.Thread -> currentValue.parentRoom.parentSpace.parentSession
@@ -167,6 +166,10 @@ class DefaultAppNavigationStateService @Inject constructor(
state.getAndUpdate { it.copy(navigationState = NavigationState.Root) }
}
+ private fun logError(logPrefix: String) {
+ Timber.tag(loggerTag.value).w("$logPrefix must be call first.")
+ }
+
private fun NavigationState.assertOwner(owner: String): Boolean {
if (this.owner != owner) {
Timber.tag(loggerTag.value).d("Can't leave current state as the owner is not the same (current = ${this.owner}, new = $owner)")
diff --git a/services/appnavstate/impl/src/test/kotlin/io/element/android/services/appnavstate/impl/DefaultNavigationStateServiceTest.kt b/services/appnavstate/impl/src/test/kotlin/io/element/android/services/appnavstate/impl/DefaultNavigationStateServiceTest.kt
index dd0e576c79..ab272478cc 100644
--- a/services/appnavstate/impl/src/test/kotlin/io/element/android/services/appnavstate/impl/DefaultNavigationStateServiceTest.kt
+++ b/services/appnavstate/impl/src/test/kotlin/io/element/android/services/appnavstate/impl/DefaultNavigationStateServiceTest.kt
@@ -29,7 +29,6 @@ import io.element.android.services.appnavstate.test.A_THREAD_OWNER
import io.element.android.tests.testutils.runCancellableScopeTest
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
-import org.junit.Assert.assertThrows
import org.junit.Test
class DefaultNavigationStateServiceTest {
@@ -63,8 +62,8 @@ class DefaultNavigationStateServiceTest {
@Test
fun testFailure() = runCancellableScopeTest { scope ->
val service = createStateService(scope)
-
- assertThrows(IllegalStateException::class.java) { service.onNavigateToSpace(A_SPACE_OWNER, A_SPACE_ID) }
+ service.onNavigateToSpace(A_SPACE_OWNER, A_SPACE_ID)
+ assertThat(service.appNavigationState.value.navigationState).isEqualTo(NavigationState.Root)
}
private fun createStateService(
diff --git a/settings.gradle.kts b/settings.gradle.kts
index e1894c16d7..105befcd04 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -52,6 +52,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
rootProject.name = "ElementX"
include(":app")
include(":appnav")
+include(":tests:konsist")
include(":tests:uitests")
include(":tests:testutils")
include(":anvilannotations")
diff --git a/tests/konsist/build.gradle.kts b/tests/konsist/build.gradle.kts
new file mode 100644
index 0000000000..6658c0c5e0
--- /dev/null
+++ b/tests/konsist/build.gradle.kts
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+plugins {
+ id("io.element.android-compose-library")
+}
+
+android {
+ namespace = "io.element.android.tests.konsist"
+}
+
+dependencies {
+ val composeBom = platform(libs.androidx.compose.bom)
+ testImplementation(composeBom)
+ testImplementation("androidx.compose.ui:ui-tooling-preview")
+ testImplementation(libs.test.junit)
+ testImplementation(libs.test.konsist)
+ testImplementation(libs.test.truth)
+ testImplementation(projects.libraries.architecture)
+ testImplementation(projects.libraries.designsystem)
+}
+
+// Make sure Konsist tests are always run. This is needed because otherwise we'd have to either:
+// - Add every single module as a dependency of this one.
+// - Move the Konsist tests to the `app` module, but the `app` module does not need to know about Konsist.
+tasks.withType().configureEach {
+ outputs.upToDateWhen { false }
+}
diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistArchitectureTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistArchitectureTest.kt
new file mode 100644
index 0000000000..f4bd7175fd
--- /dev/null
+++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistArchitectureTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.tests.konsist
+
+import com.lemonappdev.konsist.api.Konsist
+import com.lemonappdev.konsist.api.ext.list.constructors
+import com.lemonappdev.konsist.api.ext.list.modifierprovider.withSealedModifier
+import com.lemonappdev.konsist.api.ext.list.parameters
+import com.lemonappdev.konsist.api.ext.list.withNameEndingWith
+import com.lemonappdev.konsist.api.ext.list.withoutName
+import com.lemonappdev.konsist.api.verify.assertEmpty
+import com.lemonappdev.konsist.api.verify.assertTrue
+import org.junit.Test
+
+class KonsistArchitectureTest {
+ @Test
+ fun `Data class state MUST not have default value`() {
+ Konsist
+ .scopeFromProject()
+ .classes()
+ .withNameEndingWith("State")
+ .withoutName(
+ "CameraPositionState",
+ )
+ .constructors
+ .parameters
+ .assertTrue { parameterDeclaration ->
+ parameterDeclaration.defaultValue == null &&
+ // Using parameterDeclaration.defaultValue == null is not enough apparently,
+ // Also check that the text does not contain an equal sign
+ parameterDeclaration.text.contains("=").not()
+ }
+ }
+
+ @Test
+ fun `Events MUST be sealed interface`() {
+ Konsist.scopeFromProject()
+ .classes()
+ .withSealedModifier()
+ .withNameEndingWith("Events")
+ .assertEmpty(additionalMessage = "Events class MUST be sealed interface")
+ }
+}
diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt
new file mode 100644
index 0000000000..64ca2e899b
--- /dev/null
+++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.tests.konsist
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import com.bumble.appyx.core.node.Node
+import com.lemonappdev.konsist.api.Konsist
+import com.lemonappdev.konsist.api.ext.list.withAllParentsOf
+import com.lemonappdev.konsist.api.verify.assertTrue
+import io.element.android.libraries.architecture.Presenter
+import org.junit.Test
+
+class KonsistClassNameTest {
+ @Test
+ fun `Classes extending 'Presenter' should have 'Presenter' suffix`() {
+ Konsist.scopeFromProject()
+ .classes()
+ .withAllParentsOf(Presenter::class)
+ .assertTrue {
+ it.name.endsWith("Presenter")
+ }
+ }
+
+ @Test
+ fun `Classes extending 'Node' should have 'Node' suffix`() {
+ Konsist.scopeFromProject()
+ .classes()
+ .withAllParentsOf(Node::class)
+ .assertTrue {
+ it.name.endsWith("Node")
+ }
+ }
+
+ @Test
+ fun `Classes extending 'PreviewParameterProvider' name MUST end with 'Provider' and MUST contain provided class name`() {
+ Konsist.scopeFromProject()
+ .classes()
+ .withAllParentsOf(PreviewParameterProvider::class)
+ .assertTrue {
+ // Cannot find a better way to get the type of the generic
+ val providedType = it.text
+ .substringBefore(">")
+ .substringAfter("<")
+ .removeSuffix("?")
+ .replace(".", "")
+ it.name.endsWith("Provider") && it.name.contains(providedType)
+ }
+ }
+}
diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt
new file mode 100644
index 0000000000..9eb23cfc61
--- /dev/null
+++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistComposableTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.tests.konsist
+
+import androidx.compose.runtime.Composable
+import com.lemonappdev.konsist.api.KoModifier
+import com.lemonappdev.konsist.api.Konsist
+import com.lemonappdev.konsist.api.ext.list.modifierprovider.withoutModifier
+import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf
+import com.lemonappdev.konsist.api.ext.list.withTopLevel
+import com.lemonappdev.konsist.api.ext.list.withoutName
+import com.lemonappdev.konsist.api.ext.list.withoutNameEndingWith
+import com.lemonappdev.konsist.api.verify.assertTrue
+import org.junit.Test
+
+class KonsistComposableTest {
+ @Test
+ fun `Top level function with '@Composable' annotation starting with a upper case should be placed in a file with the same name`() {
+ Konsist
+ .scopeFromProject()
+ .functions()
+ .withTopLevel()
+ .withoutModifier(KoModifier.PRIVATE)
+ .withoutNameEndingWith("Preview")
+ .withAllAnnotationsOf(Composable::class)
+ .withoutName(
+ // Add some exceptions...
+ "OutlinedButton",
+ "TextButton",
+ "SimpleAlertDialogContent",
+ )
+ .assertTrue(
+ additionalMessage =
+ """
+ Please check the filename. It should match the top level Composable function. If the filename is correct:
+ - consider making the Composable private or moving it to its own file
+ - at last resort, you can add an exception in the Konsist test
+ """.trimIndent()
+ ) {
+ if (it.name.first().isLowerCase()) {
+ true
+ } else {
+ val fileName = it.containingFile.name.removeSuffix(".kt")
+ fileName == it.name
+ }
+ }
+ }
+}
diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistConfigTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistConfigTest.kt
new file mode 100644
index 0000000000..45247adc60
--- /dev/null
+++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistConfigTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.tests.konsist
+
+import com.google.common.truth.Truth.assertThat
+import com.lemonappdev.konsist.api.Konsist
+import org.junit.Test
+
+class KonsistConfigTest {
+ @Test
+ fun `assert that Konsist detect all the project classes`() {
+ assertThat(
+ Konsist
+ .scopeFromProject()
+ .classes()
+ .size
+ )
+ .isGreaterThan(1_000)
+ }
+
+ @Test
+ fun `assert that Konsist detect all the test classes`() {
+ assertThat(
+ Konsist
+ .scopeFromTest()
+ .classes()
+ .size
+ )
+ .isGreaterThan(100)
+ }
+}
diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistFieldTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistFieldTest.kt
new file mode 100644
index 0000000000..867c1ad141
--- /dev/null
+++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistFieldTest.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.tests.konsist
+
+import com.lemonappdev.konsist.api.Konsist
+import com.lemonappdev.konsist.api.ext.list.properties
+import com.lemonappdev.konsist.api.verify.assertFalse
+import org.junit.Test
+
+class KonsistFieldTest {
+ @Test
+ fun `no field should have 'm' prefix`() {
+ Konsist
+ .scopeFromProject()
+ .classes()
+ .properties()
+ .assertFalse {
+ val secondCharacterIsUppercase = it.name.getOrNull(1)?.isUpperCase() ?: false
+ it.name.startsWith('m') && secondCharacterIsUppercase
+ }
+ }
+}
diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt
new file mode 100644
index 0000000000..40c73c8eaa
--- /dev/null
+++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.tests.konsist
+
+import com.lemonappdev.konsist.api.Konsist
+import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf
+import com.lemonappdev.konsist.api.verify.assertTrue
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import org.junit.Test
+
+class KonsistPreviewTest {
+ @Test
+ fun `Functions with '@PreviewsDayNight' annotation should have 'Preview' suffix`() {
+ Konsist
+ .scopeFromProject()
+ .functions()
+ .withAllAnnotationsOf(PreviewsDayNight::class)
+ .assertTrue {
+ it.hasNameEndingWith("Preview") &&
+ it.hasNameEndingWith("LightPreview").not() &&
+ it.hasNameEndingWith("DarkPreview").not()
+ }
+ }
+}
diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistTestTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistTestTest.kt
new file mode 100644
index 0000000000..32fd6dd624
--- /dev/null
+++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistTestTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.tests.konsist
+
+import com.lemonappdev.konsist.api.Konsist
+import com.lemonappdev.konsist.api.ext.list.modifierprovider.withoutOverrideModifier
+import com.lemonappdev.konsist.api.ext.list.withFunction
+import com.lemonappdev.konsist.api.ext.list.withReturnType
+import com.lemonappdev.konsist.api.verify.assertTrue
+import org.junit.Test
+
+class KonsistTestTest {
+ @Test
+ fun `Classes name containing @Test must end with 'Test''`() {
+ Konsist
+ .scopeFromTest()
+ .classes()
+ .withFunction { it.hasAnnotationOf(Test::class) }
+ .assertTrue { it.name.endsWith("Test") }
+ }
+
+ @Test
+ fun `Function which creates Presenter in test MUST be named 'createPresenterName'`() {
+ Konsist
+ .scopeFromTest()
+ .functions()
+ .withReturnType { it.name.endsWith("Presenter") }
+ .withoutOverrideModifier()
+ .assertTrue { functionDeclaration ->
+ functionDeclaration.name == "create${functionDeclaration.returnType?.name}"
+ }
+ }
+}
diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/IsInDebug.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/IsInDebug.kt
new file mode 100644
index 0000000000..d2bef1c4c2
--- /dev/null
+++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/IsInDebug.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.tests.testutils
+
+/**
+ * Returns true if the app is in debug mode.
+ */
+fun isInDebug() = BuildConfig.DEBUG
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-D-0_0_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-D-0_1_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-D-0_0_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-D-0_0_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-D-0_1_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-D-0_0_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-N-0_1_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-N-0_2_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-N-0_1_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-N-0_1_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-N-0_2_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_LoggedInView-N-0_1_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_SyncStateView-D-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_SyncStateView-D-1_1_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_SyncStateView-D-1_2_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_SyncStateView-D-1_1_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_SyncStateView-N-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_SyncStateView-N-1_2_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_SyncStateView-N-1_3_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_null_SyncStateView-N-1_2_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e0a91a62b7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa1ddb8a63e7b348861732be8c79cacf136b84b1316670dc511947b8794582eb
+size 9707
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_2_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..259dd7ce8c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_2_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df78caa663ea94758cf16c70fcce66f5af7bb0523e18e25c1a5a131e41687b08
+size 11920
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1f9e8548c5..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:efc62a8981579a8d573449af5cb8f0cb83be9c38c750c3897e885bd5e71e8f3a
-size 9667
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_3_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index cf27960aa7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-D-2_3_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3ed4dc5ba331a1535248921013451dc05d0ecf40245f6a8fb2e4e6e226d015dc
-size 11870
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_3_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3518efa14f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_3_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:be5d601d56794ba4f707584bc6f9c7d76c1e2341da69e7a8dd0fe3224388a7b5
+size 9237
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_3_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..85ef954246
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_3_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4ada59e4a20323a9485e4c7dae97a8d53c9736e28e099545229cd9845c6496f3
+size 11336
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_4_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 613c8959aa..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_4_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2aa1c935d7edf22dec032f0939fcbab77db2993ca7e6a7f8659cbf4f765ea7e7
-size 9245
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_4_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0f66aa6af2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.room_null_LoadingRoomNodeView-N-2_4_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:92f8e01bf645641bfc00bda9c4573374afa0312605028883697197df282b6435
-size 11338
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_3_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_4_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_3_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_3_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_4_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_3_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_3_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_4_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-D-3_3_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_4_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_5_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_4_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_4_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_5_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_4_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_4_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_5_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[appnav.root_null_Root-N-3_4_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-D-0_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..bf9fd3aea2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dbd443a41c50072013892b4db0592d465862a087676f5ccc7270e1f77601f822
+size 50696
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-D-0_1_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index e56d6112cd..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-D-0_1_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e5bfb7ac9cffa2ef6057862d889c5fe2a520cc377fc02a72ff5f99f46953fb8a
-size 50784
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-N-0_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..bdf8633954
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0d9828a527145ccb1778d56327c4b709ff57ca9be415ced7bdf5dbfa605e2828
+size 49541
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-N-0_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 871df393ab..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.analytics.impl_null_AnalyticsOptInView-N-0_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:909b3ccdbc1a7cb8585c11acc088ee4f65cefb013d84b42433ccef7964cf6ff9
-size 49616
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.call_null_CallScreenView-D-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.call_null_CallScreenView-D-0_0_null,NEXUS_5,1.0,en].png
index 9fdfd38cee..4e99b44510 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.call_null_CallScreenView-D-0_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.call_null_CallScreenView-D-0_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:26df292602cdf27ddd42b3b4c1dcc3fc7ae41e207af48d76c7b65bd66babf649
-size 10561
+oid sha256:d0264d691ec2946cda4d5860f02079dd9f3e69ddd30a2e5c2f9c701253fd659c
+size 10499
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.call_null_CallScreenView-N-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.call_null_CallScreenView-N-0_1_null,NEXUS_5,1.0,en].png
index e524d75c96..5301f939c3 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.call_null_CallScreenView-N-0_1_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.call_null_CallScreenView-N-0_1_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f28b5214727111f39cef9a1d625469f30481fe2cbbe02df0efc53807a968d210
-size 9787
+oid sha256:20ec46c4c66a68d93c45a17eafd945536c9c137b18e66a82e83eced674708d98
+size 9732
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ee1a9f7134
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:280ebbe16a131cb4db1632898d58f78a80af7bc83d2c4aa4396f9af35851d4ce
+size 15377
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_0_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e408b763cd
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_0_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:05d6cc8105f90ad7d3376239cc8851a422ca24002f2fd37db1357e869abd5b41
+size 30263
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_0_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6ee48072cb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_0_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e9bff1424c5aaef2b4638a25878bbadd8c5267b32ffc4a504ce28e90016338bb
+size 83711
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_1_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 18d4e892cc..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_1_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2e82e6e9f3463f449cb8f1eec4b2fe12ce60c9245f6b4adc79967e1accce2af4
-size 15285
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_1_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 439ea49b7b..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_1_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f8b78908805bb8745613be2f9fad2c1c4fd0d6087dce7f9b301b4fe3dc485d19
-size 30273
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_1_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 75c9154caf..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-D-0_1_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:25b86e5211512c4a676cb142b847455ba34c13eb3712f75a22c8b313ca725eeb
-size 83750
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..840b6c8bd2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:541c5823f11bbf4bcf6061a479f7d491dddd7004d2328e6bb0c38d2367a8c42f
+size 14270
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_1_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e25d694596
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_1_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:78b011770254431118a24382c9892fa9163b6d5692cbe7a38543eae549771221
+size 28809
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_1_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..bafd6e8f29
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_1_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:832862848001c1e672872607b03967388e29345589157140dd26f50557874cf5
+size 81005
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 838f3e86a3..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f4ea73eabf37a9fd4534c182036dc83c4d44c0fffcfff63ca960426af5f20726
-size 14222
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_2_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index f0232ef1fc..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_2_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2dc520372ac1b4ae340a1e75e0a1f4cc65f54c9e9ebf44c750309018613fa5d4
-size 28865
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index dffeee671e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.addpeople_null_AddPeopleView-N-0_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7bf3e51b483ca9600d9f589721bdfe93e0977ebcfe59ff66484fcbd7cfb12e20
-size 81101
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-D-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-D-1_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..71a2028f03
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-D-1_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f332381c7d390fd7b18b3ffc802d688add498afc7dc7fc597fa226440dfa8253
+size 35649
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-D-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-D-1_2_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index c6a01656b9..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-D-1_2_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5a7dffd1d10d39574e767925b0cce138cbe2b7ee6212cc722474583e46adf8f1
-size 35929
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-N-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-N-1_2_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..655e013467
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-N-1_2_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6ed6444c7bfcf9a64fe239e04d2cfb3023f6023b3ec78fc7bc1a27b7ca78ba78
+size 33606
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-N-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-N-1_3_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3cba1de1b2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_RoomPrivacyOption-N-1_3_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b75ae4531d0d31d856e3d9eec8f81d5ca3fca76f21747ab778daef603c3577e9
-size 33935
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_SearchMultipleUsersResultItem_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_SearchMultipleUsersResultItem_0_null,NEXUS_5,1.0,en].png
index 19e170130f..047877ab68 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_SearchMultipleUsersResultItem_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_SearchMultipleUsersResultItem_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b051a4492303db0e5a1343269cb57301939105ab4c062db67326edb699a02d0f
-size 86337
+oid sha256:fc5058851b3ed2605906ea27916af6dc07c518e485b815d577295ad2cb965c63
+size 86365
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_SearchSingleUserResultItem_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_SearchSingleUserResultItem_0_null,NEXUS_5,1.0,en].png
index 037a2c9e40..e51d18ae64 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_SearchSingleUserResultItem_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_SearchSingleUserResultItem_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:88608834541e31673c52014cc56072953f1ff1814bb3f89852822ebc6f762188
-size 45269
+oid sha256:2d75c2c82789f2fbeb90d64cb3c582f25cc1987bc8f066ae2bae9783af076d5b
+size 45295
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b281254e41
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cd03b2dbb40ab82f6775ed36d198635121f2be32b4160fdc8905afbf9eb1d15e
+size 10520
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..10f174d7e9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2f9d563fa3bd6f84f13908b6598b66185debc8abe15b567998defd8fdf6b8060
+size 26202
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4b8c7c51f3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:99757e5f48eeea2b5797d1379d9d50886cc6f950c1ce1557a03e06295e5b4209
+size 8875
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e6fb56e1b3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1180179773f57495767d85d3b97e5923a75f773156b661abffa51ee280325164
+size 7602
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e6fb56e1b3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1180179773f57495767d85d3b97e5923a75f773156b661abffa51ee280325164
+size 7602
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f44d0b3e45
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d0f2433ecde91f665826dc0beae0900ed75d10ad5d7290b637b3256a4164212f
+size 64623
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..629b9c5545
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7b144041542e179406dcc674e43344c32dae3ba540102d26c5b82b7258fef434
+size 69289
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..81f59e0fe6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_2_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e8ee231d6ded9b516126f0893a475ab08694aeaa1e073caa7fcc0ec57531358a
+size 12368
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 7a6f914f58..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e1dae4febc4801c4de178cf937e8ac6e22cd871766dcb1870edf3019caab900a
-size 10467
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 85cb7efca2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7c01cba121aac57126e79becb6b5d2de7c3bee0f50418f9c17ab9228156c2030
-size 26274
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index f46ac355de..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:81a8495067496b7daa8f14795b601264b467c43404563814ff6802c2db2c4cda
-size 8857
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 28e8de94f7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:72cc5e561f69b8f871570f68d7283d03fd427c8daf3d9f3ac99678e7a769ceda
-size 7655
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 28e8de94f7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:72cc5e561f69b8f871570f68d7283d03fd427c8daf3d9f3ac99678e7a769ceda
-size 7655
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 09e93acea2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:663b97daf508ebb9e619c1a6a14c5ea005b93bf004ed6ef7c08a202284b87e33
-size 64676
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index dc6259ef33..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e85d3ebafb87a593fa8977f9def17c8f45f9e067e02fe05c48b3f9650835a000
-size 69456
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9692954254..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-D-2_3_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:bee0d98b7b58d3aa6074a57788ff3716289c44a598f8d26bb6deef052d5da6a5
-size 12411
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..82d6a9c88d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:72fd14733980344161ea94155f6d50e037a88a01393ec0e10e5df7ee8392dece
+size 9971
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..dee7827b58
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8a639ce5cd7a796f35c9436cc5a1efeba12e0480cdad655778850788f01827de
+size 25436
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..857bd27b17
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:55167302862c369542fd6541bd376e28aaadfd72d45f1d0a41c961bc020d3302
+size 8622
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..afad0f4668
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c4eb4cb9406ff5afa7f562782e221a07397858ba05beb000e62cc4608d02e24c
+size 7204
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..afad0f4668
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c4eb4cb9406ff5afa7f562782e221a07397858ba05beb000e62cc4608d02e24c
+size 7204
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6cbec0c506
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0404c3ed85057f4fa74a89ca81b69a82f00823fbcb1e3dc169fb0f45fd467cfa
+size 63775
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c3651eff14
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3b1708591dcdbfbd1e9df6742f3eef62d033b5026c87b965639ed37c99aa6b09
+size 67979
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f0d073a0a1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_3_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f96ce0b4f5b2ae5f1aec3fc9594d0430762e25bfb4305b1af73167228050e8d1
+size 11560
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4bda75e3e0..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:04fbc20b114ad6cedded4fea6f3c5902969f5480df796274754f4b05c78c517f
-size 9928
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3fb73b0329..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:01d213d4f3483c7f36557ceea3fa74fb6e8695c7315c87dff0db152cd051bf16
-size 25490
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index ab44fe8502..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:be08f781bae5096983b331377f16236193edfb5cad0e34f4d23a0e84ba15965a
-size 8628
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index c2c1090017..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:eba459839d22c4a2809195bba52c79ad72723bdabc0677bb87c4054032cba332
-size 7292
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index c2c1090017..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:eba459839d22c4a2809195bba52c79ad72723bdabc0677bb87c4054032cba332
-size 7292
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index fd718e2025..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c67a25ba48fdd4edc640f848712427656299cfd397a24805e6ebb89da533e1d9
-size 63838
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3bb77b278a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:67861c2fc225ca366c3c2206fcee0d59f77b232eedd29b1bce9ed630af002ea0
-size 68143
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 92a44e38ca..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.components_null_UserListView-N-2_4_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d74867084116e6784cf2da68345c5367cb48fd017692d072f012efb27dd88786
-size 11633
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_3_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..456c1b8a2b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_3_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8931b6e86b7eaa41de10c13259b5b0da5e34837204191f1258b968d6814f04b5
+size 61157
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_3_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c872252b2e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_3_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2f331af0d2dd34f4afef67a23e1b47d41bb31971fbe18752c61225a280f7f06f
+size 84819
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_4_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index a5c25801e8..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_4_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:bca29fc313aec50c9a7f1e5e6c2a0b6c288e1faa971760300b5145e847490e89
-size 61236
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_4_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 80d8dc14fd..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-D-3_4_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e116827aaa9d30b356088805bf6a820cce3ba42c49e6ffca2460aab1a4cdc8ba
-size 87872
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_4_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..28eeec5424
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_4_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3e39f1f6e0a32bba634c12e7b58aa11eb45081575f2f348d6da701973bafa176
+size 57718
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_4_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..03d4d04550
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_4_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:57a2b92097bf41a2ef79c9f6f41ecca1d1b631fe15377458df2a5bd27165caa0
+size 81467
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_5_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index a43a069bad..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_5_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e6137817016d7892f947a4add91925ac4bdecde22abdb9ba89e44ff21b40e22a
-size 57903
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_5_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 6e3c656d80..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.configureroom_null_ConfigureRoomView-N-3_5_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a88f6e1fdc1b3cd26f21a94866d0348e458509c5ce129c86c69b2673c7dc75f1
-size 84357
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_4_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..37eb2546da
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_4_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b513c5dc3dcab57aa0161dda218a23bca21591e5d3e74c57affded1ee9160ba8
+size 23575
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_4_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ddd84aa690
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_4_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6da12bea6f0b282922ab2a8860a213aa6af2da49b72b11528ab8cb177ba6b11b
+size 23676
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_4_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..904b4e0e74
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_4_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cc0df4d0e7e3241860fbe8388ba773ecf8455483874af23d7ef4c4e62feedf97
+size 31038
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_5_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 280e314f63..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_5_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:97edb6b42adb8f91e84b2ee6cca7bd08c5758fff4e403b7dc4049ebd505b468b
-size 23501
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_5_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3de381303a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_5_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d0be20df5f6bdf095fd9a93df3dc6ca81939365e4f27c8f605a1ab12b4959fc6
-size 23724
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_5_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 49c0cecf77..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-D-4_5_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:31bfc0054eff2bce1e43c49fa628968504e16853a198579710d08a2134d24efd
-size 31099
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_5_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0f8d42fc4d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_5_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3a326bbd2a5b8682aba465f5ffe988243893121d4dcc615159982cc1d3a6c52f
+size 22039
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_5_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d1b193a630
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_5_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a317ad736c41bfa5380e7743f28814a5601dfa87919f7edb35d0a4f09544762f
+size 20701
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_5_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..95e47794be
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_5_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fa52b86839fea55196c5f155c074098289cba8ca962d69b21293f07bfe391704
+size 27021
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_6_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index a895a86361..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_6_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7ce2386124550f79fc5cf32efbe44448ca81ce96041ca4419f25d503ec8a8ec3
-size 21914
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_6_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 20ea3618e0..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_6_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8f04a88c84cb7e4e6d0f487b47aaf5e80c9ae1472ab25bf5412e084c356d4bbe
-size 20765
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_6_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index d8fa732c18..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_null_CreateRoomRootView-N-4_6_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b85cf6982f409276e1bedc1a06322ecefef8ea86d4e5459a39d607fe10f5d08b
-size 27093
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_null_MigrationView-D-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_null_MigrationView-D-0_0_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_null_MigrationView-D-0_1_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_null_MigrationView-D-0_0_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_null_MigrationView-N-0_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_null_MigrationView-N-0_1_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_null_MigrationView-N-0_2_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.migration_null_MigrationView-N-0_1_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-D-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-D-1_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e5ef44e8d2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-D-1_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7c4863b3c785f8f2ea7561dda464eae2a7bfdab4e08d12bcad1659dbea358e2d
+size 37187
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-D-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-D-1_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index aa866c7812..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-D-1_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2a47f94d02280f2122c0743fadc2d53824555f2e57e2c595b07dc32836f80586
-size 37142
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-N-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-N-1_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..33e9145d85
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-N-1_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bb1fcd755cbc62fe56f37bc2b43c5d80285fa7233e238fa1cedda151c21841f9
+size 33720
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-N-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-N-1_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 49c3a7bafb..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.notifications_null_NotificationsOptInView-N-1_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f9028f6a5a6f67954385bcbcc098535071c096f37d12428c32af94a3e898bf6a
-size 33685
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_null_WelcomeView-D-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_null_WelcomeView-D-2_2_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_null_WelcomeView-D-2_3_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_null_WelcomeView-D-2_2_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_null_WelcomeView-N-2_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_null_WelcomeView-N-2_3_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_null_WelcomeView-N-2_4_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_null_WelcomeView-N-2_3_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_1_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_2_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_1_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_1_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_2_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_1_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_1_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_2_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_1_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_1_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_2_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_1_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_1_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_2_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-D-1_1_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_2_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_3_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_2_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_2_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_3_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_2_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_2_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_3_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_2_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_2_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_3_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_2_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_2_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_3_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl.components_null_InviteSummaryRow-N-1_2_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..153fca40a1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:90bdbaee0cc44c246e8cb0e1f685facf6d4aa40118f0fafdea0ec25255fe0f8d
+size 54700
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_1,NEXUS_5,1.0,en].png
similarity index 82%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_8,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_1,NEXUS_5,1.0,en].png
index 185288adb1..d022c40230 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_8,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3202fed8c4344f427442905d4088441ea97446b4ef9350e588df26d5497192e4
-size 53932
+oid sha256:2e934e2defeaf5b246d47680cde2221f7b7dac84f233407d8366f503762d12c2
+size 9023
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e274ec5ab3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d1eb6909c600aef67978922cd565af5f55ea4640fc355ac354613e2b304b9c3
+size 53932
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..31fbbb4768
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3dfebc124e08a0ae7ba5d254ed9c078b5d9004e88665d91b0eb80bf145a3a9c
+size 54713
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8cfe321257
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eddbd29af458a0776699e5108cfe6c685e4f4c8bb12963dfac938d109b799cad
+size 44421
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8cfe321257
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_0_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eddbd29af458a0776699e5108cfe6c685e4f4c8bb12963dfac938d109b799cad
+size 44421
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 6b5fc9bfeb..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9766ca3ad7562ead5bbc4b40eaf4712383a8c90876010fc0a23fc3ba4eb7eb42
-size 54662
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1de36bf77b..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:031a481d54d714ef38795b988f505e9c0e8d8780f3b9039396dce35dffe8de29
-size 8978
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 39c860e6f5..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b564f1c08b839c6758e3abba916a0b20f6cc0f590b27773de1d6a1e88a2cdc56
-size 53899
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 7e1c4760e3..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6881032ea2d387c2bb8f8dd776fe6b581c100328d13b023e190c22dc0832e4a1
-size 54689
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index cb2628494c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9717ce6ae0559ae73cc9397976887b7acb1a534a957a3a29b0d0da2442e77130
-size 44397
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index cb2628494c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-D-0_1_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9717ce6ae0559ae73cc9397976887b7acb1a534a957a3a29b0d0da2442e77130
-size 44397
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0e95e8364c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:727289c1a0bf3b98c723a5baac8b25ccebd4d0e9e21cc16d0ef52f5d76e8b768
+size 51857
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6f7d3fd4d7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f78607796efb5ba1be8eb4c7d3072ed6634daf0c9ecf39764c8a849f5d27f8c4
+size 8758
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..572122f7b9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:18b2944aa47c83685c049e82f5c63127f7d14b789e72abf65be5f887231f6812
+size 49284
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4371030b33
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4c361b187c660dd6a099539e56cb313472dd8e1c64bde6525470399f4275781b
+size 49960
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f815a0ffdf
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:571c11b5cc2352f6eef293ec567b89925d83176700cb5b8f6b63c2211f742e71
+size 39769
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f815a0ffdf
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_1_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:571c11b5cc2352f6eef293ec567b89925d83176700cb5b8f6b63c2211f742e71
+size 39769
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index af4db9ed9a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:079fe39c7dced18433ef0c768b30ec0c66bda3d910b392bfc33319f6be7ce892
-size 51852
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 913aaedb7a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e98defa7f3900853df13a3b211fd3b39068b36a82d826afb72d8e8dd5e0b33e5
-size 8749
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 68751cdec3..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e002caef4c4be424468938e2ca05a25c6f6502340f216eca908ba8784a4d98b2
-size 49289
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4ba1ea763c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d7bfc8f00d9286162292ed04e9a53f21f5f8f2f44bab88c00356d330b56fd426
-size 49965
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index c7a3568641..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6f23a5df96bb6faca94a8096cf4b884af1dc97a755f0664ca05c9d64657052f8
-size 39772
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index c7a3568641..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.invitelist.impl_null_InviteListView-N-0_2_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6f23a5df96bb6faca94a8096cf4b884af1dc97a755f0664ca05c9d64657052f8
-size 39772
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-D-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-D-1_1_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-D-1_2_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-D-1_1_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-D-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-D-1_1_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-D-1_2_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-D-1_1_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-N-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-N-1_2_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-N-1_3_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-N-1_2_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-N-1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-N-1_2_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-N-1_3_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api.internal_null_StaticMapPlaceholder-N-1_2_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_null_StaticMapView-D-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_null_StaticMapView-D-0_0_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_null_StaticMapView-D-0_1_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_null_StaticMapView-D-0_0_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_null_StaticMapView-N-0_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_null_StaticMapView-N-0_1_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_null_StaticMapView-N-0_2_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.location.api_null_StaticMapView-N-0_1_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c162c61c2d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:322558b9928b3e408320e9c002439b0ebd6b09a86c03cd3668ffd64f03d0b5c8
+size 22317
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..eef86b480c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3555a707ef787466d493247a5198131469c3e61d4f2aaf10e7a86e99f7fcccd6
+size 41368
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..764d0fbdd5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d902d99d379baa2f4fb0eebb9479fc6c497cf1ed62059cda799f4acea72efd7
+size 39897
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c162c61c2d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:322558b9928b3e408320e9c002439b0ebd6b09a86c03cd3668ffd64f03d0b5c8
+size 22317
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..50c97e76a1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_0_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2d3b5105a703083f7ef66c74e54232a4626e47c2409637d72170381212e2becd
+size 22456
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 8d2683d552..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ad8519086ae725ac7b1aeae9cd628237afeb489c6000c814b4d8e3f644592e58
-size 22278
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 173cc88678..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f4cc5cd0ba741aa217ee000749ec4041186c2c83d13f6c87023d16f16689a9ed
-size 41359
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 153b681988..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1ff04ca56c00e2bbf4858bcf6f8faed596e2c538937ec26d61abac7922cda2e1
-size 39890
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 8d2683d552..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ad8519086ae725ac7b1aeae9cd628237afeb489c6000c814b4d8e3f644592e58
-size 22278
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 95dff5613b..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-D-0_1_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b461bb08151c6efb41b0c4d7a40c33b3710dc5a1d0a32cb707ba90f0ef1ee2d1
-size 22419
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6fb5eeaed2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3700451074385a0cf8601faa11432124b60c05060cf79459b8aab35cefa0bc0c
+size 20723
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0e65ab4069
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d9973e81339db25c145bdd64e9949d117f0e9328e99a5e22bbd94a6b67169858
+size 38190
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a6f3d6efa7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f8e7ec04162181a014b66018ce81b08ca1fa606d8dccce90fa562992d75a4084
+size 36622
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6fb5eeaed2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3700451074385a0cf8601faa11432124b60c05060cf79459b8aab35cefa0bc0c
+size 20723
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d99081af1a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_1_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5c9bba26f9a57eeb103087ed9b630eb9964983db2692e1019a3d6b4fc66c3296
+size 20888
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index ba420c0094..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0a59281bb44aea2e220f48f1229afd6b4d2d0551dc0a7dd6e19b7a6ff3984f09
-size 20742
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index ed4299da45..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ac124e959b3c2303ecabe6eeba7c4faf7de55929757c0b69ceb4c0021cc1a390
-size 38197
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 29f176bdf8..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7e064103aa27ef0b2765962bb5d21f24387ac215d27691570991a75dd85f8725
-size 36633
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index ba420c0094..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0a59281bb44aea2e220f48f1229afd6b4d2d0551dc0a7dd6e19b7a6ff3984f09
-size 20742
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4a37a3011c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_null_SendLocationView-N-0_2_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5b7db80ee7f9c4a4d74b0665803c1cf85a391b6af0643f7cc823de5e8897ba6e
-size 20904
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..390f4e6623
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5bbf5bf95e68def0f8e6da83c649ab0e511fef68869e3aa6523fcd27c4e774b7
+size 13686
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2e27aae665
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:812f724738df351229c4a21acef24e4ca87b743623f6da6fc9a7e4b61daabf05
+size 36138
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8ef5afb54f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4aef2ff025f442667c25c75a7479c69196cf8996c568eabcd9966234b378eb1c
+size 34614
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..390f4e6623
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5bbf5bf95e68def0f8e6da83c649ab0e511fef68869e3aa6523fcd27c4e774b7
+size 13686
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..5cb2831efd
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b99d166a325408d309b97796498b4daf4443cb77467e4a17fe3ea642871e6c3b
+size 13892
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4c6d685db5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6a0df7b9a6cde1039dce7cd63d71b28ea1fe7aaec3ee0ae4a420319f0c4b1375
+size 17693
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f364a3d650
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3c0694186f358a87eb4a76e426a91d63d50e2f4f8b14663e16656ee9aa7d2baf
+size 26668
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b055e1b64a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_1_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:324e3758b4b1ff314f19c362062c365e5ad245ac71bdf7f186de41213df08388
+size 29009
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 2c284aec09..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c581e4cf30caec4e5039f11091417780ab0decf779ec7b31112a94fec0a596e1
-size 13524
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 09e3c2f716..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e82b0029ac4e23aacfde992e36267eec369108d6ef9021138510d4b421017bd3
-size 36033
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index b346f9e4a7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9229d55e1a109abacfa82f066a48fa60606a0d0ed0d155634b4c31fb2c7be98c
-size 34501
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 2c284aec09..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c581e4cf30caec4e5039f11091417780ab0decf779ec7b31112a94fec0a596e1
-size 13524
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 60d4ccb3c9..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9703d279e6401268fd066984d4cc91403c7f8f8e2c248c6ba2ea4c242694170f
-size 13729
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index ca5503a3e0..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:11a6fd8151812c8733bab26904f53754899db2caeaa0f4f24ea010dd2d54b538
-size 17522
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index 81bf0e3914..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0083578f572e51de0a0464caf90ec68e8a82930b97e0217d20a550ce269f7519
-size 26504
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 050b78e680..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-D-1_2_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1a5e11eed79ab3f92c130c3055e70e5cf0df2b22200e8e73e2180c0114ff5b04
-size 28881
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8a098474d5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:819b1b4df7ada9574c1ca295aa233723bdc0cf5da806f700807f8b105655fb45
+size 12495
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e62590cac2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:10e98fafe17bba6162b6a86eb4a2f919afe21e45a25a01ae231f9f20c372aa97
+size 33166
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d4b63ac486
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:45ae0828c440e81e4bb262c4a0e72039038de95d6d9a6efe19db0f2f46787fc6
+size 31516
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8a098474d5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:819b1b4df7ada9574c1ca295aa233723bdc0cf5da806f700807f8b105655fb45
+size 12495
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..310fb8ea22
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:53f6b031d5bb3bf1e33f6ec5449ab5aa8c95e2dd6d844cab5df81038dc54fa3a
+size 12685
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7b8002f1d3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e8cfbc773d5b5a5248edc83fe472035474c557ad6c30cb197a8fef15dae1d557
+size 15914
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..dcefcff71b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:227d97d78c52e9bbc8aa141666bedb72cc43d79c2af5fce89f2c9002c3b4ccf7
+size 23477
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..197c84d3f6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_2_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:41b5113a41c1fc0b1ac6228a6344a92e8765fdc6834c900571f0556ff0dfec40
+size 25574
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index f2ece2ce45..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:905d574bc64f14e309072a508732281c87dd00f8fa756bbd20b6d59c9b50eee6
-size 12428
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index cc035b2513..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1cb7669bfe98a30d0658c4444f08c11de6b8589a8a81163cb9a83366343c3aea
-size 33113
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index b5ee476b1e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:520a4053273d7d72a7339fdc609355ffbca183cbf151a24c701b9674e806306d
-size 31453
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index f2ece2ce45..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:905d574bc64f14e309072a508732281c87dd00f8fa756bbd20b6d59c9b50eee6
-size 12428
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 535f1e9742..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:71e4415423627091abedfccd7a03e20afa27fc9f599de70dd82be1571c4131a0
-size 12616
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 7d9397fdf7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ca76be1bf0a76e93614c5bca704269932d690f6133f53d09275e2f08d3d27200
-size 15843
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index a30c830e66..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4d0e5e459ed40f12fa3d2897a53928bc6808903bc8a36938e7954be41eab80b2
-size 23415
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 7186047697..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.show_null_ShowLocationView-N-1_3_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:adb557b5ccc00a8bbb5b605e372c892f6c0b7d194ed3e6e352f05b975e2e0eab
-size 25525
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-D-0_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f1992899f6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f19d53c688e3f862894775756f00040adb5cbba99de71c053ed503c1b8af9518
+size 15239
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-N-0_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f7a21c8025
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.auth_null_PinAuthenticationView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:11c145595f7713bc7b66f9d07e917bca82d6e06a1b55367801eb4d2cbfef89b0
+size 14353
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e8dd1a28d7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9914a33ba23544bdfce1e21b52ad247024392730fb22b60bc9b6fa6440f004d4
+size 9216
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..157a7c52c3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4ad524a918e499fcea6fd5293358167ff52f8877cc31b778c8def01925fa662f
+size 8582
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_null_AccountProviderView-D-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_null_AccountProviderView-D-0_0_null_4,NEXUS_5,1.0,en].png
index f44337c092..e6096ed152 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_null_AccountProviderView-D-0_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_null_AccountProviderView-D-0_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9e04acd879ab8948ae917bda762c880b5d343228cb91260abed2ac7bdc2a7088
-size 6993
+oid sha256:f368e6b153eab423ca5d4eac3c5325293b4a34b21f428de7f2c4a1712900faaa
+size 7062
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_null_AccountProviderView-N-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_null_AccountProviderView-N-0_1_null_4,NEXUS_5,1.0,en].png
index 94dbdc8e11..3cd1c0467c 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_null_AccountProviderView-N-0_1_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.accountprovider_null_AccountProviderView-N-0_1_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:674c6b6e9cdac8eb7c4e29a477df0d8b14ddcadcfed69bbd9fd0e2f26fb740a6
-size 7011
+oid sha256:e8ef1dc02b468d3aa21bc0a4452edb007799f0038402be6f8ea24c5d4e3ef4bd
+size 7047
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.dialogs_null_SlidingSyncNotSupportedDialog-D-2_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.dialogs_null_SlidingSyncNotSupportedDialog-D-2_2_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7650c954d8
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.dialogs_null_SlidingSyncNotSupportedDialog-D-2_2_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1883434cfce8e494f9ed92daf6ad885956b5d7f5c471968b443afe752b954aa7
+size 22827
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.dialogs_null_SlidingSyncNotSupportedDialog-N-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.dialogs_null_SlidingSyncNotSupportedDialog-N-2_3_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2d088f837d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.dialogs_null_SlidingSyncNotSupportedDialog-N-2_3_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:555f303cff58f52399fe6f8b8b6bc14cbea2cd71c084a66faa8bd2c0be2d0ff8
+size 19163
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-D-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-D-3_3_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-D-2_2_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-D-3_3_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-D-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-D-3_3_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-D-2_2_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-D-3_3_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-N-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-N-3_4_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-N-2_3_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-N-3_4_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-N-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-N-3_4_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-N-2_3_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.oidc.webview_null_OidcView-N-3_4_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-D-3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-D-3_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3b94b23ab9..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-D-3_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f2154c7d196058a0a47aa46658ad9d2ea3edf6b89e5973396980178a2399179a
-size 50726
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-D-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-D-4_4_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ff7779939f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-D-4_4_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7d41ebbf26c419c5ffe5f2e37b31a8297fbd3aef38fcc9e7b2d2cbfc91691954
+size 50801
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-N-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-N-3_4_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index bc067f20c1..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-N-3_4_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a7f1d73aa1698bb02c03773ca9c3ec8494089d03c26d52b3b6f1aaa40a081528
-size 49266
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-N-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-N-4_5_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..bd253cbe8c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.changeaccountprovider_null_ChangeAccountProviderView-N-4_5_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5f5e96ca97984f3d5a5c8def6222d9d3c88bbaa7499e60c56a1d1813939a9133
+size 49317
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_null_ConfirmAccountProviderView-D-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_null_ConfirmAccountProviderView-D-5_5_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_null_ConfirmAccountProviderView-D-4_4_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_null_ConfirmAccountProviderView-D-5_5_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_null_ConfirmAccountProviderView-N-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_null_ConfirmAccountProviderView-N-5_6_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_null_ConfirmAccountProviderView-N-4_5_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.confirmaccountprovider_null_ConfirmAccountProviderView-N-5_6_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-5_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-5_5_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index f9d68dc89c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-5_5_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a17b65e7c093a737e7413e9d750e6b84dfc30b042a5e85a7674f148ff85088f6
-size 39423
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-5_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-5_5_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 16321ba1c4..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-5_5_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:793327b9523a1223f23b350b7f1aeeb59692bd5cbd815bcd1ac788d81e0448b6
-size 40583
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-5_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-5_5_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index f9d68dc89c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-5_5_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a17b65e7c093a737e7413e9d750e6b84dfc30b042a5e85a7674f148ff85088f6
-size 39423
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-6_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-6_6_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..dd0e30059c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-6_6_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0a5ca3bd244941e910ae25e5d2d68f3aacb4ad7d89ed6439168c931d3b1f11df
+size 39644
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-6_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-6_6_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..63cffc0ff9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-6_6_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5172b5fd76582589f6dec777428dc73aaeb42d94151da08afc32a629554309c0
+size 40804
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-6_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-6_6_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..685f99c2d9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-D-6_6_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:544c75b9711b7baa0f63d1219c33281a143b3cb0ae601edcb2c070e7c4c57a0c
+size 30747
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-5_6_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 160bbee268..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-5_6_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0e89e3b361081ee9ee8772690695e0b6c0a85646cfb3d53560bf04622872a994
-size 37387
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-5_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-5_6_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index be65c0aa0b..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-5_6_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:47f06b071d09d16cbc6f8659106e1fca128aa513eebac036bc000cf1392a9fde
-size 38388
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-5_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-5_6_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 160bbee268..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-5_6_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0e89e3b361081ee9ee8772690695e0b6c0a85646cfb3d53560bf04622872a994
-size 37387
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-6_7_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0168b4f9a8
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-6_7_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c21cb3f0b3b594a8b3b63762ea7c0864ed6010d3b8479be35e87ffa215bb9a43
+size 37570
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-6_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-6_7_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c5b72f4517
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-6_7_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b80d77309b1fee94e8eb896973a637a3ceabc97de6b06e2d177815b0aae47829
+size 38567
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-6_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-6_7_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ca97e134d3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.loginpassword_null_LoginPasswordView-N-6_7_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1f1b84d70f67596af405a341d962d83cbd26e3925ffe86af35390e26c7eb0800
+size 27422
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-6_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-6_6_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 54d60d6642..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-6_6_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8222670d7b08fc9f5e446e86a31fad4fa773bbac03742df9bb5dfb35189571aa
-size 25617
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-6_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-6_6_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 79b7a23804..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-6_6_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a4830db08b7e09971e299c31f18ce45dfa500c59a056b407c7df397b679e23d3
-size 55455
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-7_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-7_7_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..47179975a9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-7_7_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:033d639153d83e7b1e7c88195d999243ed7131822f25a4c74904334cd37e7329
+size 25947
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-7_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-7_7_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2612d19e10
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-D-7_7_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7573d66c1663829cbed5d793acf726c8a851976beaf2095399b57a67d074f894
+size 55798
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-6_7_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index bc70d900bc..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-6_7_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:856fc14eb010048f355c902419d7d346f77bfb26e79bbba7e966363a39f503f5
-size 24884
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-6_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-6_7_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index ebb99ef2c6..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-6_7_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d4329c822e128dcfa495df0c81126d929fa5f787e28f3c144009aaaaafae4dc6
-size 53371
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-7_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-7_8_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..9c07d61c37
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-7_8_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dfb124e9735a84d623524d82d93abc2316d878825007f8e84aa714c9bf0b1ed0
+size 25055
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-7_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-7_8_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..5a10858510
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderView-N-7_8_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bd41270c0b7bf19a42398d6939551ac975e31f456c96c78805583032f05c209f
+size 53638
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-7_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-8_8_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-7_7_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-8_8_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-7_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-8_8_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-7_7_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-8_8_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-7_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-8_8_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-7_7_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-8_8_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-7_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-8_8_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-7_7_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-8_8_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-7_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-8_8_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-7_7_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-D-8_8_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-7_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-8_9_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-7_8_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-8_9_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-7_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-8_9_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-7_8_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-8_9_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-7_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-8_9_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-7_8_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-8_9_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-7_8_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-8_9_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-7_8_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-8_9_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-7_8_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-8_9_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-7_8_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.waitlistscreen_null_WaitListView-N-8_9_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.api_null_LogoutPreferenceView-D-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.api_null_LogoutPreferenceView-D-0_0_null,NEXUS_5,1.0,en].png
index ea66a1c88e..4e323f3bb1 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.api_null_LogoutPreferenceView-D-0_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.api_null_LogoutPreferenceView-D-0_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:62778d740a6f147d7de50f7ba98f5e603e4d00243b9e3e6a1ad8aade564c0800
-size 6946
+oid sha256:6a648ab1e1a85e4442378e16dc6ccf81fdfd7e8d04903519d239b6858f7b8488
+size 7081
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.api_null_LogoutPreferenceView-N-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.api_null_LogoutPreferenceView-N-0_1_null,NEXUS_5,1.0,en].png
index 9e751206ca..b20bb92b2f 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.api_null_LogoutPreferenceView-N-0_1_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.api_null_LogoutPreferenceView-N-0_1_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b6f35fbbb7815b105d08f691e6a1362b815f6c1a9b223f9660efd1a7758b709b
-size 6756
+oid sha256:3628567719c53c3590f4932e56f45b8a10fc76f4ce6dc77ac620c507f9190729
+size 6872
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ac13aa8b52
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2e7684929f93f437c50549b309e1ba75b95cddb506ad9c8ef97373d0552bddfb
+size 39017
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..18aaab9fd1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:56f71535fd676a86e79b0e987701d34c85c96276e5338f77117b13e1b3a05144
+size 45715
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..9098eef49b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5554661aeb29ec0d95464942da03df5e83dc78762bc08e2f57851a8f7e811a5a
+size 39438
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0c6cb3114f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d398070330d5c66358b7ac17d4e1b93795fb9001ffc6eabe533d3bae86de7d58
+size 39810
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..74448d6967
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:979138753000ada021a67f4bc14a89b912430c4331ac69aa702b2e50c050d7cb
+size 41200
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3cb12c4c94
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e06e1a32d8c9d06a307a5116a396a03308d5e5486f911d34002139485e7d9ffe
+size 28176
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_8,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..af7782f5d7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_1_null_8,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:85692ea3847fe5a79f955bb71153ccb6e5b24cf451292c7b9d56f26e2eff95b7
+size 28840
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 04e2ba64b3..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:db2d848fded08ca0532f718f12ee9dab007d7bb129afcf949d26585e47efa054
-size 38389
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1f851222f4..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8421be9fe6518a4a610e561692352c28c63b1e588fae4f7617f6be94439852f2
-size 45078
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3384c7454e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fa24c8db1ddd037b4a8466fbff9cbb7f6731ecc9f683bf2c9f5e5fcb209efb0f
-size 38832
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1c7dc84d07..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fac283c6bb39d2561f38f6b2be294ed6381c0eba4bdbc976adb8d64ec19e2b52
-size 39134
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index 27af994151..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:265f29d841c4e777e7175fcd50bf748fe3a7cfa66289245b7c2186c7a83c605f
-size 40588
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3b1113a5e8..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c5feb8d094844f805415d452f824d7560fea4077433fe7a39832fb1fc5ee23af
-size 27621
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_8,NEXUS_5,1.0,en].png
deleted file mode 100644
index c94ec815dd..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-D-1_2_null_8,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d6a15254ca331bc6d8ec6a6408b0a5a3d4fd26961e7c6025805ec7841a22e017
-size 28313
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2e051be73c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cb4be22a56036d53f7c394c3829a9f8f09820276d7529ace0d3731965a8a92f9
+size 37556
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ea837fc372
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:369e8b4e3f741476cfa5ff7e3f9d5d1e1399fcf3494fadb95540129436ef4d68
+size 43744
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..1c028e2b60
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5cd8fe02340aeb0edb116c0f3d8f07efc1d331849688718782db25e17bb6e1ab
+size 37766
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..cb01f9579d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9b2aaa54bf065e6496957c3a60d61336f65e6b76f97e5dbc773284ee49fac91d
+size 37988
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8d45efac8c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:752abe37a810cc8e4b36ae5f4c4aa38c9f22757b6458f272ba991050c69aee53
+size 39409
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..811cb7e4cd
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0c53fdbe427ddb7088d9bd8f9b46a1281d145fd6f9ccc3a7de339750e3079075
+size 26498
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_8,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ee2fa7b46f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_2_null_8,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5df2d3b6e4698867277712214fa526e08a9bf790140cd000cfe2bf94c70e77f4
+size 27525
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index a5a732b30f..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:67060b3bd4d09915ec4eda311726a8657cf09e455b5e8ca42631fa3b448ad5b7
-size 37049
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 8fae4446ea..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4c30a6c0c7169044216a02dde5605d59fba9307499855ffc1fa0d3fcd1650f5e
-size 43291
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index ce98e4a44f..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d609e0b359437b05690ea953bc426b6627059f14c18dc5a538eac21ed6cc103a
-size 37259
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1f43ed59f8..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c3f086b8339dd6077e53e6370a098569f8fe9e65c12f8f241fade5ddc9dd0e0d
-size 37433
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index 978c28507c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6ad62a694120a91a4369d6acd86af1aecdf5bdaa5c126168858503b552b0adb5
-size 38897
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index f78c05772e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a51378a6d45806982abbc887d85b1ab4784f5b08d7ea5fdf395649a180529ac6
-size 26025
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_8,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3152bb0b19..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_null_SheetContent-N-1_3_null_8,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e0355effa8376bc88f9474f4d0e7f595708702f4fda5e39becf811fef10b0d16
-size 27081
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.attachments.preview_null_AttachmentsPreviewView_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.attachments.preview_null_AttachmentsPreviewView_0_null_1,NEXUS_5,1.0,en].png
index 556b49479e..950fe34a29 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.attachments.preview_null_AttachmentsPreviewView_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.attachments.preview_null_AttachmentsPreviewView_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:230f512f93b50c4d6c780437f687d6273be0264b87f1c7ef6dd0d0ce3d1c6342
-size 16412
+oid sha256:911789cdf8a94e0b405bee2e9fcc024ffeb40682b99de9bde10a71242cae6640
+size 16238
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3044d7352c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:db1af097c46be0b83ff50e61807042319da0abeeb18ac2ecc308421fb7346193
+size 14964
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..33e091b922
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:efde1c7f011e0a2021e7b94daa693e34ea13255d9ad5030bdff6b87edccc0758
+size 12614
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a2eb035fbc
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:524f2068f2ba08f5f3c9532e7b41ea09066b28c799ab95ee4dc21e722a4a5647
+size 28377
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7c59953d03
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1ef92d0b6b2000763f5efda39d40e81871f1b35f7d164b30ca267055d7dd8117
+size 26263
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8a0c607aef
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fdfe2087c1b0697baa2a4d1ed2dff1114066938071a3a7e3164a1cfd092b559c
+size 31066
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..664177707e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:817e008ff8a7f3619ebf09ccb673c654f1bc1ab8119f8ede919e91020ad228b2
+size 28204
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_6,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_6,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_6,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d160cfe6b5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_2_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3fe3542419140ac2bd473b44d369a72b140bb2d7e47323321bc340e98ac78b6f
+size 24865
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 6bba01210d..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:45273af6f9e6af8d4bb31b4f7f4ab64a94ffe81ae034f3ad8db56cdfb0573564
-size 14839
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index ba85764e13..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:09ce2d1c067621321b7c8db66e6cf126f769be998f58d6fba9bffcc2903e18b4
-size 14243
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3c82e866f9..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2333902757fee47ccdcab42188b5f5379fe48e2419896a4d525855510b7925a1
-size 28241
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 2281938ed5..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7c4bcdf48d7ba2f11cb7e09ad56fb1facce1722fe71b2b10ac5c4a86ed201e37
-size 27646
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 605e036675..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fa25fe1ec3626e1726043987c5b727d5fd10660c4134d7b714616933a9e0d01c
-size 27678
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 605e036675..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fa25fe1ec3626e1726043987c5b727d5fd10660c4134d7b714616933a9e0d01c
-size 27678
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 605e036675..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-D-2_3_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fa25fe1ec3626e1726043987c5b727d5fd10660c4134d7b714616933a9e0d01c
-size 27678
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4232b929e6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:30c6b224c35ed1bd4d6191b6dcb9fa95b6ffbbee5f0ce617124d54bcf2e1f4cc
+size 13848
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b6cd97e0b6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:18f82098a4c81d7648ee03f02d5fb15f061553b8e35b315ec260090a799086e0
+size 11759
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..db250e3e9f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b2391e9bb20c9e5cb5a25fe1ccc3267bbe4e807ab282681c4e9fb5ca4b7b4497
+size 27106
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..311f6ee899
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:609cb79eaabd440908359ae01d0e508aebe55e956fc27f460f4f30d8f9105e2d
+size 25456
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d97646351e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5289e07b1e9177cd143c7ad4c28698e398539b41c15d48b295c8080578666740
+size 29502
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..be12429385
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1f0d4d45f42ccaa4188910b03c85de2b79655a86a5e6c9ca32cb4516ad8e0557
+size 25723
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_6,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_6,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_6,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..1a4a05b147
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_3_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e967c504776e055c3ea45c0088a2c0636edb99fd6679dfe878eb3733ba116f8f
+size 21815
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index aca7ba10ce..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3abcec7166680da6c21bccb1d9f9e0bc37c13eda05bfa11dc1237ee7630535ae
-size 13801
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3a84549ced..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:56b1ac721fb28a0e717de9682c08c8841a2acbeb75b5d5008df6df25437782e7
-size 13282
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 80a6200a6c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7afef32647fb7ca666ba516c3f348d1fb168be290279869b73c3b64522e20301
-size 27069
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 28307e18cc..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e2d9ea4c550094975fbdaa0f42c85dd871e414b1553c96b1256c8534233d6050
-size 26618
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 58213edfc0..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2a216a2aefd132b3ffb64e89de3c8b2013595573783f7dcb9ad6e0f04e1cbf1c
-size 26607
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 58213edfc0..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2a216a2aefd132b3ffb64e89de3c8b2013595573783f7dcb9ad6e0f04e1cbf1c
-size 26607
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 58213edfc0..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.forward_null_ForwardMessagesView-N-2_4_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2a216a2aefd132b3ffb64e89de3c8b2013595573783f7dcb9ad6e0f04e1cbf1c
-size 26607
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png
index 0c6de663a2..91d87cca80 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5a55394e59395b36f7a1e964bdce7d9f7d5f33252c463d0de7a87c213117231a
-size 395169
+oid sha256:0596b9207380d08c11dd15fca3ffdbc586c4efeb02da4a0c452b9034a4f2635e
+size 395473
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png
index f644a8c108..55a075e88b 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:44acd9adb83a35d38a9da31f8055988740d238e459d7e77145c5c2ad53725ad6
-size 395169
+oid sha256:c3cb399c19390d6efdff9d6df5065bb8fec0f359de8fd4b596dedc2f52be5441
+size 395476
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png
index 0c6de663a2..8ff9e04c67 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5a55394e59395b36f7a1e964bdce7d9f7d5f33252c463d0de7a87c213117231a
-size 395169
+oid sha256:97e5ebc64d490011841f813a4804f2ea702b0180c476bcc717bbb33512979d78
+size 114803
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png
index e2663fb42c..d62f53418c 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e4e9e0b3c5be9a07af8cd9a4f4ae725505148805327455350d9504a5974032c2
-size 395377
+oid sha256:469ae4d0a704ba90dc79ff8db073a87de6fdb5cfc4ebdb71b92595cf26fe0688
+size 395667
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png
index fae8a6fca3..2f2ac0e0c0 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8c89ac73df77c2bccb0c2aa80cee1420f78e7d07f0eda89a90bffef55e8cf753
-size 4464
+oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0
+size 4462
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png
index d16aed691d..e4e9b605db 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9225a19843eb5c9e1a78a7bd307958c2cad544c58ca2637224455326dfcfffb6
-size 6315
+oid sha256:766c51df0e0ca0c5983060505503ff95d9576cd547ae471d208d7d9c31ead7cb
+size 6631
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png
index ba242cdf8f..a322a9a364 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fe6d1328ff5142f5096b2708578e6d54d8b42f22b4b72d07ef91a9865447a1c5
-size 15317
+oid sha256:bd2a831abc63de6366f2a4fbac653d551e29fbbbb698f5dc3ae51de04dbf6138
+size 15941
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png
index 52e4989080..01188c15b3 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:01ed2fe5a7a8d77dc6924850ffad45ce1dd7a3bf1135b5006bec7d408a3476e4
-size 15408
+oid sha256:6b7dea2e1df15375ae07db4e6cf7b2a14a26c0ff1f2cbb11efdaea263d954550
+size 16067
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png
index 9c5277ab84..7eb0638b43 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_8,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6fc7c524a2441fa62bbb73705fd83e25ba184cb8a65f0a7d13e76ae91d021709
-size 14127
+oid sha256:45de22760cb98ede41d039060051e9cee9ceddbb05e300c8db2a72fc5fd6022c
+size 14441
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png
index 24bf62d2b5..7d3cd078d9 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.media.viewer_null_MediaViewerView_0_null_9,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c9a0e99ff56a7c4b55625d8ef6fa153b0e9c861638b031af6cdcf8c8b92687ec
-size 14216
+oid sha256:02ef108f718e1ef284f5c20c1bbf29b679848575ba3d189830b2ff57a3f938ff
+size 14523
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-D-3_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-D-3_3_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..920d470185
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-D-3_3_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b426d17bd2623e57d21ae8bb1c5f47247a729bb3c402da830dde4ca7554e8319
+size 26026
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-D-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-D-3_4_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index efcd596b40..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-D-3_4_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e67b171ca09fd2efb25338a20fe7af7464e0e1ecb1b02afd679dbfc7cd3cd7df
-size 25646
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-N-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-N-3_4_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b45fb3e146
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-N-3_4_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3c3e621c437cbc681672bd938fc8e5f3e9c956463d7d1b185e3aabe6717fd024
+size 23729
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-N-3_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-N-3_5_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index a385dd8498..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_AttachmentSourcePickerMenu-N-3_5_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e92e96cc117bf42ffb9fa282bcb9e40fd51c414e1d432862940c0f8fb6600fd0
-size 23453
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-D-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-D-4_4_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..eb1900be3a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-D-4_4_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7b8e9c99cb13a27ebc80c664d185cd3e3ad7b2bf6a7a944022c662ba1ee59ab7
+size 18876
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-D-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-D-4_5_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index f88e231886..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-D-4_5_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:933d3530f5267dd015c1a80536c92428b4b5018a76952f5911782acc2d82b53a
-size 10813
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-N-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-N-4_5_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d1e45b4cb1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-N-4_5_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e26cc86e44601b64ed04e62e2e714252896d669c98d5aebb0b0df1d852b8ab6
+size 17261
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-N-4_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-N-4_6_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 96db9ce2a8..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerView-N-4_6_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d2eefafc3d1ec7f88f27ee9698733fffc7050c3a4010c536642da282abfd8f66
-size 10093
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerViewVoice-D-5_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerViewVoice-D-5_5_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8a7c62e651
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerViewVoice-D-5_5_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:96999517a90c46cc256a1703eb0919adc81682478344e8dab922f39ebb000530
+size 8420
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerViewVoice-N-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerViewVoice-N-5_6_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..482fd62f5a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_null_MessageComposerViewVoice-N-5_6_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a7be984402697ade0648a7c7cf6785f6f22ac3f3fbb44bc3e1a71be73400461f
+size 8057
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index e0750da1cf..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e14131f1288140703189caf3fa7ba8fd985c8a99465d63748eab255d8d81c35c
-size 45934
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 2f34c37394..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d8d4675398df6bc58c9c73280dcead7215c54d8f9988cf52e2219e7e2b3b0a6d
-size 47449
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1703576a40..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a4360f8d26b8b19caa15153758a67ca3dc27c8165ed31d46d92aa908d5343fcd
-size 46973
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 36759fbbfb..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:89d61414f33975cacffc1a80f5f4e1039a33a961b7a907411ad2907fa85a73dd
-size 45430
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 8c1aa0f1f2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:81bf6ab47b70673e67575e3ed779f96e2a3537cc7079e190b6251d462410875c
-size 38280
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8e8d4282e9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3168e64fb861ddbd160ee09a75bcce7c6f7a1b6d47f73f83915f1af0e152256e
+size 45863
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..283008f0fd
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:de6b85a67d9149cc8e82fb293ec9b81e963588832bc0489b412dfc7a2bb409c2
+size 47387
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a806573632
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b09b37b87bc25d3e5e46cbafbe468cab3d025fc855fc531fc30a08f4b872520a
+size 46911
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..56fdf44aeb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dbed31f6579154ff6674b7201d6f36b2ea0ad10e20a4b59b59c3cc76f95b78bb
+size 45370
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b414b0ea96
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c4d5b41b5ea9986d7dfab84646ccd89f66f8a36ea3ae870d2773a27e22f51e38
+size 38218
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-5_6_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-D-6_6_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index d6be763ad0..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0765a835e6b18cf9c28f019de5bc8cacaef741af71bc6c2f30b53b1ad4a35d0a
-size 44178
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index d64045ea09..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ae3f32674a12b1ba78ea097d4b1db64ad8db7a3cce9312a76d4aa209788e3e6a
-size 44940
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0f7a696886..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9392f729b4d57f7f24c51bf120e0a7083654e77a2816ac475064679c34778742
-size 44537
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4ce1e34423..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:28c7f97c4060a4866843c5949c3c04c060aa7cc8748d75eb036188df642f6324
-size 43121
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index a8f11dfd5c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1545d55d818d4c70f8a1dd8980346f511020c7cbdf7234bdd10d70f212462d68
-size 34507
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b83003bf73
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e5c1354c569a124786bae728a13bcaf878c3a9bafd70074067e51a06f5b9ac18
+size 44094
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a9c91042af
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ad43f99ac09531dfa53c56317e83fc256b5632c91ac455ca27b7dc62196a9f71
+size 44850
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ae79ef4871
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:692b4493554c2a11b278b8c0421ce3463e883ee9944209bd0f8fc4dc4d325446
+size 44443
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..74ad437756
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d7be3c56e9f87e376dc381752109554425febc03d49b4294a84a2d50fee35335
+size 43036
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..60c95f487b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a58d7b8e0a1d631bf222d022d5c8432b654d8c934960d5b09c1864e4c349ddbc
+size 34370
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-5_7_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.report_null_ReportMessageView-N-6_7_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_null_EmojiPicker-D-24_25_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_null_EmojiPicker-D-25_25_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_null_EmojiPicker-D-24_25_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_null_EmojiPicker-D-25_25_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_null_EmojiPicker-N-24_26_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_null_EmojiPicker-N-25_26_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_null_EmojiPicker-N-24_26_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.customreaction_null_EmojiPicker-N-25_26_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-25_26_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-26_26_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-25_26_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-26_26_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-25_26_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-26_26_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-25_26_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-26_26_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-25_26_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-26_26_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-25_26_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-D-26_26_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-25_27_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-26_27_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-25_27_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-26_27_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-25_27_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-26_27_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-25_27_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-26_27_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-25_27_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-26_27_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-25_27_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemAudioView-N-26_27_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-D-26_27_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-D-26_27_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index d1d57c34ff..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-D-26_27_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:877d99d429388309ff42192b30859c9b717775d6162bae34f7d7c03795d62552
-size 8157
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-D-27_27_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-D-27_27_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..59c709b506
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-D-27_27_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f9eafafb5d374c341df0e4206652cbc14bf6b5d665c3a1ba89b6d029eb26b1d4
+size 8228
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-N-26_28_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-N-26_28_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index e8e44a0fc5..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-N-26_28_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a9e02ac93ea5b133cef2120b609b700170df7eaf111e7f9ff44778d9af1b3aa5
-size 8037
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-N-27_28_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-N-27_28_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..85ededab65
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemEncryptedView-N-27_28_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:13c80439b33af959e0b1d1056120915a37ad8fd7ac1f40a84db0c9a38cbd7a15
+size 8123
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-27_28_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-27_28_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 8205b06710..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-27_28_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c3dab3551f8524466b54e30f52dd64294086e638995819a7fa39cab927cc0023
-size 9439
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-27_28_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-27_28_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 6d15f761db..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-27_28_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:146bcaf3fe4bc14d18364e08cbcffa778fd5dde57e2602e8dd2d680bd007a285
-size 11928
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-27_28_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-27_28_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index d93fdc0084..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-27_28_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f923ed3f173447bcc6f31837a07dd55d5a767c59f0d67ee4c60d2bf8a04a7dac
-size 22978
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-28_28_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-28_28_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f73ebf56a4
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-28_28_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:459275549e3e53f44ae5c5c480040ac8354c8c021d0263bfe5e795a73c09e8b9
+size 9355
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-28_28_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-28_28_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f45d25d6ca
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-28_28_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:14fb452b7e66227b474d95411809005be857efa722e1115724b3459ef380966d
+size 11814
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-28_28_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-28_28_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..204fb81a9c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-D-28_28_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dd7de7f9b9f34171ca7234fa98ea0e33b3d566375ad2830a7e2bc3005bf5e613
+size 22904
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-27_29_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-27_29_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 76c71f34c1..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-27_29_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:01e1d36c4b2ed668faad099312c252f917500cc6af6d22a5ab3429e8ef4f480f
-size 9082
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-27_29_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-27_29_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index eb25217b5c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-27_29_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5857b35df74e741b68a4609fbfd4a5f1933f288b7cc5f0749f98b725ad2a7fa6
-size 11125
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-27_29_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-27_29_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index e1c906de0b..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-27_29_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f9a0a9cb5465cf87c0b3d73a619802bf61d832b2eb6b6f502db06c9e0b145766
-size 20658
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-28_29_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-28_29_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..dd94fd8de8
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-28_29_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5d01be2b13a0edcb248e28d6f005e9f5cb7840f2ad1696917c48236554e5ca1f
+size 9015
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-28_29_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-28_29_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b95f65405f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-28_29_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:15c311d9b6fb698cefead9f46b3f9bc13a4f4b7f384f73954581136820acaee5
+size 11045
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-28_29_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-28_29_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..aad1862c10
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemFileView-N-28_29_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:60e95bcee07a4a0fec19a87f9af3397758a6d81a7b8d5441af84612a87f22f4e
+size 20567
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-28_29_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-29_29_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-28_29_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-29_29_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-28_29_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-29_29_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-28_29_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-29_29_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-28_29_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-29_29_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-28_29_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-D-29_29_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-28_30_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-29_30_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-28_30_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-29_30_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-28_30_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-29_30_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-28_30_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-29_30_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-28_30_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-29_30_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-28_30_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemImageView-N-29_30_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-D-29_30_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-D-29_30_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0e4dfa91b2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-D-29_30_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c20c9cfea50fe4d2dfd9679b45e790aae18152fb38b099fcc6b252a847360395
-size 5678
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-D-30_30_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-D-30_30_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..bd2a95e805
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-D-30_30_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:59754add1de8ada9dab57df474147234d9978b3950a36b299150dc161bbfae6a
+size 5815
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-N-29_31_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-N-29_31_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 6ba938caff..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-N-29_31_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ba842bd76f8d1715e246be35389fbfba7bd5320f8c4a0fefc272c8c14e570f9a
-size 5642
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-N-30_31_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-N-30_31_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b2fef1c697
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemInformativeView-N-30_31_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c48859e45109cb1d4bb733dcbaf3014bf8a3f3edbc5746865f39837e9903c598
+size 5768
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-D-30_31_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-D-31_31_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-D-30_31_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-D-31_31_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-D-30_31_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-D-31_31_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-D-30_31_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-D-31_31_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-N-30_32_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-N-31_32_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-N-30_32_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-N-31_32_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-N-30_32_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-N-31_32_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-N-30_32_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemLocationView-N-31_32_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-D-33_33_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-D-33_33_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..1ac340b172
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-D-33_33_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:09440b685e219b4a5f5dc78d2694707d7eeb1905d6fdea6395e3dcc941482ac5
+size 51846
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-D-33_33_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-D-33_33_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..540d962b0c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-D-33_33_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ae8d6b561d358c7ebccae2da4281f3ea1b4d433f52de3c3b2008c1208c0c8bd7
+size 54037
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-N-33_34_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-N-33_34_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7c14f911dd
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-N-33_34_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a0cd0936ace9bac190f21680bf339cc23839e5f5d241522cb8f328b2db0887f6
+size 48293
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-N-33_34_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-N-33_34_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e14dee1d70
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollCreatorView-N-33_34_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cc96f4b704396e0931a08a968139ee9d586cd1e14358b546361d11bc91e7bab5
+size 50373
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-31_32_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-32_32_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-31_32_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-32_32_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-31_32_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-32_32_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-31_32_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-32_32_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-31_33_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-32_33_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-31_33_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-32_33_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-31_33_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-32_33_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-31_33_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-32_33_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-D-32_33_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-D-32_33_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 6647620c34..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-D-32_33_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d55d1e3365f83eb482c94d652aa1e042188e62c7ed903bc623437aeb1f1caa8e
-size 8471
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-D-34_34_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-D-34_34_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..70d22913aa
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-D-34_34_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f809cbf4c3fb897ea81d1cc4742178e847af1db758064cdd3e9cab081ac6a915
+size 8562
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-N-32_34_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-N-32_34_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 40bea19cd0..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-N-32_34_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7ad70776d28b3dc2b2aa2aadc16d12617445aa3f0f2462e4eb9a2b66dbc20965
-size 8355
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-N-34_35_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-N-34_35_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..198121784d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemRedactedView-N-34_35_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fd335a3ce4e931b6e7d123f9f4a8f9acb285b958f03c25979a793874c6176d65
+size 8471
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemStateView-D-33_34_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemStateView-D-35_35_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemStateView-D-33_34_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemStateView-D-35_35_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemStateView-N-33_35_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemStateView-N-35_36_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemStateView-N-33_35_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemStateView-N-35_36_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-34_35_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-D-36_36_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-34_36_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemTextView-N-36_37_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-D-35_36_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-D-35_36_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index cfcf316e9a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-D-35_36_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:088026e1119e1dbdc2b18a18a4587d59da3a4f41725a5ee99504f49fe4274035
-size 8701
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-D-37_37_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-D-37_37_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d9ffa583ae
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-D-37_37_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:14e83525a099c960c6b4cde848df4a1572b72859f1566565a24cc50417c2fbb7
+size 8726
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-N-35_37_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-N-35_37_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index c434242fb4..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-N-35_37_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3b90934767b577d12773db1ff4d4a6382dbee6e3a9500f30d4197cb9e4ba415a
-size 8632
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-N-37_38_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-N-37_38_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4f87b4a5ff
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemUnknownView-N-37_38_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:21241d8b7dc438f151689cb6f65125b1deb5c005152b93dd3a7c1f6c6452f021
+size 8644
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-36_37_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-38_38_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-36_37_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-38_38_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-36_37_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-38_38_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-36_37_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-38_38_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-36_37_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-38_38_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-36_37_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-D-38_38_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-36_38_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-38_39_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-36_38_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-38_39_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-36_38_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-38_39_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-36_38_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-38_39_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-36_38_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-38_39_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-36_38_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemVideoView-N-38_39_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-D-37_38_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-D-37_38_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0d27200d1a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-D-37_38_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a7987689b4c0527d51988582326053856e74f719b3f18255b131a1282bba3413
-size 25452
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-D-39_39_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-D-39_39_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2d34a3f1ed
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-D-39_39_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e387949352779b84674d3dc23760305b49c197342e3f90fa8ca8576debfa3201
+size 25382
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-N-37_39_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-N-37_39_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 75edbf14dd..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-N-37_39_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6273bc6b62eb2144cb26bd85c636de153707d59afc159263330ae40307946264
-size 25067
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-N-39_40_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-N-39_40_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d2a061dd54
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_null_GroupHeaderView-N-39_40_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:55b5ec0b3483805b0898a39fcc6d9d20adcebdc03a1926eaa52c5f34f6484be6
+size 25026
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_10,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_10,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_10,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_11,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_11,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_11,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_12,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_12,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_12,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_13,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_13,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_13,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_14,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_14,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_14,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_15,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_15,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_15,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_16,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_16,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_16,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_16,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_17,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_17,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_17,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_17,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_18,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_18,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_18,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_18,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_19,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_19,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_19,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_19,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_20,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_20,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_20,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_20,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_21,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_21,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_21,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_21,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_6,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_6,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_6,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_7,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_7,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_7,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_8,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_8,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_8,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_9,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-38_39_null_9,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-D-40_40_null_9,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_10,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_10,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_10,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_11,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_11,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_11,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_12,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_12,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_12,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_13,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_13,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_13,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_14,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_14,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_14,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_15,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_15,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_15,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_16,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_16,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_16,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_16,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_17,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_17,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_17,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_17,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_18,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_18,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_18,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_18,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_19,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_19,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_19,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_19,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_20,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_20,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_20,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_20,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_21,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_21,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_21,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_21,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_6,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_6,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_6,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_7,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_7,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_7,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_8,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_8,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_8,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_9,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-38_40_null_9,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_null_HtmlDocument-N-40_41_null_9,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_null_SheetContent-D-39_40_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_null_SheetContent-D-41_41_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_null_SheetContent-D-39_40_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_null_SheetContent-D-41_41_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_null_SheetContent-N-39_41_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_null_SheetContent-N-41_42_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_null_SheetContent-N-39_41_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_null_SheetContent-N-41_42_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-D-40_41_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-D-42_42_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-D-40_41_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-D-42_42_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-D-40_41_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-D-42_42_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-D-40_41_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-D-42_42_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-N-40_42_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-N-42_43_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-N-40_42_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-N-42_43_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-N-40_42_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-N-42_43_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-N-40_42_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_null_RetrySendMessageMenu-N-42_43_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-D-41_42_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-D-41_42_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 32eb8228f6..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-D-41_42_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:155ab320ed6c9a7a92ea1bb7aec63db92bcb991fc03804802a0140b255c69194
-size 14833
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-D-43_43_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-D-43_43_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..57ff1f481f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-D-43_43_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dc16dd2b27cf281c39932013a904a72bb6b417a07105d3d51b96f4b49a642e25
+size 14865
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-N-41_43_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-N-41_43_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 2c9b30732a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-N-41_43_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2d94deb190b221a5c378816851930814216654f292b5bad596c43fa4751602c5
-size 14368
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-N-43_44_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-N-43_44_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a565274d3c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineEncryptedHistoryBannerView-N-43_44_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b93e0fc28c37a90991ff9778f40fc8e9b07da593d21c6bfc73451006ef6d067c
+size 14415
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-D-42_43_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-D-44_44_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-D-42_43_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-D-44_44_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-D-42_43_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-D-44_44_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-D-42_43_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-D-44_44_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-N-42_44_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-N-44_45_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-N-42_44_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-N-44_45_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-N-42_44_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-N-44_45_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-N-42_44_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineItemDaySeparatorView-N-44_45_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineLoadingMoreIndicator-D-43_44_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineLoadingMoreIndicator-D-45_45_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineLoadingMoreIndicator-D-43_44_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineLoadingMoreIndicator-D-45_45_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineLoadingMoreIndicator-N-43_45_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineLoadingMoreIndicator-N-45_46_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineLoadingMoreIndicator-N-43_45_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_null_TimelineLoadingMoreIndicator-N-45_46_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_10,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_10,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_10,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_11,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_11,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_11,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_12,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_12,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_12,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_13,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_13,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_13,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_14,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_14,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_14,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_15,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_15,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_15,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_6,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_6,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_6,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_7,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_7,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_7,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_8,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_8,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_8,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_9,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-7_8_null_9,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-D-8_8_null_9,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_10,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_10,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_10,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_11,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_11,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_11,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_12,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_12,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_12,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_13,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_13,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_13,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_14,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_14,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_14,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_15,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_15,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_15,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_6,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_6,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_6,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_7,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_7,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_7,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_8,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_8,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_8,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_9,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-7_9_null_9,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageEventBubble-N-8_9_null_9,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageStateEventContainer-D-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageStateEventContainer-D-9_9_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageStateEventContainer-D-8_9_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageStateEventContainer-D-9_9_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageStateEventContainer-N-8_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageStateEventContainer-N-9_10_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageStateEventContainer-N-8_10_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessageStateEventContainer-N-9_10_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-D-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-D-10_11_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 91af1135e8..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-D-10_11_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1763f6e5114d406d9e2a5cbad96113d5d36f6ccfdee1cfade0146c1804d0a725
-size 5982
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-D-11_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-D-11_11_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..cc04d555c5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-D-11_11_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ea353acccccc924a9effb3e707dc60689dfe291a11df3da24c5ae43c2595ed91
+size 5987
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-N-10_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-N-10_12_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 25592c4b2f..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-N-10_12_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0582cf0802ca4a1e5fc823518f0dee9fbf31b3b2803c0a89baab35885139805e
-size 5917
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-N-11_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-N-11_12_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..9ad99f76fb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesAddReactionButton-N-11_12_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7d2b0a85773bdf65bb06ee11936351e39c8635838293e03490d62f4f77942c51
+size 5923
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-9_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-10_10_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-9_10_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-10_10_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-9_10_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-10_10_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-9_10_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-10_10_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-9_10_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-10_10_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-9_10_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-10_10_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-9_10_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-10_10_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-9_10_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-D-10_10_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-9_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-10_11_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-9_11_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-10_11_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-9_11_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-10_11_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-9_11_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-10_11_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-9_11_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-10_11_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-9_11_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-10_11_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-9_11_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-10_11_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-9_11_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionButton-N-10_11_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionExtraButtons-D-11_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionExtraButtons-D-12_12_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionExtraButtons-D-11_12_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionExtraButtons-D-12_12_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionExtraButtons-N-11_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionExtraButtons-N-12_13_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionExtraButtons-N-11_13_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_MessagesReactionExtraButtons-N-12_13_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-D-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-D-12_13_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 8c614712a5..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-D-12_13_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7f64649306919ac8dfeb6a7729f73a0617e8fc809c2d62f3aeabac6566bced1e
-size 9151
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-D-13_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-D-13_13_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f42dd2000d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-D-13_13_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:444e3f4899a35af8f493a001f6d0d5be87fc77413219f855870c5c354fa09fa7
+size 9235
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-N-12_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-N-12_14_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9d06193cee..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-N-12_14_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2a022d836fe4290d82a5a532807d62c12eff47d319a10d0a72e97d844b99b184
-size 9100
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-N-13_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-N-13_14_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..82e557521c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_ReplySwipeIndicator-N-13_14_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cdebbea1142060f776ba9d7a9583e9b613d1110a0631394da916c5262d1938cc
+size 9122
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-13_14_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-13_14_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index d0bd9f8962..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-13_14_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7ce9984a7198a01e888e9b1f683b2609630fcb4e0fc37333ac3afeb4def9e9ea
-size 5909
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-13_14_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-13_14_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index e1e6f49473..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-13_14_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9251a47bd1067eb85bec6885319f8fd2c5d0211736d440da3b053c8bb8de7433
-size 7301
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-13_14_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-14_14_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-13_14_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-14_14_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-14_14_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-14_14_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a004828d70
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-14_14_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8dc8b6abeec47a4d3287e02943a63fc4809cae93fb590cae279e3ef64ac7cc4b
+size 5934
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-13_14_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-14_14_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-13_14_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-14_14_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-14_14_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-14_14_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..1e2f0697ee
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-D-14_14_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e6c7c6768a5f01584eceebd471a8b016bbca0bfc48be6edbe4756a97c6f451e9
+size 7314
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-13_15_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-13_15_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0f0bd085a8..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-13_15_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:17b943efd27048dd07ceb96b7458a4ca08315f4493f3fdf991aab073ee20d563
-size 5821
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-13_15_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-13_15_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9244e68f5d..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-13_15_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a7a8b330571b95a3b4b4e2b575002e513b7dfb33edaa4e67111e110d1200558e
-size 7050
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-13_15_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-14_15_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-13_15_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-14_15_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-14_15_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-14_15_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3a120ea542
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-14_15_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e83528181f62f9f86df1c7042ee013c8c61012cbf78d369b3b893b789561efd1
+size 5828
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-13_15_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-14_15_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-13_15_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-14_15_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-14_15_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-14_15_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e870e65204
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineEventTimestampView-N-14_15_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2df2b9e0028e9513b60c9309f14dc1973d10a424c3f4ccba79210a0226322dcc
+size 7063
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-D-14_15_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-D-14_15_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index fb9457779e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-D-14_15_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:676b62ab782652c45a7267aac5df12943fd181bea48964d28dce664884ae4182
-size 156619
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-D-15_15_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-D-15_15_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e55f448d13
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-D-15_15_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8598a191c36789a925c1fdff3a6c9faef19d2d41896a2b47533f7ceed9fb60ba
+size 156693
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-N-14_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-N-14_16_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index a17027f25d..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-N-14_16_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4d4a735faaa653e10c5bb4213634abae3fdf324c01cdb994cf75eb5876896744
-size 151498
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-N-15_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-N-15_16_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e005a309d5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRow-N-15_16_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d738bc8aef1846dc2bae8bd669abbb40dbc9cad28e8b2581fab592930d21f8ff
+size 151630
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowLongSenderName_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowLongSenderName_0_null,NEXUS_5,1.0,en].png
index 4011a2572a..0134e6c367 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowLongSenderName_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowLongSenderName_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:82459b6a9a3882bcdba78e89f8938f3749d6fabb4673bd0a4cc953d2b7f069d9
-size 18215
+oid sha256:52e88cae16af0e46208511678717c44c1d9fc91e6c6e676dc68247e3372eec64
+size 18255
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-16_17_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-16_17_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 7fedf5139d..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-16_17_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:daee8f18d24bcc07cf8edd9b902b03c3fb406199a925606b1caf34cee08ff9a1
-size 65076
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-16_17_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-16_17_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index ab0a440978..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-16_17_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:54a52c6de5fd501e5c4f260343dd9ec582b0a1dd094a18f43f2fc4aa2c8acfcc
-size 72145
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-16_17_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-17_17_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-16_17_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-17_17_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-17_17_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-17_17_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..71d9675e92
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-17_17_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:861e5248516afd049fb0ce58e810881abe3be2c176405e6bfb65f834dd7f3b2a
+size 65169
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-16_17_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-17_17_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-16_17_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-17_17_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-17_17_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-17_17_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..74c7c5080b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-D-17_17_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1d1f93a483d72bccac7befaba22bcf8c977c5a9cea1ffa157d653b229ec061f6
+size 72224
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-16_18_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-16_18_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index dd455c1d3b..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-16_18_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2da0629ca98e636b9ea9b34da9990238b59616ec934e9b9a2d69296b4417a652
-size 65122
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-16_18_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-16_18_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index ea98289ff2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-16_18_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b6b4d92b030df00a07430f795c2998dae4c63c72ce4b0e962e2c2b6149c5300a
-size 71550
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-16_18_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-17_18_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-16_18_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-17_18_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-17_18_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-17_18_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..1a138a2494
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-17_18_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9eef20e4d04bb5ffc6c2bf39930866f7f3096c69798ab1312aad81ab2d5ef329
+size 65163
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-16_18_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-17_18_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-16_18_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-17_18_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-17_18_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-17_18_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..5608e8d7b1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowTimestamp-N-17_18_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7c074e3330ab178107fb8e4d51b9e09cf60b9b33b483deaa7104cb687942ecda
+size 71593
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-D-17_18_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-D-17_18_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 37620fce39..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-D-17_18_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:155baab65e9a3431f7afc8320e0b9ff12454a5faaef6c34701e50e7b0d1c3c0f
-size 84763
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-D-18_18_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-D-18_18_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..37614c5021
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-D-18_18_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:31e1b67d218d8f394c8d14b66c1964a12ff7b7ffbac79f353342f3342f867287
+size 84807
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-N-17_19_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-N-17_19_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index e27ebfee6e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-N-17_19_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9c738452ec0da21bb172fc3193499a2e632d4652685cbd27daf46d5658a33695
-size 81272
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-N-18_19_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-N-18_19_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..05fa0e511c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithManyReactions-N-18_19_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9bb2095dd487e0371c5c876d2d86cccdc77e883df4a06ed1444c93e27e7b0044
+size 81320
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-D-15_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-D-15_16_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9c01abd666..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-D-15_16_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8f5f950fce40c10710eb7fe4b193b4623633acdedfcf9029d6e0920e0c8d43d6
-size 133043
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-D-16_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-D-16_16_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..dd2855cf76
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-D-16_16_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:55eb9c74142b1e88d0ffa51f58bcda084acecae98a94075479fc080d60c432ed
+size 133105
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-N-15_17_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-N-15_17_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index a41e57eecc..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-N-15_17_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2bf08003a076d8a206782888cc2fc3df297e53478cd4c0e0aa5c8a26069bb7fa
-size 128065
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-N-16_17_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-N-16_17_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3d11659512
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventRowWithReply-N-16_17_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:46611834ba7748c328a5e51b3ed939af8f208306926e1f1656f052ad1cdc63d6
+size 128083
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png
index 0b071c5d9d..92eba5f9cd 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5490f2501c6ef257f926fc2f4bd9d94ef4e6e3017d4da290e2199eaeaa2ac5b5
-size 56571
+oid sha256:53d9982e5a8bfd18e007bd4dc80469367f6946b993fcc5faeb8a28ae2343e0b2
+size 56596
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-D-18_19_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-D-18_19_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index b4489c33cd..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-D-18_19_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b39ca90921e6b14ac33fdc370555d6c956a4d43543c50a53678945078e724e81
-size 26494
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-D-19_19_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-D-19_19_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..844661cdf0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-D-19_19_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8e4189c4f765657b0b73c66988b199878982107a02b3f7affbcd36c9909595b6
+size 26494
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-N-18_20_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-N-18_20_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 5db054276e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-N-18_20_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6855ebdc6e1ecaed89ed1f137d64fb93b72c12eed204b5b5c4e4863c87d2b68e
-size 26082
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-N-19_20_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-N-19_20_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d0a0b540a2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsLayout-N-19_20_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:27b0d2e120793a6e1a8f20c85c0981a280f1bc95a15c67f1e13e81bec8e06fdb
+size 26069
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-D-19_20_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-D-19_20_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index e09007ace6..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-D-19_20_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:582bd39f677d85a4429baa5065d5dadc8098178b83471c7cbe99c3bf8be18c1d
-size 7663
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-D-20_20_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-D-20_20_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e57050c297
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-D-20_20_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c3ffbc86b2acfe1b4a57af729c813f65ef232154193469f1442cc263c0965535
+size 7626
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-N-19_21_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-N-19_21_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 612b27550a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-N-19_21_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e03455d25a3b6be8deb66ce2fb52c52fd9fbb05baec2347fc44bb7ae87a6c02a
-size 7615
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-N-20_21_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-N-20_21_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3bb5b846cd
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsView-N-20_21_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0cfd9575e2ae9118a0ed187c7a2117b14e3b277771a00a9e3e032a3ef65667b7
+size 7602
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-D-20_21_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-D-20_21_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index d7f104d82a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-D-20_21_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:800c5813db728c6b5ad16b1aa0c2c42dbaf9306efc399d37da2c1a38add8a47b
-size 12219
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-D-21_21_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-D-21_21_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..fea7e1b4ff
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-D-21_21_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c5fdf959ae01598ccd77444794313f72706164e4edcfefc2a31cd3e2da50196a
+size 12258
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-N-20_22_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-N-20_22_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9222863b08..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-N-20_22_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3a06a2248f869d07949bcff05c0b4c2ee9a83cb1fc0f063f2e1cdbcecd93cdca
-size 12069
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-N-21_22_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-N-21_22_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..9ec88d801f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewFew-N-21_22_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bb30aa04fe5d61342ee6176bc74d7aac5ef87d15c4f5b2cff3c20109b88c2c18
+size 12084
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-D-21_22_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-D-21_22_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index b75593610e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-D-21_22_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:479c7471429194cb4f3b50140df65aa8e85aca0ad5e580376ef5a43e98f4d7ad
-size 25780
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-D-22_22_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-D-22_22_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0c6d736b90
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-D-22_22_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:63438f54ce85471ac8ff85dbea686d58d941de3c59fd3ba5abc90bd66ef85bf1
+size 25791
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-N-21_23_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-N-21_23_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0e33ba7e65..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-N-21_23_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cea17c4774d6e3816588e895627f11a06795967604be35d18ceacb31b1dc3d68
-size 25513
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-N-22_23_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-N-22_23_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4b2206f375
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewIncoming-N-22_23_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:073124a176fbf79b4cc785025655a21b75aa3faabe472910a71314768d028a13
+size 25532
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-D-22_23_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-D-22_23_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 7b4f609d98..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-D-22_23_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9503ad879861a692190ed1bc97fc96a8e0d78aaa2ecf2af0d20377d392587fc1
-size 25832
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-D-23_23_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-D-23_23_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0730b3d211
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-D-23_23_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0e1696f096efd5d8533437cc62d67f208703bf16686cff829e3401a222f5fd90
+size 25860
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-N-22_24_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-N-22_24_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index a7d5efa0b0..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-N-22_24_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d3af89df1ab5f114c26b36af04c8c156dafea6d2ab3e7350fba6fbbeb62c7b52
-size 25614
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-N-23_24_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-N-23_24_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6f7d1cde82
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemReactionsViewOutgoing-N-23_24_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4b85dbeeeeda0e3aa6f90e2d7702758f41163bb0c7c3b06174bdb59125ac0288
+size 25638
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemStateEventRow-D-23_24_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemStateEventRow-D-24_24_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemStateEventRow-D-23_24_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemStateEventRow-D-24_24_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemStateEventRow-N-23_25_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemStateEventRow-N-24_25_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemStateEventRow-N-23_25_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemStateEventRow-N-24_25_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-D-44_45_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-D-44_45_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index de1c7e71c2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-D-44_45_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7b43fa8d8587ecb1545d09939824f9ed58100095f06ea55aeee90dd0c0b31eb7
-size 34529
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-D-46_46_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-D-46_46_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..84ff0e89da
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-D-46_46_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7037bab35eb36d248b7ed2605a67c003c9d8f74e315d2b5c9097415c50c0be2d
+size 34912
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-N-44_46_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-N-44_46_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index a66722f538..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-N-44_46_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:376e2ca695f47ebea7c38fe11c5595e0d1399866c835d711e0e09acb42b03961
-size 32506
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-N-46_47_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-N-46_47_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..87c41b8968
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_null_EventDebugInfoView-N-46_47_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:da19273bbf3dfe1bad06e63ab05b30a58885386f7c2017912ed15f3a1856fdba
+size 32843
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 93b0744c82..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e40acbd30f8d9f815161b6a3ed1646733568015f03ffb0f24efaf6ec7fcdad6e
-size 52145
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 021bf7cada..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fd2166469e00817c11b429c3e4c8508f56aba93696bb182310a277873e8ef080
-size 63847
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_10,NEXUS_5,1.0,en].png
deleted file mode 100644
index f3e8bdfb28..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_10,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:53c5d2bce5d3502d5b09610faefeb68d20514076ba4a8189f7fc8f41c8e3174c
-size 50150
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_11,NEXUS_5,1.0,en].png
deleted file mode 100644
index 2c4900d9e7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_11,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1223ea8bca4d42e8f5a6cace4a530055df5f6f0eea02c62e9b35104aff7408ad
-size 67145
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_12,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3138d3ee2a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_12,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:48784809032f7474179742d8cf83986a366c62e1678a2d2f4a94e09856ff6302
-size 57274
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 307e6a7acf..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0d7bc773e70c57226840b30675f14a7fd99026100d61f48eeab64fb55d43f7c6
-size 230426
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4d97155b83..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:87f93b06f658352e7d97617c28dc0a4bd7454263fd402abfc2676f548c41d6b2
-size 231314
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index ef3c05fdb1..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ed11b0598ea4b2a991bbf6188372c33f35a3a661785ffdaefe4ef5257576e460
-size 72181
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 83b1c4316c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5c1d4fa3e1f403c0e5cd7583f9204759a0cba7dc63b4d1bb5d6a889cfcd10ab8
-size 87675
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index e95bbde5c4..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2ac8eba5238dc7be29bd0354ea3a23790c5a9b64e694685c82a645bbb611340b
-size 393217
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index f92fd35942..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fcb9003c252a391ae389011ed671aab66c917852638ec7c959c6f516d1b2cf77
-size 347634
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_8,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0e34e05ec5..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_8,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:edcc8b013786ec7b316161deb347278085fa196520b2d4acee74b5e2b426efc6
-size 53677
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_9,NEXUS_5,1.0,en].png
deleted file mode 100644
index 89f5efe6e2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-6_7_null_9,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d9ee29be03d3e25834cfa50194ae8280b09fcac2dbf8fc79416638b36ffd07eb
-size 65852
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..caa0e940f9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:15f1315ee78f9285ba33b6c0f3601b409a6965585446cdac566b65a2e6b55e57
+size 52374
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..defd4f5665
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b6abcfd0b6fac2fc054876463d444afe0a0ef0755cf2452aec7a75490c8ea9d8
+size 64674
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_10,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0f72363fc0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_10,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8f531805df6b1b7541e1c4d443979cb5434d3ea6c688725debc7f8845abebccf
+size 50375
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_11,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..024e61ae28
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_11,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d98aac1065f283b232c96f5a0032865e81bca8816bd235615e131d4bb104a9a5
+size 67474
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_12,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2eb5e3ea2f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_12,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2b06025fe43c1cd3f39bcee49222952f3750d0cf7ab2b9281716fcc670ea6934
+size 57499
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a51b6701aa
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7397940f42e0d5c5b860fcb0f08519fab590eb155efccaea84e22c499c9b9a5b
+size 230534
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f5d969f29a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:effff5164fe2fac13a9387d2bcab801c3d27cb77a7150cb0a062f8d82f4299f8
+size 231415
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..54c2dc5b1b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5cdd36c1ce3517f3a8c90fc4081e59c798f31c77bee3507d99d334da91878b0c
+size 72111
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c43efb8476
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8592f1320fdae003723d7f2bfc0f0e8dadf3b8edd1b5605dd0771e9b21bc05f7
+size 87604
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..98e3ba6dbe
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4f14b44ab65ac39c868de2c28644f886923fb4058f29a0d5b12dca7b6d978238
+size 393377
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..039022ff12
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ee02c20c1ab0b514e12461c95428ae89985ae86fab9e8538eb2301eeaa910603
+size 347767
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_8,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7defda84d6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_8,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7a121baf52bf6997fa6dcfeee9ce77e6d0f3b374227addae4a28305551a6914f
+size 53893
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_9,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e1cc82e2af
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-D-7_7_null_9,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4c433475dd2eb980e95ef67bf04c2465db1449bf34b3c0eca7f8c6f43b35113e
+size 66532
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index a9b5cc33de..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:bdd5697196181f91a5d36f8ec4170d5d8668052f67478d22f0c2f1d39ed3cf37
-size 50397
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 15a02387b7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b029a486993477abddf2cd602c5b935dd1fa4b22d1df5ef918c428f5714ca6bf
-size 61327
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_10,NEXUS_5,1.0,en].png
deleted file mode 100644
index 7b2dc3f516..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_10,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d9c28c48732089f547388c779922c3cf3dfe1c41d5d38df93366ab6c0a546327
-size 48617
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_11,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9e476a55e1..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_11,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:56e14c49836bd57eec889ac66bf0016da7e0c5bcdc9c51309e680ef77adc12ba
-size 64406
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_12,NEXUS_5,1.0,en].png
deleted file mode 100644
index b43b1872c1..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_12,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cb26b5d108b12720d523d2912b4f71a7799322565bda95087fc8cf52a6bfd2b9
-size 55070
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 946096b37e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f13fbb9bc776fa230b69c41e3faab659c52083d4e4f737f84b472c966f42c8dd
-size 229746
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 419a96a180..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d312c80758e940b368e0b9eb2d41efb55c158691164bfde3456d7540080e46a9
-size 230651
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 2a2361797c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:27d7a635a477aa1b2a4d0ae657333ecf55eafb9a84f29e5494526a64e7c085a0
-size 69525
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 8507c48fa6..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8776d4f031fbb8c934081ac29b6b6bd5746bdd67edac0904e1468b76727ef541
-size 82893
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index 011634c4e3..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:31b39041f6ac2630cb743fd8ee330412a2c0e7cf440dd760b773d1a8b3fcdf63
-size 189391
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0f00d749a9..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:414a89243ab6e291f36e3b49294c5162907e5ab03509d23a843af93b07a4ef4e
-size 178077
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_8,NEXUS_5,1.0,en].png
deleted file mode 100644
index be7f3f8601..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_8,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:60858ea74ed5fe4ef865ccd3f0290edd32bf87ad010f5f7691153491c163c42a
-size 51734
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_9,NEXUS_5,1.0,en].png
deleted file mode 100644
index d69df0df42..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-6_8_null_9,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4745bb37c991a9a90996a9ce8975639cf5fcb2d8981d0147d6a3445650d63620
-size 63304
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7c5a9e6200
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4fea4f3bdf3730d26880aa44f32bbcb5a41f5dfbe760d893852ded057f2aafeb
+size 50581
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..20866c94ab
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d0b2da862f047951267beddb0cccaa6ac374ab1f78c770e91451199f02da0eb8
+size 62209
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_10,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e8d97cbf89
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_10,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:93482a9053b5efc9331457b907964409e85855e4988358df6a01f1bea7ff1b9f
+size 48797
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_11,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..462776ac31
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_11,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:70e0a380cca7ba52a3b695b82c31e68c82c1e03ad6364e6ba73e84f070295528
+size 64748
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_12,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c54f9ea050
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_12,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bab28935e51e099811884a707858e5f463bb591a8a2766a276044c0dbbae92a1
+size 55244
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..04337c2fd3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0a7783a8fbf96f9029ccac649f43b05c2dab7bde9372e92669c27f1097cb1cb0
+size 229846
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f4137243e1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fe254fe851bbf538defe1a7e3d62fa9584a24fc94f9c3902f1b7d0258726a615
+size 230751
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7c4d6b85b5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ab7bd9d8d2ea21c3034010f521b92b0637d430b231a75bc3d5b241f10d172c4d
+size 69620
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d7db8f7395
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:39bcfcd74eff6df911cb6401e4bcd65d3bf1c6d942c0db730072e762e0afb3c8
+size 82864
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0ba45d341a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5cf70a69d194c8d297e03ee75be7659d06515c9698751181bdfc8f9da7da6055
+size 189520
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..99404cf6b2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1b1164272dc31a867c7dbc2bae37ac960168f31da63b233531d509583ea0cea2
+size 178173
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_8,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..55f9987532
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_8,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bd5f7838b304537265aa2616fd3b96db72346e963a785aa683826870e1a0190a
+size 51920
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_9,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..01ed22d8b0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_null_TimelineView-N-7_8_null_9,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9157f4531a208f2605bfc93314a669d188904b43b2f29bfd154786a50607d8db
+size 63928
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..72056b5ed9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:88bdef3999877e5017bfe0e0ead1514e4e6a58abcde0b0167d4b0ad9d4abd1e0
+size 54020
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..90b4f4652f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:35f420b550029d7f8b22d73ea0349d2794cc2e5c5f3080799f496774fad7d2ff
+size 55440
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_8,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c99c22634e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9518f3e4809856f3787bcd076f1a4f33067ea911e66cd6692790451309e6e192
+size 55769
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..1ae57f1414
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3f62a8a4eded0b742e911970837fe1003228834dd07a216a9bdc4acef37aa468
+size 55800
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..31edf89ccf
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1314aaf5394d03b5d08eccdb29783ce8d949f44d3eea28ec8ce434b830515304
+size 51662
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..617d8da89b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_0_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ecdc26ae1b8943734a8ec1020d7ca9ad4a8e570f5295eb568f80ed314585a9a9
+size 51981
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index a960aed41d..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:53bafb3688148dbeb3859aef1b8a9bf086340fc799f3d825ed246eba0cdda0f6
-size 53463
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 87c5b51154..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:22854689cac05b6ff1ec58a2b086c751516938161b844694b9300641de5e6ca9
-size 54871
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 6d8c175733..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ea6bebc608259c37a2b88c14e6b247696a2f9967152bcf2eb0c2a5d70f540ee4
-size 53944
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 47d15c2bf6..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fa96d668bd220fb1e47850c86c7ded9fe99b14b9704f1a5662cfde1e932c5299
-size 55443
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 7071e778c7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:885000bd08b05ca49eddbae17aa672fbf10a3acdcaf64d71fef77888229ae5c5
-size 55813
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1bac3210f9..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-D-0_1_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f1fd5d62a43ccfca8e157371068dcf125a72cd22920df447689b50464d8d42e6
-size 51137
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..58b944edce
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:32ae9c61f8a01bd54b9fb51af5f0dff222f4df0be1003a9c6ad680d20877448b
+size 52275
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e28209550e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1785f0fe49a5afd9b152f6ddb51acad7ba1700b2711cf302ef3a490d948fcd96
+size 53618
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_8,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..5113ea50e1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8907587eb26b29d273fcce14fe4f17acd3ab8ae2fffa83d2c5d5c2c0d8c29bc6
+size 54270
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6162f39468
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:455b414e5da5d5174a8d87ef37219c9754e3558b39f025d7aab226b041c51096
+size 51305
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3b7c0855e3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a0b83b1d37b34cdf0769e83f625d66479638ce402d5c8e76ef40548414d34400
+size 49862
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..500b83a53e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_1_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b09d4a60c3d9944cd6d50a8f0a06d5b3522eba7884f3b5e1e6889f93e8dd1794
+size 50026
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index e01bc2261a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ed4ba249dab6dc1e906bfb70886b531c4bf76e3acae8b8162496afe3b5a0f572
-size 52085
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index bf8898fadc..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9cfd42ac1e4931d4fb01ac8cecffa635adc92d67f1107a4c4051d66eeaab954a
-size 53336
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 78bffbdb73..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7eac088a0d14a2b1b85f541d0df5a1d38bdca3f7612560770bbfcffe0c1b2389
-size 52442
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3c98269fe1..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1d6ba255d9b1aee8d560b51f2bf0c1d4433c63502c83f6cf18ef8eaa598f05c4
-size 54071
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 7a830c8020..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9c65887eeeff0a815136ddfe9dbfae86d60e6abf4f409192d7950aad01c0bb0c
-size 51098
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 04a7cbfe71..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_null_MessagesView-N-0_2_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:eddb7e59898d7aaf644f958ae0b05578b7e1ef3802e352d7fb78651c2de97cf6
-size 49638
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_null_ConnectivityIndicatorView-D-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_null_ConnectivityIndicatorView-D-0_0_null,NEXUS_5,1.0,en].png
index d7f0e83b26..5416e206b3 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_null_ConnectivityIndicatorView-D-0_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_null_ConnectivityIndicatorView-D-0_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:58da3abe8a47ff009bf9ea99e151cf5cade3601e8fd0b46c3feec8295b0c9b0c
-size 6636
+oid sha256:1f8835edda00d6276ae8922f1d87ee0147b5caed4e6cabe52ff777bd18dfb5d9
+size 6609
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_null_ConnectivityIndicatorView-N-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_null_ConnectivityIndicatorView-N-0_1_null,NEXUS_5,1.0,en].png
index f80956bd5d..dbfbf2a730 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_null_ConnectivityIndicatorView-N-0_1_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.networkmonitor.api.ui_null_ConnectivityIndicatorView-N-0_1_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:87ce9aa965c8b5ae8f3296162f52aeaeb3c82f672b409531f8bc9efd5a655702
-size 6382
+oid sha256:c0c25984496c38f73e875878238a9e602f90582a31e2f5ac10163fa1ac0c0990
+size 6378
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_null_OnBoardingScreen-D-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_null_OnBoardingScreen-D-0_0_null_4,NEXUS_5,1.0,en].png
index dd8ee56aac..a561f96b8b 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_null_OnBoardingScreen-D-0_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_null_OnBoardingScreen-D-0_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:caff66495d4532e31adc6a1fe8c1da8f540d6509172c02b5c915f92b93101df1
-size 316886
+oid sha256:b9ab4177d12e1fb15ab6b8823729cf18021dcda7013520589b8e7cf21b4bf29d
+size 316946
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_null_OnBoardingScreen-N-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_null_OnBoardingScreen-N-0_1_null_4,NEXUS_5,1.0,en].png
index 58eb15ca48..69757c3199 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_null_OnBoardingScreen-N-0_1_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_null_OnBoardingScreen-N-0_1_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:46d4c3980adb36d04e4efcbf5eae1d615d22c6cb956af49c510cb564d5d671a1
-size 413267
+oid sha256:67735dd53d6dd9d86d3f31b1772c239e16c708cf890d6806fa3f6f11f02d0dfb
+size 413350
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreator-D-11_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreator-D-11_11_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..1ac340b172
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreator-D-11_11_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:09440b685e219b4a5f5dc78d2694707d7eeb1905d6fdea6395e3dcc941482ac5
+size 51846
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreator-N-11_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreator-N-11_12_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7c14f911dd
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreator-N-11_12_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a0cd0936ace9bac190f21680bf339cc23839e5f5d241522cb8f328b2db0887f6
+size 48293
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorEnded-D-12_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorEnded-D-12_12_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c8e001c9af
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorEnded-D-12_12_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:88230f28f7761d8f76bc5a50dbc4ba08a694eaf0d64207257f5196c741fb7b52
+size 49078
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorEnded-N-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorEnded-N-12_13_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..495cb4c484
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorEnded-N-12_13_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6e31f773b884608499081ab3c7a27297edfbf8af76f502d9076b4753956e2db
+size 45929
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorNoVotes-D-10_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorNoVotes-D-10_10_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2d9643e989
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorNoVotes-D-10_10_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5fbe23f6b00973c497f791c37f9d884e5d6baa6e6f5376f10633be0cb3043d49
+size 52067
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorNoVotes-N-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorNoVotes-N-10_11_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..48329ff374
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentCreatorNoVotes-N-10_11_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2a38081492746fdc6a4ae9f5408005456ab0f2143d4f937139d637aa39eabe77
+size 48435
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..68a1dc84ab
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bf95dbf44f917fe16223fa42562c6ac7241f3392c5b89ff0efc4c004fa081252
+size 34773
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..dcc3303e35
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5c814e41a183503c94a0631c0653f44d3ac7908c4db1d7ed5e04d9cce7cdaa83
+size 39180
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..72d9e37ee1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d089fbf26e86aefdecb2d5ab04f177099fa414c9d2a13e9ee0076afc49ded5d2
+size 40296
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..69599297ab
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d3b26e20df7cd33893b53d0b512c14afda8889696ecb9a3f8afc645de339f6b7
+size 47180
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3e31b627c9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8317bc1617bf491b5a1c6c71db7f9a26a96fe520cb6240baf3bf454288cc6722
+size 29604
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..16c9d3ce40
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_0_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:365b87e9487595f51cca28523d01ef222b2ab1016c025fd86df6efe9c6058ecc
+size 124268
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 175d444754..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:32651c4e32cea6891c8695b468db30dd3b6eefe72b1d441c3a5fbacfa08276a9
-size 34493
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 43bdd9bee7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:95a8fd37e77521464a4af078b1e5b4113872fe875f6de90c6218d77b0168467b
-size 38913
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9e63cc654a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0087805cdddb75fdb34775264949ce5974d8679c752b1eea87cfc44563e57de4
-size 40202
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index f36ed1b6ee..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:15f481a983765a5aeffff837aa0b7727e0fe87e69cba189ec78d56928630ff63
-size 46634
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 536ea963c9..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2aca4289813d6dbce44fc19bc5e0f7f1dd9e670db4e31c5be93a9d0eac125fff
-size 28696
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 29d5f43751..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e2b68997a63739074bbb0622c2f96af9ad2309e61b4ac246a929d3d5e0134939
-size 124111
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..058ea34e33
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:998aab1eadadb0f45da7f3f28c19a667c150bc6a7c6db8cb5600d1be75636b05
+size 32882
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..fa614f7c39
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:00123f8e5a2c7900da1db0d28c8364f5dbf9cef733f5e6ce9639090a10c6b214
+size 36426
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..12d0641cff
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5f84f63c2da7fc27bb58d7c7c24c9b131a5ebf8962c21c513806936fd0ea2a71
+size 36179
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8f23191d70
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:951dbf53f6f96aef3a8679e58aebe700d7f00875e915a80571d245cdc9c4e7dd
+size 44325
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d372a42ea2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d696d56a265ff539dcfbd5ba3f6ac28a4d8f278d8dfda1f3a369caab05bd410e
+size 27870
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..83c6c6b6a0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_1_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1a0fb1a13df4ff6bc99a2f91e00bb6b9dd5338b3c459c7467f68fe6a153b6bb1
+size 108687
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index e31b3b4ce2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9ea6ba41fb57c9353ffe26014852ab8517a634e09106168cf9352391bee45270
-size 32643
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 995416c8df..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:91680df981504bad5d82760ffe38ee3aca9a7b34adf9ab4ab1b204e4381ce855
-size 36189
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index ad4b412b67..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5b2bfcc5e61adb3b41d79bc25e4a5c159b0a729da4026bf4347f9ac0a7966b5d
-size 36097
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3839132e39..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:71420ae88ea8d3e71a7799597f8132514bab5757cdf644d157bbb527248a4c4e
-size 43862
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 43d4dbb763..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5b954906d2f4f0bb97f4ae78e246caab98a4d01efe04379ccf4ad79e8ae62310
-size 27091
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4a8356b4a6..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:528e3f1f4fc9b8c236427882409bc718cd6565816ed137a47ba3dd2c69e103cc
-size 108555
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-D-0_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..97f8aa0030
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6113823a0f37c6ac559d895dd7e07c8200bfbf5efca8fbc32904a8326512053
+size 17275
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-D-0_1_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index a0a9ec407f..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-D-0_1_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:60b0599ab33290e5a158a55a3a5c2e179ecfb5288ec260efbf171f2f9480c92e
-size 17218
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-N-0_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7272afb056
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2014c0ffff49cfbe684902c35719c96452cbef7f164f9017409702da497fc70b
+size 15988
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-N-0_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3a911dc70a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.about_null_AboutView-N-0_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1643286046c0c62a1b277d161a869728eb3d95e541cd180c9ee4b3d4572430b8
-size 15980
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..87469e8d76
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:20ed65388204495106f428d0445c3091bac5b953e46931d21d5ee73c5ae876cc
+size 37889
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_1_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4588e60de3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_1_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7bd91af47b7b336b5bd1abf52ebfcd588d29cb8b0bbb67115fbeaac8ba5bb469
+size 37473
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_1_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d9d2d0a9f5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_1_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:40bddbf21b6ca1d3f2647ff5168bd7b620b36d5cf3c88f15bab148963c3426b7
+size 37530
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index aa5e7b202a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a11d23d53b983a6cb5448f6083e448902b50889d6393f48454ef1b19c0ad0f07
-size 38028
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_2_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 99efbef109..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_2_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:18063647932eef08f66236825751456801e2a172a72ff09691a1d4b93897466d
-size 37617
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9cfecc1b15..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-D-1_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:333e3cb2b49f6d2794e732b7b9fc4204a7fc8564d0518b8cf60a9e7f14ea9a39
-size 37688
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..11451941bf
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:21e918bf1bbc6f024099d7bb8a1f38eaa356a50a244a9348411489a678579afa
+size 35444
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_2_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d9300615dc
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_2_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c051bfc9f9eafd112ccc60eb77b7ffbc9d91c493f2b588fbf51476c2be39ab1a
+size 35069
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_2_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..fe0e7dfff1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_2_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:943800b5c6fbdda0c39569ab44375ec2e4487c54644663fc21417b077ce5b8e3
+size 35181
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index ca2c271e89..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:47332a39d0c1a95a8396548b45bc3822b734111b268c06d917fa2ffb2d277cc1
-size 35649
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_3_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index e31840c360..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_3_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9deacb3ff9bdd2dbee7dd97686941cee9b1f19855f6b87c7e6860e9d47c671e4
-size 35278
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_3_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 44081fe3ab..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_null_AdvancedSettingsView-N-1_3_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:16b226707d1e862aa004c426221b89dbc9c7b2cedee19b11ff8843fa524a76ad
-size 35378
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-D-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-D-2_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..af8f8b8364
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-D-2_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cbe0660d1c39f4e9fb3db31b27bdac3d77a516ba7cbfe1de599a27b4b4d2b416
+size 26380
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-D-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-D-2_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 48fc8ae23e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-D-2_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:bc234611dd3df74196467129476c69a0212a4c665f2a94797c175cbd911c2083
-size 26339
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-N-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-N-2_3_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..77c1624a6c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-N-2_3_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2e150198c0568db5ce52811727df66406974795c4e3d39b67a13d55d2d5552ed
+size 25179
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-N-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-N-2_4_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 98964b2b77..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.analytics_null_AnalyticsSettingsView-N-2_4_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e22dc131e8f1f7461c050e871eaa408895529976ef7445aa6faf09852370df90
-size 25176
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-D-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-D-4_4_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d66b4d0ff6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-D-4_4_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:26cc783e78ec99e26b23cd7623ba6cd145af5efc548ac0ebd7380051be3d441a
+size 35350
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-D-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-D-4_5_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index acf8d934bb..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-D-4_5_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e55b03652dc0149cdc2623fa81b678a68a8080f57043c1f71cc99565e44c98e0
-size 35025
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-N-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-N-4_5_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7e38d91520
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-N-4_5_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3b17880c0e49d06ab6f015b64c286c5ba75d23f74e12ca2876112cc44a0d2421
+size 31611
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-N-4_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-N-4_6_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 2a8c0680e3..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-N-4_6_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e38e363ecdec4e70699204a83a01ac3e4a840f20f1ebc84bd014b49e0e52c77b
-size 31291
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_3_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..00797a4f11
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_3_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c221b092112d787bdc0f9db9e9da3afe75d6b5754b98db119df3c63514da03b4
+size 53808
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_3_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..00797a4f11
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_3_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c221b092112d787bdc0f9db9e9da3afe75d6b5754b98db119df3c63514da03b4
+size 53808
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_4_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index c1ebd00c8e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_4_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6814348aebbbb561fe42ae5e7c5ae9bec77cc7838b41adbc5158954b338fb0d1
-size 53755
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_4_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index c1ebd00c8e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-D-3_4_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6814348aebbbb561fe42ae5e7c5ae9bec77cc7838b41adbc5158954b338fb0d1
-size 53755
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_4_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..46ec60efb6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_4_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:419ba4098c8b77a6e6ea2af72eeb5a614a9982d04e003fedfcdee92772484eb1
+size 48839
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_4_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..46ec60efb6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_4_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:419ba4098c8b77a6e6ea2af72eeb5a614a9982d04e003fedfcdee92772484eb1
+size 48839
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_5_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9dbdf12053..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_5_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e6b1bfbd1d8c29347433e0c8a1037ea219b0935f9a74196f4c96952d144417e9
-size 48845
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_5_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9dbdf12053..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsView-N-3_5_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e6b1bfbd1d8c29347433e0c8a1037ea219b0935f9a74196f4c96952d144417e9
-size 48845
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-D-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-D-7_7_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-D-7_8_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-D-7_7_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-N-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-N-7_8_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-N-7_9_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-N-7_8_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsView-D-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsView-D-6_6_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsView-D-6_7_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsView-D-6_6_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsView-N-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsView-N-6_7_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsView-N-6_8_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsView-N-6_7_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-D-5_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-D-5_5_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0b21f0ad39
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-D-5_5_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:06c0841727ce28b358a757133b28a8e3973f99666db02cf8cfa46e5b6e9769c0
+size 52833
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-D-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-D-5_6_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index dc08c6b74b..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-D-5_6_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3d3ebc3196c825b08f7622ad370bb69f2989e57a0fc69058f41877163b43fd1a
-size 52756
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-N-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-N-5_6_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2dd0f18938
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-N-5_6_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d2271f5652918a2c1d3e55e7c1733481ca5f535d64517998296cc8c00fec3cb7
+size 49180
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-N-5_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-N-5_7_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 926b1b641a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsView-N-5_7_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c100e53723c6ed01ed1d82bba61e355bfc89ef45152f1490f664cf056beb95ce
-size 49228
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 72fbec227e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:56f2a7a7aca8336fb669030a98af4fd851ab1bf52c68d33be36fe8df922c01ee
-size 46097
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index a2d529d3a0..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6936337fd9365fc5d38d5570798ceaa409099240593e32a468e7688398926e4d
-size 45415
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_3_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..9e6006a88a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_3_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:81ab934cdcd8e7ad76c9bce474b59fbe449960ac2abf2d7b4fa85df89270fc25
+size 46489
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_3_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..11092fbadf
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_3_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e1a66ecf4b07ec0e57d1d5e92f9af7e4989288dec756eedef2b93d8b6b980424
+size 45815
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 29b2a82cea..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c9bc0aa48e94e2a4fb61ebc52b5d62d3fd960a3a3c52300906c76ad52f92b662
-size 49333
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 97fc36e283..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:bc40ec0fb6fa758768fc697496f54b9381a0fd0a67cce1ee36b6dd703e11ea18
-size 49229
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c0b3ad3ba5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:49cf3240371d9311320681520de8f44f5f8a013ba61949023a25e43b2960f513
+size 49806
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_2_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d9e8c5bdea
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_2_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:12afe63bad3f3586d10c4e59e9cc05ec038cc47942ebead5bed11e58b119e648
+size 49686
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-D-9_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-D-9_10_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index a030c7b043..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-D-9_10_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:717f9e1f59a958df1a2fef727e33280805a9c50e55c4d78d6ba16b428594589b
-size 22646
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-D-9_9_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-D-9_9_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..00ebc234da
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-D-9_9_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6a935000d8191f1682929c49d0dd3cf2fb8ab7908de574370717a1865967060c
+size 22694
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-N-9_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-N-9_10_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..90b2807e3e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-N-9_10_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e13e24436b8d15cc981bc3e997af4c0140ee95513157bc23e0b4429de800aaf3
+size 21105
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-N-9_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-N-9_11_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index c547d18192..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_null_EditUserProfileView-N-9_11_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:727a8edc3a35b431583c53f73049c7e6e4e0e8d4cf998fb134d74ac86e077856
-size 21119
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_9_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_8_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_9_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_8_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_9_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_8_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_9_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_8_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_9_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_8_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_9_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-D-8_8_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_9_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_10_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_9_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_10_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_9_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_10_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_9_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_10_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_9_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_10_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_null_UserPreferences-N-8_9_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..5f5be1fdc5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bf178a51be45cfd5fee676628ae3192374c1bc873e6eef726053ae593974aafe
+size 68108
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_0_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..15e4e18e8e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_0_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:922ffb470cc64a29cd2d9a65840d267cb742efa523f3d93676ec148f818850e1
+size 204829
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_0_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f328f044b8
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_0_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bdc1ab359d66c5017079bd789b687b3a36adf41c14fb12ceeb8aa55660253851
+size 60281
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_0_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_1_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_0_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_1_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index d28a735e0d..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_1_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f9f006f3f53e7c00a1447c8b915d8150368e7df460724d979cf6a18ba55ecd0b
-size 68058
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_1_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 2a6173f3e3..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_1_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2987bf6412a143650b50e97c6933be18d3b935132d65e89365a82298755d9ac8
-size 204786
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_1_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 187ef25d33..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-D-0_1_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2af0e1d935ee48e3ccb02ca67e789e0947fc5ad6d626a74c4fbe37aa507436c7
-size 60229
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..adc80daf5f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:310bd5d524d61cbc3b8237811c8d6623a6fb1046bb8a9823ff5173c2f2b3c862
+size 65218
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_1_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..618c296fbf
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_1_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3bd787f27931a5f71da9ed4248ff7c310acce37168b36e2575995ea1a1dc2cac
+size 200484
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_1_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ec1dc267b7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_1_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8ce770961f7eae4f6e9e396f7102761a67d049b8f65958d57f75c3b3a33be084
+size 55548
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_1_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_2_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_1_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0085065471..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7885a0e0cc79f1027d9783ad4ac0e8fe1f45f6cb8957919e67509a5efba3e624
-size 65228
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_2_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index f3f850f0e7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_2_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e9961d08d81637ac3364aea5008953856f966d8604c80427bde4c581bf0cd04a
-size 200495
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 869bc4f4c6..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_null_BugReportView-N-0_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:208222aafbb0e8c9627a0edce866b3eeddeb3d990c57b97de62f82394181ad18
-size 55557
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8a25f4290a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9fd6f767bee84dc91389caa5b31699a2faba02c17cefbdbc033269bb7c9d79db
+size 30376
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4179c023a7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a56708fb7d9de71d778a675ab586876355fbd13384c9a348905dfb165e1f0554
+size 23701
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..08f65eefdb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a136414791bb62050d1972eb0cab956d47806c6ea21b1a7c2b7041c61151fe62
+size 56031
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ae12b78071
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e1184486c1440d78b181802ab5e67f9c2c52891e1c9d4e5eec36bc67ec4570b3
+size 30096
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..44007d9af6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:88654c6ff85cb596e4bd07a63c263b8a25a01baa6859f9cf8fb3e96e1a9e17f1
+size 30097
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0d931cfa14
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3e80a2dc627a919484e7a906546e6fd06169b5052124af6876c6f0f4b89fb06a
+size 30309
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..edffdf8e16
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_0_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f507fac42cf9411f557e3b49e9979450c56ded313cb8071524fc2d9263054689
+size 27706
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 104d4e7042..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a2fa1c83e3ecddef2b489a8ce48db4195953fccd42534d2641bef619d5eb8bd1
-size 30325
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1a66b0af31..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5ea00ab4789b78416b938057b7cf5df117042000894b3000536384e61284ffd0
-size 23652
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4cad8e8d88..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4bae552fc42c0a19ab3b859c581212c94a884a23d6a4d1f60d6bd4a61d6f1584
-size 55975
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index dfa5a058c1..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a9018f1e5339d1ad9fa6deab845e1365215d727bc68f243cccc907ead233a6e1
-size 30041
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index bd9e15f3a5..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4c4af6710212228b3597d2e6cfaed5f6577e6aea90fb27240a7687d87f6f7ad7
-size 30043
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 357b55f52d..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:baee8e594977d8ae84c71b12e764005c7f04d34e57c80fce92dfa7431a8a6392
-size 30258
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index abcd30f771..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-D-0_1_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b5f05df46a49a9d1de0495655ca3828727c0c7d00afd11abbd643c8fc6712c03
-size 27667
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..656ae5e057
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8949fa811aff6197026661079d4176e7199e399925470cd044d7e459ca10c23e
+size 29049
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d57e0b309b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cda4b7b2b62f30e2d0bf6dea06545a9fa82bf1cf0c0278c5973f8e0e60f49784
+size 22717
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e45f0574f3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:403689283f938ad412caf12282d4a22deb49337a8e4039ece4618c08b1ac7347
+size 54301
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..38785f077f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:26377928764db3fa8a645a922a010c6450822ded6b55afa69667b15d5dfec4c7
+size 27978
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f7de7aa2c9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5b47d8274102e757e6374e1956e12b5ac5cbbe6fb9bc4991bc835e8464cc9e44
+size 28748
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0ecaac6847
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e0b2e3d9518ba4a4b19b54ac2d7f5ccd1840d04a0f1fcf252e90c505107b2f36
+size 28164
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a3f61a205d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_1_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c85f84bc8a8ed279785e1213f7f930df9ccdea34e658a79ca88fd06d2d3c0a39
+size 24407
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index e7b0671305..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5b9122206068d76d4e169dbd364d75e28c2fb5102ff2749340f829d15b02b124
-size 29053
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 096101e331..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:821f02fa92efca6f5912bbc8675f113e45f78654a0ec78bc30abc0b44c2ab0ed
-size 22724
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 36a5b635ab..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ee5d61f173c82bfd34aef6c1db5b044789bc064ba9d543e270f20d0d127dd1e9
-size 54302
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 26474b93d6..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:840b6dfc827adbf183e04c68c8c4978cb87cd356141127a7e548229234359a0e
-size 27984
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 116c775557..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7e27c699d975a911fbac4cd1456258ad1305d3bfbea7f059f4e736892d33d4c6
-size 28749
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3b3cb18ea2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2362fecce2c14174679dbda32e9f163ffb23498ef6b15e4846590460faab1011
-size 28142
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index fb78e13908..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_null_RoomDetailsEditView-N-0_2_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9ad8c77df2595c643f3a394a406592328268f81a5f6bd14ebd1687f95773f305
-size 24396
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..94e56c3293
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e1bb47814ea3c7003183a564684224fec48dae098c95fee95c04ef249249a5ad
+size 15578
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..390566be7d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ec95d7886981e8e17430d1afe69fc00f2fb04d6deabcd4cd801b4b1ff52ef9ef
+size 30528
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..5fdec07b67
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7cee7fdbad9c2c986354b6f256c1cb7d67d41c21aab9796a5f5e46c7f7443bbd
+size 12769
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e481b64ac0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dba2c9ede1524451f64d9a9a0bd5c07451848904b6abf3181bfa72609e83c635
+size 28019
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..76e83846b3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cece8d5911f6f9aa75a93e566ff99014877cbcb79a6b746cabee4036381d2d67
+size 14829
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e2c77bff36
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2c8a1a2aa65a07712f00e4c6ce99d1224950272cccc1471389686f0fdefc983d
+size 46185
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..784bbc1183
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_1_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3f1b7b07c44a12944ec53a2030cc3da0e286d9a4f26ad65d957c91e7ee8ed407
+size 40248
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index b9d74e1f34..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ebc08b4dcf097494137d42dcd029831dbb1049118ac0c214b2b987ae35e0412a
-size 15466
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index f4b3fc01a4..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d1071bdafa2d9c1aa08e81ecc4b7017957725eb90d0fcb23f455b5f80f98691e
-size 30568
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 79f6d696a2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e2ad056b4a78feeb61fa4dad3e98b216a1856c7318e83cd73b67e2007bef8baa
-size 12766
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 205a77a3e7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e391357db9d24d00fa2dcadb4cad590c7e71971b113ccccf7370edc396ae4da7
-size 28171
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index b74fb452a7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:422ee94b2769f2e3341aa7fb6ae71ca33e6d2ecf56e8a5342fdabb3931158b5c
-size 14828
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 33303a2529..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6fec1683e88a36fa4c95f9bd83b01030f9b375cfff2034f723c81c31c6615f5d
-size 46229
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index e50fd62303..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-D-1_2_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:03881c5bc6ce8b1008679cdb0db2e86f713db25c9bea1eacecb713fc8274b91a
-size 40281
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7677f7e067
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:097db9a6a96fa6216137f36f8ce0a654b1ef40e21afa3e6215de6241d2790ef4
+size 14539
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4b9ee5bca0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1f387a8541020893ee3853c014eae8fa03eb6cb37f5ddd5401c78087a33568ba
+size 29052
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..49a19ec2e1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4214598beca85afed8a110aefcba75509504efaf3668321c2323d2eecafb44ed
+size 11900
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a0e913a5b3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ef0ccaa515bf802289710d23d350cb16d39c9d3172374bc2417508d86be59975
+size 26898
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b261362bd1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:76b256e44e6b45d98bdcd66ad9138c267a3532e8a0b2676f97bc42e01d6d8d08
+size 13916
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6c20eee72a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b2a9aad2f5ac1f50a211a31f417849483eff8c6f53955390dc2df93a4dff8f5a
+size 45268
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..1de10c8bbc
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_2_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:afef5cd1a0281c42978678f71b18e33fa48576b5a30b08084c11a268893c875c
+size 38438
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 940ca5d820..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:01d5da14b8db0d887d80fa6dfdc57c27e390d0c800ee4256c3a82c99fb324918
-size 14484
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 56e17f1b80..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:44917f87510b19e963ab02f1324057b694815af1437300c8c4763ba617848dc5
-size 29113
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1c5c8e3f62..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:10362a8e927b83e8b2fa300278d3cb1581cee082eaec16072086a4f4cf3ba580
-size 11944
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index cdc1d79045..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c5fc0b0ad8f0b5524db430195f56526cf4c0d0ec48ac3bb90e7f339e4859507c
-size 27041
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index cc282ff776..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e44f674ce57bff51f454afa22bc6761cab1ed691e7c9882c13c324537a8b505b
-size 13964
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index ce2912e13f..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6d289d744e4ab63c6f9fc679b3e431ca4a8a3e905b0a34f4dd4b6d8ec5f345b3
-size 45347
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index ae4b8097c8..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.invite_null_RoomInviteMembers-N-1_3_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c61dfd03185e0eff646f3e6d4c433881825299129d979369645d689305a6234c
-size 38500
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 74a3f56bee..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:33c80b7d24e8f0ea86d22ac1fa92c72926edc2d51ede177046787c2d43440790
-size 19399
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 54685640c5..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a89af486f34afce8a092954f6adbc4a4a38154a8219de751982cddaeec6029d5
-size 17181
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4d2060d15e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:235918bd815cadfd3f202ab259081ef48da0e2fd563547ce7173b098c585bca4
-size 19814
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 74a3f56bee..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:33c80b7d24e8f0ea86d22ac1fa92c72926edc2d51ede177046787c2d43440790
-size 19399
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 74a3f56bee..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:33c80b7d24e8f0ea86d22ac1fa92c72926edc2d51ede177046787c2d43440790
-size 19399
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4a93a6cfa8..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_3_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cd3658bdce62cf7ca62c9138271df8b2d44ad5c0229b468fb40049a575036070
-size 20418
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d5fe3105d2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fdaa865f47a54001607d2c796255e0d50ec7f42f3908f9459bc2555af311274b
+size 19572
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b0b467544d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b43c9657a14b5dc8c6a33fb22d7227e99707f4b75286b1d2fa0d493bc1ef179e
+size 17386
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b00c827576
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0e722bb9e2d0782727f031ac115a6e06e7335cddf1f73201753c7474a1f42591
+size 19986
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..acd834cafd
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d5b98b3001442f362540855822813e1d951b3e2bf2bef74189b0182e6cefd6dd
+size 36963
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..52e42ff13a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:276afc61d84cdd461e00121e9c38756b91a0f422c848772d52492c9dfe3c4890
+size 28494
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..06744c416e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewDark--3_5_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bb0c078a9df374fed543ab11a55f7efa651c1d30f99370a48e1bcd5e8477f657
+size 20587
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index c0ca95c0ed..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:342a89e74b6067ff1fa1213edeb76dd05099d91083b6be92f48aff6ea6979393
-size 19817
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 42407d39fa..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c276773e89fbaba8b81ca5ace972364f2c4de24f670ac8a39f3a66c243a56699
-size 17517
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index bb14682c7d..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b6bdeb3f25b2f7a98ac55e3ba32858bc2fd2154f1f211e50a9bc36ac1be3090e
-size 20270
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index c0ca95c0ed..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:342a89e74b6067ff1fa1213edeb76dd05099d91083b6be92f48aff6ea6979393
-size 19817
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index c0ca95c0ed..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:342a89e74b6067ff1fa1213edeb76dd05099d91083b6be92f48aff6ea6979393
-size 19817
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4f768927cb..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_2_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:427da8bbf67861ee3214540b57e573ff8b2879aaf3639769d4b9fccd591676c5
-size 20881
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..26058a7bfe
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6ccb14dda83f03265d2de3fcd8bfe1d88ded896563e06208887e779a1f1a776e
+size 20035
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..32030b1237
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7dfad096895e851722bb777260f88ff843de36b9fb28993ad73ac927ec64343d
+size 17787
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..9487deb25b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c91c4d504dee8f6ef3d86974f2e280bacbf749bf4d44690e706e08bf56b12b83
+size 20479
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4bbfb31331
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:576027f683e1be5c343977e1a2808e2d8b9632db95552b3001bf6cb68eeef70c
+size 41501
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2827c77e10
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:64aed73e7a31bb58ef07eaee008c0fa2b1820319b180a4627e216e84919cf98a
+size 32488
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7c16b7de84
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_null_RoomMemberDetailsViewLight--2_4_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:90b756e00831455e4e47df17587d382ae6986314aceb5bfe54bc9592bcd8b0dc
+size 21074
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..42c65ee38c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6f2b33b2220dff71f51c71946e24f0ecbbc9845387b91e8c597457d529d229f1
+size 38928
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a4b732efb7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2fb43f26b23c1f7b371c8a8e7a95f08f00015846778f829522986b4e15b40709
+size 14836
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..282172214a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:84d07b1aab374b2c2394e9b48687fc79763a46ab2c71462c09e03217b50d8c35
+size 14141
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e4e04b944f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2db97913e33cfe4445f033c47343ba2b4be02966090b8768dfb215bc754201df
+size 13122
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4b8c7c51f3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:99757e5f48eeea2b5797d1379d9d50886cc6f950c1ce1557a03e06295e5b4209
+size 8875
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e6fb56e1b3
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1180179773f57495767d85d3b97e5923a75f773156b661abffa51ee280325164
+size 7602
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e0f603b254
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dc3225f8072957e14022237f0e406e269d17933fa486bd0be813640e1bf73883
+size 25263
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..81f59e0fe6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_2_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e8ee231d6ded9b516126f0893a475ab08694aeaa1e073caa7fcc0ec57531358a
+size 12368
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 5de307f4aa..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c6312be03f23e39df742082f9886490107939fef35971c4476f3afe7285848fb
-size 38836
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index fd942e991a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:523ac1db50b097e143068382825163769e4ee996a41b2c03d96c16307ef1093b
-size 14736
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index d3b247b0c0..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8a8d05fc2aee46e7c321dce542546782e2fe428ae35b5bafcecff4dc1e6a2914
-size 14027
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 33fc637eab..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c006ef780d4520a888a90778ad42304327e6059ff8690fa88949327c19a0d9fa
-size 13014
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index f46ac355de..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:81a8495067496b7daa8f14795b601264b467c43404563814ff6802c2db2c4cda
-size 8857
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 28e8de94f7..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:72cc5e561f69b8f871570f68d7283d03fd427c8daf3d9f3ac99678e7a769ceda
-size 7655
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4f504958a5..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c526ac195ee5c2d3f691a0fd13ea10a1efdd10b1c56bca579885d90d2f1658ac
-size 25308
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9692954254..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-D-2_3_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:bee0d98b7b58d3aa6074a57788ff3716289c44a598f8d26bb6deef052d5da6a5
-size 12411
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8441418c73
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:859619d904125c00e9c658280c47ef432ae971c567548b1e8a4ed519ce17687c
+size 38230
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..021972dd8e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f0003b5e5481ff20d8578e8581da478060ea8b329c9f9e9b976a010c288e4b85
+size 13957
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6b542cb9ca
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:881c635e0ae757d88120da498f3bd5c3c6cef2499ff2c205262f1871cdefad86
+size 13192
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..411697aa20
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d1e84c09381a653fe1f30b231989d493218bcecbcfba20d7ac6ff4c1e8ded12
+size 12318
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..857bd27b17
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:55167302862c369542fd6541bd376e28aaadfd72d45f1d0a41c961bc020d3302
+size 8622
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..afad0f4668
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c4eb4cb9406ff5afa7f562782e221a07397858ba05beb000e62cc4608d02e24c
+size 7204
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0214142895
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6bb44f2aaf3cc5ff536fd12f8b3d1894fd7287a303ff4fb532ba3a4e4ca263ea
+size 24909
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f0d073a0a1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_3_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f96ce0b4f5b2ae5f1aec3fc9594d0430762e25bfb4305b1af73167228050e8d1
+size 11560
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 22ab8f88bb..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:deecef6a5d8161828745db707304195d360e3454c1d092fe92c57cf87f55cb9c
-size 38196
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index b8a3c51611..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:64665620b20f3c306dd31447d531e3f0f984e5eb54e779f985f96fc88924f094
-size 13927
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3f2e377514..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:77c91d637a081bd54faeb27f0e92922359a1e46df126bb49dd3ddd7ea023768a
-size 13139
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index c4d1e1acce..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b1dd2a2bd8d460574e14f686d1fcb2a2d5cd560fc11ecf94b6f2a12edefb58b2
-size 12272
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index ab44fe8502..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:be08f781bae5096983b331377f16236193edfb5cad0e34f4d23a0e84ba15965a
-size 8628
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index c2c1090017..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:eba459839d22c4a2809195bba52c79ad72723bdabc0677bb87c4054032cba332
-size 7292
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index de021bcb26..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ae2b5be95736d02b76b34d3156b61a626edb738f3dd64f75c1a6c36b9413df0b
-size 24968
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 92a44e38ca..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members_null_RoomMemberList-N-2_4_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d74867084116e6784cf2da68345c5367cb48fd017692d072f012efb27dd88786
-size 11633
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-D-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-D-4_4_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..88554d0d72
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-D-4_4_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f34dde086bbd14cc5cfeb0003d6bfd0d3868ecbc497956dc17794e69f6773279
+size 40070
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-D-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-D-4_5_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4dc3771f2f..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-D-4_5_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c1900985fb6b9d25eee44af5cdc0c6fcdee994ffc3da8dae5a6e742c3d0b7127
-size 40016
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-N-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-N-4_5_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..9adff83203
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-N-4_5_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ef0545d55a8a78af5d8286fc590998c973cee986a3f6aff91850cf277d47a5e5
+size 36415
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-N-4_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-N-4_6_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 352d1a95e1..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomNotificationSettings-N-4_6_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7d1c7b862db8afe25dd958c9011de82e1439d298a95ec15a86dfbae0457f7937
-size 36436
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomPrivacyOption-D-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomPrivacyOption-D-3_3_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomPrivacyOption-D-3_4_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomPrivacyOption-D-3_3_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomPrivacyOption-N-3_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomPrivacyOption-N-3_4_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomPrivacyOption-N-3_5_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_null_RoomPrivacyOption-N-3_4_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index cd275d2b96..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b845bb8d92a35424cd81a5355c0eb69cd069bdf327d28caf69326355e4c33e93
-size 53658
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4f5d817402..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6825637037556cf9440d0cc2303f73e88e29385d1fce9e7670106c96ea3143f6
-size 53597
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 53423131be..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6cca72e75685d2dbc6eb1b2025f8c487c443006d077cc560e6abafd16e06e6c9
-size 46510
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 8425c5405e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6a4c6e92b895251c2c075f3679f7d31480bf1eacfa3592ef17e7d11c79120397
-size 54529
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 7b04c98e13..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2072ec79ee1aad73cd20eaf0d87106b8757223a2499fff67c5c806223a705c66
-size 51626
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index e54b8591f4..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:537e9ab994b8a6c6589b03e9c4231cd9b984cad4f233d106425d3d0e65af8864
-size 53852
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index e54b8591f4..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:537e9ab994b8a6c6589b03e9c4231cd9b984cad4f233d106425d3d0e65af8864
-size 53852
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index dc6c756bd1..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_0_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:183145910ec9cd8a5c19e30bcdf0995dcfc70c7a9a176fe28f6106b2dd5b21d3
-size 54827
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d1f3592333
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9b8490acab6fef18eda1b673caed8f499356532fe0383b5ea64eae3a98bf4d8c
+size 53884
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..782e6bf7f6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:78974cfe5b86c3f6788c58250936c0e786f5843ddf24f8e9e3bb832d1a9e8ead
+size 53935
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b0a7809d5b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:90065f5871a1a0886d5a51ac3334998e549326e49aa612dbe6c6e399422a453f
+size 46716
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..18db460f0c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b365242f676e9fd7325e35b98580761b9c0dac1edf9a7b10e27c52c1604d68ce
+size 54820
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..68579460b4
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa7d1423e88bd4ff8abd0487525b25dfc30e912c03c626282f67f6b5a3681d56
+size 51948
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b792ec9bda
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:edc60ef43aae57282fea83915e71d5a59fc38747631e13ddb9609b5393f01cd4
+size 54074
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b792ec9bda
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:edc60ef43aae57282fea83915e71d5a59fc38747631e13ddb9609b5393f01cd4
+size 54074
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a1ecae8832
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6a7ea54f3b28185d4af4ef042645daffc606bb09092a28e19f3a993aff086f5f
+size 55111
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..09de93bdda
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2f9081ff6774d7f117d2da282bd37b84cdd0fd13f10f0279ba7913f722939d9a
+size 54138
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index ae23a77cbc..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:470a655997e6cd489a74d31e6db8641b48fd7974db87547f2d2efc9a4c8901bf
-size 52503
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1f23fa479a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:70b8a0df5f3184e54b372a07bba99381ddc107e6dea58eee96dcfd4f0d04c022
-size 50857
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 00c3f8d140..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fc70f4008e918612b15ce7d55d26092c632a08f3179c98452bd3acc9111dbb9f
-size 43979
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index ab80b1cf18..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:dd9f45108f2ec9739cb414bd11751e9ed41044948d5ddd70e9afaf7d75b52b3a
-size 53305
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 40942f08a9..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c01f60be5fdd30d9f75de6dc8a272954123e14a05eca2dc3fe643fe8769ff218
-size 50239
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0efa94ce47..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:275e0112aeed3097ef7eb295ef6dc7aedf6bccb0add1167c823b78953c887e33
-size 51963
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0efa94ce47..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:275e0112aeed3097ef7eb295ef6dc7aedf6bccb0add1167c823b78953c887e33
-size 51963
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1b87302d36..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:eb9e612cc985121de0a8a506066dfecad22eb283232a6815e7a50197c1af5a1c
-size 53640
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_8,NEXUS_5,1.0,en].png
deleted file mode 100644
index 5914da7d56..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_1_null_8,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2efcf4a4db75ffdf8f2a2bcc841eb8c27b5497f8724754360256dec83b494f5b
-size 52747
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d48e98ffbf
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d2d5651dbb53d95939d999538b86415cbc6e35334d418b4d922345b7e5413597
+size 52622
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4c88ba2eba
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5e0c0d5cd843bc4b6b8010c845c311280ad03c96416ae311d6795d79e5126fcb
+size 51046
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..70abc2078f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:037159d025ba2c8b675b7cdbed7f223cd544471f96950b2f21b3c3e7a81fd69f
+size 44133
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..86d3e67efb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8695ecfe2e090e382586008e79e767a9f2ca76fd09e92ae88d4a63cf245a0d54
+size 53487
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..386bdd6eea
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c890ffdf4d553dcbc4cecabc8acb434f32344a0b5e97c21b99485f113ab8aa6a
+size 50466
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..11c8c67636
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:61083c142467bb3634710cc5739d55c04b89e2e705193e7f3e4375539b1e2bb5
+size 52149
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..11c8c67636
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:61083c142467bb3634710cc5739d55c04b89e2e705193e7f3e4375539b1e2bb5
+size 52149
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8f37512aae
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4438e1dde9935d4f1bbf651727edcce5b82b534fd4b45360622c063d82971da5
+size 53813
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f81d5404a6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:19679e9c3990478c0ec969c934f225d6d559d99711c29b5a5433dadc2ea02db5
+size 52840
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-D-4_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-D-4_4_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7a4021f41f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-D-4_4_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1ea39cc532f9e523365e08d836a11d3aef4ffc8a653a95949be6c595c3f8711a
+size 36845
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-D-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-D-4_5_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 0d8fd2c681..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-D-4_5_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8ec89c6b12c4eb1f8059d9d0d183f6635f5647393134c858720f3ef72b8c4c36
-size 36837
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-N-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-N-4_5_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0450054b40
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-N-4_5_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:30c7cc4bc2c7abe40766edeaac6cd82f0c260f7135cc9964313ccc159011dc6d
+size 42280
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-N-4_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-N-4_6_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 66dd7327db..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_DefaultRoomListTopBar-N-4_6_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e1cd441b7d2955d20d63aa714f3114465b4170d8788c46d27f2605088b9389d6
-size 43030
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-D-3_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-D-3_3_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..5b4c6141ef
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-D-3_3_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cf2f1ba80fa68e1a1277e00a90efd157b38f367cf264b1f75969067f0b71bd65
+size 28736
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-D-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-D-3_4_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 78a9f4cb02..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-D-3_4_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d6aba1cdc5374a31d367202a8f38248c20911ffb5749611ff4b1a19d72eb167a
-size 28779
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-N-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-N-3_4_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d0b1ef9ac2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-N-3_4_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:66111b25139ada1f03833b8c3532fcb138aed48644bd7ff8a6b7e9cca3d99f35
+size 28127
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-N-3_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-N-3_5_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index bd873cf598..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RequestVerificationHeader-N-3_5_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:83a1430cb96aaef6f640c2476b46e4ba59e541c383f6e0cc7b3e7f08cb96d008
-size 28168
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryPlaceholderRow-D-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryPlaceholderRow-D-5_5_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryPlaceholderRow-D-5_6_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryPlaceholderRow-D-5_5_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryPlaceholderRow-N-5_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryPlaceholderRow-N-5_6_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryPlaceholderRow-N-5_7_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryPlaceholderRow-N-5_6_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..17690c5868
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b8f07e6b40dc7b9416bfe5d5c54643e0d7db9bd833c0bf9356ad423b788a16a7
+size 12610
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ccc6a19f74
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:14e5fcabf19783912bb03af5cde9df47eab7b6e22d6c9184f3513a8ab365507d
+size 13527
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3adbb062f0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:387f4543d02524e71992346335bd85c7fc457408dbe5cb23f8a1d4492dc0aeba
+size 13280
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_6,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_6,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_6,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_7,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_7,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_6_null_7,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 6b4a39b0f8..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:496059d16235c52562acf31c209d917b3eff6142324e07476e01a732d9aaf816
-size 12661
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index d08e94a349..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6f6bfe205fe096afe6d976ed242d823d02d06a01b8f81e5feca7b9a71d7b5767
-size 13543
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4a51acc8ba..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-D-6_7_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8337468ce85f5f1aa9fec867d9037b71c7460fa823c7f6fdcbf0efe48ad97d40
-size 13293
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8674ffbf54
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7ec6af35e17f96fbbb0ea56f2b290c0056be5c4b88ef6ae04201052ee665d46f
+size 12441
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..90537c126f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6495acf5edc645ad532334bf6f90cd07a7580d38057c42fe6922a1571d0c1a08
+size 13318
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..cc9127f0ad
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6054256c5f6791485c9505fe7354a01659c4552908c639aa5f6abd17fe8adcec
+size 13101
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_6,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_6,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_6,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_7,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_7,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_7_null_7,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index 44621eb2da..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4129396a4857169b149cb13df2296770264ef4561c76296240da44da1c14a6f2
-size 12489
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 39aeda0981..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:442b27761e8d74a654d6a4e0a873c26768a625d9acbdc32dcfa008b7b7038e15
-size 13343
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index ad84f80ca2..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_null_RoomSummaryRow-N-6_8_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a536dab221ace52712e46f375744dc62f7825b2d7ef4e9fe903a8159f623825a
-size 13122
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-D-7_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-D-7_7_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ea9b2edb09
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-D-7_7_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e649c7fc1608661f8910e86200016a5e2dfe3395cf140c3819dcc7c42d28a2a6
+size 29940
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-D-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-D-7_8_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 976af63965..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-D-7_8_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0a7a00ebee01aea35dd50cfa5155f6ff58efb21fdf62efb934bf70411563d7cc
-size 29908
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-N-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-N-7_8_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f3a113fc8f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-N-7_8_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:86bc6476c9cdc9187e3e4411c693124e1b6e2ac28dc85014c05925741d322be0
+size 29790
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-N-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-N-7_9_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 5d23ac3205..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_null_RoomListSearchResultContent-N-7_9_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:392c865a3d45c0ab5270241d31159075edfe020526b0fbe39593e46b29da958b
-size 29819
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-D-0_0_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-D-0_1_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-D-0_0_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-D-0_0_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-D-0_1_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-D-0_0_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-N-0_1_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-N-0_2_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-N-0_1_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-N-0_1_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-N-0_2_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_InvitesEntryPointView-N-0_1_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-D-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-D-1_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..28386ad492
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-D-1_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f990c6058d9dab7273cf886da59672f611c47b5507548fb18c66ceea16ccf1a8
+size 12257
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-D-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-D-1_2_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4d8b6fc79c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-D-1_2_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ca4fc08cbb7b10efa57f94b2f1d4791722575a6a6809a7b6acf19087a2477116
-size 12203
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-N-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-N-1_2_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c234c4fd9a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-N-1_2_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e0b16b915427c8e1e64d2f62c16cece51dc119e2844d50f585c9f245fca27f3c
+size 11615
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-N-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-N-1_3_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 00b723308a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListModalBottomSheetContent-N-1_3_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f73042f7a89cffd436c8498b8643884d4396c1e69c28e3880966a7e8e6ce57dd
-size 11566
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d1e7ae7489
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0e1be8a38303b5d443f8e0911c34497400b1b577b711191c52ad00b67229a7d1
+size 65314
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0cdf38dc42
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:05e8a1cabb49befa77b2a89c20e83e2f8355242e8868ae075e7967c7a14ca870
+size 89054
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d1e7ae7489
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0e1be8a38303b5d443f8e0911c34497400b1b577b711191c52ad00b67229a7d1
+size 65314
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..90ead2e7fe
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3057d4d0d83a51dda75f7701698ed8be2f2a99d01e5e43ab639f59210cc3a9a5
+size 65326
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4fbb84a0e6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:952766c0bbe97ba343ebc68966c0148da68bb4dd8c802dd98f937ca8974a314e
+size 66408
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6389639819
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:44b50286480b8351ace34656d7396e2dab9694fc5a19fd9226d5008b87b2bb30
+size 66794
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..cec02adb7a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dcb426273cb249eeb92d60733ede317bc211177fe55915a6b8cd64a871628e8c
+size 4913
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ea9b2edb09
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e649c7fc1608661f8910e86200016a5e2dfe3395cf140c3819dcc7c42d28a2a6
+size 29940
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_8,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2f2ac0e0c0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_2_null_8,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0
+size 4462
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index aa486dbf8e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3a9958e430c07445b9dcdc31a483c7925e2bd2a0d3fbbaf1ee5482051e2afe51
-size 65488
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4ae4e14d2e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c238218a16f7dc5c25125177434a8397821d5881971dd0404b9d79b7b181a592
-size 89294
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index aa486dbf8e..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3a9958e430c07445b9dcdc31a483c7925e2bd2a0d3fbbaf1ee5482051e2afe51
-size 65488
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index b12ac33325..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8176884d48589815521a35465f564858b4082b523f905e8d4cf7cbc96e25ac5e
-size 65496
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index 1f5a18bf95..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:86f43007158129ed8e4a637df2db4044bdeb9d504e2a5c40da957bedea9ff395
-size 66618
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index f8847daf13..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4543927c7e26cd464d28d3fe4dffda79479bcc8835d82088863031775bf6262e
-size 67004
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index b1f6332ebf..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:eb67d4f855911385bbbe7a41a6f92bfa225dae3c048f0aea078320a6969a133d
-size 4864
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 976af63965..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-D-2_3_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:0a7a00ebee01aea35dd50cfa5155f6ff58efb21fdf62efb934bf70411563d7cc
-size 29908
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2feff83fa7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c1e7bd8361314f95f67930a3795066d0028fd64dbafb79c2d699596d95f6db95
+size 67522
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..222acb9e53
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5e4fdc7a1af79a5c2113f7c8f27f1c22463fbb3a3dbdddf83f7258ba513ea06a
+size 90775
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_2,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2feff83fa7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_2,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c1e7bd8361314f95f67930a3795066d0028fd64dbafb79c2d699596d95f6db95
+size 67522
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..dacdeac66b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f630b7f9eae799cfea12bebb5df52c60e8a5a3007d0a7d6febd90c945bd67566
+size 67292
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_4,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4137dcfae9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_4,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0c1fc5a06c2399f36ea14f1135be2a98a836485c980f08523dca2b79edbcee78
+size 69111
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3a9f34d9e1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b83df3739ab45b9b96461dd56e69befdb7119e3bc68fa141bd6b4086e384a02c
+size 69466
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_6,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..64886ad79c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_6,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8f24387fc575a3c154b6f3dfa3590b175e4d8878e576f8dbafbc7b48676e86af
+size 4865
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_7,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f3a113fc8f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_7,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:86bc6476c9cdc9187e3e4411c693124e1b6e2ac28dc85014c05925741d322be0
+size 29790
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_8,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2f2ac0e0c0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_3_null_8,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0
+size 4462
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index b95bcd7122..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:383b8960e8213b4446550f375928fa31e3614fb1ab4fa01af9e4d5e049dfb7d6
-size 68203
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index ca548d6a4d..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2cd15aeda43f9b3fa48381e0055accff87c935e32151af6e7f8c3d19d16277cb
-size 91432
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_2,NEXUS_5,1.0,en].png
deleted file mode 100644
index b95bcd7122..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_2,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:383b8960e8213b4446550f375928fa31e3614fb1ab4fa01af9e4d5e049dfb7d6
-size 68203
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_3,NEXUS_5,1.0,en].png
deleted file mode 100644
index 084ac01b53..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_3,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:933fa8151559d5419566da1b7e3f6327be492c00d31cbb4a28c20e8af48b8c1e
-size 68303
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_4,NEXUS_5,1.0,en].png
deleted file mode 100644
index b6688390cb..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_4,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6a29a7b4dc950e19b81b3654787d0a5de27cb261dfbbca4da3437fa5bd5c6b2b
-size 69793
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_5,NEXUS_5,1.0,en].png
deleted file mode 100644
index 33ddf8045c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_5,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:93afdae4908c62ec1c642c2cc9cc08e11cb5f08cddfee49ec8bfa9bbf7cfc685
-size 70129
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_6,NEXUS_5,1.0,en].png
deleted file mode 100644
index 5da66a5ec9..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_6,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3ac28972ef51cdaced878078d7f2e4ecbd7d5923a56b3ec9b07f5dd92997bc54
-size 4858
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_7,NEXUS_5,1.0,en].png
deleted file mode 100644
index 5d23ac3205..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_null_RoomListView-N-2_4_null_7,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:392c865a3d45c0ab5270241d31159075edfe020526b0fbe39593e46b29da958b
-size 29819
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_null_SignedOutView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_null_SignedOutView-D-0_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..82a7ca445f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_null_SignedOutView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:25d69103d52132a0d47f3e5c4feebc4a981e4d382255e312c83973fff4e0b3e8
+size 60658
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_null_SignedOutView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_null_SignedOutView-N-0_1_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..32059527ae
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_null_SignedOutView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5844f66e9c07ba824fb3d1871281fc385ec7c0898cae0bf8e532bea1ef2278fb
+size 58885
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_1_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-D-0_0_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_3,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_3,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_3,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_4,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_4,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_4,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_5,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_2_null_5,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.verifysession.impl_null_VerifySelfSessionView-N-0_1_null_5,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.atoms_null_InfoListItemMolecule-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.atoms_null_InfoListItemMolecule-D_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 8a24228226..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.atoms_null_InfoListItemMolecule-D_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:55525ed3084083c2d76dc94d17fe5fb41975c021c1b7849914a79784a853882f
-size 19311
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.atoms_null_InfoListItemMolecule-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.atoms_null_InfoListItemMolecule-N_1_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index e5c14e10d5..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.atoms_null_InfoListItemMolecule-N_1_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3077b55e49bae679bf5afa96158c1720131d4e8de0a7269544362c866c4b659a
-size 18673
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_IconTitleSubtitleMolecule-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_IconTitleSubtitleMolecule-D_0_null,NEXUS_5,1.0,en].png
index 4dfeab877c..8fb30525d0 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_IconTitleSubtitleMolecule-D_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_IconTitleSubtitleMolecule-D_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:162a11f98fc16c2cf2de9380594bb29893c067479f5c9b2405b66b58474ae8c7
-size 9850
+oid sha256:b514737761239d60aac83a2b5d822c1079b7029474097f5ea4a4a29a88174e81
+size 10687
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_IconTitleSubtitleMolecule-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_IconTitleSubtitleMolecule-N_1_null,NEXUS_5,1.0,en].png
index a3b7c10f3d..95a1fb946e 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_IconTitleSubtitleMolecule-N_1_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_IconTitleSubtitleMolecule-N_1_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:084f2204ad301b53da20491393024995a9391e6e2f00f7d7be63ef8f003b1972
-size 9753
+oid sha256:d648d16fa94fa4add5784cb0ca32173cc447dfd592bcf88ff82b53894c02f137
+size 10527
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_InfoListItemMolecule-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_InfoListItemMolecule-D_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..48421f17bf
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_InfoListItemMolecule-D_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cfaf25db5b5f53240416bc0c0bbebc5d1f3f95818c93ea47b67ed7d919e32ab5
+size 19527
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_InfoListItemMolecule-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_InfoListItemMolecule-N_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..34e2bad93b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.molecules_null_InfoListItemMolecule-N_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:524df16c773bdd643389fc7de33247ed2e6c8d77e67c3119a9f438bf0b5b0fdd
+size 18837
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.organisms_null_InfoListOrganism-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.organisms_null_InfoListOrganism-D_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b98fc16eeb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.organisms_null_InfoListOrganism-D_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:897ee73ef00a3a322a5ab99b6b3d39945e0b62fea72f1099ace542e4cff893cf
+size 13104
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.organisms_null_InfoListOrganism-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.organisms_null_InfoListOrganism-N_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d703c086a7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.atomic.organisms_null_InfoListOrganism-N_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:770f21753ce0d8f85e8e20b7d74f4ff903da693e5ca9704ef6f2430895656454
+size 12437
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_null_Buttons_BackButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_null_Buttons_BackButton_0_null,NEXUS_5,1.0,en].png
index b7bbe45340..6a9db51c01 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_null_Buttons_BackButton_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_null_Buttons_BackButton_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c37c5cb1394da3a0b28fdd749f356f8a5d132f5408a88bc6e367052452a828d0
-size 7607
+oid sha256:e46b9b820f7ff19ed00db0e786d50ee6c94d764112507d2ba0c5ee7312d4f86f
+size 7634
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png
index a37e0d6661..44b3ccdc7f 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:537fafbe54aa0b7ddbc7e756ca07f82b20b9b20e47f1f422bbeb3d385b6290bc
-size 14427
+oid sha256:ef39621c2f80917dcce22382566345d84a93dd96cf8db0bad1b873874d267591
+size 15110
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_PreferenceView-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_PreferenceView-D_0_null,NEXUS_5,1.0,en].png
index 5f626ab733..41be7186b0 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_PreferenceView-D_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_PreferenceView-D_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0f4dc8afc12c4870cb5685f1570ab44fea5f48660dcaef17173e7540751104e8
-size 27533
+oid sha256:759a7ccc4c2cc545660cfad732330b19cb4701d305bfdf451ecb353f8de7500a
+size 28588
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_PreferenceView-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_PreferenceView-N_1_null,NEXUS_5,1.0,en].png
index 608e4ce6c6..8ea3601b6d 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_PreferenceView-N_1_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_PreferenceView-N_1_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:884a8c4a156f16318d45377b4c722176f3a828384b2a3ce772c19e29c903ea50
-size 25556
+oid sha256:ccc75c01551d2bf417ca005f0f2da7b916929d590d99257107362f6abe018653
+size 26556
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png
index fbb0ac4e60..9d135a0b9b 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3b60bc49d6f61e5034963c930d377e85729c592d826b114b786d090218390945
-size 28872
+oid sha256:c38a3ec04f2e88faa51b95cebe18a61f6590150bf655a066544e4bcc5c3b00e6
+size 30441
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceCheckbox_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceCheckbox_0_null,NEXUS_5,1.0,en].png
index e47c72a8a1..4a3139eb0d 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceCheckbox_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceCheckbox_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:14f5d1ade1a2f9331732615aa15441cc6ef62bf3980cad731a95b3c8a9512da7
-size 28383
+oid sha256:b29ba6e66834babaf615ecf7c384cc03bbee0a3373cfc478e3517819da0ad853
+size 28805
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceSlide_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceSlide_0_null,NEXUS_5,1.0,en].png
index 8ef91487d5..b3db45f136 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceSlide_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceSlide_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:97c1c93f1430364928c1ea10571202695173464e64685c19528ef90eebc2ec73
-size 13917
+oid sha256:8382aa61eddcbd357a269e83018ead4bd951f73a0e27eaa512a87f1c75f42c74
+size 15809
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceSwitch_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceSwitch_0_null,NEXUS_5,1.0,en].png
index 48c2c528e5..615066b23b 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceSwitch_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceSwitch_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a60c6f2121be35d5c1bc3a35e662e52166b01a18d5cc07fb751ea8e70506fd8b
-size 17714
+oid sha256:f0f7189265e954d287b19dfd023ced8229393f84fb4dd3a1e9eb269b0debe3bd
+size 17874
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceText_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceText_0_null,NEXUS_5,1.0,en].png
index f757da3a86..97d734e024 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceText_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_null_Preferences_PreferenceText_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:bd05144d4b3527a44e1c9f3fb92d227ccc38fd7afce5436f32b59f14f0b54e27
-size 39028
+oid sha256:514851f2cdb43698787949873ca19e2da26287e341dbc43192b76593532ec582
+size 43820
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom-N_1_null,NEXUS_5,1.0,en].png
index 2c9be4925e..366b13e42d 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom-N_1_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom-N_1_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0a2f25931e9de2edbcc29d149fcb656683d1ff30757b9acde43e058f214d8afc
-size 70873
+oid sha256:b2d4bd4cfa4d1556682f1b59b080d551ab8a11009755ed85181c00b32005c1ff
+size 68722
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_0,NEXUS_5,1.0,en].png
index a6024a3965..7b151a7e89 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:52d6fc6f88ce1e80dfe48ad83476e70afa5b5499ec24e8a94bbbc4b0e611e16d
-size 76231
+oid sha256:ade2748c46ccc2705ff1f3074e279b048519f25f7e0f2985c116e4da7e1adfdc
+size 71065
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_1,NEXUS_5,1.0,en].png
index 51aed9c9e7..cd4a12174b 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4611148bcc9140bf40a63859ec88bac212a9774d81a5fe5abcfea9afec4192bf
-size 75130
+oid sha256:9baff29bc497962832f186c65bd82df8484593f279b4fd49c65a942b3b8e2b52
+size 72038
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_2,NEXUS_5,1.0,en].png
index 0860041efc..075fb9482d 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:39473b8fb90fcef316df257c4b3a45aa9c441199ef169b275e660d58d7fae52b
-size 72606
+oid sha256:323786260b184088351d98658634c45432209ee6efa99aa6d140408c70ecdb14
+size 67982
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_3,NEXUS_5,1.0,en].png
index b5b50230ac..7db5d45a6f 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:572db9d4c1796ec37e113907d5c13728cb87ce9959d94bb9396d7ea38914e52b
-size 76355
+oid sha256:e5d104097c15d823264d6e2216d9c70ced3ed42968840c53b2c2577db4472d4e
+size 72268
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_4,NEXUS_5,1.0,en].png
index c7f31854cb..8789a2cccf 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6ca9d811ba70d8642e4831a1d62b94960c79fb28f93a3c36d67d2d67c1b376fc
-size 74706
+oid sha256:26b53f6566ab650e62025a2a2c408bd327b9faa07f622cb5679600039f4257fc
+size 70057
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_5,NEXUS_5,1.0,en].png
index 81a543bd06..689c652901 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2319d9245e6f218f0b77d419d99ba101eda112184fc70729633006da0a4ee03d
-size 74372
+oid sha256:4d392afe26f6ac4294488cd4e9f4fb33dbf3e12a418053b2e4184475bfbe97f4
+size 69399
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_6,NEXUS_5,1.0,en].png
index 781cf6f3a6..33e7c6e3e6 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6131175dabf7e1835856d37c37f8cdacaaa7294b44d4676a0bd4d32d4d2a1337
-size 79118
+oid sha256:c4a4754541af2817156fd1aba7e65f9b9ea48de68f3142646fb0dedc4a4d1073
+size 72811
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_7,NEXUS_5,1.0,en].png
index 9f56ea678a..f6d4d064f6 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_BloomInitials-N_1_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ce7f7219bd9027f0e6f3d8b44a4170b1e058c549cc143fbf63d67eab3e0eee2b
-size 69351
+oid sha256:1d16d96dcf90fb83c8dd870da0c70b53b6f7655998e362b76811e4ce7092de8c
+size 66529
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_0,NEXUS_5,1.0,en].png
index d06c97ace6..682b614df0 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3bf9d2e16fa86ec87e38fd82ecde90176542057d58729837d8aee73eb4828d12
-size 63936
+oid sha256:7c50b77630a1fbeef982fc61c8003b61f9468e2d20eb83461c5544598fe320a2
+size 63841
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_1,NEXUS_5,1.0,en].png
index f0e507f655..352ac80480 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e6575825152b070f8c2f6abe5ab89f5d1426d76df908638f3934c217193f1aac
-size 58297
+oid sha256:ad7d52dec927841c7b05b447c85cec5dd22715ab51eb9ac58408a5e5cd58152c
+size 60607
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_2,NEXUS_5,1.0,en].png
index 4f5be56d3f..949ed18a4c 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:17d225fbb3e9d7f1a954c0bc94b94cc1d7f131d226347b32c30940ba03991154
-size 67928
+oid sha256:ed35ba6ea3c1b59c325e6e87682f65d37030ce22b21f1971a23b582f7920f008
+size 67287
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_3,NEXUS_5,1.0,en].png
index cd121a5eb5..51bf08207a 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1f05a11dcd4174a1784d6c7fcd32154a1064d0a3369b18c857f051ef72c09943
-size 64470
+oid sha256:4f319642073688178211971992ea9f8f148056bdcf32d1784c3f5058d738908e
+size 63928
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_4,NEXUS_5,1.0,en].png
index 053b94e8a0..39849a8444 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1e6b22c6a7acbdbc77ebad06831318467ad40bdfbe8c56a0a81e8e9d69de4ff0
-size 66145
+oid sha256:77fcc8873a0e2f9bb2b8199aef58cb4477a4997930d84417dd689da183faa1c8
+size 66073
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_5,NEXUS_5,1.0,en].png
index a51c3b1758..f142fd872c 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:29fd768d347c14c0e9f99de9e2c4e341f76850b8447ccd30fb240590bc8cd706
-size 68885
+oid sha256:116672eb16c9ce26568e84167137d96de3cf846e7516831976e1a66287f24983
+size 67517
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_6,NEXUS_5,1.0,en].png
index 5328ba5fe0..8c17959d1c 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8e18c010bfd1cf93b5b30cf38b58b3cc24d3e1fdb992050786e35004d112563f
-size 61822
+oid sha256:47571cc16e4ca99e8d00ad19f8bafa16a94f3b263ad71fbaeec70dbaa2dcbaed
+size 61781
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_7,NEXUS_5,1.0,en].png
index 46e82926c4..33018c3a2b 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_BloomInitials_0_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:21465f36c39930d08edbe2b099ff0cd2765292ee29a724720f75760066845440
-size 67954
+oid sha256:a62203cc72ab5e0619ad353f6741a3c26f7a3f72ea4b263c1a8539028f43359b
+size 68141
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_Bloom_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_Bloom_0_null,NEXUS_5,1.0,en].png
index 17ad5a90a2..021d782909 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_Bloom_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_null_Bloom_Bloom_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b06bbc06573f8b25cb97e6de6c621c2580a29a2b0c688a5e5135e886af524eb8
-size 77557
+oid sha256:f81ce6f66e9d1054b6c272b64fa9c9859f4a21e9f94839f2a58fec49476868c8
+size 77558
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart1-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart1-D_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ce8e61a07f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart1-D_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f01e4a5aa80ebd835acd0db64c7cdd5a85401a18bec329d7c72e76d3d3162c6e
+size 70477
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart1-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart1-N_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e6d7e99f37
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart1-N_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1a21c942689c26517e6f11075cf57899dafff43c9e66ae5b1f1aaa78caf7f6f9
+size 67641
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart2-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart2-D_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..45a128bb67
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart2-D_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6c26892b083fb99f5fceaaf8a2d8e394845923909e6b42d9edaaeaec3a67b68c
+size 84056
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart2-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart2-N_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0c5dacc25a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsCompoundPart2-N_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6a609735752829eb320c52cc07fa9e9beae50046b36555c5043d603805f5f435
+size 80929
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsOther-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsOther-D_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3ff2f3e4a2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsOther-D_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:69c2af16154cecb849b9af865583e4150edf4105f8ec10b78dbb4aab082fb51e
+size 33648
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsOther-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsOther-N_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d6390cfb0f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsOther-N_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ec7c6add0edda4898d3c03623c12c7e72de17bf3a8fade410aa2da39b5a4fa42
+size 32056
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsSeptember-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsSeptember-D_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..50f1985c0d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsSeptember-D_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:750d377a57496379e29530e0e984a992a8a88b687a448862699e57cfcfb4d1d9
+size 45043
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsSeptember-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsSeptember-N_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e60e74e4fb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_null_IconsSeptember-N_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:89fcbc1c72c1020d58627c1782bfa7ef0e4844f0ea819b999be90c8b5d52e8ea
+size 42998
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_0_75f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_0_75f__0_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_0_75f_0_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_0_75f__0_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_1_0f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_1_0f__0_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_1_0f_0_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_1_0f__0_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_1_5f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_1_5f_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 6f7eca2dec..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_1_5f_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c6ac3ab7ac24942fca51adfc3cc8bf5ad797e912baf835baa73e8289b6d4a94d
-size 28308
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_1_5f__0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_1_5f__0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..7335dae68d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.text_null_DpScale_1_5f__0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:54244cea4f74dbc5b78360f5ca58807532145e63c4d5d8d2faf057a9c1fc6a3a
+size 28270
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerDark_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..938e97d2af
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerDark_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:00b8a072864b530c3b736b24254775f4c3885a66d6fadc0a09b797dfa685757c
+size 32407
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerLight_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..91266d7cde
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerLight_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1ab11e7dd119809b04dcc3d495a78404ac8fb28186d5080f69d9c024a09555a2
+size 33676
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewDark_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index c839472d5c..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewDark_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2776f6426818ed5faa46dc877848940a91b68773ee58a9a42490b659c865d1ca
-size 33154
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewLight_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index aa9d0c1950..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewLight_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5a2003610e82a97d359cf009321c2e41cfe40f4fe48604637d6d3b966b21f5ab
-size 34435
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalDark_0_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewDark_0_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalDark_0_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalLight_0_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewLight_0_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalLight_0_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_AppBars_MediumTopAppBar_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_AppBars_MediumTopAppBar_0_null,NEXUS_5,1.0,en].png
index c930226c4a..7dcedfcb46 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_AppBars_MediumTopAppBar_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_AppBars_MediumTopAppBar_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1a84e4bb925ccf3384c7616b8d23da7b7307d3c879b0fddf41111c94c46cd4cc
-size 12272
+oid sha256:d52a08dd8698b4367d48cdefb3def8f2256de2b7378d908a12ccad13bdd3fb66
+size 12888
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_AppBars_TopAppBar_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_AppBars_TopAppBar_0_null,NEXUS_5,1.0,en].png
index 641075f7b8..76256beff5 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_AppBars_TopAppBar_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_AppBars_TopAppBar_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b64905b858125cecf68aa7369d8e7224cdda5509856582b7fe5b311eb370ea19
-size 11724
+oid sha256:d285cede9fc617e24b127530e770ed4ff118080714fa1e9071198932898c02fa
+size 12292
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_BottomSheetDragHandle-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_BottomSheetDragHandle-D_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..182cc3a083
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_BottomSheetDragHandle-D_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3c5bd25caa8c462fd9a98fef14f011450adb414ec14d1a8b285d37692185442c
+size 5616
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_BottomSheetDragHandle-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_BottomSheetDragHandle-N_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..86a758d3b0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_BottomSheetDragHandle-N_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:84d00d9738ffbd2b3798e3c31b13d4a086377abaf250049aca24fdfef1132ed8
+size 5459
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_FilledButtonLarge_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_FilledButtonLarge_0_null,NEXUS_5,1.0,en].png
index 4ffb736f71..a82b9c0a1c 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_FilledButtonLarge_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_FilledButtonLarge_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:65faf620bf0074d0cc53f06e0409734a10fcccd0f0c12acda346c9f91d160297
-size 43782
+oid sha256:d0f2dc7bfcf59bf26362775625ddc9dedf53f400057997560628614136e2880b
+size 44144
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_FilledButtonMedium_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_FilledButtonMedium_0_null,NEXUS_5,1.0,en].png
index b14e6fca54..322c44b61f 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_FilledButtonMedium_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_FilledButtonMedium_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5531979897c84dd3f097ec14b270ac51806964482fa1f709fd217480d2749cb8
-size 42386
+oid sha256:1e240c572aab6db77995c93787a7b0aaba76060861c45934eb87d4ba48c5675c
+size 42789
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_IconButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_IconButton_0_null,NEXUS_5,1.0,en].png
index d25604d927..1ef5d1f3e2 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_IconButton_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_IconButton_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0de49c41130b525fd6baf687ccd63b3d98997d9a31d23a0ea826d0dbe7c1998e
-size 10404
+oid sha256:de399d6941e299218c65c17d39a63f1568bb4fb543f6001b2136ea57c9d5c65d
+size 10038
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_OutlinedButtonLarge_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_OutlinedButtonLarge_0_null,NEXUS_5,1.0,en].png
index 572e04a998..47ca9e7c31 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_OutlinedButtonLarge_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_OutlinedButtonLarge_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6e8b24eb2d3d2301ab3575af6764d37327e9a3bc6a64faa380d034734d1a8e2e
-size 47762
+oid sha256:54497bfab92c6ed850f753df61a31366ca68d41c7d90e322e7cc7eceb6dd4277
+size 48139
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_OutlinedButtonMedium_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_OutlinedButtonMedium_0_null,NEXUS_5,1.0,en].png
index a46fe42618..916f75d13e 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_OutlinedButtonMedium_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_OutlinedButtonMedium_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fa9f27f1a530667d8c8c1e975bdaee628cb3387e290a05e7530789b9622c5dd9
-size 47151
+oid sha256:e7d3f2e50b395f86ed7c78146d5a428b099c672d763c278c1b2899d915abf717
+size 47615
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_TextButtonLarge_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_TextButtonLarge_0_null,NEXUS_5,1.0,en].png
index 1f610f0fa1..996018bf70 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_TextButtonLarge_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_TextButtonLarge_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d8233ab7e58175a8dbd14aa8c30dda882176099232cd56417102fa3675efe92b
-size 31114
+oid sha256:876eba4a9f8214aa2d33dad84ccbbb95994c615f25f158cb6596fd4512241bd9
+size 31582
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_TextButtonMedium_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_TextButtonMedium_0_null,NEXUS_5,1.0,en].png
index 9c23dea6cf..021f9c9358 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_TextButtonMedium_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Buttons_TextButtonMedium_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:974dd693b78cc08e278ff81a65e234629cdbf1075d3fbf255895cf1ee2eb906c
-size 29264
+oid sha256:3dd7259e0b14b9ad5e9a6aed46383f91822ee11f1e77b0f2cdc5d2e0b38f7abd
+size 29702
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png
index 30ca4d7948..a2744cb3ed 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0182810e251fe8a355d275bbe97a1f7c8f82577b7f70a769a852811f8e9ee715
-size 57958
+oid sha256:174291d52614bc86f3cf527b24156656f71d6ae616236f1bef5c95b8272fd258
+size 57938
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_FloatingActionButtons_FloatingActionButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_FloatingActionButtons_FloatingActionButton_0_null,NEXUS_5,1.0,en].png
index a09bf2aa83..f08ca0852f 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_FloatingActionButtons_FloatingActionButton_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_FloatingActionButtons_FloatingActionButton_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:63f00a1f3ec151b25e481f9ac389944d457e08b2c5ed21912dbcdbb2b47a579d
-size 11510
+oid sha256:d21a8208c1f5df3ed04281b45066524dccf720c00e9c9386b7c814218cf9c91e
+size 11397
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Icons_IconImageVector_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Icons_IconImageVector_0_null,NEXUS_5,1.0,en].png
index ab2504faaa..3320c28478 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Icons_IconImageVector_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Icons_IconImageVector_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8d18194db4723ff7442839e45aaca9562a135ef31ab840ad0aead84af15fa8bc
-size 5817
+oid sha256:3947efc28ab49d9bbcd2a627bb6ee4daf008293d374820475bc27212dbe41f67
+size 5722
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-BothIcons_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-BothIcons_0_null,NEXUS_5,1.0,en].png
index 388b2d45cc..4e5751bcf4 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-BothIcons_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-BothIcons_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b876fc23d35a7f5867ee23483ab4157e436fb0e3e30b364502a2a513df2246db
-size 11223
+oid sha256:07019c744bdf25bced1ee77b94ca280bf03a612c49b307c40266a1bc37dda73e
+size 11775
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-LeadingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-LeadingIcon_0_null,NEXUS_5,1.0,en].png
index 96f09225de..6a9d76cbcb 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-LeadingIcon_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-LeadingIcon_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e61341edb5605cfd12a81844ed221553afe6a89ad4e8d570f4a8ec27fda81bec
-size 9723
+oid sha256:68c54765226f37b45f4822dd19bac309c5116299369b1dba8e23332cf8ad82b4
+size 10039
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-TrailingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-TrailingIcon_0_null,NEXUS_5,1.0,en].png
index d8c9583b5c..a506fefb81 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-TrailingIcon_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(1line)-TrailingIcon_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6c315182baa7ab1c47bcfc877722b2f51bde7e132af52af2daad5b3dea6059c6
-size 9718
+oid sha256:ffb7d0746445d1472083bb2b464305110966409331ab2ff95370ebfda12df397
+size 10035
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-BothIcons_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-BothIcons_0_null,NEXUS_5,1.0,en].png
index 275f7f041b..1c19cce902 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-BothIcons_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-BothIcons_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ada64d53648a2ff884ab2a19ed5f7194419c5b1491f406231a2857a931fad9cd
-size 21125
+oid sha256:e3ee9115e42c92ec140231039b3622e70558ea43d00a1a6f11f4f1aa45da58e1
+size 21613
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png
index e1d2d2071e..39852f7e8b 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:287cdabed03bd0652fb004d946bedaee7d21c974fc3a2314eaedb6c021e2ff56
-size 21334
+oid sha256:d9c9db6d6372adadb2f5442671e13d0cbd8ea00c7ea7d280acb19164cf578c16
+size 21712
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png
index ff74a35999..94723fda17 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(2lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:eed5943478f8c2f779cf81700affa9dc351b97439e1b43ec7ae58d45dcf88b54
-size 21524
+oid sha256:fa6c920555aa0520b8f6719512c76f92038e4853ac8bee3e5686c42718bc7293
+size 21702
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-BothIcons_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-BothIcons_0_null,NEXUS_5,1.0,en].png
index e094187b2d..ce00aae7c3 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-BothIcons_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-BothIcons_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:79e62ed5a4a628c9ee458e4cc4b91f9bd1f3a1d09a6d6844eee041797d075eb5
-size 27925
+oid sha256:e596872da988ae742c022ddbe8634578046b44529d10593e1b054d0b09178803
+size 28289
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png
index bc32904d8c..a71dfaee29 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-LeadingIcon_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:bc169b112a966f6aceaf8e876907a207d5c102d703bc16b7f22c88f40b802472
-size 26683
+oid sha256:1246741e480d23fc8c0137b1434b6768868fe07ad2ac3dbd576e8fbbc78d96b4
+size 26939
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png
index 36de5644e0..736e3ce12c 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem(3lines)-TrailingIcon_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c61f46ffe12bc61640176e30189e07e2d0cbd259de714fbddc33ce71170aa316
-size 26761
+oid sha256:cf2c7e9650b7d3189931784a1343364e149b2f05fbe1d7b20b7df45d617f9f0c
+size 26945
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Disabled&Icon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Disabled&Icon_0_null,NEXUS_5,1.0,en].png
index 487380c3db..1c50219374 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Disabled&Icon_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Disabled&Icon_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2791dcc27d133c8071c570ebf701cc90be1ce2c87b62f67ebbe5108beae34878
-size 9435
+oid sha256:6cfcc18770fa629f2ca75afb94837966a178734f539393cbc0a02327b0aa3a1e
+size 9724
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Error&Icon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Error&Icon_0_null,NEXUS_5,1.0,en].png
index 9be10924e7..d2c968cfd6 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Error&Icon_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Error&Icon_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:68538824c68e226b340965533bc7a587b5f485a881dcc63c1b678c16a1f573ca
-size 9611
+oid sha256:85e11259b0214eeda3af3833aea3238fd975421cb00f26d989e564bef400e661
+size 9895
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Primaryaction&Icon_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Primaryaction&Icon_0_null,NEXUS_5,1.0,en].png
index d795863c2f..28d6ee0c13 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Primaryaction&Icon_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listitems_Listitem-Primaryaction&Icon_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a410afa415f88ddf85bb33128ed0c2a935c63bd8a93937b301582dd5208a5f0b
-size 9613
+oid sha256:706eea0721a1279648f1344b4a26373adfde1e9ef57655ed089b13d31bed6c0e
+size 9870
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listsections_Listsupportingtext-smallpadding_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listsections_Listsupportingtext-smallpadding_0_null,NEXUS_5,1.0,en].png
index b6851470fc..bc54689325 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listsections_Listsupportingtext-smallpadding_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Listsections_Listsupportingtext-smallpadding_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:acb1adfada4482915a2b42b86c9b73c5815fe992647e67b065f569b45ae69779
-size 26304
+oid sha256:90e79fd9f2d16666c1d339776ae8fac04ba879edc946522320d7c672ad9bf805
+size 26577
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png
index adde7faeb5..1bbc577143 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e41414d3715550657ae815cf0977123073e34995f659cb8da754c59b444341fc
-size 21415
+oid sha256:0e9aaaad9ee2138d37f93e4d4adfbe235ca42c1cd5fd1eaa5fa55bbe55879379
+size 25687
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveEmptyQuery_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveEmptyQuery_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..584bea65be
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveEmptyQuery_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f66546fc56a478990d29431011668318350c1ebb36eb721017b115d951da27ef
+size 8395
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithContent_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithContent_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..73976dc148
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithContent_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a367bc814cebbdfd7315784cf4c8cb1e9c02d3c35eea8c6597065f67e7cafcf3
+size 25585
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithNoResults_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithNoResults_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c245c46a4a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithNoResults_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:45b6ef423c50b68d6e93e9d7674f5e2e79a5fde2ca100d35feb1ee61fd07a57b
+size 10132
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithQueryNoBackButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithQueryNoBackButton_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d2d5289d29
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithQueryNoBackButton_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f1c56b67d460b88f8b3ada622149f6d3966755a71d08febabcc8fb4d9203a7a1
+size 7707
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithQuery_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithQuery_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..fcffb603f5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarActiveWithQuery_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:67bc7d4f6bb49560aa0422d84abb42a59e9f4c2d174370cea54957d1b06f2825
+size 8012
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarInactive_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarInactive_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e43c39827f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarInactive_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5b5768b8ac2807b1ed4765b0bcfcd0a1e69cde50da9a3aa1fae7ae2791e25c24
+size 15359
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveEmptyQuery_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveEmptyQuery_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index fdc2f44016..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveEmptyQuery_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7b71883bd5345105597f7cd5215825adabb1dd92cdb6360bcd550fb25a984cc1
-size 8332
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithContent_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithContent_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index afa725f225..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithContent_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8490dff1e4b3f070ef98413986f0386b781a93e0cbee4b9eec389271bd9377a1
-size 25648
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithNoResults_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithNoResults_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 2413bdb894..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithNoResults_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:adef37216ff245220a2ba26920deb1904225b1dbce9337c2dd0f81e74c0f4968
-size 10210
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithQueryNoBackButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithQueryNoBackButton_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 5dc03ad34a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithQueryNoBackButton_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e4b713c1e470b43abeff5e757385c57f3fc7af7a88352448a25c351e2b6f62ad
-size 7805
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithQuery_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithQuery_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index be8a910bfb..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewActiveWithQuery_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b19967ac5da41682f480458292e60d28419697e928bb8f4d69584fe42b128a5c
-size 8090
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewInactive_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewInactive_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 90fd68e3a9..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Searchviews_SearchBarPreviewInactive_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:56ee11a5894d05b76affa75ef15259349afe5c1696ad227ff600e3e0f937fca2
-size 15196
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png
index c9f389feeb..ed53456e0c 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c3db0996d8d1f7bf3e0c5cf06e1a775d74a898ecaef1452c9bb1ac0972d9d14a
-size 19510
+oid sha256:08b390f64db460575ca9461c6bf3cc48955b3d4909fe6013821342f603f2baf9
+size 19422
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png
index 91aa0c225f..1527ab6ad3 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:16c31631b7768267803310f7a81d5cd6323caf125bf88ace8390de9bc5363c37
-size 20001
+oid sha256:c004a5843ef4ef402ec6437dbabc721fe31d18f9c9fb515ddb7b0daf22c826c9
+size 19831
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_AvatarActionBottomSheet-D-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_AvatarActionBottomSheet-D-0_0_null,NEXUS_5,1.0,en].png
index 293aa9d1ca..7e75e60938 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_AvatarActionBottomSheet-D-0_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_AvatarActionBottomSheet-D-0_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ce2248eee21e5ea1a9d852ede40378c437e7159e5e85aba6b2565581ddc7593a
-size 15039
+oid sha256:c33717dcff9a7ba5f05ae7f84b1327c4e9a1834d58f40692c255a70d22d3860b
+size 15144
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_AvatarActionBottomSheet-N-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_AvatarActionBottomSheet-N-0_1_null,NEXUS_5,1.0,en].png
index 3fe16d2d01..321d1a07eb 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_AvatarActionBottomSheet-N-0_1_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_AvatarActionBottomSheet-N-0_1_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d5a1ccdaf1887ced2324be150eea5fbb31aade6fa1362cb69181db5bc3a54933
-size 13279
+oid sha256:a93a7a7668f542a7984f0b6df5571acd202308b6d03500e0e2bedbddb544c30a
+size 13312
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_CheckableUnresolvedUserRow_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_CheckableUnresolvedUserRow_0_null,NEXUS_5,1.0,en].png
index a65a5470e1..c23c20ab26 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_CheckableUnresolvedUserRow_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_CheckableUnresolvedUserRow_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fd6796e3461493cde5e861a05dfccd8bf40e1ceebb66273d1c5486a7a96684b1
-size 116160
+oid sha256:1b097e8b5359de857a2027cefbf26204bd9399bcef97b5bebdf194b2a0e74077
+size 116201
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedRoom-D-5_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedRoom-D-5_5_null,NEXUS_5,1.0,en].png
index 0fa91f8ca3..c51ed2f7bc 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedRoom-D-5_5_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedRoom-D-5_5_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5d725ba73157dbb895b34594538ebc149765db558d87a463e7ec24b424bac4da
-size 8791
+oid sha256:8659449fd048ce6c3666e5dcfd4412a5d8ab7a302fb73d2282a45bfd2543044f
+size 8750
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedRoom-N-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedRoom-N-5_6_null,NEXUS_5,1.0,en].png
index 7a2afc7da3..31b251d020 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedRoom-N-5_6_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedRoom-N-5_6_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:05b49d8680ea7b0c768c25f0295372bc69ed2362a1863b29fa862619987e3d70
-size 8954
+oid sha256:a84879fdb00ec04ce11f43ffb20477a46007af593b85ffa4b2dbe1e40f67b8b1
+size 8916
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUser-D-6_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUser-D-6_6_null,NEXUS_5,1.0,en].png
index e766c6c946..193828ce0c 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUser-D-6_6_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUser-D-6_6_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2b00ab54a071453b87e3243bc8bc2f07520af89864c3776c112b23748e2e6e5e
-size 8965
+oid sha256:5eff8f248a844b07ab8031f51a01a4d94c6b5dd1649f96c565e7d29cd1edabf4
+size 8925
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUser-N-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUser-N-6_7_null,NEXUS_5,1.0,en].png
index 50f413e0b9..8055c5dc4d 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUser-N-6_7_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUser-N-6_7_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f4e35f6fbdb80ea675663ded5952cd247600102167dc7d1a01fad11a6d32bcca
-size 9094
+oid sha256:39705ea5ac180cd9b6864ebf9fbc3d2d6ef1b0892eacd15916738f337ef9caee
+size 9057
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUsersList-D-7_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUsersList-D-7_7_null,NEXUS_5,1.0,en].png
index ac50871cb5..c5ce8b21bf 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUsersList-D-7_7_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUsersList-D-7_7_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0d3fa7cc10b81df36f01ef13c39f033d5cda8489bc2226f5358f21fb7ad9801f
-size 74098
+oid sha256:3a14903508a496eab5dd55e43c055d8b915472b21a9d9806fd9aa1a308b0d955
+size 73502
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUsersList-N-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUsersList-N-7_8_null,NEXUS_5,1.0,en].png
index 1165f7e1ca..865246e2ff 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUsersList-N-7_8_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_SelectedUsersList-N-7_8_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3743af1c1e9a2ed7f59eba8961a1ba183a42991fd0134a055e6de974c8660259
-size 73025
+oid sha256:08e67defd047f92efb8cd86e63ece4ecd5f86cab07383840efffc2e49bf74958
+size 72490
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_UnresolvedUserRow_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_UnresolvedUserRow_0_null,NEXUS_5,1.0,en].png
index 89a8e3dcfd..7cb0f5eb8e 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_UnresolvedUserRow_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_null_UnresolvedUserRow_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7beca493206b2dd0c48792e391ae5b980ee6bd5e12ee435dadabaab647b816f6
-size 32239
+oid sha256:3c260ee71b504fd44adcad0e434b424a0dfd574afaa354197f64983177a3d643
+size 32242
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_0,NEXUS_5,1.0,en].png
index 9cc7127ed4..502407ff36 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9787976d603dddf43993583b316cf05fde9473d09e17641632ee93f1869a4314
-size 28307
+oid sha256:31363274a1a4205e24640d1b02d32108afcabc62ee019da8f85442562fcf1b32
+size 32713
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_1,NEXUS_5,1.0,en].png
index 2cfb0b3e04..1a95753f0a 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a10a6b3ec837277fe6665847c85c9dac481d121b053b924b0c3334824f4497e3
-size 38688
+oid sha256:9ba708a9862d0b67b444ec1f92e076e8d6b0be27d3d519b997c2270add4c5ace
+size 31946
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_2,NEXUS_5,1.0,en].png
index e633306e21..58dae239ef 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0c492e1d390829e70e099ccd7c94f970c51f0d2289376cae273dded8e1e2adb5
-size 32183
+oid sha256:7c31acff0050557fee63af7653416990e72d0b865d1f7dc8c572f197a330f891
+size 32491
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..207e50c689
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-D-0_0_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:abddf374b03a92fc98014c693cad5aca21cab5d79e781016ac761dc1e1242ec2
+size 25233
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_0,NEXUS_5,1.0,en].png
index 635b47b57f..c59c20845e 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6bd4c43636ee860ee8f22548596327d8be88b3c14246a763be1fd5580606f6d3
-size 24193
+oid sha256:f4557db65e49c39dcec5a74d44d1662bef08a7c557027f8cf0af494e8262edb9
+size 28389
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_1,NEXUS_5,1.0,en].png
index f6db105f79..60236462b3 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:109f7dafffc356b4c79e6325d462e3e313183419956cbf7f492c5767d0d6cfa8
-size 33810
+oid sha256:1e4d70183cbb0d145850007c894bda6bec14e1ab955cecc362c4842de4455e8b
+size 27548
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_2,NEXUS_5,1.0,en].png
index 5398306b1b..8c22c300dc 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3a4e69aca1d1697796ceb859efe993c30ad778e6633be7cf471edbee3ea7fbbe
-size 27821
+oid sha256:cf1535f9bfe230c0fd8cd777ac3afefed5d1dd383a5a17eceffd466e6712477b
+size 28149
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_3,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..0793b6e369
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.permissions.api_null_PermissionsView-N-0_1_null_3,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:70677ed0d1f162a17414deb276cfe7b20ea1b8a76f688b6566c67c2fe984a2ee
+size 21679
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_ComposerOptionsButton-D-7_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_ComposerOptionsButton-D-7_7_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d90adf9d9e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_ComposerOptionsButton-D-7_7_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:377c36c5f1019bb6838749fd93c91dec703da825f75e5600ef028d2aaeaa79ac
+size 5646
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_ComposerOptionsButton-N-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_ComposerOptionsButton-N-7_8_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..073873fe76
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_ComposerOptionsButton-N-7_8_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6bbe1dbe62743ca682a48e9e777e8b2b09064afeb2ce36448a62452faaca5d67
+size 5629
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_DismissTextFormattingButton-D-8_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_DismissTextFormattingButton-D-8_8_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ffb2d2a6e1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_DismissTextFormattingButton-D-8_8_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bb2d844445877bdd615c4b9fcedd9759ccf4fa7e72b8a740a74af18f8b7d18f8
+size 5925
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_DismissTextFormattingButton-N-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_DismissTextFormattingButton-N-8_9_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..94f6a515a8
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_DismissTextFormattingButton-N-8_9_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8e3fb36aa1c559c6d3ae8ca2399531b5090229131ba23b710d46639d3a26b6ec
+size 5847
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_FormattingButton-D-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_FormattingButton-D-9_9_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_FormattingButton-D-7_8_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_FormattingButton-D-9_9_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_FormattingButton-N-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_FormattingButton-N-9_10_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_FormattingButton-N-7_9_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_FormattingButton-N-9_10_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordButton-D-10_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordButton-D-10_10_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f0c42b1810
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordButton-D-10_10_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a0e8a1efaf22dd86e6e27904d72a820cfb0b5d1d38ffeb5745bfa6ca3b0a1c85
+size 6003
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordButton-N-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordButton-N-10_11_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..27368109ad
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordButton-N-10_11_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7f2db2983459eae8b7538c51d125836807b29334b01fbca495c3c9630fb510c2
+size 5969
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordingProgress-D-11_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordingProgress-D-11_11_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..015499b3dc
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordingProgress-D-11_11_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:47e727f1ff283a022aa174266b83c837aa60b379c0c410e94d9ff6b792f2aead
+size 7390
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordingProgress-N-11_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordingProgress-N-11_12_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8c5cc08564
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_RecordingProgress-N-11_12_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:16ac898bc3ca7c827f52612fb5f2c6051de4f746a1328dacaf6fba1cefa6f896
+size 7053
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_SendButton-D-12_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_SendButton-D-12_12_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d32c73e543
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_SendButton-D-12_12_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:60410832f66d0c70166443d855743ef956b1b97a3684624ce5797805d71e04f6
+size 8712
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_SendButton-N-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_SendButton-N-12_13_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..93591c72a9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_SendButton-N-12_13_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:54c5208fd88e1f404667cdc0b39ff87824115a1c414dfdbd55a334fa6a445548
+size 8619
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_TextFormatting-D-13_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_TextFormatting-D-13_13_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..635fbd2b36
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_TextFormatting-D-13_13_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b43968125ef27b3b9918251590befe6e6a7d781342e8d915833fa1cba395f17d
+size 7211
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_TextFormatting-N-13_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_TextFormatting-N-13_14_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..361795f953
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_null_TextFormatting-N-13_14_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a7ea766f311321684f8a387a7be9e2f88a4f27a4adb85a9b0f4d0b3e56854ee0
+size 7034
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-D-2_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-D-2_2_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e6274d3ab9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-D-2_2_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a0c5c53f53eb3cdda73391fabda2658b5c009e9a13761ef9acae37530f3cb1b5
+size 14017
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-D-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-D-2_3_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 3fa65b5075..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-D-2_3_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:192e86517a6add5eb3d58d9a3d4633afc455666fff4472f112c605dc441f24d1
-size 13843
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-N-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-N-2_3_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..59f70b2c3c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-N-2_3_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e5b786b07d92459399099e0f7730804de2f2123d8c5eb51ca64f7609768ecc34
+size 13157
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-N-2_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-N-2_4_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index c11445bc3f..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerEdit-N-2_4_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ea17e3d7f26d1b3f233ee9db5875dcb363b22de12ed07d32693a2c0aa727fbb6
-size 13001
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-D-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-D-1_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..bfc1d62dbc
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-D-1_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:54f6712d9cbd60c6a6d108ddcff08492a5e74cbba7d1f4b5f3312a068e4dd798
+size 43207
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-D-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-D-1_2_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index ed1d6af41d..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-D-1_2_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:347218e4ad8d52fcd2b0deda0d2dda981abea78254e8e76d561ab231f32038cc
-size 41518
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-N-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-N-1_2_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e30b7ac05a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-N-1_2_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e156ed6831abb52d8bbd95934517f423d0cae06e21e9266b099174aad55228ab
+size 40662
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-N-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-N-1_3_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 909c894d2a..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerFormatting-N-1_3_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:c7c73e66ae25e77a2f70c3051339a352dfda3d4448870b26bc7cce8145861c2c
-size 39241
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLink-D-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLink-D-4_4_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLink-D-4_5_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLink-D-4_4_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLink-N-4_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLink-N-4_5_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLink-N-4_6_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLink-N-4_5_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLinkWithoutText-D-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLinkWithoutText-D-5_5_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLinkWithoutText-D-5_6_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLinkWithoutText-D-5_5_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLinkWithoutText-N-5_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLinkWithoutText-N-5_6_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLinkWithoutText-N-5_7_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogCreateLinkWithoutText-N-5_6_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogEditLink-D-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogEditLink-D-6_6_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogEditLink-D-6_7_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogEditLink-D-6_6_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogEditLink-N-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogEditLink-N-6_7_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogEditLink-N-6_8_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerLinkDialogEditLink-N-6_7_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-D-3_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-D-3_3_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..9c64fdc104
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-D-3_3_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:339df847ee60d9b2b6da7458b9b99c54877551325cfcdcefe8677f4f9edd72e3
+size 87578
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-D-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-D-3_4_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index c244d01fb3..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-D-3_4_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1b8da842d61ebcdede9a5337890279d740288c1a07b1b42505457ab65408cc76
-size 84240
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-N-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-N-3_4_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b0073ea6e0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-N-3_4_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ff51194566d9c09bb054cf7be73147215ec267382ce7bf0552f4b8de6bb357ec
+size 83398
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-N-3_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-N-3_5_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index a50bba4ac8..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerReply-N-3_5_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:feae2409d9887cd752d2cff4be059a566c071094955b824faa72555e69269d91
-size 80648
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-D-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-D-0_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f00037d6dd
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-D-0_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f50efca595312474668341a2bb6715992286bb52d4645774ed9fb1a83f94ca9f
+size 48112
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-D-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-D-0_1_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index de4ca7c908..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-D-0_1_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ed1aa73f94dac6839f91729bcea59b4b3b14b885fa32a90335faf483e9f886f8
-size 45176
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-N-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-N-0_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..9d14e51ab1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-N-0_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7c50736a0b06dd848b79b263cdb231fa9b3ae23ad049615d6073ce1838b8ca5f
+size 45219
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-N-0_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-N-0_2_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index f5a53317ca..0000000000
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerSimple-N-0_2_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:76f54b7c7ab5c54b53a0fa9e410db942a2bce35fc0552569707ba2f3928ebd3b
-size 42306
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_null_ColorsSchemePreviewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_null_ColorsSchemeDark_0_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_null_ColorsSchemePreviewDark_0_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_null_ColorsSchemeDark_0_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_null_ColorsSchemePreviewLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_null_ColorsSchemeLight_0_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_null_ColorsSchemePreviewLight_0_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.theme_null_ColorsSchemeLight_0_null,NEXUS_5,1.0,en].png
diff --git a/tools/adb/callLinkCustomScheme.sh b/tools/adb/callLinkCustomScheme.sh
new file mode 100755
index 0000000000..d556d88f70
--- /dev/null
+++ b/tools/adb/callLinkCustomScheme.sh
@@ -0,0 +1,23 @@
+#! /bin/bash
+#
+# Copyright (c) 2023 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.
+#
+
+# Format is:
+# element://call?url=some-encoded-url
+# For instance
+# element://call?url=https%3A%2F%2Fcall.element.io%2FTestElementCall
+
+adb shell am start -a android.intent.action.VIEW -d element://call?url=https%3A%2F%2Fcall.element.io%2FTestElementCall
diff --git a/tools/adb/callLinkCustomScheme2.sh b/tools/adb/callLinkCustomScheme2.sh
new file mode 100755
index 0000000000..d6dabd595c
--- /dev/null
+++ b/tools/adb/callLinkCustomScheme2.sh
@@ -0,0 +1,23 @@
+#! /bin/bash
+#
+# Copyright (c) 2023 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.
+#
+
+# Format is:
+# io.element.call:/?url=some-encoded-url
+# For instance
+# io.element.call:/?url=https%3A%2F%2Fcall.element.io%2FTestElementCall
+
+adb shell am start -a android.intent.action.VIEW -d io.element.call:/?url=https%3A%2F%2Fcall.element.io%2FTestElementCall
diff --git a/tools/adb/callLinkHttps.sh b/tools/adb/callLinkHttps.sh
new file mode 100755
index 0000000000..00ff341de3
--- /dev/null
+++ b/tools/adb/callLinkHttps.sh
@@ -0,0 +1,23 @@
+#! /bin/bash
+#
+# Copyright (c) 2023 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.
+#
+
+# Format is:
+# https://call.element.io/*
+# For instance
+# https://call.element.io/TestElementCall
+
+adb shell am start -a android.intent.action.VIEW -d https://call.element.io/TestElementCall
diff --git a/tools/danger/dangerfile.js b/tools/danger/dangerfile.js
index 7270f334ae..d183911c39 100644
--- a/tools/danger/dangerfile.js
+++ b/tools/danger/dangerfile.js
@@ -132,8 +132,8 @@ if (allowList.includes(user)) {
const previewAnnotations = [
'androidx.compose.ui.tooling.preview.Preview',
- 'io.element.android.libraries.designsystem.preview.LargeHeightPreview',
- 'io.element.android.libraries.designsystem.preview.DayNightPreviews'
+ 'io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight',
+ 'io.element.android.libraries.designsystem.preview.PreviewsDayNight'
]
const filesWithPreviews = editedFiles.filter(file => file.endsWith(".kt")).filter(file => {
diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml
index 14193a1c70..1c226dd5ed 100644
--- a/tools/detekt/detekt.yml
+++ b/tools/detekt/detekt.yml
@@ -3,6 +3,8 @@
style:
AlsoCouldBeApply:
active: true
+ OptionalWhenBraces:
+ active: false
CascadingCallWrapping:
active: true
includeElvis: true
@@ -124,7 +126,7 @@ potential-bugs:
allowExplicitReturnType: false
MissingPackageDeclaration:
active: true
- excludes: ['**/*.kts']
+ excludes: [ '**/*.kts' ]
NullCheckOnMutableProperty:
active: true
NullableToStringCall:
@@ -173,7 +175,7 @@ naming:
active: true
FunctionNaming:
active: true
- ignoreAnnotated: ['Composable']
+ ignoreAnnotated: [ 'Composable' ]
LambdaParameterNaming:
active: true
NonBooleanPropertyPrefixedWithIs:
@@ -234,7 +236,7 @@ Compose:
active: true
MultipleEmitters:
active: true
- # You can optionally add your own composables here
+ # You can optionally add your own composables here
# contentEmitters: MyComposable,MyOtherComposable
MutableParams:
active: true
@@ -244,7 +246,7 @@ Compose:
# allowedComposableFunctionNames: .*Presenter,.*MoleculePresenter
ComposableParamOrder:
active: true
- PreviewNaming:
+ PreviewAnnotationNaming:
active: true
PreviewPublic:
active: true
diff --git a/tools/lint/lint.xml b/tools/lint/lint.xml
index 715131226a..4da7a0bba3 100644
--- a/tools/lint/lint.xml
+++ b/tools/lint/lint.xml
@@ -124,4 +124,7 @@
+
+
+
diff --git a/tools/localazy/config.json b/tools/localazy/config.json
index c51e0f1eed..7e07d269c0 100644
--- a/tools/localazy/config.json
+++ b/tools/localazy/config.json
@@ -25,6 +25,12 @@
"screen_onboarding_.*"
]
},
+ {
+ "name": ":features:signedout:impl",
+ "includeRegex": [
+ "screen_signed_out_.*"
+ ]
+ },
{
"name": ":features:invitelist:impl",
"includeRegex": [
@@ -50,6 +56,12 @@
"rich_text_editor.*"
]
},
+ {
+ "name": ":libraries:permissions:api",
+ "includeRegex": [
+ "dialog\\.permission_.*"
+ ]
+ },
{
"name": ":libraries:androidutils",
"includeRegex": [
@@ -132,6 +144,7 @@
{
"name": ":features:preferences:impl",
"includeRegex": [
+ "screen_advanced_settings_.*",
"screen_edit_profile_.*"
]
},
diff --git a/tools/sdk/build_rust_sdk.sh b/tools/sdk/build_rust_sdk.sh
new file mode 100755
index 0000000000..6853642218
--- /dev/null
+++ b/tools/sdk/build_rust_sdk.sh
@@ -0,0 +1,80 @@
+#!/usr/bin/env bash
+
+# Exit on error
+set -e
+
+# Ask to build from local source or to clone the repository
+read -p "Do you want to build the Rust SDK from local source (yes/no) default to yes? " buildLocal
+buildLocal=${buildLocal:-yes}
+
+date=$(gdate +%Y%m%d%H%M%S)
+
+# Ask for the Rust SDK local source path
+# if folder rustSdk/ exists, use it as default
+if [ ${buildLocal} == "yes" ]; then
+ read -p "Please enter the path to the Rust SDK local source, default to ../matrix-rust-sdk" rustSdkPath
+ rustSdkPath=${rustSdkPath:-../matrix-rust-sdk/}
+ if [ ! -d "${rustSdkPath}" ]; then
+ printf "\nFolder ${rustSdkPath} does not exist. Please clone the matrix-rust-sdk repository in the folder ../matrix-rust-sdk.\n\n"
+ exit 0
+ fi
+else
+ read -p "Please enter the Rust SDK repository url, default to https://github.com/matrix-org/matrix-rust-sdk.git " rustSdkUrl
+ rustSdkUrl=${rustSdkUrl:-https://github.com/matrix-org/matrix-rust-sdk.git}
+ read -p "Please enter the Rust SDK branch, default to main " rustSdkBranch
+ rustSdkBranch=${rustSdkBranch:-main}
+ cd ..
+ git clone ${rustSdkUrl} matrix-rust-sdk-$date
+ cd matrix-rust-sdk-$date
+ git checkout ${rustSdkBranch}
+ rustSdkPath=$(pwd)
+ cd ../element-x-android
+fi
+
+
+cd ${rustSdkPath}
+git status
+
+read -p "Will build with this version of the Rust SDK ^. Is it correct (yes/no) default to yes? " sdkCorrect
+sdkCorrect=${sdkCorrect:-yes}
+
+if [ ${sdkCorrect} != "yes" ]; then
+ exit 0
+fi
+
+# Ask if the user wants to build the app after
+read -p "Do you want to build the app after (yes/no) default to yes? " buildApp
+buildApp=${buildApp:-yes}
+
+# If folder ../matrix-rust-components-kotlin does not exist, clone the repo
+if [ ! -d "../matrix-rust-components-kotlin" ]; then
+ printf "\nFolder ../matrix-rust-components-kotlin does not exist. Cloning the repository into ../matrix-rust-components-kotlin.\n\n"
+ git clone https://github.com/matrix-org/matrix-rust-components-kotlin.git ../matrix-rust-components-kotlin
+fi
+
+printf "\nResetting matrix-rust-components-kotlin to the latest main branch...\n\n"
+cd ../matrix-rust-components-kotlin
+git reset --hard
+git checkout main
+git pull
+
+printf "\nBuilding the SDK for aarch64-linux-android...\n\n"
+./scripts/build.sh -p ${rustSdkPath} -m sdk -t aarch64-linux-android -o ../element-x-android/libraries/rustsdk
+
+cd ../element-x-android
+mv ./libraries/rustsdk/sdk-android-debug.aar ./libraries/rustsdk/matrix-rust-sdk.aar
+mkdir -p ./libraries/rustsdk/sdks
+cp ./libraries/rustsdk/matrix-rust-sdk.aar ./libraries/rustsdk/matrix-rust-sdk-${date}.aar
+
+
+if [ ${buildApp} == "yes" ]; then
+ printf "\nBuilding the application...\n\n"
+ ./gradlew assembleDebug
+fi
+
+if [ ${buildLocal} == "no" ]; then
+ printf "\nCleaning up...\n\n"
+ rm -rf ../matrix-rust-sdk-$date
+fi
+
+printf "\nDone!\n"
diff --git a/tools/templates/file_templates.zip b/tools/templates/file_templates.zip
deleted file mode 100644
index 7352ac3074..0000000000
Binary files a/tools/templates/file_templates.zip and /dev/null differ
diff --git a/tools/templates/files/IntelliJ IDEA Global Settings b/tools/templates/files/IntelliJ IDEA Global Settings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tools/templates/files/fileTemplates/Template Module Feature Build Gradle API.kts b/tools/templates/files/fileTemplates/Template Module Feature Build Gradle API.kts
new file mode 100644
index 0000000000..5c72896315
--- /dev/null
+++ b/tools/templates/files/fileTemplates/Template Module Feature Build Gradle API.kts
@@ -0,0 +1,11 @@
+plugins {
+ id("io.element.android-library")
+}
+
+android {
+ namespace = "io.element.android.features.${MODULE_NAME}.api"
+}
+
+dependencies {
+ implementation(projects.libraries.architecture)
+}
diff --git a/tools/templates/files/fileTemplates/Template Module Feature Build Gradle Impl.kts b/tools/templates/files/fileTemplates/Template Module Feature Build Gradle Impl.kts
new file mode 100644
index 0000000000..6b40a1d06f
--- /dev/null
+++ b/tools/templates/files/fileTemplates/Template Module Feature Build Gradle Impl.kts
@@ -0,0 +1,34 @@
+plugins {
+ id("io.element.android-compose-library")
+ alias(libs.plugins.anvil)
+ alias(libs.plugins.ksp)
+ id("kotlin-parcelize")
+}
+
+android {
+ namespace = "io.element.android.features.${MODULE_NAME}.impl"
+}
+
+anvil {
+ generateDaggerFactories.set(true)
+}
+
+dependencies {
+ implementation(projects.anvilannotations)
+ anvil(projects.anvilcodegen)
+ api(projects.features.${MODULE_NAME}.api)
+ implementation(projects.libraries.core)
+ implementation(projects.libraries.architecture)
+ implementation(projects.libraries.matrix.api)
+ implementation(projects.libraries.matrixui)
+ implementation(projects.libraries.designsystem)
+
+ testImplementation(libs.test.junit)
+ testImplementation(libs.coroutines.test)
+ testImplementation(libs.molecule.runtime)
+ testImplementation(libs.test.truth)
+ testImplementation(libs.test.turbine)
+ testImplementation(projects.libraries.matrix.test)
+
+ ksp(libs.showkase.processor)
+}
diff --git a/tools/templates/files/fileTemplates/Template Module Feature Entry Point API.kt b/tools/templates/files/fileTemplates/Template Module Feature Entry Point API.kt
new file mode 100644
index 0000000000..6297ec4e24
--- /dev/null
+++ b/tools/templates/files/fileTemplates/Template Module Feature Entry Point API.kt
@@ -0,0 +1,20 @@
+package io.element.android.features.${MODULE_NAME}.api
+
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import io.element.android.libraries.architecture.FeatureEntryPoint
+
+interface ${FEATURE_NAME}EntryPoint : FeatureEntryPoint {
+
+ fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
+
+ interface NodeBuilder {
+ fun callback(callback: Callback): NodeBuilder
+ fun build(): Node
+ }
+
+ interface Callback : Plugin {
+ // Add your callbacks
+ }
+}
diff --git a/tools/templates/files/fileTemplates/Template Module Feature Entry Point Flow Impl.kt b/tools/templates/files/fileTemplates/Template Module Feature Entry Point Flow Impl.kt
new file mode 100644
index 0000000000..adfd142ae5
--- /dev/null
+++ b/tools/templates/files/fileTemplates/Template Module Feature Entry Point Flow Impl.kt
@@ -0,0 +1,30 @@
+package io.element.android.features.${MODULE_NAME}.impl
+
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.features.${MODULE_NAME}.api.${FEATURE_NAME}EntryPoint
+import io.element.android.libraries.architecture.createNode
+import io.element.android.libraries.di.AppScope
+import javax.inject.Inject
+
+@ContributesBinding(AppScope::class)
+class Default${FEATURE_NAME}EntryPoint @Inject constructor() : ${FEATURE_NAME}EntryPoint {
+
+ override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): ${FEATURE_NAME}EntryPoint.NodeBuilder {
+ val plugins = ArrayList()
+
+ return object : ${FEATURE_NAME}EntryPoint.NodeBuilder {
+
+ override fun callback(callback: ${FEATURE_NAME}EntryPoint.Callback): ${FEATURE_NAME}EntryPoint.NodeBuilder {
+ plugins += callback
+ return this
+ }
+
+ override fun build(): Node {
+ return parentNode.createNode<${FEATURE_NAME}FlowNode>(buildContext, plugins)
+ }
+ }
+ }
+}
diff --git a/tools/templates/files/fileTemplates/Template Module Feature Node Flow Impl.kt b/tools/templates/files/fileTemplates/Template Module Feature Node Flow Impl.kt
new file mode 100644
index 0000000000..d08d67ae38
--- /dev/null
+++ b/tools/templates/files/fileTemplates/Template Module Feature Node Flow Impl.kt
@@ -0,0 +1,57 @@
+package io.element.android.features.${MODULE_NAME}.impl
+
+import android.os.Parcelable
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.bumble.appyx.core.composable.Children
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import com.bumble.appyx.navmodel.backstack.BackStack
+import com.bumble.appyx.navmodel.backstack.operation.push
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import io.element.android.anvilannotations.ContributesNode
+import io.element.android.libraries.architecture.BackstackNode
+import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
+import io.element.android.libraries.architecture.createNode
+import io.element.android.libraries.di.AppScope
+import kotlinx.parcelize.Parcelize
+
+// CHANGE THE SCOPE
+@ContributesNode(AppScope::class)
+class ${FEATURE_NAME}FlowNode @AssistedInject constructor(
+ @Assisted buildContext: BuildContext,
+ @Assisted plugins: List,
+) : BackstackNode<${FEATURE_NAME}FlowNode.NavTarget>(
+ backstack = BackStack(
+ initialElement = NavTarget.Root,
+ savedStateMap = buildContext.savedStateMap,
+ ),
+ buildContext = buildContext,
+ plugins = plugins,
+) {
+
+ sealed interface NavTarget : Parcelable {
+ @Parcelize
+ object Root : NavTarget
+ }
+
+ override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
+ return when (navTarget) {
+ NavTarget.Root -> {
+ //Give your root node or completely delete this FlowNode if you have only one node.
+ createNode<>(buildContext)
+ }
+ }
+ }
+
+ @Composable
+ override fun View(modifier: Modifier) {
+ Children(
+ navModel = backstack,
+ modifier = modifier,
+ transitionHandler = rememberDefaultTransitionHandler(),
+ )
+ }
+}
diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt
new file mode 100644
index 0000000000..aa44bc4269
--- /dev/null
+++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt
@@ -0,0 +1,22 @@
+#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}#end
+
+import androidx.compose.runtime.Composable
+import io.element.android.libraries.architecture.Presenter
+import javax.inject.Inject
+
+class ${NAME}Presenter @Inject constructor() : Presenter<${NAME}State> {
+
+ @Composable
+ override fun present(): ${NAME}State {
+
+ fun handleEvents(event: ${NAME}Events) {
+ when (event) {
+ ${NAME}Events.MyEvent -> Unit
+ }
+ }
+
+ return ${NAME}State(
+ eventSink = ::handleEvents
+ )
+ }
+}
diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.0.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.0.kt
new file mode 100644
index 0000000000..26372fc970
--- /dev/null
+++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.0.kt
@@ -0,0 +1,15 @@
+#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}#end
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+
+open class ${NAME}StateProvider : PreviewParameterProvider<${NAME}State> {
+ override val values: Sequence<${NAME}State>
+ get() = sequenceOf(
+ a${NAME}State(),
+ // Add other states here
+ )
+}
+
+fun a${NAME}State() = ${NAME}State(
+ eventSink = {}
+)
diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.1.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.1.kt
new file mode 100644
index 0000000000..0c997351f0
--- /dev/null
+++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.1.kt
@@ -0,0 +1,29 @@
+#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}#end
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import io.element.android.anvilannotations.ContributesNode
+import io.element.android.libraries.di.AppScope
+
+// CHANGE THE SCOPE
+@ContributesNode(AppScope::class)
+class ${NAME}Node @AssistedInject constructor(
+ @Assisted buildContext: BuildContext,
+ @Assisted plugins: List,
+ private val presenter: ${NAME}Presenter,
+) : Node(buildContext, plugins = plugins) {
+
+ @Composable
+ override fun View(modifier: Modifier) {
+ val state = presenter.present()
+ ${NAME}View(
+ state = state,
+ modifier = modifier
+ )
+ }
+}
diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.2.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.2.kt
new file mode 100644
index 0000000000..0080f8d905
--- /dev/null
+++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.2.kt
@@ -0,0 +1,35 @@
+#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}#end
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.preview.PreviewsDayNight
+import io.element.android.libraries.designsystem.theme.components.Text
+
+@Composable
+fun ${NAME}View(
+ state: ${NAME}State,
+ modifier: Modifier = Modifier,
+) {
+ Box(modifier, contentAlignment = Alignment.Center) {
+ Text(
+ "${NAME} feature view",
+ color = MaterialTheme.colorScheme.primary,
+ )
+ }
+}
+
+@PreviewsDayNight
+@Composable
+internal fun ${NAME}ViewPreview(
+ @PreviewParameter(${NAME}StateProvider::class) state: ${NAME}State
+) = ElementPreview {
+ ${NAME}View(
+ state = state,
+ )
+}
diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.3.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.3.kt
new file mode 100644
index 0000000000..3fcdd7f219
--- /dev/null
+++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.3.kt
@@ -0,0 +1,7 @@
+#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}#end
+
+// TODO add your ui models. Remove the eventSink if you don't have events.
+// Do not use default value, so no member get forgotten in the presenters.
+data class ${NAME}State(
+ val eventSink: (${NAME}Events) -> Unit
+)
diff --git a/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.4.kt b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.4.kt
new file mode 100644
index 0000000000..4b47069304
--- /dev/null
+++ b/tools/templates/files/fileTemplates/Template Presentation Classes.kt.child.4.kt
@@ -0,0 +1,6 @@
+#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}#end
+
+// TODO Add your events or remove the file completely if no events
+sealed interface ${NAME}Events {
+ data object MyEvent: ${NAME}Events
+}
diff --git a/tools/templates/files/options/file.template.settings.xml b/tools/templates/files/options/file.template.settings.xml
new file mode 100644
index 0000000000..c7d26d1fb7
--- /dev/null
+++ b/tools/templates/files/options/file.template.settings.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/templates/generate_templates.sh b/tools/templates/generate_templates.sh
new file mode 100755
index 0000000000..9b59ea69f8
--- /dev/null
+++ b/tools/templates/generate_templates.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+#
+# Copyright 2023 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.
+#
+
+echo "Zipping the contents of the 'files' directory..."
+
+# Ensure tmp folder exists
+mkdir -p tmp
+
+rm -f ./tmp/file_templates.zip
+pushd ./tools/templates/files
+zip -r ../../../tmp/file_templates.zip .
+popd