Browse Source

Improve session recovery screens (#2657)

* Improve enter recovery key screen UI

* Add instructions to reset the encryption of the logged in account.

* Update screenshots

* Fix maestro flow

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
pull/2681/head
Jorge Martin Espinosa 1 month ago committed by GitHub
parent
commit
cf072fa1e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      .maestro/tests/account/verifySession.yaml
  2. 1
      changelog.d/2579.feature
  3. 37
      features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt
  4. 7
      features/ftue/impl/src/main/res/values-be/translations.xml
  5. 7
      features/ftue/impl/src/main/res/values-ru/translations.xml
  6. 7
      features/ftue/impl/src/main/res/values/localazy.xml
  7. 1
      features/lockscreen/impl/src/main/res/values-sv/translations.xml
  8. 2
      features/roomdetails/impl/src/main/res/values-be/translations.xml
  9. 3
      features/roomdetails/impl/src/main/res/values-cs/translations.xml
  10. 3
      features/roomdetails/impl/src/main/res/values-de/translations.xml
  11. 3
      features/roomdetails/impl/src/main/res/values-hu/translations.xml
  12. 3
      features/roomdetails/impl/src/main/res/values-in/translations.xml
  13. 2
      features/roomdetails/impl/src/main/res/values-it/translations.xml
  14. 3
      features/roomdetails/impl/src/main/res/values-ru/translations.xml
  15. 3
      features/roomdetails/impl/src/main/res/values-sk/translations.xml
  16. 5
      features/roomdirectory/impl/src/main/res/values-it/translations.xml
  17. 2
      features/roomlist/impl/src/main/res/values-be/translations.xml
  18. 2
      features/roomlist/impl/src/main/res/values-cs/translations.xml
  19. 2
      features/roomlist/impl/src/main/res/values-de/translations.xml
  20. 2
      features/roomlist/impl/src/main/res/values-hu/translations.xml
  21. 2
      features/roomlist/impl/src/main/res/values-in/translations.xml
  22. 1
      features/roomlist/impl/src/main/res/values-it/translations.xml
  23. 7
      features/roomlist/impl/src/main/res/values-ru/translations.xml
  24. 2
      features/roomlist/impl/src/main/res/values-sk/translations.xml
  25. 4
      features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt
  26. 8
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt
  27. 41
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyNode.kt
  28. 135
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyView.kt
  29. 2
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt
  30. 9
      features/securebackup/impl/src/main/res/values-be/translations.xml
  31. 9
      features/securebackup/impl/src/main/res/values-cs/translations.xml
  32. 22
      features/securebackup/impl/src/main/res/values-de/translations.xml
  33. 7
      features/securebackup/impl/src/main/res/values-hu/translations.xml
  34. 7
      features/securebackup/impl/src/main/res/values-in/translations.xml
  35. 13
      features/securebackup/impl/src/main/res/values-ru/translations.xml
  36. 7
      features/securebackup/impl/src/main/res/values-sk/translations.xml
  37. 9
      features/securebackup/impl/src/main/res/values/localazy.xml
  38. 1
      features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt
  39. 17
      features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt
  40. 2
      features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt
  41. 2
      features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt
  42. 22
      features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt
  43. 25
      features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt
  44. 29
      features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt
  45. 245
      features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt
  46. 188
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/SquareSizeModifier.kt
  47. 9
      libraries/ui-strings/src/main/res/values-be/translations.xml
  48. 7
      libraries/ui-strings/src/main/res/values-cs/translations.xml
  49. 20
      libraries/ui-strings/src/main/res/values-de/translations.xml
  50. 7
      libraries/ui-strings/src/main/res/values-hu/translations.xml
  51. 7
      libraries/ui-strings/src/main/res/values-in/translations.xml
  52. 11
      libraries/ui-strings/src/main/res/values-ru/translations.xml
  53. 7
      libraries/ui-strings/src/main/res/values-sk/translations.xml
  54. 7
      libraries/ui-strings/src/main/res/values/localazy.xml
  55. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.createkey_CreateNewRecoveryKeyView_null_CreateNewRecoveryKeyView-Day-0_1_null,NEXUS_5,1.0,en].png
  56. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.createkey_CreateNewRecoveryKeyView_null_CreateNewRecoveryKeyView-Night-0_2_null,NEXUS_5,1.0,en].png
  57. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-1_2_null_0,NEXUS_5,1.0,en].png
  58. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-1_2_null_1,NEXUS_5,1.0,en].png
  59. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-1_2_null_2,NEXUS_5,1.0,en].png
  60. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-1_2_null_3,NEXUS_5,1.0,en].png
  61. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-1_3_null_0,NEXUS_5,1.0,en].png
  62. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-1_3_null_1,NEXUS_5,1.0,en].png
  63. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-1_3_null_2,NEXUS_5,1.0,en].png
  64. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-1_3_null_3,NEXUS_5,1.0,en].png
  65. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-2_3_null_0,NEXUS_5,1.0,en].png
  66. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-2_3_null_1,NEXUS_5,1.0,en].png
  67. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-2_3_null_2,NEXUS_5,1.0,en].png
  68. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-2_4_null_0,NEXUS_5,1.0,en].png
  69. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-2_4_null_1,NEXUS_5,1.0,en].png
  70. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-2_4_null_2,NEXUS_5,1.0,en].png
  71. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_0,NEXUS_5,1.0,en].png
  72. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_1,NEXUS_5,1.0,en].png
  73. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_2,NEXUS_5,1.0,en].png
  74. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_3,NEXUS_5,1.0,en].png
  75. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-3_4_null_0,NEXUS_5,1.0,en].png
  76. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-3_4_null_1,NEXUS_5,1.0,en].png
  77. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-3_4_null_2,NEXUS_5,1.0,en].png
  78. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-3_4_null_3,NEXUS_5,1.0,en].png
  79. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_0,NEXUS_5,1.0,en].png
  80. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_1,NEXUS_5,1.0,en].png
  81. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_2,NEXUS_5,1.0,en].png
  82. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_3,NEXUS_5,1.0,en].png
  83. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-3_5_null_0,NEXUS_5,1.0,en].png
  84. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-3_5_null_1,NEXUS_5,1.0,en].png
  85. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-3_5_null_2,NEXUS_5,1.0,en].png
  86. 3
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-3_5_null_3,NEXUS_5,1.0,en].png
  87. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_0,NEXUS_5,1.0,en].png
  88. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_1,NEXUS_5,1.0,en].png
  89. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_2,NEXUS_5,1.0,en].png
  90. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_3,NEXUS_5,1.0,en].png
  91. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_4,NEXUS_5,1.0,en].png
  92. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_5,NEXUS_5,1.0,en].png
  93. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_6,NEXUS_5,1.0,en].png
  94. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_7,NEXUS_5,1.0,en].png
  95. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_8,NEXUS_5,1.0,en].png
  96. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-4_6_null_0,NEXUS_5,1.0,en].png
  97. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-4_6_null_1,NEXUS_5,1.0,en].png
  98. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-4_6_null_2,NEXUS_5,1.0,en].png
  99. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-4_6_null_3,NEXUS_5,1.0,en].png
  100. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-4_6_null_4,NEXUS_5,1.0,en].png
  101. Some files were not shown because too many files have changed in this diff Show More

2
.maestro/tests/account/verifySession.yaml

@ -6,7 +6,7 @@ appId: ${MAESTRO_APP_ID} @@ -6,7 +6,7 @@ appId: ${MAESTRO_APP_ID}
id: "verification-recovery_key"
- inputText: ${MAESTRO_RECOVERY_KEY}
- hideKeyboard
- tapOn: "Confirm"
- tapOn: "Continue"
- extendedWaitUntil:
visible: "Device verified"
timeout: 10000

1
changelog.d/2579.feature

@ -0,0 +1 @@ @@ -0,0 +1 @@
Move session recovery to the login flow.

37
features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt

@ -58,13 +58,27 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor( @@ -58,13 +58,27 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor(
@Parcelize
data object EnterRecoveryKey : NavTarget
@Parcelize
data object CreateNewRecoveryKey : NavTarget
}
interface Callback : Plugin {
fun onDone()
}
private val callback = plugins<Callback>().first()
private val secureBackupEntryPointCallback = object : SecureBackupEntryPoint.Callback {
override fun onCreateNewRecoveryKey() {
backstack.push(NavTarget.CreateNewRecoveryKey)
}
override fun onDone() {
lifecycleScope.launch {
// Move to the completed state view in the verification flow
backstack.newRoot(NavTarget.Root)
}
}
}
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) {
@ -75,8 +89,12 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor( @@ -75,8 +89,12 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor(
backstack.push(NavTarget.EnterRecoveryKey)
}
override fun onCreateNewRecoveryKey() {
backstack.push(NavTarget.CreateNewRecoveryKey)
}
override fun onDone() {
callback.onDone()
plugins<Callback>().forEach { it.onDone() }
}
})
.build()
@ -84,14 +102,13 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor( @@ -84,14 +102,13 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor(
is NavTarget.EnterRecoveryKey -> {
secureBackupEntryPoint.nodeBuilder(this, buildContext)
.params(SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey))
.callback(object : SecureBackupEntryPoint.Callback {
override fun onDone() {
lifecycleScope.launch {
// Move to the completed state view in the verification flow
backstack.newRoot(NavTarget.Root)
}
}
})
.callback(secureBackupEntryPointCallback)
.build()
}
is NavTarget.CreateNewRecoveryKey -> {
secureBackupEntryPoint.nodeBuilder(this, buildContext)
.params(SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.CreateNewRecoveryKey))
.callback(secureBackupEntryPointCallback)
.build()
}
}

7
features/ftue/impl/src/main/res/values-be/translations.xml

@ -2,6 +2,13 @@ @@ -2,6 +2,13 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Вы можаце змяніць налады пазней."</string>
<string name="screen_notification_optin_title">"Дазвольце апавяшчэнні і ніколі не прапускайце іх"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Адкрыйце Element на настольнай прыладзе"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Націсніце на свой аватар"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Выберыце %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"“Звязаць новую прыладу”"</string>
<string name="screen_qr_code_login_initial_state_item_4">"Выберыце %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_4_action">"“Паказаць QR-код”"</string>
<string name="screen_qr_code_login_initial_state_title">"Адкрыйце Element на іншай прыладзе, каб атрымаць QR-код"</string>
<string name="screen_welcome_bullet_1">"Званкі, апытанні, пошук і многае іншае будзе дададзена пазней у гэтым годзе."</string>
<string name="screen_welcome_bullet_2">"Гісторыя паведамленняў для зашыфраваных пакояў пакуль недаступна."</string>
<string name="screen_welcome_bullet_3">"Мы будзем рады пачуць вашае меркаванне, паведаміце нам аб гэтым праз старонку налад."</string>

7
features/ftue/impl/src/main/res/values-ru/translations.xml

@ -2,6 +2,13 @@ @@ -2,6 +2,13 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Вы можете изменить настройки позже."</string>
<string name="screen_notification_optin_title">"Разрешите уведомления и никогда не пропустите сообщение"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Откройте Element на настольном устройстве"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Нажмите на свое изображение"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Выбрать %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"\"Привязать новое устройство\""</string>
<string name="screen_qr_code_login_initial_state_item_4">"Выбрать %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_4_action">"\"Показать QR-код\""</string>
<string name="screen_qr_code_login_initial_state_title">"Откройте Element на другом устройстве, чтобы получить QR-код"</string>
<string name="screen_welcome_bullet_1">"Звонки, опросы, поиск и многое другое будут добавлены позже в этом году."</string>
<string name="screen_welcome_bullet_2">"История сообщений для зашифрованных комнат в этом обновлении будет недоступна."</string>
<string name="screen_welcome_bullet_3">"Мы будем рады услышать ваше мнение, сообщите нам об этом через страницу настроек."</string>

