Repository: govuk-one-login/mobile-android-one-login-app
Pull request #457: feat: Amend `SplashScreen` with rebranding updates
Author: login: BiancaMihaila
Created: 2025-05-14T08:30:38Z | Merged: 2025-05-16T14:10:13Z

[Tutorial for writing good descriptions]: https://cbea.ms/git-commit/

[//]: # (Be mindful that the PR title also needs to follow conventional commit standards)

# [Update `SplashScreen`](https://govukverify.atlassian.net/browse/DCMAW-12966)

- update the composable to use Material3 only - remove all old components from use
- update the  logo, icon and text to meet accessibility requirements and display as per design
- switch to using `SubcomposeLayout` to render the screen to meet design requirements

[//]: # (e.g. "- Create 'androidLibrary' Gradle module.")

## Evidence of the change
![splash_screen](https://github.com/user-attachments/assets/eebcd168-87eb-4b61-b1fb-b5437a25799e)
![splash_screen_loading](https://github.com/user-attachments/assets/bb5566d2-ffb8-4207-910d-4c7ccd4b2575)
![splash_screen_unlock](https://github.com/user-attachments/assets/8d7bf826-af6b-4df6-bd64-3a4c2f210b8e)

**Logo will still be called in build and staging - this is due to the icon being a button which allows to access the dev menu - however this was disabled fro production and integration (same env that the dev menu is not available at all) - this would need to be tested after the merge. There are unit tests that are checking this behaviour - see features/src/testProduction/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt and features/src/testIntegration/kotlin/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt**

https://github.com/user-attachments/assets/130dc77c-ccc5-46fb-89ae-439643a3659e


https://github.com/user-attachments/assets/4087b36a-9c9b-43b6-b37e-050dabebb8ce

**Welsh evidence**

https://github.com/user-attachments/assets/dc91e9a6-0b13-492a-a75e-45dc56e8e93b

_This is moving two quick to be able to take a video of it, but it should read the text in Welsh as it is displayed_

![splash_loading_welsh](https://github.com/user-attachments/assets/82470e78-783b-4122-847c-0a1ea4ae3fc0)

[//]: # (Screenshots / uploaded videos go here)

## Checklist

### Before creating the pull request

- [x] Commit messages that conform to conventional commit messages.
- [x] Tests pass locally.
- [x] Pull request has a clear title with a short description about the feature or update.

### Before the CODEOWNERS review the pull request

- [x] Complete all Acceptance Criteria within Jira ticket.
- [x] Self-review code.
- [x] Successfully run changes on a testing device.
- [x] Complete automated Testing:
  * [x] Unit Tests.
  * [x] Integration Tests.
  * [x] Instrumentation / Emulator Tests.
- [x] Review [Accessibility considerations].
- [ ] Handle PR comments.

### Before merging the pull request

- [x] [Sonar cloud report] passes inspections for your PR.
- [ ] Resolve all comments.

[Sonar cloud report]: https://sonarcloud.io/project/overview?id=di-mobile-android-ui
[Accessibility considerations]: https://developer.android.com/guide/topics/ui/accessibility/testing

Resolves: DCMAW-12966



Diff:
diff --git a/core/src/main/java/uk/gov/onelogin/core/utils/ModifierExtensions.kt b/core/src/main/java/uk/gov/onelogin/core/utils/ModifierExtensions.kt
new file mode 100644
index 000000000..dd4c404b0
--- /dev/null
+++ b/core/src/main/java/uk/gov/onelogin/core/utils/ModifierExtensions.kt
@@ -0,0 +1,11 @@
+package uk.gov.onelogin.core.utils
+
+import androidx.compose.foundation.layout.offset
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.IntOffset
+
+object ModifierExtensions {
+    fun Modifier.drawAfterMeasured(previousItemHeightPx: Int): Modifier = this.offset {
+        IntOffset(x = 0, y = previousItemHeightPx)
+    }
+}
diff --git a/core/src/main/res/drawable/ic_splash_logo.xml b/core/src/main/res/drawable/ic_splash_logo.xml
new file mode 100644
index 000000000..acc143bf2
--- /dev/null
+++ b/core/src/main/res/drawable/ic_splash_logo.xml
@@ -0,0 +1,48 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="200dp"
+    android:height="92dp"
+    android:viewportWidth="200"
+    android:viewportHeight="92">
+  <path
+      android:pathData="M11.75,18C11.75,19.49 11.94,20.93 12.31,22.3C12.68,23.68 13.25,24.89 14.01,25.95C14.77,27 15.73,27.85 16.91,28.49C18.09,29.13 19.5,29.45 21.13,29.45C22.76,29.45 24,29.2 25.04,28.69C26.08,28.19 26.92,27.56 27.54,26.81C28.16,26.06 28.59,25.24 28.85,24.37C29.1,23.49 29.22,22.68 29.22,21.94V21.6H20.33V16.23H36.05V35.33H29.9V31.24C29.46,31.92 28.92,32.54 28.28,33.1C27.63,33.66 26.9,34.14 26.1,34.54C25.29,34.93 24.4,35.24 23.44,35.47C22.48,35.7 21.46,35.81 20.38,35.81C17.84,35.81 15.59,35.35 13.62,34.43C11.65,33.5 9.99,32.23 8.64,30.61C7.29,28.99 6.27,27.1 5.57,24.93C4.86,22.76 4.51,20.42 4.51,17.91C4.51,15.39 4.9,13.08 5.69,10.9C6.47,8.72 7.58,6.82 9.03,5.21C10.48,3.6 12.22,2.33 14.26,1.4C16.31,0.47 18.59,0 21.13,0C22.78,0 24.34,0.19 25.82,0.57C27.3,0.95 28.64,1.49 29.86,2.2C31.08,2.9 32.16,3.75 33.1,4.75C34.03,5.75 34.79,6.85 35.37,8.07L29.22,11.54C28.85,10.79 28.41,10.1 27.91,9.46C27.41,8.82 26.84,8.27 26.18,7.81C25.53,7.35 24.78,6.98 23.94,6.72C23.1,6.45 22.16,6.32 21.13,6.32C19.5,6.32 18.09,6.64 16.92,7.28C15.74,7.92 14.77,8.76 14.01,9.82C13.25,10.87 12.68,12.09 12.31,13.47C11.94,14.86 11.76,16.29 11.76,17.76V18H11.75Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M58.42,0C61.01,0 63.33,0.47 65.38,1.4C67.43,2.33 69.17,3.6 70.6,5.21C72.03,6.82 73.12,8.72 73.87,10.9C74.62,13.08 74.99,15.41 74.99,17.91C74.99,20.4 74.62,22.76 73.87,24.93C73.12,27.1 72.03,28.99 70.6,30.61C69.17,32.23 67.43,33.5 65.38,34.43C63.33,35.35 61.01,35.81 58.42,35.81C55.84,35.81 53.5,35.35 51.46,34.43C49.42,33.5 47.68,32.23 46.25,30.61C44.82,28.99 43.73,27.1 42.98,24.93C42.23,22.76 41.86,20.42 41.86,17.91C41.86,15.39 42.23,13.08 42.98,10.9C43.73,8.72 44.82,6.82 46.25,5.21C47.68,3.6 49.42,2.33 51.46,1.4C53.5,0.47 55.82,0 58.42,0ZM58.42,29.45C59.94,29.45 61.28,29.16 62.43,28.58C63.59,28.01 64.56,27.21 65.35,26.19C66.14,25.17 66.74,23.96 67.14,22.57C67.55,21.18 67.75,19.65 67.75,18V17.81C67.75,16.17 67.55,14.65 67.14,13.25C66.74,11.85 66.14,10.64 65.35,9.62C64.56,8.6 63.59,7.8 62.43,7.23C61.28,6.65 59.94,6.37 58.42,6.37C56.91,6.37 55.57,6.65 54.4,7.23C53.24,7.8 52.27,8.6 51.48,9.62C50.7,10.64 50.11,11.85 49.7,13.25C49.3,14.65 49.1,16.17 49.1,17.81V18C49.1,19.65 49.3,21.18 49.7,22.57C50.11,23.96 50.7,25.17 51.48,26.19C52.27,27.21 53.24,28.01 54.4,28.58C55.57,29.16 56.91,29.45 58.42,29.45Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M87.38,35.33L77.57,0.49H85.44L92.15,26.68H92.4L98.96,0.49H106.74L96.93,35.33H87.38H87.38Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M146.53,29.44C147.54,29.44 148.49,29.29 149.37,28.99C150.25,28.69 151.01,28.24 151.65,27.63C152.29,27.03 152.79,26.26 153.16,25.32C153.54,24.39 153.72,23.3 153.72,22.03V0.43H160.62V23.08C160.62,25.02 160.27,26.78 159.57,28.35C158.87,29.92 157.89,31.26 156.64,32.36C155.39,33.46 153.9,34.31 152.18,34.91C150.45,35.51 148.56,35.81 146.51,35.81C144.46,35.81 142.58,35.51 140.85,34.91C139.12,34.31 137.63,33.46 136.37,32.36C135.11,31.26 134.13,29.92 133.43,28.35C132.73,26.78 132.38,25.02 132.38,23.08V0.43H139.33V22.03C139.33,23.3 139.52,24.39 139.89,25.32C140.26,26.26 140.76,27.03 141.4,27.63C142.04,28.24 142.8,28.69 143.68,28.99C144.56,29.29 145.51,29.44 146.53,29.44Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M166.86,0.49H173.88V15.58L186.29,0.49H194.94L182.87,14.34L195.71,35.33H187.55L178.37,19.71L173.88,24.73V35.33H166.86V0.49Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M117.76,23.79C121,23.79 123.63,21.16 123.63,17.91C123.63,14.66 121,12.03 117.76,12.03C114.52,12.03 111.9,14.66 111.9,17.91C111.9,21.16 114.52,23.79 117.76,23.79Z"
+      android:fillColor="#00FFE0"/>
+  <path
+      android:pathData="M179.06,57.99H183.12V60.82C183.95,59.68 184.98,58.84 186.21,58.31C187.44,57.77 188.82,57.51 190.33,57.51C191.7,57.51 192.9,57.73 193.93,58.19C194.96,58.64 195.82,59.25 196.51,60.03C197.21,60.8 197.72,61.7 198.06,62.71C198.41,63.73 198.59,64.8 198.59,65.93V82.74H194.54V67.22C194.54,66.14 194.4,65.22 194.12,64.46C193.87,63.68 193.5,63.06 193.04,62.59C192.59,62.11 192.05,61.76 191.42,61.55C190.81,61.33 190.15,61.21 189.44,61.21C188.67,61.21 187.9,61.36 187.15,61.65C186.41,61.92 185.73,62.34 185.12,62.91C184.52,63.47 184.04,64.18 183.67,65.04C183.3,65.89 183.12,66.9 183.12,68.07V82.74H179.06V57.99Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M172.61,82.74H168.56V58H172.61V82.74ZM167.81,50.85C167.81,50.47 167.88,50.1 168.03,49.76C168.19,49.42 168.39,49.13 168.63,48.87C168.89,48.61 169.18,48.41 169.52,48.26C169.88,48.1 170.25,48.02 170.63,48.02C171.02,48.02 171.38,48.1 171.72,48.26C172.06,48.41 172.35,48.61 172.59,48.87C172.85,49.13 173.05,49.42 173.19,49.76C173.35,50.1 173.43,50.47 173.43,50.85C173.43,51.24 173.35,51.61 173.19,51.97C173.05,52.31 172.85,52.6 172.59,52.86C172.35,53.1 172.06,53.31 171.72,53.47C171.38,53.61 171.02,53.69 170.63,53.69C170.25,53.69 169.88,53.61 169.52,53.47C169.18,53.31 168.89,53.1 168.63,52.86C168.39,52.6 168.19,52.31 168.03,51.97C167.88,51.61 167.81,51.24 167.81,50.85Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M141.1,69.01C141.1,67.38 141.34,65.86 141.8,64.46C142.27,63.05 142.94,61.84 143.81,60.82C144.68,59.79 145.72,58.98 146.94,58.4C148.18,57.81 149.57,57.51 151.1,57.51C152.45,57.51 153.74,57.77 154.96,58.31C156.18,58.82 157.22,59.66 158.07,60.82V57.99H162.13V82.74C162.13,84.27 161.82,85.56 161.19,86.59C160.58,87.64 159.77,88.48 158.77,89.11C157.78,89.74 156.64,90.19 155.37,90.46C154.12,90.74 152.84,90.88 151.53,90.88C150.81,90.88 150.05,90.83 149.26,90.75C148.49,90.67 147.71,90.55 146.92,90.39C146.13,90.23 145.35,90.02 144.58,89.76C143.82,89.52 143.1,89.23 142.4,88.89L143.88,85.48C144.36,85.7 144.87,85.92 145.4,86.13C145.93,86.36 146.5,86.56 147.11,86.73C147.74,86.91 148.41,87.06 149.12,87.17C149.82,87.28 150.6,87.34 151.43,87.34C152.46,87.34 153.39,87.24 154.21,87.03C155.05,86.82 155.75,86.48 156.31,86.03C156.88,85.6 157.31,85.03 157.62,84.34C157.92,83.66 158.07,82.84 158.07,81.89V77.19C157.22,78.36 156.18,79.2 154.96,79.74C153.74,80.25 152.45,80.51 151.1,80.51C149.57,80.51 148.18,80.22 146.94,79.64C145.72,79.04 144.68,78.23 143.81,77.19C142.94,76.16 142.27,74.95 141.8,73.56C141.34,72.16 141.1,70.64 141.1,69.01ZM158.17,68.91C158.17,67.8 158.05,66.77 157.81,65.84C157.57,64.88 157.19,64.06 156.67,63.37C156.16,62.66 155.5,62.11 154.69,61.72C153.91,61.32 152.96,61.12 151.85,61.12C150.72,61.12 149.74,61.32 148.92,61.72C148.1,62.12 147.42,62.68 146.87,63.39C146.34,64.09 145.95,64.91 145.69,65.86C145.43,66.81 145.3,67.83 145.3,68.91V69.11C145.3,70.19 145.43,71.2 145.69,72.16C145.95,73.09 146.34,73.92 146.87,74.63C147.42,75.32 148.1,75.87 148.92,76.27C149.74,76.68 150.72,76.88 151.85,76.88C152.96,76.88 153.91,76.69 154.69,76.3C155.5,75.89 156.16,75.35 156.67,74.65C157.19,73.96 157.57,73.13 157.81,72.18C158.05,71.23 158.17,70.2 158.17,69.11V68.91Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M126.68,57.51C128.38,57.51 129.94,57.83 131.34,58.48C132.74,59.11 133.93,59.99 134.91,61.14C135.91,62.27 136.68,63.63 137.23,65.21C137.77,66.77 138.05,68.5 138.05,70.39C138.05,72.25 137.77,73.97 137.23,75.55C136.68,77.11 135.91,78.47 134.91,79.62C133.93,80.75 132.74,81.63 131.34,82.28C129.94,82.91 128.38,83.22 126.68,83.22C124.97,83.22 123.42,82.91 122.02,82.28C120.63,81.63 119.44,80.75 118.44,79.62C117.44,78.47 116.67,77.11 116.13,75.55C115.58,73.97 115.31,72.25 115.31,70.39C115.31,68.5 115.58,66.77 116.13,65.21C116.67,63.63 117.44,62.27 118.44,61.14C119.44,59.99 120.63,59.11 122.02,58.48C123.42,57.83 124.97,57.51 126.68,57.51ZM126.68,79.66C127.85,79.66 128.88,79.42 129.77,78.94C130.67,78.44 131.42,77.77 132.01,76.93C132.62,76.09 133.08,75.12 133.39,74.02C133.69,72.91 133.85,71.75 133.85,70.53V70.24C133.85,69 133.69,67.82 133.39,66.71C133.08,65.6 132.62,64.63 132.01,63.8C131.42,62.96 130.67,62.3 129.77,61.82C128.88,61.32 127.85,61.07 126.68,61.07C125.5,61.07 124.46,61.32 123.56,61.82C122.68,62.3 121.93,62.96 121.32,63.8C120.72,64.63 120.27,65.6 119.96,66.71C119.66,67.82 119.51,69 119.51,70.24V70.53C119.51,71.75 119.66,72.91 119.96,74.02C120.27,75.12 120.72,76.09 121.32,76.93C121.93,77.77 122.68,78.44 123.56,78.94C124.46,79.42 125.5,79.66 126.68,79.66Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M95.7,48.02H99.95V78.92H113.78V82.74H95.7V48.02Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M61.58,71.62C61.66,72.79 61.9,73.86 62.28,74.85C62.67,75.83 63.19,76.69 63.85,77.41C64.53,78.12 65.32,78.68 66.22,79.08C67.13,79.47 68.15,79.66 69.26,79.66C70.63,79.66 71.83,79.44 72.86,78.99C73.9,78.52 74.86,77.94 75.73,77.24L77.81,80.17C76.7,81.06 75.41,81.79 73.94,82.38C72.49,82.94 70.87,83.22 69.07,83.22C67.28,83.22 65.66,82.91 64.21,82.28C62.76,81.63 61.53,80.75 60.49,79.62C59.48,78.47 58.7,77.11 58.15,75.55C57.61,73.97 57.33,72.25 57.33,70.39C57.33,68.5 57.6,66.77 58.13,65.21C58.68,63.63 59.44,62.27 60.42,61.14C61.4,59.99 62.58,59.11 63.95,58.48C65.32,57.83 66.84,57.51 68.51,57.51C70.14,57.51 71.6,57.83 72.9,58.48C74.21,59.11 75.32,59.99 76.24,61.14C77.15,62.27 77.85,63.63 78.34,65.21C78.84,66.77 79.09,68.48 79.09,70.34V71.62H61.58ZM68.37,61.07C67.34,61.07 66.42,61.27 65.61,61.67C64.81,62.08 64.12,62.62 63.54,63.29C62.97,63.97 62.52,64.76 62.18,65.64C61.86,66.52 61.68,67.42 61.63,68.36H74.69C74.66,67.4 74.49,66.49 74.18,65.62C73.89,64.73 73.48,63.96 72.93,63.29C72.4,62.62 71.75,62.08 70.97,61.67C70.22,61.27 69.35,61.07 68.37,61.07Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M34.31,57.99H38.36V60.82C39.2,59.68 40.23,58.84 41.46,58.31C42.69,57.77 44.07,57.51 45.58,57.51C46.95,57.51 48.15,57.73 49.18,58.19C50.21,58.64 51.07,59.25 51.76,60.03C52.46,60.8 52.97,61.7 53.31,62.71C53.66,63.73 53.84,64.8 53.84,65.93V82.74H49.78V67.22C49.78,66.14 49.65,65.22 49.37,64.46C49.12,63.68 48.75,63.06 48.29,62.59C47.84,62.11 47.3,61.76 46.67,61.55C46.06,61.33 45.4,61.21 44.69,61.21C43.92,61.21 43.15,61.36 42.4,61.65C41.66,61.92 40.98,62.34 40.37,62.91C39.77,63.47 39.29,64.18 38.92,65.04C38.55,65.89 38.36,66.9 38.36,68.07V82.74H34.31V57.99Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M0,65.43C0,62.94 0.35,60.62 1.04,58.46C1.75,56.28 2.75,54.39 4.06,52.79C5.36,51.18 6.95,49.91 8.81,48.99C10.68,48.05 12.78,47.58 15.11,47.58C17.45,47.58 19.55,48.05 21.42,48.99C23.28,49.91 24.87,51.18 26.17,52.79C27.49,54.39 28.5,56.28 29.19,58.46C29.9,60.62 30.25,62.94 30.25,65.43C30.25,67.93 29.9,70.27 29.19,72.43C28.5,74.59 27.49,76.47 26.17,78.07C24.87,79.67 23.28,80.93 21.42,81.85C19.55,82.77 17.45,83.23 15.11,83.23C12.78,83.23 10.68,82.77 8.81,81.85C6.95,80.93 5.36,79.67 4.06,78.07C2.75,76.47 1.75,74.59 1.04,72.43C0.35,70.27 0,67.93 0,65.43ZM25.76,65.09C25.76,63.3 25.55,61.57 25.13,59.91C24.73,58.25 24.1,56.78 23.23,55.5C22.36,54.23 21.25,53.21 19.89,52.45C18.56,51.69 16.96,51.31 15.11,51.31C13.26,51.31 11.67,51.69 10.33,52.45C9,53.21 7.89,54.23 7.03,55.5C6.16,56.78 5.51,58.25 5.09,59.91C4.69,61.57 4.49,63.3 4.49,65.09V65.75C4.49,67.54 4.69,69.26 5.09,70.93C5.51,72.59 6.16,74.06 7.03,75.33C7.89,76.59 9,77.61 10.33,78.39C11.67,79.14 13.26,79.52 15.11,79.52C16.96,79.52 18.56,79.14 19.89,78.39C21.25,77.61 22.36,76.59 23.23,75.33C24.1,74.06 24.73,72.59 25.13,70.93C25.55,69.26 25.76,67.54 25.76,65.75V65.09Z"
+      android:fillColor="#ffffff"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_tudor_crown.xml b/core/src/main/res/drawable/ic_tudor_crown.xml
new file mode 100644
index 000000000..802f1b766
--- /dev/null
+++ b/core/src/main/res/drawable/ic_tudor_crown.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="56dp"
+    android:height="53dp"
+    android:viewportWidth="56"
+    android:viewportHeight="53">
+  <path
+      android:pathData="M35.291,16.607C34.608,14.946 35.396,13.02 37.059,12.349C38.687,11.678 40.578,12.473 41.26,14.133C41.943,15.794 41.155,17.684 39.527,18.373C37.864,19.062 35.974,18.267 35.308,16.607H35.291ZM26.783,24.115C25.12,24.804 24.35,26.73 25.015,28.373C25.698,30.033 27.571,30.828 29.234,30.139C30.862,29.468 31.65,27.56 30.967,25.899C30.285,24.239 28.394,23.426 26.766,24.115H26.783ZM48.175,23.567C49.803,22.896 50.591,20.988 49.908,19.327C49.225,17.667 47.335,16.854 45.707,17.543C44.044,18.232 43.273,20.158 43.939,21.801C44.621,23.461 46.494,24.256 48.158,23.55L48.175,23.567ZM49.681,30.404C50.363,32.065 52.236,32.86 53.899,32.153C55.527,31.482 56.315,29.574 55.632,27.913C54.95,26.253 53.059,25.44 51.431,26.129C49.768,26.818 48.998,28.744 49.663,30.387L49.681,30.404ZM26.293,8.268C26.416,8.427 26.556,8.568 26.713,8.692L24.368,15.829V15.865C24.263,16.218 24.193,16.607 24.193,16.995C24.193,18.921 25.611,20.529 27.449,20.794H27.536C27.694,20.811 27.851,20.829 28.009,20.829C28.166,20.829 28.324,20.829 28.481,20.794H28.569C30.407,20.511 31.825,18.921 31.825,16.995C31.825,16.607 31.772,16.218 31.65,15.865V15.829L29.304,8.692C29.462,8.568 29.602,8.427 29.724,8.268C29.847,8.109 33.803,10.423 33.803,10.423V4.346L29.724,5.653C29.619,5.494 29.479,5.353 29.322,5.229C29.164,5.106 30.967,0 30.967,0H25.085L26.731,5.212C26.591,5.335 26.451,5.477 26.328,5.636C26.206,5.795 22.25,4.346 22.25,4.346V10.423L26.328,8.25L26.293,8.268ZM16.438,18.373C18.101,19.062 19.991,18.267 20.656,16.607C21.339,14.946 20.551,13.02 18.888,12.349C17.26,11.678 15.37,12.473 14.687,14.133C14.004,15.794 14.792,17.684 16.42,18.373H16.438ZM7.79,23.585C9.453,24.274 11.344,23.479 12.009,21.836C12.691,20.175 11.904,18.25 10.241,17.578C8.613,16.907 6.722,17.702 6.039,19.363C5.357,21.023 6.144,22.914 7.772,23.603L7.79,23.585ZM2.066,32.189C3.729,32.878 5.619,32.083 6.284,30.44C6.967,28.779 6.179,26.853 4.516,26.182C2.888,25.511 0.998,26.306 0.315,27.966C-0.368,29.627 0.42,31.517 2.048,32.206L2.066,32.189ZM51.641,35.863C52.079,38.107 52.149,39.149 51.659,40.598C50.941,39.891 50.258,38.566 49.715,36.57L47.597,43.707C48.893,42.806 49.891,42.223 51.028,42.206C49.015,46.605 46.477,47.735 44.849,47.417C42.853,47.046 41.926,45.244 42.241,43.725C42.696,41.57 44.902,41.004 45.917,43.513C47.877,39.467 44.551,38.213 42.416,39.414C45.707,36.093 46.074,33.16 43.431,29.592C39.737,32.454 39.685,35.28 41.348,39.255C39.195,36.764 35.816,38.107 37.042,42.135C38.6,39.697 40.665,41.234 40.333,43.548C40.053,45.562 37.427,47.188 34.136,46.905C29.427,46.481 29.147,43.195 29.024,40.492C30.179,40.28 32.263,41.358 34.048,43.884L34.696,36.323C32.77,38.354 31.002,38.743 29.059,38.796C29.707,36.747 32.7,33.408 32.7,33.408H23.335C23.335,33.408 26.311,36.764 26.976,38.796C25.033,38.725 23.265,38.354 21.339,36.323L21.987,43.884C23.772,41.375 25.838,40.28 27.011,40.492C26.888,43.213 26.608,46.481 21.899,46.905C18.608,47.188 15.983,45.562 15.702,43.548C15.37,41.234 17.435,39.697 18.993,42.135C20.201,38.107 16.84,36.764 14.687,39.255C16.35,35.28 16.315,32.454 12.604,29.592C9.943,33.16 10.328,36.093 13.619,39.414C11.484,38.213 8.14,39.485 10.118,43.513C11.151,41.004 13.339,41.57 13.794,43.725C14.109,45.244 13.182,47.046 11.186,47.417C9.558,47.718 7.02,46.587 5.007,42.206C6.144,42.241 7.142,42.806 8.438,43.707L6.319,36.57C5.777,38.566 5.094,39.891 4.376,40.598C3.886,39.149 3.956,38.107 4.376,35.863L0,37.436C2.328,40.633 4.586,45.138 6.425,53C12.954,52.064 20.271,51.534 27.991,51.534C35.711,51.534 43.046,52.064 49.576,53C51.431,45.138 53.689,40.633 56,37.436L51.624,35.863H51.641Z"
+      android:fillColor="#ffffff"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/core/src/main/res/drawable/tudor_crown_with_gov_uk.xml b/core/src/main/res/drawable/tudor_crown_with_gov_uk.xml
deleted file mode 100644
index 7f674e19d..000000000
--- a/core/src/main/res/drawable/tudor_crown_with_gov_uk.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="107dp"
-    android:height="114dp"
-    android:viewportWidth="107"
-    android:viewportHeight="114">
-  <path
-      android:pathData="M64.18,24.13C63.18,21.71 64.33,18.92 66.77,17.94C69.15,16.97 71.92,18.12 72.92,20.53C73.92,22.95 72.76,25.69 70.38,26.69C67.94,27.69 65.18,26.54 64.2,24.13H64.18ZM51.72,35.03C49.28,36.04 48.16,38.83 49.13,41.22C50.13,43.63 52.87,44.79 55.31,43.79C57.69,42.81 58.84,40.04 57.84,37.63C56.85,35.21 54.08,34.03 51.69,35.03H51.72ZM83.04,34.24C85.43,33.26 86.58,30.49 85.58,28.08C84.58,25.67 81.81,24.49 79.43,25.49C76.99,26.49 75.86,29.29 76.84,31.67C77.84,34.09 80.58,35.24 83.02,34.21L83.04,34.24ZM85.25,44.17C86.25,46.58 88.99,47.74 91.42,46.71C93.81,45.74 94.96,42.97 93.96,40.55C92.96,38.14 90.19,36.96 87.81,37.96C85.37,38.96 84.25,41.76 85.22,44.15L85.25,44.17ZM51,12.01C51.18,12.24 51.39,12.45 51.62,12.63L48.18,23V23.05C48.03,23.56 47.92,24.13 47.92,24.69C47.92,27.49 50,29.82 52.69,30.21H52.82C53.05,30.24 53.28,30.26 53.51,30.26C53.74,30.26 53.97,30.26 54.2,30.21H54.33C57.02,29.8 59.1,27.49 59.1,24.69C59.1,24.13 59.02,23.56 58.84,23.05V23L55.41,12.63C55.64,12.45 55.85,12.24 56.02,12.01C56.2,11.78 62,15.14 62,15.14V6.31L56.02,8.21C55.87,7.98 55.67,7.78 55.44,7.6C55.2,7.42 57.84,0 57.84,0H49.23L51.64,7.57C51.44,7.75 51.23,7.96 51.05,8.19C50.87,8.42 45.08,6.31 45.08,6.31V15.14L51.05,11.99L51,12.01ZM36.57,26.69C39,27.69 41.77,26.54 42.75,24.13C43.75,21.71 42.59,18.92 40.16,17.94C37.77,16.97 35.01,18.12 34.01,20.53C33.01,22.95 34.16,25.69 36.54,26.69H36.57ZM23.91,34.26C26.34,35.27 29.11,34.11 30.08,31.72C31.08,29.31 29.93,26.51 27.5,25.54C25.11,24.56 22.34,25.72 21.34,28.13C20.34,30.54 21.5,33.29 23.88,34.29L23.91,34.26ZM15.52,46.76C17.96,47.77 20.73,46.61 21.7,44.22C22.7,41.81 21.55,39.01 19.11,38.04C16.73,37.06 13.96,38.22 12.96,40.63C11.96,43.04 13.12,45.79 15.5,46.79L15.52,46.76ZM88.12,52.1C88.76,55.36 88.86,56.88 88.14,58.98C87.09,57.96 86.09,56.03 85.3,53.13L82.2,63.5C84.09,62.19 85.55,61.34 87.22,61.32C84.27,67.71 80.56,69.35 78.17,68.89C75.25,68.35 73.89,65.73 74.35,63.53C75.02,60.39 78.25,59.57 79.74,63.22C82.61,57.34 77.74,55.52 74.61,57.26C79.43,52.44 79.97,48.18 76.1,42.99C70.69,47.15 70.61,51.26 73.05,57.03C69.89,53.41 64.95,55.36 66.74,61.22C69.02,57.67 72.05,59.91 71.56,63.27C71.15,66.19 67.3,68.56 62.48,68.14C55.59,67.53 55.18,62.76 55,58.83C56.69,58.52 59.74,60.09 62.36,63.76L63.3,52.77C60.49,55.72 57.9,56.29 55.05,56.36C56,53.39 60.38,48.54 60.38,48.54H46.67C46.67,48.54 51.03,53.41 52,56.36C49.16,56.26 46.57,55.72 43.75,52.77L44.7,63.76C47.31,60.11 50.33,58.52 52.05,58.83C51.87,62.78 51.46,67.53 44.57,68.14C39.75,68.56 35.9,66.19 35.49,63.27C35.01,59.91 38.03,57.67 40.31,61.22C42.08,55.36 37.16,53.41 34.01,57.03C36.44,51.26 36.39,47.15 30.96,42.99C27.06,48.18 27.62,52.44 32.44,57.26C29.32,55.52 24.42,57.37 27.32,63.22C28.83,59.57 32.03,60.39 32.7,63.53C33.16,65.73 31.8,68.35 28.88,68.89C26.5,69.33 22.78,67.68 19.83,61.32C21.5,61.37 22.96,62.19 24.86,63.5L21.75,53.13C20.96,56.03 19.96,57.96 18.91,58.98C18.19,56.88 18.29,55.36 18.91,52.1L12.5,54.39C15.91,59.03 19.22,65.58 21.91,77C31.47,75.64 42.18,74.87 53.49,74.87C64.79,74.87 75.53,75.64 85.09,77C87.81,65.58 91.12,59.03 94.5,54.39L88.09,52.1H88.12Z"
-      android:fillColor="#ffffff"
-      android:fillType="evenOdd"/>
-  <path
-      android:pathData="M13.39,112.96C12.27,113.64 10.9,113.97 9.27,113.97C6.59,113.97 4.39,113.06 2.68,111.24C0.89,109.41 0,106.91 0,103.73C0,100.52 0.9,97.94 2.7,96C4.51,94.06 6.89,93.09 9.86,93.09C12.43,93.09 14.49,93.74 16.05,95.02C17.62,96.29 18.51,97.89 18.74,99.8H14.58C14.26,98.44 13.48,97.5 12.23,96.96C11.54,96.66 10.76,96.51 9.91,96.51C8.28,96.51 6.94,97.12 5.89,98.33C4.85,99.53 4.33,101.35 4.33,103.77C4.33,106.21 4.89,107.94 6.03,108.95C7.16,109.97 8.45,110.47 9.9,110.47C11.32,110.47 12.48,110.07 13.39,109.28C14.29,108.47 14.85,107.41 15.06,106.11H10.38V102.8H18.81V113.45H16.01L15.58,110.97C14.77,111.91 14.04,112.58 13.39,112.96Z"
-      android:fillColor="#ffffff"/>
-  <path
-      android:pathData="M31.81,114C28.92,114 26.71,113.23 25.18,111.68C23.13,109.79 22.11,107.06 22.11,103.5C22.11,99.87 23.13,97.14 25.18,95.32C26.71,93.77 28.92,93 31.81,93C34.71,93 36.92,93.77 38.44,95.32C40.49,97.14 41.51,99.87 41.51,103.5C41.51,107.06 40.49,109.79 38.44,111.68C36.92,113.23 34.71,114 31.81,114ZM35.81,108.67C36.79,107.46 37.28,105.74 37.28,103.5C37.28,101.27 36.78,99.56 35.79,98.34C34.82,97.12 33.49,96.51 31.81,96.51C30.14,96.51 28.8,97.12 27.8,98.33C26.81,99.54 26.31,101.26 26.31,103.5C26.31,105.74 26.81,107.46 27.8,108.67C28.8,109.88 30.14,110.49 31.81,110.49C33.49,110.49 34.82,109.88 35.81,108.67Z"
-      android:fillColor="#ffffff"/>
-  <path
-      android:pathData="M43.25,93.61H47.77L52.16,108.67L56.6,93.61H60.99L54.08,113.45H50.09L43.25,93.61Z"
-      android:fillColor="#ffffff"/>
-  <path
-      android:pathData="M60.54,109.44H64.7V113.45H60.54V109.44Z"
-      android:fillColor="#ffffff"/>
-  <path
-      android:pathData="M80.72,105.8V93.61H85.02V105.8C85.02,107.91 84.68,109.55 84.02,110.73C82.77,112.88 80.4,113.96 76.89,113.96C73.38,113.96 71,112.88 69.75,110.73C69.08,109.55 68.75,107.91 68.75,105.8V93.61H73.04V105.8C73.04,107.17 73.21,108.16 73.54,108.79C74.05,109.9 75.17,110.46 76.89,110.46C78.6,110.46 79.71,109.9 80.23,108.79C80.56,108.16 80.72,107.17 80.72,105.8Z"
-      android:fillColor="#ffffff"/>
-  <path
-      android:pathData="M107,113.45H101.56L95.32,104.78L93.3,106.81V113.45H89.14V93.61H93.3V101.78L101.11,93.61H106.57L98.28,101.78L107,113.45Z"
-      android:fillColor="#ffffff"/>
-</vector>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index b153afb46..5c8d5fe88 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -55,7 +55,6 @@
     <string name="app_splashScreenAnalyticsScreenName" translatable="false">one login splash screen</string>
     <string name="app_splashScreenUnlockAnalyticsScreenName" translatable="false">one login unlock screen</string>
     <string name="app_splashScreenLoadingIndicatorText">Loading</string>
-    <string name="app_splashScreenLoadingContentDescription" translatable="false">Circular progress/ loading indicator</string>
 
     <!-- Sign in -->
     <string name="app_signInTitle" translatable="false">GOV.UK One Login</string>
diff --git a/core/src/main/res/values/test_tags.xml b/core/src/main/res/values/test_tags.xml
index c1e117f52..ea74202ab 100644
--- a/core/src/main/res/values/test_tags.xml
+++ b/core/src/main/res/values/test_tags.xml
@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="splashIconTestTag" translatable="false">splash icon test tag</string>
+    <string name="splashLogoTestTag" translatable="false">logo test tag</string>
+    <string name="splashCrownIconTestTag" translatable="false">crown icon test tag</string>
+    <string name="splashUnlockBtnTestTag" translatable="false">unlock button test tag</string>
+    <string name="splashLoadingSpinnerTestTag" translatable="false">splash loading spinner test tag</string>
     <string name="optInSwitchTestTag" translatable="false">opt in switch test tag</string>
     <string name="appCriCardTestTag" translatable="false">Cri Orchestrator test tag</string>
     <string name="yourServicesCardTestTag" translatable="false">Your services card test tag</string>
diff --git a/features/src/main/java/uk/gov/onelogin/features/login/ui/signin/splash/SplashScreen.kt b/features/src/main/java/uk/gov/onelogin/features/login/ui/signin/splash/SplashScreen.kt
index 67b1578c5..e23c41ae8 100644
--- a/features/src/main/java/uk/gov/onelogin/features/login/ui/signin/splash/SplashScreen.kt
+++ b/features/src/main/java/uk/gov/onelogin/features/login/ui/signin/splash/SplashScreen.kt
@@ -1,34 +1,45 @@
 package uk.gov.onelogin.features.login.ui.signin.splash
 
+import android.annotation.SuppressLint
 import androidx.activity.compose.BackHandler
 import androidx.activity.compose.LocalActivity
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.focusGroup
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
-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.width
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonColors
 import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.collectAsState
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.SubcomposeLayout
+import androidx.compose.ui.layout.SubcomposeMeasureScope
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.dp
 import androidx.fragment.app.FragmentActivity
 import androidx.hilt.navigation.compose.hiltViewModel
@@ -36,16 +47,11 @@ import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.compose.LocalLifecycleOwner
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
+import kotlin.math.roundToInt
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.launch
 import uk.gov.android.onelogin.core.R
-import uk.gov.android.ui.components.GdsHeading
-import uk.gov.android.ui.components.HeadingParameters
-import uk.gov.android.ui.components.HeadingSize
-import uk.gov.android.ui.components.images.icon.IconParameters
-import uk.gov.android.ui.components.m3.images.icon.GdsIcon
-import uk.gov.android.ui.theme.m3.GdsTheme
-import uk.gov.android.ui.theme.mediumPadding
+import uk.gov.android.ui.theme.GdsTheme
 import uk.gov.android.ui.theme.smallPadding
 import uk.gov.onelogin.core.ui.meta.ExcludeFromJacocoGeneratedReport
 import uk.gov.onelogin.core.ui.meta.ScreenPreview
@@ -118,54 +124,138 @@ internal fun SplashBody(
     onLogin: () -> Unit,
     onOpenDeveloperPortal: () -> Unit
 ) {
-    Column(
-        modifier = Modifier
-            .fillMaxSize()
-            .background(colorResource(id = R.color.govuk_blue)),
-        horizontalAlignment = Alignment.CenterHorizontally,
-        verticalArrangement = Arrangement.Center
-    ) {
-        GdsIcon(
-            parameters = IconParameters(
-                image = R.drawable.tudor_crown_with_gov_uk,
-                backGroundColor = colorResource(id = R.color.govuk_blue),
-                foreGroundColor = Color.White,
-                modifier = Modifier
-                    .weight(1F)
-                    .clickable(enabled = DeveloperTools.isDeveloperPanelEnabled()) {
-                        onOpenDeveloperPortal()
-                    }
-                    .testTag(stringResource(id = R.string.splashIconTestTag))
+    val displayUnlock = isUnlock && !loading
+    SubcomposeLayout(
+        modifier = Modifier.background(colorResource(R.color.govuk_blue))
+    ) { constraints ->
+        // Get full specs of device
+        val fullHeight = constraints.maxHeight
+        val fullWidth = constraints.maxWidth
+        // Measure logo and create slot to be displayed
+        val logoPlaceable = createSubcomposeLogo(onOpenDeveloperPortal, constraints)
+        // Measure crown icon and create slot to be displayed
+        val crownPlaceable = createSubcomposeCrown()
+        // Measure loading spinner and/ or unlock text and create slot to be displayed
+        val dynamicContentPlaceable = subcompose("content") {
+            if (displayUnlock) UnlockButton(trackUnlockButton, onLogin) else LoadingIndicator()
+        }.first().measure(Constraints(maxWidth = fullWidth))
+        // Measure unlock button height to be used to enable equal space distribution between the logo, crown and loading spinner (when this is displayed)
+        val loadingSpinnerHeight = subcompose("loading spinner") {
+            LoadingIndicator()
+        }.first().measure(Constraints(maxWidth = fullWidth)).height
+
+        // Calculate logo height
+        val imageHeight = logoPlaceable.maxOf { it.height }
+        // Y coordinate for logo to be placed (pos: absolute center at all times)
+        val imageY = (fullHeight - imageHeight) / 2
+        // Calculate the total space available to display the crown and optional/ dynamic content (loading spinner/ unlock button)
+        val availableContentHeight = fullHeight - (imageY + imageHeight) - BOTTOM_PADDING.toPx()
+        // Calculate the exact total space taken by the items - assists in defining the gap between items and space distribution to meet design requirements
+        val concreteContentHeight = crownPlaceable.height + loadingSpinnerHeight
+        // Calculate the max gap that can be added between item to be equally distributed
+        val gapHeight =
+            ((availableContentHeight - concreteContentHeight).coerceAtLeast(0f)) / 2
+        // Y coordinate for crown icon to be centred and have a exact gap between the logo and itself which is equal to the gap between the crown and loading spinner when that is displayed
+        val crownY = imageY + imageHeight + gapHeight
+        // Y coordinate for loading spinner - includes bottom padding of 48.dp and an equal gap between crown icon as the crown icon to logo
+        val loadingY = crownY + crownPlaceable.height + gapHeight
+        // Y coordinate for unlock button - this ensured the unlock button is positioned at a 48.dp gap from the screen bottom
+        val unlockY =
+            crownY + crownPlaceable.height + (concreteContentHeight - BOTTOM_PADDING.toPx())
+
+        // Create layout and place content accordingly
+        layout(fullWidth, fullHeight) {
+            logoPlaceable.forEach {
+                it.placeRelative(x = (fullWidth - it.width) / 2, y = imageY)
+            }
+            crownPlaceable.placeRelative(
+                x = (fullWidth - crownPlaceable.width) / 2,
+                y = crownY.roundToInt()
             )
-        )
-        if (isUnlock && !loading) {
-            GdsHeading(
-                headingParameters = HeadingParameters(
-                    size = HeadingSize.H3(),
-                    text = R.string.app_unlockButton,
-                    color = Color.White,
-                    backgroundColor = colorResource(id = R.color.govuk_blue),
-                    modifier = Modifier
-                        .clickable {
-                            trackUnlockButton()
-                            onLogin()
-                        }
-                        .padding(bottom = mediumPadding)
+            if (displayUnlock || loading) {
+                dynamicContentPlaceable.placeRelative(
+                    x = (fullWidth - dynamicContentPlaceable.width) / 2,
+                    y = if (loading) loadingY.roundToInt() else unlockY.roundToInt()
                 )
-            )
+            }
         }
-        if (loading && !isUnlock) {
-            LoadingIndicator()
+    }
+}
+
+private fun SubcomposeMeasureScope.createSubcomposeLogo(
+    onOpenDeveloperPortal: () -> Unit,
+    constraints: Constraints
+): List<Placeable> {
+    val imagePlaceable = subcompose("logo") {
+        Icon(
+            painter = painterResource(R.drawable.ic_splash_logo),
+            contentDescription = if (DeveloperTools.isDeveloperPanelEnabled()) {
+                "Open dev menu"
+            } else {
+                null
+            },
+            tint = Color.Unspecified,
+            modifier = Modifier
+                .fillMaxWidth()
+                .testTag(stringResource(id = R.string.splashLogoTestTag))
+                .then(
+                    // This allows for the logo not to be treated as a button in production/ integration and be ignored for accessibility purposes
+                    if (DeveloperTools.isDeveloperPanelEnabled()) {
+                        Modifier.clickable(enabled = DeveloperTools.isDeveloperPanelEnabled()) {
+                            onOpenDeveloperPortal()
+                        }
+                    } else {
+                        Modifier
+                    }
+                )
+        )
+    }.map { it.measure(constraints = constraints) }
+    return imagePlaceable
+}
+
+private fun SubcomposeMeasureScope.createSubcomposeCrown() =
+    subcompose("crown") {
+        Icon(
+            painter = painterResource(R.drawable.ic_tudor_crown),
+            contentDescription = null,
+            tint = Color.White,
+            modifier = Modifier.testTag(stringResource(id = R.string.splashCrownIconTestTag))
+        )
+    }.first().measure(Constraints())
+
+@Composable
+private fun UnlockButton(trackUnlockButton: () -> Unit, onLogin: () -> Unit) {
+    Button(
+        colors = ButtonColors(
+            containerColor = Color.Transparent,
+            contentColor = Color.White,
+            disabledContainerColor = Color.Transparent,
+            disabledContentColor = Color.Gray
+        ),
+        modifier = Modifier
+            .fillMaxWidth()
+            .padding(horizontal = smallPadding)
+            .semantics(true) {}
+            .testTag(stringResource(R.string.splashUnlockBtnTestTag)),
+        contentPadding = PaddingValues(0.dp),
+        onClick = {
+            trackUnlockButton()
+            onLogin()
         }
+    ) {
+        Text(
+            text = stringResource(R.string.app_unlockButton),
+            style = MaterialTheme.typography.headlineSmall,
+            textAlign = TextAlign.Center,
+            fontWeight = FontWeight.W600,
+            color = Color.White
+        )
     }
 }
 
 @Composable
 internal fun LoadingIndicator() {
-    val loadingContentDescription =
-        stringResource(
-            id = R.string.app_splashScreenLoadingContentDescription
-        )
+    val loadingText = stringResource(R.string.app_splashScreenLoadingIndicatorText)
     Column(
         modifier = Modifier
             .fillMaxWidth()
@@ -174,7 +264,8 @@ internal fun LoadingIndicator() {
                 end = smallPadding,
                 bottom = PROGRESS_BAR
             )
-            .semantics { contentDescription = loadingContentDescription }
+            .focusGroup()
+            .semantics(true) { contentDescription = loadingText }
     ) {
         Row(
             horizontalArrangement = Arrangement.Center,
@@ -189,6 +280,8 @@ internal fun LoadingIndicator() {
                 modifier = Modifier
                     .width(PROGRESS_BAR)
                     .height(PROGRESS_BAR)
+                    .semantics { contentDescription = "" }
+                    .testTag(stringResource(R.string.splashLoadingSpinnerTestTag))
             )
         }
         Row(
@@ -196,13 +289,16 @@ internal fun LoadingIndicator() {
             modifier = Modifier.fillMaxWidth()
         ) {
             Text(
-                text = stringResource(id = R.string.app_splashScreenLoadingIndicatorText),
-                color = Color.White
+                text = loadingText,
+                style = MaterialTheme.typography.bodyLarge,
+                color = Color.White,
+                modifier = Modifier.semantics { contentDescription = "" }
             )
         }
     }
 }
 
+@SuppressLint("UnrememberedMutableState")
 @ExcludeFromJacocoGeneratedReport
 @ScreenPreview
 @Composable
@@ -218,6 +314,7 @@ internal fun SplashScreenPreview() {
     }
 }
 
+@SuppressLint("UnrememberedMutableState")
 @ExcludeFromJacocoGeneratedReport
 @Preview
 @Composable
@@ -233,6 +330,7 @@ internal fun UnlockScreenPreview() {
     }
 }
 
+@SuppressLint("UnrememberedMutableState")
 @ExcludeFromJacocoGeneratedReport
 @Preview
 @Composable
@@ -249,3 +347,4 @@ internal fun LoadingSplashScreenPreview() {
 }
 
 val PROGRESS_BAR = 48.dp
+val BOTTOM_PADDING = 48.dp
diff --git a/features/src/main/java/uk/gov/onelogin/features/login/ui/signin/splash/SplashScreenViewModel.kt b/features/src/main/java/uk/gov/onelogin/features/login/ui/signin/splash/SplashScreenViewModel.kt
index cd699bda2..2650fb588 100644
--- a/features/src/main/java/uk/gov/onelogin/features/login/ui/signin/splash/SplashScreenViewModel.kt
+++ b/features/src/main/java/uk/gov/onelogin/features/login/ui/signin/splash/SplashScreenViewModel.kt
@@ -38,6 +38,7 @@ class SplashScreenViewModel @Inject constructor(
             handleLocalLogin(
                 fragmentActivity,
                 callback = {
+                    println(it)
                     when (it) {
                         LocalAuthStatus.SecureStoreError ->
                             nextScreen(SignOutRoutes.Info)
@@ -81,7 +82,10 @@ class SplashScreenViewModel @Inject constructor(
                 AppInfoServiceState.Unavailable -> navigateToAppUnavailableError()
                 AppInfoServiceState.UpdateRequired -> navigateToUpdateRequiredError()
                 // WHEN successful AppInfo response/ status
-                else -> onSuccess()
+                else -> {
+                    onSuccess()
+                    _loading.emit(false)
+                }
             }
         }
     }
diff --git a/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashBodyTest.kt b/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashBodyTest.kt
index d86bb9f9c..ddfc515a6 100644
--- a/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashBodyTest.kt
+++ b/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashBodyTest.kt
@@ -3,9 +3,9 @@ package uk.gov.onelogin.features.login.ui.splash
 import android.content.Context
 import android.content.res.Resources
 import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assertCountEquals
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotDisplayed
-import androidx.compose.ui.test.hasContentDescription
 import androidx.compose.ui.test.hasTestTag
 import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.performClick
@@ -21,7 +21,8 @@ import uk.gov.onelogin.features.login.ui.signin.splash.SplashBody
 
 @RunWith(AndroidJUnit4::class)
 class SplashBodyTest : FragmentActivityTestCase() {
-    private lateinit var splashIcon: SemanticsMatcher
+    private lateinit var logo: SemanticsMatcher
+    private lateinit var crownIcon: SemanticsMatcher
     private lateinit var unlockButton: SemanticsMatcher
     private lateinit var loadingText: SemanticsMatcher
     private lateinit var loadingIndicator: SemanticsMatcher
@@ -31,13 +32,11 @@ class SplashBodyTest : FragmentActivityTestCase() {
         val context: Context = ApplicationProvider.getApplicationContext()
         val resources: Resources = context.resources
 
-        splashIcon = hasTestTag(resources.getString(R.string.splashIconTestTag))
+        logo = hasTestTag(context.getString(R.string.splashLogoTestTag))
+        crownIcon = hasTestTag(resources.getString(R.string.splashCrownIconTestTag))
         unlockButton = hasText(resources.getString(R.string.app_unlockButton))
         loadingText = hasText(resources.getString(R.string.app_splashScreenLoadingIndicatorText))
-        loadingIndicator =
-            hasContentDescription(
-                resources.getString(R.string.app_splashScreenLoadingContentDescription)
-            )
+        loadingIndicator = hasTestTag(context.getString(R.string.splashLoadingSpinnerTestTag))
     }
 
     @Test
@@ -55,9 +54,10 @@ class SplashBodyTest : FragmentActivityTestCase() {
         // Then loading indicator is NOT displayed
         composeTestRule.onNode(loadingText).assertIsNotDisplayed()
         composeTestRule.onNode(loadingIndicator).assertIsNotDisplayed()
-        // And both `splashIcon` and `unlockButton` are displayed
-        composeTestRule.onNode(splashIcon).assertIsDisplayed()
-        composeTestRule.onNode(unlockButton).assertIsDisplayed()
+        // And `logo`, `crownIcon` and `unlockButton` are displayed
+        composeTestRule.onNode(logo).assertIsDisplayed()
+        composeTestRule.onNode(crownIcon).assertIsDisplayed()
+        composeTestRule.onNode(unlockButton).assertExists()
     }
 
     @Test
@@ -72,11 +72,13 @@ class SplashBodyTest : FragmentActivityTestCase() {
                 onOpenDeveloperPortal = {}
             )
         }
-        // Then loading indicator is displayed
-        composeTestRule.onNode(loadingText).assertIsDisplayed()
-        composeTestRule.onNode(loadingIndicator).assertIsDisplayed()
-        // And only `splashIcon` is displayed and`unlockButton` is not
-        composeTestRule.onNode(splashIcon).assertIsDisplayed()
+        // Then loading indicator and logo are displayed
+        composeTestRule.onNode(logo).assertIsDisplayed()
+        composeTestRule.onAllNodes(loadingText).assertCountEquals(2)
+        composeTestRule.onAllNodes(loadingIndicator).assertCountEquals(2)
+        // And only `logo`, `crownIcon` is displayed and`unlockButton` is not
+        composeTestRule.onNode(logo).assertIsDisplayed()
+        composeTestRule.onNode(crownIcon).assertIsDisplayed()
         composeTestRule.onNode(unlockButton).assertIsNotDisplayed()
     }
 
diff --git a/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenTest.kt b/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenTest.kt
index e94a44ae6..3d2b78cc3 100644
--- a/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenTest.kt
+++ b/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenTest.kt
@@ -1,15 +1,15 @@
 package uk.gov.onelogin.features.login.ui.splash
 
 import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assertCountEquals
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotDisplayed
-import androidx.compose.ui.test.hasContentDescription
 import androidx.compose.ui.test.hasTestTag
 import androidx.compose.ui.test.hasText
-import androidx.compose.ui.test.isDisplayed
 import androidx.compose.ui.test.performClick
 import androidx.test.espresso.Espresso
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import kotlin.test.assertTrue
 import kotlinx.coroutines.flow.MutableStateFlow
 import org.junit.Before
 import org.junit.Test
@@ -51,7 +51,8 @@ class SplashScreenTest : FragmentActivityTestCase() {
     private var repository: OptInRepository = mock()
     private lateinit var optInViewModel: OptInRequirementViewModel
 
-    private lateinit var splashIcon: SemanticsMatcher
+    private lateinit var logo: SemanticsMatcher
+    private lateinit var crownIcon: SemanticsMatcher
     private lateinit var unlockButton: SemanticsMatcher
     private lateinit var privacyNotice: SemanticsMatcher
     private lateinit var loadingIndicator: SemanticsMatcher
@@ -75,12 +76,13 @@ class SplashScreenTest : FragmentActivityTestCase() {
         wheneverBlocking { appInfoService.get() }
             .thenReturn(AppInfoServiceState.Successful(TestUtils.appInfoData))
 
-        splashIcon = hasTestTag(resources.getString(R.string.splashIconTestTag))
-        unlockButton = hasText(resources.getString(R.string.app_unlockButton))
+        logo = hasTestTag(context.getString(R.string.splashLogoTestTag))
+        crownIcon = hasTestTag(resources.getString(R.string.splashCrownIconTestTag))
+        unlockButton = hasTestTag(resources.getString(R.string.splashUnlockBtnTestTag))
         privacyNotice = hasTestTag(NOTICE_TAG)
         loadingIndicator =
-            hasContentDescription(
-                resources.getString(R.string.app_splashScreenLoadingContentDescription)
+            hasTestTag(
+                resources.getString(R.string.splashLoadingSpinnerTestTag)
             )
         loadingText = hasText(resources.getString(R.string.app_splashScreenLoadingIndicatorText))
     }
@@ -94,7 +96,8 @@ class SplashScreenTest : FragmentActivityTestCase() {
 
         // Then
         composeTestRule.onNode(privacyNotice).assertIsNotDisplayed()
-        composeTestRule.onNode(splashIcon).assertIsDisplayed()
+        composeTestRule.onNode(logo).assertIsDisplayed()
+        composeTestRule.onNode(crownIcon).assertIsDisplayed()
     }
 
     @Test
@@ -107,8 +110,9 @@ class SplashScreenTest : FragmentActivityTestCase() {
         composeTestRule.setContent {
             SplashScreen(viewModel, analyticsViewModel, optInViewModel)
         }
+
         composeTestRule.waitUntil(15000) {
-            composeTestRule.onNode(unlockButton).isDisplayed()
+            composeTestRule.onAllNodes(unlockButton).fetchSemanticsNodes().isNotEmpty()
         }
 
         wheneverBlocking { handleLocalLogin.invoke(any(), any()) }.thenAnswer {
@@ -134,7 +138,8 @@ class SplashScreenTest : FragmentActivityTestCase() {
         }
 
         composeTestRule.onNode(privacyNotice).assertIsNotDisplayed()
-        composeTestRule.onNode(splashIcon).assertIsDisplayed()
+        composeTestRule.onNode(logo).assertIsDisplayed()
+        composeTestRule.onNode(crownIcon).assertIsDisplayed()
     }
 
     @Test
@@ -144,8 +149,11 @@ class SplashScreenTest : FragmentActivityTestCase() {
         }
 
         composeTestRule.onNode(privacyNotice).assertIsNotDisplayed()
-        composeTestRule.onNode(splashIcon).assertIsDisplayed()
-        composeTestRule.onNode(unlockButton).assertIsDisplayed()
+        composeTestRule.onNode(logo).assertIsDisplayed()
+        composeTestRule.onNode(crownIcon).assertIsDisplayed()
+        assertTrue(
+            composeTestRule.onAllNodes(unlockButton).fetchSemanticsNodes().isNotEmpty()
+        )
         composeTestRule.onNode(loadingText).assertIsNotDisplayed()
         composeTestRule.onNode(loadingIndicator).assertIsNotDisplayed()
     }
@@ -157,9 +165,10 @@ class SplashScreenTest : FragmentActivityTestCase() {
         }
 
         composeTestRule.onNode(privacyNotice).assertIsNotDisplayed()
-        composeTestRule.onNode(splashIcon).assertIsDisplayed()
-        composeTestRule.onNode(loadingText).assertIsDisplayed()
-        composeTestRule.onNode(loadingIndicator).assertIsDisplayed()
+        composeTestRule.onNode(logo).assertIsDisplayed()
+        composeTestRule.onNode(crownIcon).assertIsDisplayed()
+        composeTestRule.onAllNodes(loadingText).assertCountEquals(2)
+        composeTestRule.onAllNodes(loadingIndicator).assertCountEquals(2)
     }
 
     @Test
diff --git a/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenViewModelTest.kt b/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenViewModelTest.kt
index 64da46264..828ab8c19 100644
--- a/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenViewModelTest.kt
+++ b/features/src/test/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenViewModelTest.kt
@@ -227,14 +227,17 @@ class SplashScreenViewModelTest {
             assertFalse(viewModel.loading.value)
 
             // AND AppInfo call is successful remote
-            whenever(mockAppInfoService.get()).thenReturn(AppInfoServiceState.Successful(data))
+            whenever(mockAppInfoService.get()).thenAnswer {
+                assertTrue(viewModel.loading.value)
+                AppInfoServiceState.Successful(data)
+            }
             // AND it calls retrieveAppInfo
             viewModel.retrieveAppInfo {
                 // Callback - Nothing to do
             }
 
             // THEN loading progress indicator will be set to true
-            assertTrue(viewModel.loading.value)
+            assertFalse(viewModel.loading.value)
         }
 
     @Test
diff --git a/features/src/testBuild/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt b/features/src/testBuild/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
index 076238911..af6be06f9 100644
--- a/features/src/testBuild/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
+++ b/features/src/testBuild/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
@@ -1,5 +1,7 @@
 package uk.gov.onelogin.features.login.ui.splash
 
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.hasClickAction
 import androidx.compose.ui.test.hasTestTag
 import androidx.compose.ui.test.performClick
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -35,7 +37,7 @@ class SplashScreenDevMenuTest : FragmentActivityTestCase() {
     private var repository: OptInRepository = mock()
     private lateinit var optInViewModel: OptInRequirementViewModel
 
-    private val splashIcon = hasTestTag(resources.getString(R.string.splashIconTestTag))
+    private val splashIcon = hasTestTag(resources.getString(R.string.splashLogoTestTag))
 
     @Before
     fun setup() {
@@ -59,6 +61,7 @@ class SplashScreenDevMenuTest : FragmentActivityTestCase() {
             SplashScreen(viewModel, analyticsViewModel, optInViewModel)
         }
 
+        composeTestRule.onNode(splashIcon).assert(hasClickAction())
         composeTestRule.onNode(splashIcon).performClick()
 
         verify(navigator).openDeveloperPanel()
diff --git a/features/src/testIntegration/kotlin/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt b/features/src/testIntegration/kotlin/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
index 9d3645275..df63aa37d 100644
--- a/features/src/testIntegration/kotlin/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
+++ b/features/src/testIntegration/kotlin/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
@@ -1,5 +1,7 @@
 package uk.gov.onelogin.features.login.ui.splash
 
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.hasClickAction
 import androidx.compose.ui.test.hasTestTag
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performClick
@@ -36,7 +38,7 @@ class SplashScreenDevMenuTest : FragmentActivityTestCase() {
     private var repository: OptInRepository = mock()
     private lateinit var optInViewModel: OptInRequirementViewModel
 
-    private val splashIcon = hasTestTag(resources.getString(R.string.splashIconTestTag))
+    private val splashIcon = hasTestTag(resources.getString(R.string.splashLogoTestTag))
 
     @Before
     fun setup() {
@@ -60,6 +62,7 @@ class SplashScreenDevMenuTest : FragmentActivityTestCase() {
             SplashScreen(viewModel, analyticsViewModel, optInViewModel)
         }
 
+        composeTestRule.onNode(splashIcon).assert(hasClickAction().not())
         composeTestRule.onNode(splashIcon).performClick()
 
         composeTestRule.onNodeWithText("Developer Portal").assertDoesNotExist()
diff --git a/features/src/testProduction/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt b/features/src/testProduction/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
index 9d3645275..df63aa37d 100644
--- a/features/src/testProduction/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
+++ b/features/src/testProduction/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
@@ -1,5 +1,7 @@
 package uk.gov.onelogin.features.login.ui.splash
 
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.hasClickAction
 import androidx.compose.ui.test.hasTestTag
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performClick
@@ -36,7 +38,7 @@ class SplashScreenDevMenuTest : FragmentActivityTestCase() {
     private var repository: OptInRepository = mock()
     private lateinit var optInViewModel: OptInRequirementViewModel
 
-    private val splashIcon = hasTestTag(resources.getString(R.string.splashIconTestTag))
+    private val splashIcon = hasTestTag(resources.getString(R.string.splashLogoTestTag))
 
     @Before
     fun setup() {
@@ -60,6 +62,7 @@ class SplashScreenDevMenuTest : FragmentActivityTestCase() {
             SplashScreen(viewModel, analyticsViewModel, optInViewModel)
         }
 
+        composeTestRule.onNode(splashIcon).assert(hasClickAction().not())
         composeTestRule.onNode(splashIcon).performClick()
 
         composeTestRule.onNodeWithText("Developer Portal").assertDoesNotExist()
diff --git a/features/src/testStaging/java/uk/gov/onelogin/features/home/ui/HomeScreenDeveloperMenuTest.kt b/features/src/testStaging/java/uk/gov/onelogin/features/home/ui/HomeScreenDeveloperMenuTest.kt
new file mode 100644
index 000000000..09753e66f
--- /dev/null
+++ b/features/src/testStaging/java/uk/gov/onelogin/features/home/ui/HomeScreenDeveloperMenuTest.kt
@@ -0,0 +1,76 @@
+package uk.gov.onelogin.features.home.ui
+
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.espresso.intent.Intents
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import uk.gov.android.featureflags.FeatureFlags
+import uk.gov.android.featureflags.InMemoryFeatureFlags
+import uk.gov.android.network.client.GenericHttpClient
+import uk.gov.logging.api.Logger
+import uk.gov.logging.api.analytics.logging.AnalyticsLogger
+import uk.gov.onelogin.core.navigation.domain.Navigator
+import uk.gov.onelogin.criorchestrator.sdk.publicapi.CriOrchestratorSdkExt.create
+import uk.gov.onelogin.criorchestrator.sdk.sharedapi.CriOrchestratorSdk
+import uk.gov.onelogin.features.FragmentActivityTestCase
+import uk.gov.onelogin.features.TestUtils
+import uk.gov.onelogin.features.ext.setupComposeTestRule
+import uk.gov.onelogin.features.featureflags.data.WalletFeatureFlag
+
+@RunWith(AndroidJUnit4::class)
+@Suppress("ForbiddenComment")
+class HomeScreenDeveloperMenuTest : FragmentActivityTestCase() {
+    private lateinit var httpClient: GenericHttpClient
+    private lateinit var analyticsLogger: AnalyticsLogger
+    private lateinit var logger: Logger
+    private lateinit var criOrchestratorSdk: CriOrchestratorSdk
+
+    // TODO: Remove this after `activeSession` has been added to the CriOrchestrator and test using the stub
+    //  provided
+    private lateinit var featureFlags: FeatureFlags
+    private lateinit var navigator: Navigator
+    private lateinit var viewModel: HomeScreenViewModel
+    private lateinit var analytics: AnalyticsLogger
+    private lateinit var analyticsViewModel: HomeScreenAnalyticsViewModel
+
+    @Before
+    fun setup() {
+        Intents.init()
+        httpClient = mock()
+        analyticsLogger = mock()
+        logger = mock()
+        criOrchestratorSdk = CriOrchestratorSdk.create(
+            authenticatedHttpClient = httpClient,
+            analyticsLogger = analyticsLogger,
+            initialConfig = TestUtils.criSdkConfig,
+            logger = logger,
+            applicationContext = context
+        )
+        featureFlags =
+            InMemoryFeatureFlags(
+                setOf(WalletFeatureFlag.ENABLED)
+            )
+        navigator = mock()
+        viewModel = HomeScreenViewModel(featureFlags, navigator, criOrchestratorSdk)
+        analytics = mock()
+        analyticsViewModel = HomeScreenAnalyticsViewModel(context, analytics)
+        composeTestRule.setupComposeTestRule { _ ->
+            HomeScreen(viewModel, analyticsViewModel)
+        }
+    }
+
+    @After
+    fun tearDown() {
+        Intents.release()
+    }
+
+    @Test
+    fun homeScreenDisplayed() {
+        composeTestRule.onNodeWithText("Developer Panel", useUnmergedTree = true)
+            .assertExists()
+    }
+}
diff --git a/features/src/testStaging/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt b/features/src/testStaging/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
new file mode 100644
index 000000000..af6be06f9
--- /dev/null
+++ b/features/src/testStaging/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
@@ -0,0 +1,88 @@
+package uk.gov.onelogin.features.login.ui.splash
+
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.hasClickAction
+import androidx.compose.ui.test.hasTestTag
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import kotlinx.coroutines.flow.MutableStateFlow
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import uk.gov.android.onelogin.core.R
+import uk.gov.logging.api.analytics.logging.AnalyticsLogger
+import uk.gov.onelogin.core.navigation.domain.Navigator
+import uk.gov.onelogin.features.FragmentActivityTestCase
+import uk.gov.onelogin.features.appinfo.domain.AppInfoService
+import uk.gov.onelogin.features.login.domain.signin.locallogin.HandleLocalLogin
+import uk.gov.onelogin.features.login.ui.signin.splash.SplashBody
+import uk.gov.onelogin.features.login.ui.signin.splash.SplashScreen
+import uk.gov.onelogin.features.login.ui.signin.splash.SplashScreenAnalyticsViewModel
+import uk.gov.onelogin.features.login.ui.signin.splash.SplashScreenViewModel
+import uk.gov.onelogin.features.optin.data.OptInRepository
+import uk.gov.onelogin.features.optin.ui.OptInRequirementViewModel
+
+@RunWith(AndroidJUnit4::class)
+class SplashScreenDevMenuTest : FragmentActivityTestCase() {
+    private lateinit var handleLocalLogin: HandleLocalLogin
+    private lateinit var navigator: Navigator
+    private lateinit var appInfoService: AppInfoService
+    private lateinit var viewModel: SplashScreenViewModel
+    private lateinit var analytics: AnalyticsLogger
+    private lateinit var analyticsViewModel: SplashScreenAnalyticsViewModel
+    private var repository: OptInRepository = mock()
+    private lateinit var optInViewModel: OptInRequirementViewModel
+
+    private val splashIcon = hasTestTag(resources.getString(R.string.splashLogoTestTag))
+
+    @Before
+    fun setup() {
+        whenever(repository.isOptInPreferenceRequired()).thenReturn(MutableStateFlow(false))
+        handleLocalLogin = mock()
+        navigator = mock()
+        appInfoService = mock()
+        viewModel = SplashScreenViewModel(
+            navigator,
+            handleLocalLogin,
+            appInfoService
+        )
+        analytics = mock()
+        analyticsViewModel = SplashScreenAnalyticsViewModel(context, analytics)
+        optInViewModel = OptInRequirementViewModel(repository)
+    }
+
+    @Test
+    fun testDevMenuButton() {
+        composeTestRule.setContent {
+            SplashScreen(viewModel, analyticsViewModel, optInViewModel)
+        }
+
+        composeTestRule.onNode(splashIcon).assert(hasClickAction())
+        composeTestRule.onNode(splashIcon).performClick()
+
+        verify(navigator).openDeveloperPanel()
+    }
+
+    @Test
+    fun onOpenDeveloperPortal() {
+        // Given the SplashBody Composable
+        var actual = false
+        composeTestRule.setContent {
+            SplashBody(
+                isUnlock = false,
+                loading = false,
+                trackUnlockButton = { },
+                onLogin = {},
+                onOpenDeveloperPortal = { actual = true }
+            )
+        }
+        // When clicking the `splashIcon`
+        composeTestRule.onNode(splashIcon).performClick()
+        // Then onOpenDeveloperPortal() is called and the variable is changed to true
+        assertTrue(actual)
+    }
+}
diff --git a/features/src/testStaging/java/uk/gov/onelogin/features/login/ui/welcome/WelcomeBodyDevButtonTest.kt b/features/src/testStaging/java/uk/gov/onelogin/features/login/ui/welcome/WelcomeBodyDevButtonTest.kt
new file mode 100644
index 000000000..94a2c5e8e
--- /dev/null
+++ b/features/src/testStaging/java/uk/gov/onelogin/features/login/ui/welcome/WelcomeBodyDevButtonTest.kt
@@ -0,0 +1,33 @@
+package uk.gov.onelogin.features.login.ui.welcome
+
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import junit.framework.TestCase.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import uk.gov.android.onelogin.core.R
+import uk.gov.onelogin.features.FragmentActivityTestCase
+import uk.gov.onelogin.features.login.ui.signin.welcome.WelcomeBody
+
+@RunWith(AndroidJUnit4::class)
+class WelcomeBodyDevButtonTest : FragmentActivityTestCase() {
+    private val devButton = hasText(resources.getString(R.string.app_developer_button))
+
+    @Test
+    fun onAccessDevMenu() {
+        // Given the WelcomeBody Composable
+        var actual = false
+        composeTestRule.setContent {
+            WelcomeBody(
+                onSignIn = {},
+                openDevMenu = { actual = true }
+            )
+        }
+
+        // When clicking the icon
+        composeTestRule.onNode(devButton, useUnmergedTree = true).performClick()
+        // Then openDevMenu() is called and the variable is true
+        assertEquals(true, actual)
+    }
+}
diff --git a/features/src/testStaging/java/uk/gov/onelogin/features/login/ui/welcome/WelcomeScreenDevMenuTest.kt b/features/src/testStaging/java/uk/gov/onelogin/features/login/ui/welcome/WelcomeScreenDevMenuTest.kt
new file mode 100644
index 000000000..405d67629
--- /dev/null
+++ b/features/src/testStaging/java/uk/gov/onelogin/features/login/ui/welcome/WelcomeScreenDevMenuTest.kt
@@ -0,0 +1,110 @@
+package uk.gov.onelogin.features.login.ui.welcome
+
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import uk.gov.android.localauth.LocalAuthManager
+import uk.gov.android.localauth.LocalAuthManagerImpl
+import uk.gov.android.localauth.devicesecurity.DeviceBiometricsManager
+import uk.gov.android.network.online.OnlineChecker
+import uk.gov.android.onelogin.core.R
+import uk.gov.logging.api.analytics.logging.AnalyticsLogger
+import uk.gov.logging.testdouble.SystemLogger
+import uk.gov.onelogin.core.localauth.domain.LocalAuthPreferenceRepo
+import uk.gov.onelogin.core.navigation.domain.Navigator
+import uk.gov.onelogin.core.tokens.data.TokenRepository
+import uk.gov.onelogin.core.tokens.data.initialise.AutoInitialiseSecureStore
+import uk.gov.onelogin.core.tokens.domain.VerifyIdToken
+import uk.gov.onelogin.core.tokens.domain.save.SavePersistentId
+import uk.gov.onelogin.core.tokens.domain.save.SaveTokenExpiry
+import uk.gov.onelogin.core.tokens.domain.save.SaveTokens
+import uk.gov.onelogin.core.ui.pages.loading.LoadingScreenAnalyticsViewModel
+import uk.gov.onelogin.features.FragmentActivityTestCase
+import uk.gov.onelogin.features.login.domain.signin.loginredirect.HandleLoginRedirect
+import uk.gov.onelogin.features.login.domain.signin.remotelogin.HandleRemoteLogin
+import uk.gov.onelogin.features.login.ui.signin.welcome.SignInAnalyticsViewModel
+import uk.gov.onelogin.features.login.ui.signin.welcome.WelcomeScreen
+import uk.gov.onelogin.features.login.ui.signin.welcome.WelcomeScreenViewModel
+import uk.gov.onelogin.features.signout.domain.SignOutUseCase
+
+@RunWith(AndroidJUnit4::class)
+class WelcomeScreenDevMenuTest : FragmentActivityTestCase() {
+    private lateinit var localAuthPreferenceRepo: LocalAuthPreferenceRepo
+    private lateinit var deviceBiometricsManager: DeviceBiometricsManager
+    private lateinit var localAuthManager: LocalAuthManager
+    private lateinit var tokenRepository: TokenRepository
+    private lateinit var autoInitialiseSecureStore: AutoInitialiseSecureStore
+    private lateinit var verifyIdToken: VerifyIdToken
+    private lateinit var navigator: Navigator
+    private lateinit var saveTokens: SaveTokens
+    private lateinit var savePersistentId: SavePersistentId
+    private lateinit var saveTokenExpiry: SaveTokenExpiry
+    private lateinit var handleRemoteLogin: HandleRemoteLogin
+    private lateinit var handleLoginRedirect: HandleLoginRedirect
+    private lateinit var signOutUseCase: SignOutUseCase
+    private lateinit var onlineChecker: OnlineChecker
+    private lateinit var viewModel: WelcomeScreenViewModel
+    private lateinit var analytics: AnalyticsLogger
+    private lateinit var analyticsViewModel: SignInAnalyticsViewModel
+    private lateinit var loadingAnalyticsViewModel: LoadingScreenAnalyticsViewModel
+    private val logger = SystemLogger()
+
+    private val devButton = hasText(resources.getString(R.string.app_developer_button))
+
+    @Before
+    fun setup() {
+        localAuthPreferenceRepo = mock()
+        deviceBiometricsManager = mock()
+        tokenRepository = mock()
+        autoInitialiseSecureStore = mock()
+        verifyIdToken = mock()
+        navigator = mock()
+        saveTokens = mock()
+        savePersistentId = mock()
+        saveTokenExpiry = mock()
+        handleRemoteLogin = mock()
+        handleLoginRedirect = mock()
+        signOutUseCase = mock()
+        onlineChecker = mock()
+        analytics = mock()
+        localAuthManager = LocalAuthManagerImpl(
+            localAuthPreferenceRepo,
+            deviceBiometricsManager,
+            analytics
+        )
+        viewModel =
+            WelcomeScreenViewModel(
+                context,
+                localAuthManager,
+                tokenRepository,
+                autoInitialiseSecureStore,
+                verifyIdToken,
+                navigator,
+                saveTokens,
+                savePersistentId,
+                saveTokenExpiry,
+                handleRemoteLogin,
+                handleLoginRedirect,
+                signOutUseCase,
+                logger,
+                onlineChecker
+            )
+        analyticsViewModel = SignInAnalyticsViewModel(context, analytics)
+        loadingAnalyticsViewModel = LoadingScreenAnalyticsViewModel(context, analytics)
+        composeTestRule.setContent {
+            WelcomeScreen(viewModel, analyticsViewModel, loadingAnalyticsViewModel)
+        }
+    }
+
+    @Test
+    fun verifyDevMenuClick() {
+        composeTestRule.onNode(devButton).performClick()
+
+        verify(navigator).openDeveloperPanel()
+    }
+}


Reviews:
user: login: chrisdondigital
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T09:54:35Z
user: login: alex-bradbury-gds
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T15:24:08Z
user: login: alex-bradbury-gds
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T20:02:15Z
user: login: alex-bradbury-gds
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T20:16:48Z
user: login: BiancaMihaila
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T08:33:34Z
user: login: BiancaMihaila
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T11:16:13Z
user: login: BiancaMihaila
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T11:17:32Z
user: login: alex-bradbury-gds
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T11:46:30Z

Review comments:
diff_hunk: @@ -118,54 +123,139 @@ internal fun SplashBody(
     onLogin: () -> Unit,
     onOpenDeveloperPortal: () -> Unit
 ) {
-    Column(
-        modifier = Modifier
-            .fillMaxSize()
-            .background(colorResource(id = R.color.govuk_blue)),
-        horizontalAlignment = Alignment.CenterHorizontally,
-        verticalArrangement = Arrangement.Center
-    ) {
-        GdsIcon(
-            parameters = IconParameters(
-                image = R.drawable.tudor_crown_with_gov_uk,
-                backGroundColor = colorResource(id = R.color.govuk_blue),
-                foreGroundColor = Color.White,
-                modifier = Modifier
-                    .weight(1F)
-                    .clickable(enabled = DeveloperTools.isDeveloperPanelEnabled()) {
-                        onOpenDeveloperPortal()
-                    }
-                    .testTag(stringResource(id = R.string.splashIconTestTag))
+    val displayLoading = loading && !isUnlock
+    val displayUnlock = isUnlock && !loading
+    SubcomposeLayout(
+        modifier = Modifier.background(colorResource(R.color.govuk_blue))
+    ) { constraints ->
+        // Get full specs of device
path: features/src/main/java/uk/gov/onelogin/features/login/ui/signin/splash/SplashScreen.kt
user: login: alex-bradbury-gds
body: If only design knew our pain 😭 
created_at: 2025-05-14T15:24:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,54 +123,139 @@ internal fun SplashBody(
     onLogin: () -> Unit,
     onOpenDeveloperPortal: () -> Unit
 ) {
-    Column(
-        modifier = Modifier
-            .fillMaxSize()
-            .background(colorResource(id = R.color.govuk_blue)),
-        horizontalAlignment = Alignment.CenterHorizontally,
-        verticalArrangement = Arrangement.Center
-    ) {
-        GdsIcon(
-            parameters = IconParameters(
-                image = R.drawable.tudor_crown_with_gov_uk,
-                backGroundColor = colorResource(id = R.color.govuk_blue),
-                foreGroundColor = Color.White,
-                modifier = Modifier
-                    .weight(1F)
-                    .clickable(enabled = DeveloperTools.isDeveloperPanelEnabled()) {
-                        onOpenDeveloperPortal()
-                    }
-                    .testTag(stringResource(id = R.string.splashIconTestTag))
+    val displayLoading = loading && !isUnlock
path: features/src/main/java/uk/gov/onelogin/features/login/ui/signin/splash/SplashScreen.kt
user: login: alex-bradbury-gds
body: Is this logic correct, what if loading and isUnlock are both true, should it not show the loading spinner?
created_at: 2025-05-14T20:02:15Z
author_association: CONTRIBUTOR
path: features/src/testBuild/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
user: login: alex-bradbury-gds
body: Is there a staging version of this test too?
created_at: 2025-05-14T20:16:48Z
author_association: CONTRIBUTOR
path: features/src/testBuild/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
user: login: BiancaMihaila
body: For some reason we never had one (I assumed is because it was the same as build so we didn't need to), but I'll add one now
created_at: 2025-05-15T08:33:34Z
author_association: CONTRIBUTOR
path: features/src/testBuild/java/uk/gov/onelogin/features/login/ui/splash/SplashScreenDevMenuTest.kt
user: login: BiancaMihaila
body: Done
created_at: 2025-05-15T11:16:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,54 +123,139 @@ internal fun SplashBody(
     onLogin: () -> Unit,
     onOpenDeveloperPortal: () -> Unit
 ) {
-    Column(
-        modifier = Modifier
-            .fillMaxSize()
-            .background(colorResource(id = R.color.govuk_blue)),
-        horizontalAlignment = Alignment.CenterHorizontally,
-        verticalArrangement = Arrangement.Center
-    ) {
-        GdsIcon(
-            parameters = IconParameters(
-                image = R.drawable.tudor_crown_with_gov_uk,
-                backGroundColor = colorResource(id = R.color.govuk_blue),
-                foreGroundColor = Color.White,
-                modifier = Modifier
-                    .weight(1F)
-                    .clickable(enabled = DeveloperTools.isDeveloperPanelEnabled()) {
-                        onOpenDeveloperPortal()
-                    }
-                    .testTag(stringResource(id = R.string.splashIconTestTag))
+    val displayLoading = loading && !isUnlock
path: features/src/main/java/uk/gov/onelogin/features/login/ui/signin/splash/SplashScreen.kt
user: login: BiancaMihaila
body: You're right - there was a bug which I thought this was fixing it at the time - but it created a different bug, but I added a fix for that now if loading is true then we display it
Thank you
created_at: 2025-05-15T11:17:32Z
author_association: CONTRIBUTOR

PR comments:
user: login: chrisdondigital
created_at: 2025-05-14T12:57:09Z
body: I see you have more occurrances of the Sonar long vector path issue - I've accepted previous instances with this text "This issue will be fixed later as part of ticket DCMAW-13197"
author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-05-15T11:48:35Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=di-mobile-android-onelogin-app&pullRequest=457) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=di-mobile-android-onelogin-app&pullRequest=457&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [7 Accepted issues](https://sonarcloud.io/project/issues?id=di-mobile-android-onelogin-app&pullRequest=457&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=di-mobile-android-onelogin-app&pullRequest=457&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [83.5% Coverage on New Code](https://sonarcloud.io/component_measures?id=di-mobile-android-onelogin-app&pullRequest=457&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=di-mobile-android-onelogin-app&pullRequest=457&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=di-mobile-android-onelogin-app&pullRequest=457)


author_association: NONE
user: login: IndyD23
created_at: 2025-05-16T13:07:33Z
body: @BiancaMihaila QA reviewed & passed. Thank you for adding the Welsh evidence/screens.
author_association: NONE