7
features/ftue/impl/src/main/res/values/localazy.xml

@ -2,6 +2,13 @@ @@ -2,6 +2,13 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"You can change your settings later."</string>
<string name="screen_notification_optin_title">"Allow notifications and never miss a message"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Open Element on a desktop device"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Click on your avatar"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Select %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"“Link new device”"</string>
<string name="screen_qr_code_login_initial_state_item_4">"Select %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_4_action">"“Show QR code”"</string>
<string name="screen_qr_code_login_initial_state_title">"Open Element on another device to get the QR code"</string>
<string name="screen_welcome_bullet_1">"Calls, polls, search and more will be added later this year."</string>
<string name="screen_welcome_bullet_2">"Message history for encrypted rooms isn’t available yet."</string>
<string name="screen_welcome_bullet_3">"We’d love to hear from you, let us know what you think via the settings page."</string>

1
features/lockscreen/impl/src/main/res/values-sv/translations.xml

@ -3,7 +3,6 @@ @@ -3,7 +3,6 @@
<string name="screen_app_lock_settings_change_pin">"Byt PIN-kod"</string>
<string name="screen_app_lock_settings_enable_biometric_unlock">"Tillåt biometrisk upplåsning"</string>
<string name="screen_app_lock_settings_remove_pin">"Ta bort PIN-kod"</string>
<string name="screen_app_lock_settings_remove_pin_alert_message">"Är du säker på att du vill ta bort PIN-koden?"</string>
<string name="screen_app_lock_settings_remove_pin_alert_title">"Ta bort PIN-koden?"</string>
<string name="screen_signout_in_progress_dialog_content">"Loggar ut …"</string>
</resources>

2
features/roomdetails/impl/src/main/res/values-be/translations.xml

@ -31,7 +31,6 @@ @@ -31,7 +31,6 @@
<string name="screen_room_change_role_confirm_demote_self_title">"Панізіць сябе?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (У чаканні)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(У чаканні)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Адміністратары аўтаматычна маюць права мадэратара"</string>
<string name="screen_room_change_role_moderators_title">"Рэдагаваць мадэратараў"</string>
<string name="screen_room_change_role_section_administrators">"Адміністратары"</string>
<string name="screen_room_change_role_section_moderators">"Мадэратары"</string>
@ -59,7 +58,6 @@ @@ -59,7 +58,6 @@
<string name="screen_room_details_room_name_label">"Назва пакоя"</string>
<string name="screen_room_details_security_title">"Бяспека"</string>
<string name="screen_room_details_share_room_title">"Падзяліцца пакоем"</string>
<string name="screen_room_details_title">"Інфармацыя аб пакоі"</string>
<string name="screen_room_details_topic_title">"Тэма"</string>
<string name="screen_room_details_updating_room">"Ідзе абнаўленне пакоя…"</string>
<string name="screen_room_member_list_ban_member_confirmation_action">"Заблакіраваць"</string>

3
features/roomdetails/impl/src/main/res/values-cs/translations.xml

@ -30,8 +30,6 @@ @@ -30,8 +30,6 @@
<string name="screen_room_change_role_confirm_demote_self_description">"Tuto změnu nebudete moci vrátit zpět, protože sami degradujete, pokud jste posledním privilegovaným uživatelem v místnosti, nebude možné znovu získat oprávnění."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Degradovat se?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (čekající)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Čeká na vyřízení)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Správci mají automaticky oprávnění moderátora"</string>
<string name="screen_room_change_role_moderators_title">"Upravit moderátory"</string>
<string name="screen_room_change_role_section_administrators">"Správci"</string>
<string name="screen_room_change_role_section_moderators">"Moderátoři"</string>
@ -59,7 +57,6 @@ @@ -59,7 +57,6 @@
<string name="screen_room_details_room_name_label">"Název místnosti"</string>
<string name="screen_room_details_security_title">"Zabezpečení"</string>
<string name="screen_room_details_share_room_title">"Sdílet místnost"</string>
<string name="screen_room_details_title">"Informace o místnosti"</string>
<string name="screen_room_details_topic_title">"Téma"</string>
<string name="screen_room_details_updating_room">"Aktualizace místnosti…"</string>
<string name="screen_room_member_list_ban_member_confirmation_action">"Vykázat"</string>

3
features/roomdetails/impl/src/main/res/values-de/translations.xml

@ -30,8 +30,6 @@ @@ -30,8 +30,6 @@
<string name="screen_room_change_role_confirm_demote_self_description">"Du stufst dich selbst herab. Diese Änderung kann nicht rückgängig gemacht werden. Wenn du der letzte Benutzer mit dieser Rolle bist, ist es nicht möglich, diese Rolle wiederzuerlangen."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Möchtest Du Dich selbst herabstufen?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Ausstehend)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Ausstehend)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Administratoren haben automatisch Moderatorenrechte"</string>
<string name="screen_room_change_role_moderators_title">"Moderatoren bearbeiten"</string>
<string name="screen_room_change_role_section_administrators">"Administratoren"</string>
<string name="screen_room_change_role_section_moderators">"Moderatoren"</string>
@ -59,7 +57,6 @@ @@ -59,7 +57,6 @@
<string name="screen_room_details_room_name_label">"Raumname"</string>
<string name="screen_room_details_security_title">"Sicherheit"</string>
<string name="screen_room_details_share_room_title">"Raum teilen"</string>
<string name="screen_room_details_title">"Raum Informationen"</string>
<string name="screen_room_details_topic_title">"Thema"</string>
<string name="screen_room_details_updating_room">"Raum wird aktualisiert…"</string>
<string name="screen_room_member_list_ban_member_confirmation_action">"Sperren"</string>

3
features/roomdetails/impl/src/main/res/values-hu/translations.xml

@ -30,8 +30,6 @@ @@ -30,8 +30,6 @@
<string name="screen_room_change_role_confirm_demote_self_description">"Ezt a változtatást nem fogja tudni visszavonni, mivel lefokozza magát, ha Ön az utolsó jogosultságokkal rendelkező felhasználó a szobában, akkor lehetetlen lesz visszaszerezni a jogosultságokat."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Lefokozza magát?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (függőben)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Függőben)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Az adminisztrátorok automatikusan moderátori jogosultságokkal rendelkeznek"</string>
<string name="screen_room_change_role_moderators_title">"Moderátorok szerkesztése"</string>
<string name="screen_room_change_role_section_administrators">"Rendszergazdák"</string>
<string name="screen_room_change_role_section_moderators">"Moderátorok"</string>
@ -59,7 +57,6 @@ @@ -59,7 +57,6 @@
<string name="screen_room_details_room_name_label">"Szoba neve"</string>
<string name="screen_room_details_security_title">"Biztonság"</string>
<string name="screen_room_details_share_room_title">"Szoba megosztása"</string>
<string name="screen_room_details_title">"Szobainformációk"</string>
<string name="screen_room_details_topic_title">"Téma"</string>
<string name="screen_room_details_updating_room">"Szoba frissítése…"</string>
<string name="screen_room_member_list_ban_member_confirmation_action">"Kitiltás"</string>

3
features/roomdetails/impl/src/main/res/values-in/translations.xml

@ -30,8 +30,6 @@ @@ -30,8 +30,6 @@
<string name="screen_room_change_role_confirm_demote_self_description">"Anda tidak akan dapat mengurungkan perubahan ini karena Anda sedang menurunkan Anda sendiri, jika Anda merupakan pengguna dengan hak khusus dalam ruangan maka tidak akan memungkinkan untuk mendapatkan hak tersebut lagi."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Turunkan Anda sendiri?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Tertunda)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Tertunda)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Admin secara otomatis memiliki hak moderator"</string>
<string name="screen_room_change_role_moderators_title">"Sunting Moderator"</string>
<string name="screen_room_change_role_section_administrators">"Admin"</string>
<string name="screen_room_change_role_section_moderators">"Moderator"</string>
@ -59,7 +57,6 @@ @@ -59,7 +57,6 @@
<string name="screen_room_details_room_name_label">"Nama ruangan"</string>
<string name="screen_room_details_security_title">"Keamanan"</string>
<string name="screen_room_details_share_room_title">"Bagikan ruangan"</string>
<string name="screen_room_details_title">"Info ruangan"</string>
<string name="screen_room_details_topic_title">"Topik"</string>
<string name="screen_room_details_updating_room">"Memperbarui ruangan…"</string>
<string name="screen_room_member_list_ban_member_confirmation_action">"Cekal"</string>

2
features/roomdetails/impl/src/main/res/values-it/translations.xml

@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
<string name="screen_polls_history_title">"Sondaggi"</string>
<string name="screen_room_change_permissions_administrators">"Solo amministratori"</string>
<string name="screen_room_change_permissions_ban_people">"Escludi membri"</string>
<string name="screen_room_change_permissions_delete_messages">"Rimuovi messaggi"</string>
<string name="screen_room_change_permissions_delete_messages">"Eliminare messaggi"</string>
<string name="screen_room_change_permissions_everyone">"Tutti"</string>
<string name="screen_room_change_permissions_invite_people">"Invitare persone"</string>
<string name="screen_room_change_permissions_member_moderation">"Moderazione dei membri"</string>

3
features/roomdetails/impl/src/main/res/values-ru/translations.xml

@ -30,8 +30,6 @@ @@ -30,8 +30,6 @@
<string name="screen_room_change_role_confirm_demote_self_description">"Вы не сможете отменить это изменение, так как понижаете себя статус. Если вы являетесь последним привилегированным пользователем в комнате, восстановить привилегии будет невозможно."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Понизить свой уровень?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Ожидание)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(В ожидании)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Администраторы автоматически получают права модератора"</string>
<string name="screen_room_change_role_moderators_title">"Редактировать роль модераторов"</string>
<string name="screen_room_change_role_section_administrators">"Администраторы"</string>
<string name="screen_room_change_role_section_moderators">"Модераторы"</string>
@ -59,7 +57,6 @@ @@ -59,7 +57,6 @@
<string name="screen_room_details_room_name_label">"Название комнаты"</string>
<string name="screen_room_details_security_title">"Безопасность"</string>
<string name="screen_room_details_share_room_title">"Поделиться комнатой"</string>
<string name="screen_room_details_title">"Информация о комнате"</string>
<string name="screen_room_details_topic_title">"Тема"</string>
<string name="screen_room_details_updating_room">"Обновление комнаты…"</string>
<string name="screen_room_member_list_ban_member_confirmation_action">"Заблокировать"</string>

3
features/roomdetails/impl/src/main/res/values-sk/translations.xml

@ -30,8 +30,6 @@ @@ -30,8 +30,6 @@
<string name="screen_room_change_role_confirm_demote_self_description">"Túto zmenu nebudete môcť vrátiť späť, pretože znižujete svoju úroveň. Ak ste posledným privilegovaným používateľom v miestnosti, nebude možné získať znova oprávnenia."</string>
<string name="screen_room_change_role_confirm_demote_self_title">"Znížiť svoju úroveň?"</string>
<string name="screen_room_change_role_invited_member_name">"%1$s (Čaká sa)"</string>
<string name="screen_room_change_role_invited_member_name_android">"(Čaká sa)"</string>
<string name="screen_room_change_role_moderators_admin_section_footer">"Správcovia majú automaticky oprávnenia moderátora"</string>
<string name="screen_room_change_role_moderators_title">"Upraviť moderátorov"</string>
<string name="screen_room_change_role_section_administrators">"Správcovia"</string>
<string name="screen_room_change_role_section_moderators">"Moderátori"</string>
@ -59,7 +57,6 @@ @@ -59,7 +57,6 @@
<string name="screen_room_details_room_name_label">"Názov miestnosti"</string>
<string name="screen_room_details_security_title">"Bezpečnosť"</string>
<string name="screen_room_details_share_room_title">"Zdieľať miestnosť"</string>
<string name="screen_room_details_title">"Informácie o miestnosti"</string>
<string name="screen_room_details_topic_title">"Téma"</string>
<string name="screen_room_details_updating_room">"Aktualizácia miestnosti…"</string>
<string name="screen_room_member_list_ban_member_confirmation_action">"Zakázať"</string>

5
features/roomdirectory/impl/src/main/res/values-it/translations.xml

@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_directory_search_loading_error">"Caricamento fallito"</string>
<string name="screen_room_directory_search_title">"Elenco delle stanze"</string>
</resources>

2
features/roomlist/impl/src/main/res/values-be/translations.xml

@ -11,8 +11,6 @@ @@ -11,8 +11,6 @@
<string name="screen_roomlist_filter_favourites_empty_state_subtitle">"Дадаць чат у абранае можна ў наладах чата.
На дадзены момант вы можаце прыбраць фільтры, каб убачыць іншыя вашыя чаты."</string>
<string name="screen_roomlist_filter_favourites_empty_state_title">"У вас пакуль няма абраных чатаў"</string>
<string name="screen_roomlist_filter_invites">"Запрашэнні"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"У вас няма непрынятых запрашэнняў."</string>
<string name="screen_roomlist_filter_low_priority">"Нізкі прыярытэт"</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Вы можаце прыбраць фільтры, каб убачыць іншыя вашыя чаты."</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"У вас няма чатаў для гэтай катэгорыі"</string>

2
features/roomlist/impl/src/main/res/values-cs/translations.xml

@ -11,8 +11,6 @@ @@ -11,8 +11,6 @@
<string name="screen_roomlist_filter_favourites_empty_state_subtitle">"V nastavení chatu můžete přidat chat k oblíbeným.
Prozatím můžete zrušit výběr filtrů, abyste viděli své další chaty"</string>
<string name="screen_roomlist_filter_favourites_empty_state_title">"Zatím nemáte oblíbené chaty"</string>
<string name="screen_roomlist_filter_invites">"Pozvánky"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"Nemáte žádné nevyřízené pozvánky."</string>
<string name="screen_roomlist_filter_low_priority">"Nízká priorita"</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Můžete zrušit výběr filtrů, abyste viděli své další chaty"</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"Nemáte chaty pro tento výběr"</string>

2
features/roomlist/impl/src/main/res/values-de/translations.xml

@ -11,8 +11,6 @@ @@ -11,8 +11,6 @@
<string name="screen_roomlist_filter_favourites_empty_state_subtitle">"In den Chat-Einstellungen kannst du einen Chat als Favorit hinzufügen.
Um deine anderen Chats zu sehen wähle diesen Filter ab."</string>
<string name="screen_roomlist_filter_favourites_empty_state_title">"Du hast noch keine Chats als Favorit markiert."</string>
<string name="screen_roomlist_filter_invites">"Einladungen"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"Du hast keine ausstehenden Einladungen."</string>
<string name="screen_roomlist_filter_low_priority">"Niedrige Priorität"</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Wähle Filter ab, um Deine Chats zu sehen."</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"Du hast keine Chats für diese Auswahl"</string>

2
features/roomlist/impl/src/main/res/values-hu/translations.xml

@ -11,8 +11,6 @@ @@ -11,8 +11,6 @@
<string name="screen_roomlist_filter_favourites_empty_state_subtitle">"A csevegési beállításokban csevegéseket adhat hozzá a kedvencekhez.
Egyelőre törölheti a szűrőket a többi csevegés megtekintéséhez."</string>
<string name="screen_roomlist_filter_favourites_empty_state_title">"Még nincsenek kedvenc csevegései"</string>
<string name="screen_roomlist_filter_invites">"Meghívások"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"Nincsenek függőben lévő meghívásai."</string>
<string name="screen_roomlist_filter_low_priority">"Alacsony prioritás"</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Kikapcsolhatja a szűrőket a többi csevegés megtekintéséhez"</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"Ehhez a kiválasztáshoz nem tartoznak csevegések"</string>

2
features/roomlist/impl/src/main/res/values-in/translations.xml

@ -11,8 +11,6 @@ @@ -11,8 +11,6 @@
<string name="screen_roomlist_filter_favourites_empty_state_subtitle">"Anda dapat menambahkan percakapan ke favorit Anda dalam pengaturan percakapan.
Untuk sementara, Anda dapat membatalkan pilihan saringan untuk melihat percakapan Anda yang lain"</string>
<string name="screen_roomlist_filter_favourites_empty_state_title">"Anda belum memiliki percakapan favorit"</string>
<string name="screen_roomlist_filter_invites">"Undangan"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"Anda tidak memiliki undangan yang tertunda."</string>
<string name="screen_roomlist_filter_low_priority">"Prioritas Rendah"</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Anda dapat membatalkan pilihan saringan untuk melihat percakapan Anda yang lain"</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"Anda tidak memiliki percakapan untuk pemilihan ini"</string>

1
features/roomlist/impl/src/main/res/values-it/translations.xml

@ -24,7 +24,6 @@ Non hai messaggi non letti!"</string> @@ -24,7 +24,6 @@ Non hai messaggi non letti!"</string>
<string name="screen_roomlist_main_space_title">"Tutte le conversazioni"</string>
<string name="screen_roomlist_mark_as_read">"Segna come letto"</string>
<string name="screen_roomlist_mark_as_unread">"Segna come non letto"</string>
<string name="screen_roomlist_room_directory_button_title">"Sfoglia tutte le stanze"</string>
<string name="session_verification_banner_message">"Sembra che tu stia usando un nuovo dispositivo. Verificati con un altro dispositivo per accedere ai tuoi messaggi cifrati."</string>
<string name="session_verification_banner_title">"Verifica che sei tu"</string>
</resources>

7
features/roomlist/impl/src/main/res/values-ru/translations.xml

@ -1,10 +1,7 @@ @@ -1,10 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="confirm_recovery_key_banner_message">"В настоящее время резервная копия вашего чата не синхронизирована. Требуется подтвердить вашим ключом восстановления, чтобы сохранить доступ к резервной копии чата."</string>
<string name="confirm_recovery_key_banner_title">
"Введите "
<b>"ключ восстановления"</b>
</string>
<string name="confirm_recovery_key_banner_title">"Подтвердите ключ восстановления"</string>
<string name="screen_migration_message">"Это одноразовый процесс, спасибо, что подождали."</string>
<string name="screen_migration_title">"Настройка учетной записи."</string>
<string name="screen_roomlist_a11y_create_message">"Создайте новую беседу или комнату"</string>
@ -14,8 +11,6 @@ @@ -14,8 +11,6 @@
<string name="screen_roomlist_filter_favourites_empty_state_subtitle">"Добавить чат в избранное можно в настройках чата.
На данный момент вы можете убрать фильтры, чтобы увидеть другие ваши чаты."</string>
<string name="screen_roomlist_filter_favourites_empty_state_title">"У вас пока нет избранных чатов"</string>
<string name="screen_roomlist_filter_invites">"Приглашает"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"У вас нет отложенных приглашений."</string>
<string name="screen_roomlist_filter_low_priority">"Низкий приоритет"</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Вы можете убрать фильтры, чтобы увидеть другие ваши чаты."</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"У вас нет чатов для этой подборки"</string>

2
features/roomlist/impl/src/main/res/values-sk/translations.xml

@ -11,8 +11,6 @@ @@ -11,8 +11,6 @@
<string name="screen_roomlist_filter_favourites_empty_state_subtitle">"Môžete pridať konverzáciu medzi obľúbené v nastaveniach konverzácie.
Zatiaľ môžete zrušiť výber filtrov, aby ste videli ostatné konverzácie"</string>
<string name="screen_roomlist_filter_favourites_empty_state_title">"Zatiaľ nemáte obľúbené konverzácie"</string>
<string name="screen_roomlist_filter_invites">"Pozvánky"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"Nemáte žiadne čakajúce pozvánky."</string>
<string name="screen_roomlist_filter_low_priority">"Nízka priorita"</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Môžete zrušiť výber filtrov, aby ste videli svoje ostatné konverzácie"</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"Nemáte konverzácie pre tento výber"</string>

4
features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt

@ -31,6 +31,9 @@ interface SecureBackupEntryPoint : FeatureEntryPoint { @@ -31,6 +31,9 @@ interface SecureBackupEntryPoint : FeatureEntryPoint {
@Parcelize
data object EnterRecoveryKey : InitialTarget
@Parcelize
data object CreateNewRecoveryKey : InitialTarget
}
data class Params(val initialElement: InitialTarget) : NodeInputs
@ -38,6 +41,7 @@ interface SecureBackupEntryPoint : FeatureEntryPoint { @@ -38,6 +41,7 @@ interface SecureBackupEntryPoint : FeatureEntryPoint {
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
interface Callback : Plugin {
fun onCreateNewRecoveryKey()
fun onDone()
}

8
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt

@ -30,6 +30,7 @@ import dagger.assisted.Assisted @@ -30,6 +30,7 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.securebackup.api.SecureBackupEntryPoint
import io.element.android.features.securebackup.impl.createkey.CreateNewRecoveryKeyNode
import io.element.android.features.securebackup.impl.disable.SecureBackupDisableNode
import io.element.android.features.securebackup.impl.enable.SecureBackupEnableNode
import io.element.android.features.securebackup.impl.enter.SecureBackupEnterRecoveryKeyNode
@ -50,6 +51,7 @@ class SecureBackupFlowNode @AssistedInject constructor( @@ -50,6 +51,7 @@ class SecureBackupFlowNode @AssistedInject constructor(
initialElement = when (plugins.filterIsInstance(SecureBackupEntryPoint.Params::class.java).first().initialElement) {
SecureBackupEntryPoint.InitialTarget.Root -> NavTarget.Root
SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey -> NavTarget.EnterRecoveryKey
SecureBackupEntryPoint.InitialTarget.CreateNewRecoveryKey -> NavTarget.CreateNewRecoveryKey
},
savedStateMap = buildContext.savedStateMap,
),
@ -74,6 +76,9 @@ class SecureBackupFlowNode @AssistedInject constructor( @@ -74,6 +76,9 @@ class SecureBackupFlowNode @AssistedInject constructor(
@Parcelize
data object EnterRecoveryKey : NavTarget
@Parcelize
data object CreateNewRecoveryKey : NavTarget
}
private val callback = plugins<SecureBackupEntryPoint.Callback>().firstOrNull()
@ -134,6 +139,9 @@ class SecureBackupFlowNode @AssistedInject constructor( @@ -134,6 +139,9 @@ class SecureBackupFlowNode @AssistedInject constructor(
}
createNode<SecureBackupEnterRecoveryKeyNode>(buildContext, plugins = listOf(callback))
}
NavTarget.CreateNewRecoveryKey -> {
createNode<CreateNewRecoveryKeyNode>(buildContext)
}
}
}

41
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyNode.kt

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
/*
* Copyright (c) 2024 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.securebackup.impl.createkey
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.SessionScope
@ContributesNode(SessionScope::class)
class CreateNewRecoveryKeyNode @AssistedInject constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
) : Node(buildContext, plugins = plugins) {
@Composable
override fun View(modifier: Modifier) {
CreateNewRecoveryKeyView(
modifier = modifier,
onBackClicked = ::navigateUp,
)
}
}

135
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyView.kt

@ -0,0 +1,135 @@ @@ -0,0 +1,135 @@
/*
* Copyright (c) 2024 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.securebackup.impl.createkey
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.securebackup.impl.R
import io.element.android.libraries.designsystem.components.BigIcon
import io.element.android.libraries.designsystem.components.PageTitle
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.modifiers.squareSize
import io.element.android.libraries.designsystem.preview.ElementPreview
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
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CreateNewRecoveryKeyView(
onBackClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
Scaffold(
modifier = modifier,
topBar = {
TopAppBar(title = {}, navigationIcon = { BackButton(onClick = onBackClicked) })
}
) { padding ->
Column(
modifier = Modifier.padding(padding)
) {
PageTitle(
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 40.dp),
title = stringResource(R.string.screen_create_new_recovery_key_title),
iconStyle = BigIcon.Style.Default(CompoundIcons.Computer())
)
Content()
}
}
}
@Composable
private fun Content() {
Column(modifier = Modifier.padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(24.dp)) {
Item(index = 1, text = AnnotatedString(stringResource(R.string.screen_create_new_recovery_key_list_item_1)))
Item(index = 2, text = AnnotatedString(stringResource(R.string.screen_create_new_recovery_key_list_item_2)))
Item(
index = 3,
text = buildAnnotatedString {
val resetAllAction = stringResource(R.string.screen_create_new_recovery_key_list_item_3_reset_all)
val text = stringResource(R.string.screen_create_new_recovery_key_list_item_3, resetAllAction)
append(text)
val start = text.indexOf(resetAllAction)
val end = start + resetAllAction.length
if (start in text.indices && end in text.indices) {
addStyle(SpanStyle(fontWeight = FontWeight.Bold), start, end)
}
}
)
Item(index = 4, text = AnnotatedString(stringResource(R.string.screen_create_new_recovery_key_list_item_4)))
Item(index = 5, text = AnnotatedString(stringResource(R.string.screen_create_new_recovery_key_list_item_5)))
}
}
@Composable
private fun Item(index: Int, text: AnnotatedString) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
ItemNumber(index = index)
Text(text = text, style = ElementTheme.typography.fontBodyMdRegular, color = ElementTheme.colors.textPrimary)
}
}
@Composable
private fun ItemNumber(
index: Int,
) {
val color = ElementTheme.colors.textPlaceholder
Box(
modifier = Modifier
.border(1.dp, color, CircleShape)
.squareSize()
) {
Text(
modifier = Modifier.padding(1.5.dp),
text = index.toString(),
style = ElementTheme.typography.fontBodySmRegular,
color = color,
textAlign = TextAlign.Center,
)
}
}
@PreviewsDayNight
@Composable
internal fun CreateNewRecoveryKeyViewPreview() {
ElementPreview {
CreateNewRecoveryKeyView(
onBackClicked = {},
)
}
}

2
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt

@ -83,7 +83,7 @@ private fun ColumnScope.Buttons( @@ -83,7 +83,7 @@ private fun ColumnScope.Buttons(
state: SecureBackupEnterRecoveryKeyState,
) {
Button(
text = stringResource(id = CommonStrings.action_confirm),
text = stringResource(id = CommonStrings.action_continue),
enabled = state.isSubmitEnabled,
showProgress = state.submitAction.isLoading(),
modifier = Modifier.fillMaxWidth(),

9
features/securebackup/impl/src/main/res/values-be/translations.xml

@ -9,6 +9,13 @@ @@ -9,6 +9,13 @@
<string name="screen_chat_backup_recovery_action_confirm_description">"Ваша рэзервовая копія чата зараз не сінхранізавана."</string>
<string name="screen_chat_backup_recovery_action_setup">"Наладзьце аднаўленне"</string>
<string name="screen_chat_backup_recovery_action_setup_description">"Атрымайце доступ да зашыфраваных паведамленняў, калі вы страціце ўсе свае прылады або выйдзеце з сістэмы %1$s усюды."</string>
<string name="screen_create_new_recovery_key_list_item_1">"Адкрыйце Element на настольнай прыладзе"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Увайдзіце ў свой уліковы запіс яшчэ раз"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Калі будзе прапанавана пацвердзіць вашу прыладу, выберыце %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"“Скінуць усе”"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Выконвайце інструкцыі, каб стварыць новы ключ аднаўлення"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Захавайце новы ключ аднаўлення ў ме́неджэры пароляў або ў зашыфраванай нататке"</string>
<string name="screen_create_new_recovery_key_title">"Скіньце шыфраванне для вашага ўліковага запісу з дапамогай іншай прылады"</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Адключыць"</string>
<string name="screen_key_backup_disable_confirmation_description">"Вы страціце зашыфраваныя паведамленні, калі выйдзеце з усіх прылад."</string>
<string name="screen_key_backup_disable_confirmation_title">"Вы ўпэўнены, што хочаце адключыць рэзервовае капіраванне?"</string>
@ -25,7 +32,7 @@ @@ -25,7 +32,7 @@
<string name="screen_recovery_key_confirm_description">"Пераканайцеся, што ніхто не бачыць гэты экран!"</string>
<string name="screen_recovery_key_confirm_error_content">"Паўтарыце спробу, каб пацвердзіць доступ да рэзервовай копіі чата."</string>
<string name="screen_recovery_key_confirm_error_title">"Няправільны ключ аднаўлення"</string>
<string name="screen_recovery_key_confirm_key_description">"Калі ў вас ёсць ключ аднаўлення або парольная фраза/ключ, гэта таксама будзе працаваць."</string>
<string name="screen_recovery_key_confirm_key_description">"Калі ў вас ёсць ключ аднаўлення або парольная фраза, гэта таксама будзе працаваць."</string>
<string name="screen_recovery_key_confirm_key_label">"Ключ аднаўлення або код доступу"</string>
<string name="screen_recovery_key_confirm_key_placeholder">"Увесці…"</string>
<string name="screen_recovery_key_confirm_success">"Ключ аднаўлення пацверджаны"</string>

9
features/securebackup/impl/src/main/res/values-cs/translations.xml

@ -9,6 +9,13 @@ @@ -9,6 +9,13 @@
<string name="screen_chat_backup_recovery_action_confirm_description">"Vaše záloha chatu není aktuálně synchronizována."</string>
<string name="screen_chat_backup_recovery_action_setup">"Nastavení obnovy"</string>
<string name="screen_chat_backup_recovery_action_setup_description">"Získejte přístup ke svým zašifrovaným zprávám, pokud ztratíte všechna zařízení nebo jste všude odhlášeni z %1$s."</string>
<string name="screen_create_new_recovery_key_list_item_1">"Otevřít Element na stolním počítači"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Znovu se přihlaste ke svému účtu"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Když budete vyzváni k ověření vašeho zařízení, vyberte %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"\"Resetovat vše\""</string>
<string name="screen_create_new_recovery_key_list_item_4">"Postupujte podle pokynů k vytvoření nového obnovovacího klíče"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Uložte nový klíč pro obnovení do správce hesel nebo do zašifrované poznámky"</string>
<string name="screen_create_new_recovery_key_title">"Obnovte šifrování účtu pomocí jiného zařízení"</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Vypnout"</string>
<string name="screen_key_backup_disable_confirmation_description">"Pokud se odhlásíte ze všech zařízení, přijdete o zašifrované zprávy."</string>
<string name="screen_key_backup_disable_confirmation_title">"Opravdu chcete vypnout zálohování?"</string>
@ -25,7 +32,7 @@ @@ -25,7 +32,7 @@
<string name="screen_recovery_key_confirm_description">"Ujistěte se, že tuto obrazovku nikdo nevidí!"</string>
<string name="screen_recovery_key_confirm_error_content">"Zkuste prosím znovu potvrdit přístup k záloze chatu."</string>
<string name="screen_recovery_key_confirm_error_title">"Nesprávný klíč pro obnovení"</string>
<string name="screen_recovery_key_confirm_key_description">"Pokud máte frázi pro obnovení nebo tajnou přístupovou frázi/klíč, bude to fungovat také."</string>
<string name="screen_recovery_key_confirm_key_description">"Pokud máte bezpečnostní klíč nebo bezpečnostní frázi, bude to fungovat také."</string>
<string name="screen_recovery_key_confirm_key_label">"Klíč pro obnovení nebo přístupový kód"</string>
<string name="screen_recovery_key_confirm_key_placeholder">"Zadejte…"</string>
<string name="screen_recovery_key_confirm_success">"Klíč pro obnovení potvrzen"</string>

22
features/securebackup/impl/src/main/res/values-de/translations.xml

@ -9,6 +9,26 @@ @@ -9,6 +9,26 @@
<string name="screen_chat_backup_recovery_action_confirm_description">"Dein Chat-Backup ist derzeit nicht synchronisiert."</string>
<string name="screen_chat_backup_recovery_action_setup">"Wiederherstellung einrichten"</string>
<string name="screen_chat_backup_recovery_action_setup_description">"Erhalte Zugriff auf deine verschlüsselten Nachrichten, wenn du alle deine Geräte verlierst oder von %1$s überall abgemeldet bist."</string>
<string name="screen_create_new_recovery_key_list_item_1">
"Öffne "
<b>"Element"</b>
" auf einem "
<b>"Desktop-Gerät"</b>
</string>
<string name="screen_create_new_recovery_key_list_item_2">"Melde dich erneut bei deinem Konto an"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Wenn du aufgefordert wirst dein Gerät zu verifizieren, wähle \"%1$s\"."</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"Alles zurücksetzen"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Folge den Anweisungen, um einen neuen Wiederherstellungsschlüssel zu erstellen"</string>
<string name="screen_create_new_recovery_key_list_item_5">
"Speichere deinen neuen "
<b>"Wiederherstellungsschlüssel"</b>
" in einem Passwortmanager oder einer verschlüsselten Notiz"
</string>
<string name="screen_create_new_recovery_key_title">
"Erstelle einen neuen "
<b>"Wiederherstellungsschlüssel"</b>
" mit einem anderen Gerät"
</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Ausschalten"</string>
<string name="screen_key_backup_disable_confirmation_description">"Du verlierst deine verschlüsselten Nachrichten, wenn du auf allen Geräten abgemeldet bist."</string>
<string name="screen_key_backup_disable_confirmation_title">"Bist du sicher, dass du das Backup ausschalten willst?"</string>
@ -29,7 +49,7 @@ @@ -29,7 +49,7 @@
<string name="screen_recovery_key_confirm_description">"Sorge dafür, dass niemand diesen Bildschirm sehen kann!"</string>
<string name="screen_recovery_key_confirm_error_content">"Bitte versuche es noch einmal, um den Zugriff auf dein Chat-Backup zu bestätigen."</string>
<string name="screen_recovery_key_confirm_error_title">"Falscher Wiederherstellungsschlüssel"</string>
<string name="screen_recovery_key_confirm_key_description">"Dies funktioniert auch mit einer Wiederherstellungspassphrase oder einer geheime Passphrase/einem geheimen Schlüssel."</string>
<string name="screen_recovery_key_confirm_key_description">"Dies funktioniert auch mit einem Sicherheitsschlüssel oder Sicherheitsphrase."</string>
<string name="screen_recovery_key_confirm_key_label">
<b>"Wiederherstellungsschlüssel"</b>
" oder Passcode"

7
features/securebackup/impl/src/main/res/values-hu/translations.xml

@ -9,6 +9,13 @@ @@ -9,6 +9,13 @@
<string name="screen_chat_backup_recovery_action_confirm_description">"A csevegéselőzményei nincsenek szinkronban."</string>
<string name="screen_chat_backup_recovery_action_setup">"Helyreállítás beállítása"</string>
<string name="screen_chat_backup_recovery_action_setup_description">"Szerezzen hozzáférést a titkosított üzeneteihez, ha elvesztette az összes eszközét, vagy ha mindenütt kijelentkezett az %1$sből."</string>
<string name="screen_create_new_recovery_key_list_item_1">"Nyissa meg az Elementet egy asztali eszközön"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Jelentkezzen be újra a fiókjába"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Amikor az eszköz ellenőrzését kéri, válassza ezt a lehetőséget: %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"„Minden visszaállítása”"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Kövesse az utasításokat egy új helyreállítási kulcs létrehozásához"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Mentse az új helyreállítási kulcsot egy jelszókezelőbe vagy egy titkosított jegyzetbe."</string>
<string name="screen_create_new_recovery_key_title">"A fiók titkosításának visszaállítása egy másik eszköz használatával"</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Kikapcsolás"</string>
<string name="screen_key_backup_disable_confirmation_description">"Ha kijelentkezik az összes eszközéről, akkor elveszti a titkosított üzeneteit."</string>
<string name="screen_key_backup_disable_confirmation_title">"Biztos, hogy kikapcsolja a biztonsági mentéseket?"</string>

7
features/securebackup/impl/src/main/res/values-in/translations.xml

@ -9,6 +9,13 @@ @@ -9,6 +9,13 @@
<string name="screen_chat_backup_recovery_action_confirm_description">"Pencadangan percakapan Anda saat ini tidak tersinkron."</string>
<string name="screen_chat_backup_recovery_action_setup">"Siapkan pemulihan"</string>
<string name="screen_chat_backup_recovery_action_setup_description">"Dapatkan akses ke pesan terenkripsi Anda jika Anda kehilangan semua perangkat Anda atau keluar dari %1$s di mana pun."</string>
<string name="screen_create_new_recovery_key_list_item_1">"Buka Element di perangkat desktop"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Masuk ke akun Anda lagi"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Saat diminta untuk memverifikasi perangkat Anda, pilih %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"“Atur ulang semua”"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Ikuti petunjuk untuk membuat kunci pemulihan baru"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Simpan kunci pemulihan baru Anda dalam pengelola kata sandi atau catatan terenkripsi"</string>
<string name="screen_create_new_recovery_key_title">"Atur ulang enkripsi untuk akun Anda menggunakan perangkat lain"</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Matikan"</string>
<string name="screen_key_backup_disable_confirmation_description">"Anda akan kehilangan pesan terenkripsi jika Anda keluar dari semua perangkat."</string>
<string name="screen_key_backup_disable_confirmation_title">"Apakah Anda yakin ingin mematikan pencadangan?"</string>

13
features/securebackup/impl/src/main/res/values-ru/translations.xml

@ -15,6 +15,17 @@ @@ -15,6 +15,17 @@
<string name="screen_chat_backup_recovery_action_confirm_description">"Резервная копия чата в настоящее время не синхронизирована."</string>
<string name="screen_chat_backup_recovery_action_setup">"Настроить восстановление"</string>
<string name="screen_chat_backup_recovery_action_setup_description">"Получите доступ к зашифрованным сообщениям, если вы потеряете все свои устройства или выйдете из системы %1$s отовсюду."</string>
<string name="screen_create_new_recovery_key_list_item_1">"Откройте Element на настольном устройстве"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Войдите в свой аккаунт еще раз"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Когда вас попросят подтвердить устройство, выберите %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"“Сбросить все”"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Следуйте инструкциям, чтобы создать новый ключ восстановления"</string>
<string name="screen_create_new_recovery_key_list_item_5">
"Сохраните новый "
<b>"ключ восстановления"</b>
" в менеджере паролей или зашифрованной заметке"
</string>
<string name="screen_create_new_recovery_key_title">"Сбросьте шифрование вашей учетной записи с помощью другого устройства."</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Выключить"</string>
<string name="screen_key_backup_disable_confirmation_description">"Вы потеряете зашифрованные сообщения, если выйдете из всех устройств."</string>
<string name="screen_key_backup_disable_confirmation_title">"Вы действительно хотите отключить резервное копирование?"</string>
@ -43,7 +54,7 @@ @@ -43,7 +54,7 @@
"Неверный "
<b>"ключ восстановления"</b>
</string>
<string name="screen_recovery_key_confirm_key_description">"Если у вас есть пароль для восстановления или секретная пароль/ключ, это тоже сработает."</string>
<string name="screen_recovery_key_confirm_key_description">"Если у вас есть пароль для восстановления или секретный пароль/ключ, это тоже сработает."</string>
<string name="screen_recovery_key_confirm_key_label">
<b>"Ключ восстановления"</b>
" или пароль"

7
features/securebackup/impl/src/main/res/values-sk/translations.xml

@ -9,6 +9,13 @@ @@ -9,6 +9,13 @@
<string name="screen_chat_backup_recovery_action_confirm_description">"Vaša záloha konverzácie nie je momentálne synchronizovaná."</string>
<string name="screen_chat_backup_recovery_action_setup">"Nastaviť obnovovanie"</string>
<string name="screen_chat_backup_recovery_action_setup_description">"Získajte prístup k vašim šifrovaným správam aj keď stratíte všetky svoje zariadenia alebo sa odhlásite zo všetkých %1$s zariadení."</string>
<string name="screen_create_new_recovery_key_list_item_1">"Otvoriť Element v stolnom počítači"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Znova sa prihláste do svojho účtu"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Keď sa zobrazí výzva na overenie vášho zariadenia, vyberte %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"\"Obnoviť všetko\""</string>
<string name="screen_create_new_recovery_key_list_item_4">"Postupujte podľa pokynov na vytvorenie nového kľúča na obnovenie"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Uložte si nový kľúč na obnovenie do správcu hesiel alebo do zašifrovanej poznámky"</string>
<string name="screen_create_new_recovery_key_title">"Obnovte šifrovanie vášho účtu pomocou iného zariadenia"</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Vypnúť"</string>
<string name="screen_key_backup_disable_confirmation_description">"Stratíte prístup k svojim zašifrovaným správam, ak sa odhlásite zo všetkých zariadení"</string>
<string name="screen_key_backup_disable_confirmation_title">"Ste si istí, že chcete vypnúť zálohovanie?"</string>

9
features/securebackup/impl/src/main/res/values/localazy.xml

@ -9,6 +9,13 @@ @@ -9,6 +9,13 @@
<string name="screen_chat_backup_recovery_action_confirm_description">"Your chat backup is currently out of sync."</string>
<string name="screen_chat_backup_recovery_action_setup">"Set up recovery"</string>
<string name="screen_chat_backup_recovery_action_setup_description">"Get access to your encrypted messages if you lose all your devices or are signed out of %1$s everywhere."</string>
<string name="screen_create_new_recovery_key_list_item_1">"Open Element in a desktop device"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Sign into your account again"</string>
<string name="screen_create_new_recovery_key_list_item_3">"When asked to verify your device, select %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"“Reset all”"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Follow the instructions to create a new recovery key"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Save your new recovery key in a password manager or encrypted note"</string>
<string name="screen_create_new_recovery_key_title">"Reset the encryption for your account using another device"</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Turn off"</string>
<string name="screen_key_backup_disable_confirmation_description">"You will lose your encrypted messages if you are signed out of all devices."</string>
<string name="screen_key_backup_disable_confirmation_title">"Are you sure you want to turn off backup?"</string>
@ -25,7 +32,7 @@ @@ -25,7 +32,7 @@
<string name="screen_recovery_key_confirm_description">"Make sure nobody can see this screen!"</string>
<string name="screen_recovery_key_confirm_error_content">"Please try again to confirm access to your chat backup."</string>
<string name="screen_recovery_key_confirm_error_title">"Incorrect recovery key"</string>
<string name="screen_recovery_key_confirm_key_description">"If you have a recovery passphrase or secret passphrase/key, this will work too."</string>
<string name="screen_recovery_key_confirm_key_description">"If you have a security key or security phrase, this will work too."</string>
<string name="screen_recovery_key_confirm_key_label">"Recovery key or passcode"</string>
<string name="screen_recovery_key_confirm_key_placeholder">"Enter…"</string>
<string name="screen_recovery_key_confirm_success">"Recovery key confirmed"</string>

1
features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt

@ -31,6 +31,7 @@ interface VerifySessionEntryPoint : FeatureEntryPoint { @@ -31,6 +31,7 @@ interface VerifySessionEntryPoint : FeatureEntryPoint {
interface Callback : Plugin {
fun onEnterRecoveryKey()
fun onCreateNewRecoveryKey()
fun onDone()
}
}

17
features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt

@ -34,17 +34,7 @@ class VerifySelfSessionNode @AssistedInject constructor( @@ -34,17 +34,7 @@ class VerifySelfSessionNode @AssistedInject constructor(
@Assisted plugins: List<Plugin>,
private val presenter: VerifySelfSessionPresenter,
) : Node(buildContext, plugins = plugins) {
private fun onEnterRecoveryKey() {
plugins<VerifySessionEntryPoint.Callback>().forEach {
it.onEnterRecoveryKey()
}
}
private fun onDone() {
plugins<VerifySessionEntryPoint.Callback>().forEach {
it.onDone()
}
}
private val callback = plugins<VerifySessionEntryPoint.Callback>().first()
@Composable
override fun View(modifier: Modifier) {
@ -52,8 +42,9 @@ class VerifySelfSessionNode @AssistedInject constructor( @@ -52,8 +42,9 @@ class VerifySelfSessionNode @AssistedInject constructor(
VerifySelfSessionView(
state = state,
modifier = modifier,
onEnterRecoveryKey = ::onEnterRecoveryKey,
onFinished = ::onDone,
onEnterRecoveryKey = callback::onEnterRecoveryKey,
onCreateNewRecoveryKey = callback::onCreateNewRecoveryKey,
onFinished = callback::onDone,
)
}
}

2
features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt

@ -103,7 +103,7 @@ class VerifySelfSessionPresenter @Inject constructor( @@ -103,7 +103,7 @@ class VerifySelfSessionPresenter @Inject constructor(
): VerifySelfSessionState.VerificationStep =
when (val machineState = this) {
StateMachineState.Initial, null -> {
VerifySelfSessionState.VerificationStep.Initial(canEnterRecoveryKey = canEnterRecoveryKey)
VerifySelfSessionState.VerificationStep.Initial(canEnterRecoveryKey = canEnterRecoveryKey, isLastDevice = encryptionService.isLastDevice.value)
}
StateMachineState.RequestingVerification,
StateMachineState.StartingSasVerification,

2
features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt

@ -29,7 +29,7 @@ data class VerifySelfSessionState( @@ -29,7 +29,7 @@ data class VerifySelfSessionState(
) {
@Stable
sealed interface VerificationStep {
data class Initial(val canEnterRecoveryKey: Boolean) : VerificationStep
data class Initial(val canEnterRecoveryKey: Boolean, val isLastDevice: Boolean) : VerificationStep
data object Canceled : VerificationStep
data object AwaitingOtherDeviceResponse : VerificationStep
data object Ready : VerificationStep

22
features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
package io.element.android.features.verifysession.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.verification.SessionVerificationData
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
@ -26,28 +27,31 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider<VerifySelfS @@ -26,28 +27,31 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider<VerifySelfS
get() = sequenceOf(
aVerifySelfSessionState(displaySkipButton = true),
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.AwaitingOtherDeviceResponse
verificationFlowStep = VerificationStep.AwaitingOtherDeviceResponse
),
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized)
verificationFlowStep = VerificationStep.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized)
),
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(aEmojisSessionVerificationData(), AsyncData.Loading())
verificationFlowStep = VerificationStep.Verifying(aEmojisSessionVerificationData(), AsyncData.Loading())
),
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Canceled
verificationFlowStep = VerificationStep.Canceled
),
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Ready
verificationFlowStep = VerificationStep.Ready
),
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(aDecimalsSessionVerificationData(), AsyncData.Uninitialized)
verificationFlowStep = VerificationStep.Verifying(aDecimalsSessionVerificationData(), AsyncData.Uninitialized)
),
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial(true)
verificationFlowStep = VerificationStep.Initial(canEnterRecoveryKey = true, isLastDevice = false)
),
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Completed,
verificationFlowStep = VerificationStep.Initial(canEnterRecoveryKey = true, isLastDevice = true)
),
aVerifySelfSessionState(
verificationFlowStep = VerificationStep.Completed,
displaySkipButton = true,
),
// Add other state here
@ -67,7 +71,7 @@ private fun aDecimalsSessionVerificationData( @@ -67,7 +71,7 @@ private fun aDecimalsSessionVerificationData(
}
internal fun aVerifySelfSessionState(
verificationFlowStep: VerifySelfSessionState.VerificationStep = VerifySelfSessionState.VerificationStep.Initial(false),
verificationFlowStep: VerificationStep = VerificationStep.Initial(canEnterRecoveryKey = false, isLastDevice = false),
displaySkipButton: Boolean = false,
eventSink: (VerifySelfSessionViewEvents) -> Unit = {},
) = VerifySelfSessionState(

25
features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt

@ -66,6 +66,7 @@ import io.element.android.features.verifysession.impl.VerifySelfSessionState.Ver @@ -66,6 +66,7 @@ import io.element.android.features.verifysession.impl.VerifySelfSessionState.Ver
fun VerifySelfSessionView(
state: VerifySelfSessionState,
onEnterRecoveryKey: () -> Unit,
onCreateNewRecoveryKey: () -> Unit,
onFinished: () -> Unit,
modifier: Modifier = Modifier,
) {
@ -114,6 +115,7 @@ fun VerifySelfSessionView( @@ -114,6 +115,7 @@ fun VerifySelfSessionView(
screenState = state,
goBack = ::resetFlow,
onEnterRecoveryKey = onEnterRecoveryKey,
onCreateNewRecoveryKey = onCreateNewRecoveryKey,
onFinished = onFinished,
)
}
@ -226,6 +228,7 @@ private fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifie @@ -226,6 +228,7 @@ private fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifie
private fun BottomMenu(
screenState: VerifySelfSessionState,
onEnterRecoveryKey: () -> Unit,
onCreateNewRecoveryKey: () -> Unit,
goBack: () -> Unit,
onFinished: () -> Unit,
) {
@ -236,12 +239,21 @@ private fun BottomMenu( @@ -236,12 +239,21 @@ private fun BottomMenu(
when (verificationViewState) {
is FlowStep.Initial -> {
BottomMenu(
positiveButtonTitle = stringResource(R.string.screen_identity_use_another_device),
onPositiveButtonClicked = { eventSink(VerifySelfSessionViewEvents.RequestVerification) },
negativeButtonTitle = stringResource(R.string.screen_session_verification_enter_recovery_key),
onNegativeButtonClicked = onEnterRecoveryKey,
)
if (verificationViewState.isLastDevice) {
BottomMenu(
positiveButtonTitle = stringResource(R.string.screen_session_verification_enter_recovery_key),
onPositiveButtonClicked = onEnterRecoveryKey,
negativeButtonTitle = stringResource(R.string.screen_identity_confirmation_create_new_recovery_key),
onNegativeButtonClicked = onCreateNewRecoveryKey,
)
} else {
BottomMenu(
positiveButtonTitle = stringResource(R.string.screen_identity_use_another_device),
onPositiveButtonClicked = { eventSink(VerifySelfSessionViewEvents.RequestVerification) },
negativeButtonTitle = stringResource(R.string.screen_session_verification_enter_recovery_key),
onNegativeButtonClicked = onEnterRecoveryKey,
)
}
}
is FlowStep.Canceled -> {
BottomMenu(
@ -334,6 +346,7 @@ internal fun VerifySelfSessionViewPreview(@PreviewParameter(VerifySelfSessionSta @@ -334,6 +346,7 @@ internal fun VerifySelfSessionViewPreview(@PreviewParameter(VerifySelfSessionSta
VerifySelfSessionView(
state = state,
onEnterRecoveryKey = {},
onCreateNewRecoveryKey = {},
onFinished = {},
)
}

29
features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt

@ -53,7 +53,7 @@ class VerifySelfSessionPresenterTests { @@ -53,7 +53,7 @@ class VerifySelfSessionPresenterTests {
presenter.present()
}.test {
awaitItem().run {
assertThat(verificationFlowStep).isEqualTo(VerificationStep.Initial(false))
assertThat(verificationFlowStep).isEqualTo(VerificationStep.Initial(false, false))
assertThat(displaySkipButton).isTrue()
}
}
@ -80,7 +80,22 @@ class VerifySelfSessionPresenterTests { @@ -80,7 +80,22 @@ class VerifySelfSessionPresenterTests {
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(true))
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(true, false))
}
}
@Test
fun `present - Initial state is received, can use recovery key and is last device`() = runTest {
val presenter = createVerifySelfSessionPresenter(
encryptionService = FakeEncryptionService().apply {
emitIsLastDevice(true)
emitRecoveryState(RecoveryState.INCOMPLETE)
}
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(true, true))
}
}
@ -103,7 +118,7 @@ class VerifySelfSessionPresenterTests { @@ -103,7 +118,7 @@ class VerifySelfSessionPresenterTests {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState.verificationFlowStep).isEqualTo(VerificationStep.Initial(false))
assertThat(initialState.verificationFlowStep).isEqualTo(VerificationStep.Initial(false, false))
val eventSink = initialState.eventSink
eventSink(VerifySelfSessionViewEvents.StartSasVerification)
// Await for other device response:
@ -122,7 +137,7 @@ class VerifySelfSessionPresenterTests { @@ -122,7 +137,7 @@ class VerifySelfSessionPresenterTests {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState.verificationFlowStep).isEqualTo(VerificationStep.Initial(false))
assertThat(initialState.verificationFlowStep).isEqualTo(VerificationStep.Initial(false, false))
val eventSink = initialState.eventSink
eventSink(VerifySelfSessionViewEvents.Cancel)
expectNoEvents()
@ -157,7 +172,7 @@ class VerifySelfSessionPresenterTests { @@ -157,7 +172,7 @@ class VerifySelfSessionPresenterTests {
awaitItem().eventSink(VerifySelfSessionViewEvents.RequestVerification)
service.shouldFail = false
assertThat(awaitItem().verificationFlowStep).isInstanceOf(VerificationStep.AwaitingOtherDeviceResponse::class.java)
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(false))
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(false, false))
}
}
@ -216,7 +231,7 @@ class VerifySelfSessionPresenterTests { @@ -216,7 +231,7 @@ class VerifySelfSessionPresenterTests {
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Canceled)
state.eventSink(VerifySelfSessionViewEvents.Reset)
// Went back to initial state
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(false))
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial(false, false))
cancelAndIgnoreRemainingEvents()
}
}
@ -297,7 +312,7 @@ class VerifySelfSessionPresenterTests { @@ -297,7 +312,7 @@ class VerifySelfSessionPresenterTests {
sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()),
): VerifySelfSessionState {
var state = awaitItem()
assertThat(state.verificationFlowStep).isEqualTo(VerificationStep.Initial(false))
assertThat(state.verificationFlowStep).isEqualTo(VerificationStep.Initial(false, false))
state.eventSink(VerifySelfSessionViewEvents.RequestVerification)
// Await for other device response:
state = awaitItem()

245
features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
package io.element.android.features.verifysession.impl
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.architecture.AsyncData
@ -29,6 +30,7 @@ import io.element.android.tests.testutils.ensureCalledOnce @@ -29,6 +30,7 @@ import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.pressBackKey
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
@ -39,16 +41,12 @@ class VerifySelfSessionViewTest { @@ -39,16 +41,12 @@ class VerifySelfSessionViewTest {
@Test
fun `back key pressed - when canceled resets the flow`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>()
rule.setContent {
VerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Canceled,
eventSink = eventsRecorder
),
onEnterRecoveryKey = EnsureNeverCalled(),
onFinished = EnsureNeverCalled(),
)
}
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Canceled,
eventSink = eventsRecorder
),
)
rule.pressBackKey()
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.Reset)
}
@ -56,16 +54,12 @@ class VerifySelfSessionViewTest { @@ -56,16 +54,12 @@ class VerifySelfSessionViewTest {
@Test
fun `back key pressed - when awaiting response cancels the verification`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>()
rule.setContent {
VerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.AwaitingOtherDeviceResponse,
eventSink = eventsRecorder
),
onEnterRecoveryKey = EnsureNeverCalled(),
onFinished = EnsureNeverCalled(),
)
}
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.AwaitingOtherDeviceResponse,
eventSink = eventsRecorder
),
)
rule.pressBackKey()
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.Cancel)
}
@ -73,16 +67,12 @@ class VerifySelfSessionViewTest { @@ -73,16 +67,12 @@ class VerifySelfSessionViewTest {
@Test
fun `back key pressed - when ready to verify cancels the verification`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>()
rule.setContent {
VerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Ready,
eventSink = eventsRecorder
),
onEnterRecoveryKey = EnsureNeverCalled(),
onFinished = EnsureNeverCalled(),
)
}
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Ready,
eventSink = eventsRecorder
),
)
rule.pressBackKey()
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.Cancel)
}
@ -90,19 +80,15 @@ class VerifySelfSessionViewTest { @@ -90,19 +80,15 @@ class VerifySelfSessionViewTest {
@Test
fun `back key pressed - when verifying and not loading declines the verification`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>()
rule.setContent {
VerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(
data = aEmojisSessionVerificationData(),
state = AsyncData.Uninitialized,
),
eventSink = eventsRecorder
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(
data = aEmojisSessionVerificationData(),
state = AsyncData.Uninitialized,
),
onEnterRecoveryKey = EnsureNeverCalled(),
onFinished = EnsureNeverCalled(),
)
}
eventSink = eventsRecorder
),
)
rule.pressBackKey()
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.DeclineVerification)
}
@ -110,19 +96,15 @@ class VerifySelfSessionViewTest { @@ -110,19 +96,15 @@ class VerifySelfSessionViewTest {
@Test
fun `back key pressed - when verifying and loading does nothing`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>()
rule.setContent {
VerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(
data = aEmojisSessionVerificationData(),
state = AsyncData.Loading(),
),
eventSink = eventsRecorder
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(
data = aEmojisSessionVerificationData(),
state = AsyncData.Loading(),
),
onEnterRecoveryKey = EnsureNeverCalled(),
onFinished = EnsureNeverCalled(),
)
}
eventSink = eventsRecorder
),
)
rule.pressBackKey()
eventsRecorder.assertEmpty()
}
@ -130,16 +112,12 @@ class VerifySelfSessionViewTest { @@ -130,16 +112,12 @@ class VerifySelfSessionViewTest {
@Test
fun `back key pressed - on Completed step does nothing`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>()
rule.setContent {
VerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Completed,
eventSink = eventsRecorder
),
onEnterRecoveryKey = EnsureNeverCalled(),
onFinished = EnsureNeverCalled(),
)
}
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Completed,
eventSink = eventsRecorder
),
)
rule.pressBackKey()
eventsRecorder.assertEmpty()
}
@ -148,16 +126,13 @@ class VerifySelfSessionViewTest { @@ -148,16 +126,13 @@ class VerifySelfSessionViewTest {
fun `when flow is completed and the user clicks on the continue button, the expected callback is invoked`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setContent {
VerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Completed,
eventSink = eventsRecorder
),
onEnterRecoveryKey = EnsureNeverCalled(),
onFinished = callback,
)
}
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Completed,
eventSink = eventsRecorder
),
onFinished = callback,
)
rule.clickOn(CommonStrings.action_continue)
}
}
@ -167,36 +142,45 @@ class VerifySelfSessionViewTest { @@ -167,36 +142,45 @@ class VerifySelfSessionViewTest {
fun `clicking on enter recovery key calls the expected callback`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setContent {
VerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial(true),
eventSink = eventsRecorder
),
onEnterRecoveryKey = callback,
onFinished = EnsureNeverCalled(),
)
}
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial(true, false),
eventSink = eventsRecorder
),
onEnterRecoveryKey = callback,
)
rule.clickOn(R.string.screen_session_verification_enter_recovery_key)
}
}
@Config(qualifiers = "h1024dp")
@Test
fun `clicking on they match emits the expected event`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>()
rule.setContent {
VerifySelfSessionView(
fun `clicking on create new recovery key calls the expected callback`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(
data = aEmojisSessionVerificationData(),
state = AsyncData.Uninitialized,
),
verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial(true, true),
eventSink = eventsRecorder
),
onEnterRecoveryKey = EnsureNeverCalled(),
onFinished = EnsureNeverCalled(),
onCreateNewRecoveryKey = callback,
)
rule.clickOn(R.string.screen_identity_confirmation_create_new_recovery_key)
}
}
@Test
fun `clicking on they match emits the expected event`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>()
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(
data = aEmojisSessionVerificationData(),
state = AsyncData.Uninitialized,
),
eventSink = eventsRecorder
),
)
rule.clickOn(R.string.screen_session_verification_they_match)
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.ConfirmVerification)
}
@ -204,19 +188,15 @@ class VerifySelfSessionViewTest { @@ -204,19 +188,15 @@ class VerifySelfSessionViewTest {
@Test
fun `clicking on they do not match emits the expected event`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>()
rule.setContent {
VerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(
data = aEmojisSessionVerificationData(),
state = AsyncData.Uninitialized,
),
eventSink = eventsRecorder
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Verifying(
data = aEmojisSessionVerificationData(),
state = AsyncData.Uninitialized,
),
onEnterRecoveryKey = EnsureNeverCalled(),
onFinished = EnsureNeverCalled(),
)
}
eventSink = eventsRecorder
),
)
rule.clickOn(R.string.screen_session_verification_they_dont_match)
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.DeclineVerification)
}
@ -224,17 +204,13 @@ class VerifySelfSessionViewTest { @@ -224,17 +204,13 @@ class VerifySelfSessionViewTest {
@Test
fun `clicking on 'Skip' emits the expected event`() {
val eventsRecorder = EventsRecorder<VerifySelfSessionViewEvents>()
rule.setContent {
VerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial(canEnterRecoveryKey = true),
displaySkipButton = true,
eventSink = eventsRecorder
),
onEnterRecoveryKey = EnsureNeverCalled(),
onFinished = EnsureNeverCalled(),
)
}
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Initial(canEnterRecoveryKey = true, isLastDevice = false),
displaySkipButton = true,
eventSink = eventsRecorder
),
)
rule.clickOn(CommonStrings.action_skip)
eventsRecorder.assertSingle(VerifySelfSessionViewEvents.SkipVerification)
}
@ -242,17 +218,30 @@ class VerifySelfSessionViewTest { @@ -242,17 +218,30 @@ class VerifySelfSessionViewTest {
@Test
fun `on Skipped step - onFinished callback is called immediately`() {
ensureCalledOnce { callback ->
rule.setContent {
VerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Skipped,
displaySkipButton = true,
eventSink = EnsureNeverCalledWithParam(),
),
onEnterRecoveryKey = EnsureNeverCalled(),
onFinished = callback,
)
}
rule.setVerifySelfSessionView(
aVerifySelfSessionState(
verificationFlowStep = VerifySelfSessionState.VerificationStep.Skipped,
displaySkipButton = true,
eventSink = EnsureNeverCalledWithParam(),
),
onFinished = callback,
)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setVerifySelfSessionView(
state: VerifySelfSessionState,
onEnterRecoveryKey: () -> Unit = EnsureNeverCalled(),
onCreateNewRecoveryKey: () -> Unit = EnsureNeverCalled(),
onFinished: () -> Unit = EnsureNeverCalled(),
) {
rule.setContent {
VerifySelfSessionView(
state = state,
onEnterRecoveryKey = onEnterRecoveryKey,
onCreateNewRecoveryKey = onCreateNewRecoveryKey,
onFinished = onFinished,
)
}
}
}

188
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/SquareSizeModifier.kt

@ -0,0 +1,188 @@ @@ -0,0 +1,188 @@
/*
* Copyright (c) 2024 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.modifiers
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.LayoutModifier
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.platform.InspectorValueInfo
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreview
import kotlin.math.max
import kotlin.math.min
/**
* Makes the content square in size.
*
* This is achieved by cropping incoming max constraints to the largest possible square size
* and measuring the content using resulting constraints.
* Next the size of layout is decided based on largest dimension of the measured content.
* Finally the content is placed inside the square layout according to specified [position].
*
* If no square exists that falls within the size range of the incoming constraints,
* the content will be laid out as usual, as if the modifier was not applied.
*
* @param position The fraction of the content's position inside its square layout.
* It determines the point on the axis that was extended to make a square.
* Typically you'd want to use values between `0` and `1`, inclusive, where `0`
* will place the content at the "start" of the square, `0.5` in the middle, and `1` at the "end".
*/
@Stable
fun Modifier.squareSize(
position: Float = 0.5f,
): Modifier =
this.then(
when {
position == 0.5f -> SquareSizeCenter
else -> createSquareSizeModifier(position = position)
}
)
private val SquareSizeCenter = createSquareSizeModifier(position = 0.5f)
private class SquareSizeModifier(
private val position: Float,
inspectorInfo: InspectorInfo.() -> Unit,
) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints,
): MeasureResult {
val maxSquare = min(constraints.maxWidth, constraints.maxHeight)
val minSquare = max(constraints.minWidth, constraints.minHeight)
val squareExists = minSquare <= maxSquare
val resolvedConstraints = constraints
.takeUnless { squareExists }
?: constraints.copy(maxWidth = maxSquare, maxHeight = maxSquare)
val placeable = measurable.measure(resolvedConstraints)
return if (squareExists) {
val size = max(placeable.width, placeable.height)
layout(size, size) {
val x = ((size - placeable.width) * position).toInt()
val y = ((size - placeable.height) * position).toInt()
placeable.placeRelative(x, y)
}
} else {
layout(placeable.width, placeable.height) {
placeable.placeRelative(0, 0)
}
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (other !is SquareSizeModifier) return false
if (position != other.position) return false
return true
}
override fun hashCode(): Int {
return position.hashCode()
}
}
@Suppress("ModifierFactoryExtensionFunction", "ModifierFactoryReturnType")
private fun createSquareSizeModifier(
position: Float,
) =
SquareSizeModifier(
position = position,
inspectorInfo = debugInspectorInfo {
name = "squareSize"
properties["position"] = position
},
)
@Preview
@Composable
internal fun SquareSizeModifierLargeWidthPreview() {
ElementPreview {
Box(
modifier = Modifier
.padding(32.dp)
.background(Color.Gray)
.squareSize(position = 0.25f)
) {
Box(
modifier = Modifier
.background(Color.Black)
.size(100.dp, 10.dp)
)
}
}
}
@Preview
@Composable
internal fun SquareSizeModifierLargeHeightPreview() {
ElementPreview {
Box(
modifier = Modifier
.padding(32.dp)
.background(Color.Gray)
.squareSize(position = 0.75f)
) {
Box(
modifier = Modifier
.background(Color.Black)
.size(10.dp, 100.dp)
)
}
}
}
@Preview
@Composable
internal fun SquareSizeModifierInsideSquarePreview() {
ElementPreview {
Box(
modifier = Modifier
.padding(32.dp)
.size(120.dp)
.background(Color.Gray),
contentAlignment = Alignment.Center,
) {
Box(
modifier = Modifier
.background(Color.Black)
.width(100.dp)
.squareSize(position = 0.75f)
)
}
}
}

9
libraries/ui-strings/src/main/res/values-be/translations.xml

@ -202,7 +202,7 @@ @@ -202,7 +202,7 @@
<string name="common_server_not_supported">"Сервер не падтрымліваецца"</string>
<string name="common_server_url">"URL-адрас сервера"</string>
<string name="common_settings">"Налады"</string>
<string name="common_shared_location">"Абагуліць месцазнаходжанне"</string>
<string name="common_shared_location">"Абагуленае месцазнаходжанне"</string>
<string name="common_signing_out">"Выхад"</string>
<string name="common_starting_chat">"Пачатак чата…"</string>
<string name="common_sticker">"Стыкер"</string>
@ -249,13 +249,6 @@ @@ -249,13 +249,6 @@
<string name="invite_friends_text">"Гэй, пагавары са мной у %1$s: %2$s"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="preference_rageshake">"Паведаміць аб памылцы з дапамогай Rageshake"</string>
<string name="screen_create_new_recovery_key_list_item_1">"Адкрыйце Element на настольнай прыладзе"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Увайдзіце ў свой уліковы запіс яшчэ раз"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Калі будзе прапанавана пацвердзіць вашу прыладу, выберыце %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"“Скінуць усе”"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Выконвайце інструкцыі, каб стварыць новы ключ аднаўлення"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Захавайце новы ключ аднаўлення ў ме́неджэры пароляў або ў зашыфраванай нататке"</string>
<string name="screen_create_new_recovery_key_title">"Скіньце шыфраванне для вашага ўліковага запісу з дапамогай іншай прылады"</string>
<string name="screen_media_picker_error_failed_selection">"Не ўдалося выбраць носьбіт, паўтарыце спробу."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Не атрымалася апрацаваць медыяфайл для загрузкі, паспрабуйце яшчэ раз."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Не атрымалася загрузіць медыяфайлы, паспрабуйце яшчэ раз."</string>

7
libraries/ui-strings/src/main/res/values-cs/translations.xml

@ -249,13 +249,6 @@ @@ -249,13 +249,6 @@
<string name="invite_friends_text">"Ahoj, ozvi se mi na %1$s: %2$s"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="preference_rageshake">"Zatřeste zařízením pro nahlášení chyby"</string>
<string name="screen_create_new_recovery_key_list_item_1">"Otevřít Element na stolním počítači"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Znovu se přihlaste ke svému účtu"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Když budete vyzváni k ověření vašeho zařízení, vyberte %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"\"Resetovat vše\""</string>
<string name="screen_create_new_recovery_key_list_item_4">"Postupujte podle pokynů k vytvoření nového obnovovacího klíče"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Uložte nový klíč pro obnovení do správce hesel nebo do zašifrované poznámky"</string>
<string name="screen_create_new_recovery_key_title">"Obnovte šifrování účtu pomocí jiného zařízení"</string>
<string name="screen_media_picker_error_failed_selection">"Výběr média se nezdařil, zkuste to prosím znovu."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>

20
libraries/ui-strings/src/main/res/values-de/translations.xml

@ -247,26 +247,6 @@ @@ -247,26 +247,6 @@
<string name="invite_friends_text">"Hey, sprich mit mir auf %1$s: %2$s"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="preference_rageshake">"Schüttel heftig zum Melden von Fehlern"</string>
<string name="screen_create_new_recovery_key_list_item_1">
"Öffne "
<b>"Element"</b>
" auf einem "
<b>"Desktop-Gerät"</b>
</string>
<string name="screen_create_new_recovery_key_list_item_2">"Melde dich erneut bei deinem Konto an"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Wenn du aufgefordert wirst dein Gerät zu verifizieren, wähle \"%1$s\"."</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"Alles zurücksetzen"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Folge den Anweisungen, um einen neuen Wiederherstellungsschlüssel zu erstellen"</string>
<string name="screen_create_new_recovery_key_list_item_5">
"Speichere deinen neuen "
<b>"Wiederherstellungsschlüssel"</b>
" in einem Passwortmanager oder einer verschlüsselten Notiz"
</string>
<string name="screen_create_new_recovery_key_title">
"Erstelle einen neuen "
<b>"Wiederherstellungsschlüssel"</b>
" mit einem anderen Gerät"
</string>
<string name="screen_media_picker_error_failed_selection">"Medienauswahl fehlgeschlagen, bitte versuche es erneut."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Das Hochladen der Medien ist fehlgeschlagen. Bitte versuche es erneut."</string>

7
libraries/ui-strings/src/main/res/values-hu/translations.xml

@ -245,13 +245,6 @@ @@ -245,13 +245,6 @@
<string name="invite_friends_text">"Beszélgessünk itt: %1$s, %2$s"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="preference_rageshake">"Az eszköz rázása a hibajelentéshez"</string>
<string name="screen_create_new_recovery_key_list_item_1">"Nyissa meg az Elementet egy asztali eszközön"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Jelentkezzen be újra a fiókjába"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Amikor az eszköz ellenőrzését kéri, válassza ezt a lehetőséget: %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"„Minden visszaállítása”"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Kövesse az utasításokat egy új helyreállítási kulcs létrehozásához"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Mentse az új helyreállítási kulcsot egy jelszókezelőbe vagy egy titkosított jegyzetbe."</string>
<string name="screen_create_new_recovery_key_title">"A fiók titkosításának visszaállítása egy másik eszköz használatával"</string>
<string name="screen_media_picker_error_failed_selection">"Nem sikerült kiválasztani a médiát, próbálja újra."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Nem sikerült a média feltöltése, próbálja újra."</string>

7
libraries/ui-strings/src/main/res/values-in/translations.xml

@ -241,13 +241,6 @@ @@ -241,13 +241,6 @@
<string name="invite_friends_text">"Hai, bicaralah dengan saya di %1$s: %2$s"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="preference_rageshake">"Rageshake untuk melaporkan kutu"</string>
<string name="screen_create_new_recovery_key_list_item_1">"Buka Element di perangkat desktop"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Masuk ke akun Anda lagi"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Saat diminta untuk memverifikasi perangkat Anda, pilih %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"“Atur ulang semua”"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Ikuti petunjuk untuk membuat kunci pemulihan baru"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Simpan kunci pemulihan baru Anda dalam pengelola kata sandi atau catatan terenkripsi"</string>
<string name="screen_create_new_recovery_key_title">"Atur ulang enkripsi untuk akun Anda menggunakan perangkat lain"</string>
<string name="screen_media_picker_error_failed_selection">"Gagal memilih media, silakan coba lagi."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Gagal memproses media untuk diunggah, silakan coba lagi."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Gagal mengunggah media, silakan coba lagi."</string>

11
libraries/ui-strings/src/main/res/values-ru/translations.xml

@ -251,17 +251,6 @@ @@ -251,17 +251,6 @@
<string name="invite_friends_text">"Привет, поговори со мной по %1$s: %2$s"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="preference_rageshake">"Встряхните устройство, чтобы сообщить об ошибке"</string>
<string name="screen_create_new_recovery_key_list_item_1">"Откройте Element на настольном устройстве"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Войдите в свой аккаунт еще раз"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Когда вас попросят подтвердить устройство, выберите %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"“Сбросить все”"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Следуйте инструкциям, чтобы создать новый ключ восстановления"</string>
<string name="screen_create_new_recovery_key_list_item_5">
"Сохраните новый "
<b>"ключ восстановления"</b>
" в менеджере паролей или зашифрованной заметке"
</string>
<string name="screen_create_new_recovery_key_title">"Сбросьте шифрование вашей учетной записи с помощью другого устройства."</string>
<string name="screen_media_picker_error_failed_selection">"Не удалось выбрать носитель, попробуйте еще раз."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Не удалось обработать медиафайл для загрузки, попробуйте еще раз."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Не удалось загрузить медиафайлы, попробуйте еще раз."</string>

7
libraries/ui-strings/src/main/res/values-sk/translations.xml

@ -248,13 +248,6 @@ @@ -248,13 +248,6 @@
<string name="invite_friends_text">"Ahoj, porozprávajte sa so mnou na %1$s: %2$s"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="preference_rageshake">"Zúrivo potriasť pre nahlásenie chyby"</string>
<string name="screen_create_new_recovery_key_list_item_1">"Otvoriť Element v stolnom počítači"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Znova sa prihláste do svojho účtu"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Keď sa zobrazí výzva na overenie vášho zariadenia, vyberte %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"\"Obnoviť všetko\""</string>
<string name="screen_create_new_recovery_key_list_item_4">"Postupujte podľa pokynov na vytvorenie nového kľúča na obnovenie"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Uložte si nový kľúč na obnovenie do správcu hesiel alebo do zašifrovanej poznámky"</string>
<string name="screen_create_new_recovery_key_title">"Obnovte šifrovanie vášho účtu pomocou iného zariadenia"</string>
<string name="screen_media_picker_error_failed_selection">"Nepodarilo sa vybrať médium, skúste to prosím znova."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Nepodarilo sa nahrať médiá, skúste to prosím znova."</string>

7
libraries/ui-strings/src/main/res/values/localazy.xml

@ -245,13 +245,6 @@ @@ -245,13 +245,6 @@
<string name="invite_friends_text">"Hey, talk to me on %1$s: %2$s"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="preference_rageshake">"Rageshake to report bug"</string>
<string name="screen_create_new_recovery_key_list_item_1">"Open Element in a desktop device"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Sign into your account again"</string>
<string name="screen_create_new_recovery_key_list_item_3">"When asked to verify your device, select %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"“Reset all”"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Follow the instructions to create a new recovery key"</string>
<string name="screen_create_new_recovery_key_list_item_5">"Save your new recovery key in a password manager or encrypted note"</string>
<string name="screen_create_new_recovery_key_title">"Reset the encryption for your account using another device"</string>
<string name="screen_media_picker_error_failed_selection">"Failed selecting media, please try again."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Failed processing media to upload, please try again."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Failed uploading media, please try again."</string>

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.createkey_CreateNewRecoveryKeyView_null_CreateNewRecoveryKeyView-Day-0_1_null,NEXUS_5,1.0,en].png

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2e7892eb99a1307f2c34968f76e994e6b8e3916ff5c218eb9cbc81424001fd54
size 64251

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.createkey_CreateNewRecoveryKeyView_null_CreateNewRecoveryKeyView-Night-0_2_null,NEXUS_5,1.0,en].png

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dbcf18bb210a68b0e02512abd949845a8c5a705a177ce0875599734ce46da7ff
size 57525

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-1_2_null_0,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-1_2_null_1,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_2,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-1_2_null_2,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-0_1_null_3,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Day-1_2_null_3,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-1_3_null_0,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-1_3_null_1,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_2,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-1_3_null_2,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-0_2_null_3,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_SecureBackupDisableView_null_SecureBackupDisableView-Night-1_3_null_3,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-2_3_null_0,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-2_3_null_1,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-1_2_null_2,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Day-2_3_null_2,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-2_4_null_0,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-2_4_null_1,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-1_3_null_2,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_SecureBackupEnableView_null_SecureBackupEnableView-Night-2_4_null_2,NEXUS_5,1.0,en].png

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_0,NEXUS_5,1.0,en].png

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bd35705e28bd0b46d2ca8752c3f338c883d5bc2e8c78af26c35eda435a6cf8cf
size 42542

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_1,NEXUS_5,1.0,en].png

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5bf4f3ff7ed970ba7eef2119525e800f80bfc056ccbd5345c1c610257180b50d
size 53908

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_2,NEXUS_5,1.0,en].png

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f963d37ac30795df5de921cef04b17de6baf45160716533984d08f6e543fb1e5
size 52566

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-2_3_null_3,NEXUS_5,1.0,en].png

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:573fd927ea37f9c3cf309dd805bf52c06e98e7b058b6f1a80bf8b19bb3ecd35e
size 46028

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-3_4_null_0,NEXUS_5,1.0,en].png

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6df37aa3a9d8411d3b3f9cd4dc67e48c271030350fee5cc464da8b564bea14d0
size 40410

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-3_4_null_1,NEXUS_5,1.0,en].png

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:923a6792ecb57eaefb86042327495ffa48294187eb97fdb2f36ca16b8e2d2859
size 52163

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-3_4_null_2,NEXUS_5,1.0,en].png

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9296a3f7d3c3ee8aa4d8ee3fc310cab8c539c7adfd4b14156da65608a5e6fd10
size 50565

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Day-3_4_null_3,NEXUS_5,1.0,en].png

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0fee5009acc5128d48791066fb45555d0fa261cf09c9b372cca8570561c3055b
size 44297

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_0,NEXUS_5,1.0,en].png

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1d1cd406230b94ba11e414aedfbe49d903d7aa54791c4dbd93616a9f16ca7068
size 39917

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_1,NEXUS_5,1.0,en].png

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f9dbe41267a197887860cda4cef70138fd1e1b79497f396d1535725bc6219f4
size 50262

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_2,NEXUS_5,1.0,en].png

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:02ebc69b55046c946142bac74f299d191b4dba2e175a670a67ed1e91dcaf51a8
size 48753

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-2_4_null_3,NEXUS_5,1.0,en].png

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6dcceb3e818fe738f6a20dc8dffb006f46c4884e615ee9c00e845d7d9327af66
size 40894

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-3_5_null_0,NEXUS_5,1.0,en].png

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b11e7ce69453d49bb916bec23ce0974e33ae788911ab12cea777ba238141bc77
size 37965

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-3_5_null_1,NEXUS_5,1.0,en].png

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:aefbf69085a8279090018dd35a465614b2c876932746e650e7faf6d84f062381
size 48364

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-3_5_null_2,NEXUS_5,1.0,en].png

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9b6c6e849d68d516000644352c347587910f99ffd0d441feb8cf72a981aa30e3
size 46849

3
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_null_SecureBackupEnterRecoveryKeyView-Night-3_5_null_3,NEXUS_5,1.0,en].png

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:65c1faa6875a6c2b9ebe282f83e8a82b6e358f724082b53525d31a6c1bff4b15
size 39312

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_0,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_1,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_2,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_2,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_3,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_3,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_4,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_4,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_5,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_5,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_6,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_6,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_7,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_7,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_4_null_8,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-4_5_null_8,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_0,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-4_6_null_0,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_1,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-4_6_null_1,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_2,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-4_6_null_2,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_3,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-4_6_null_3,NEXUS_5,1.0,en].png

0
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_5_null_4,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-4_6_null_4,NEXUS_5,1.0,en].png

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save