4S Activity WIP

This commit is contained in:
Valere 2020-02-14 09:36:23 +01:00 committed by Benoit Marty
parent 3dc89c8d87
commit cb669ad881
17 changed files with 622 additions and 20 deletions

View File

@ -19,7 +19,6 @@
android:supportsRtl="true"
android:theme="@style/AppTheme.Light"
tools:replace="android:allowBackup">
<activity
android:name=".features.MainActivity"
android:theme="@style/AppTheme.Launcher" />
@ -57,7 +56,6 @@
<activity
android:name=".features.crypto.keysbackup.settings.KeysBackupManageActivity"
android:label="@string/encryption_message_recovery" />
<activity
android:name=".features.reactions.EmojiReactionPickerActivity"
android:label="@string/title_activity_emoji_reaction_picker" />
@ -73,7 +71,7 @@
android:value=".features.home.HomeActivity" />
</activity>
<activity android:name=".features.debug.DebugMenuActivity" />
<activity android:name="im.vector.riotx.features.createdirect.CreateDirectRoomActivity" />
<activity android:name=".features.createdirect.CreateDirectRoomActivity" />
<activity android:name=".features.webview.VectorWebViewActivity" />
<activity android:name=".features.link.LinkHandlerActivity">
<intent-filter>
@ -97,6 +95,7 @@
<intent-filter>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="*/*" />
<category android:name="android.intent.category.DEFAULT" />
@ -104,15 +103,14 @@
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<data android:mimeType="*/*" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.OPENABLE" />
</intent-filter>
</activity>
<activity android:name=".features.roomprofile.RoomProfileActivity" />
<activity android:name=".features.signout.hard.SignedOutActivity" />
<activity
android:name=".features.signout.soft.SoftLogoutActivity"
@ -129,7 +127,6 @@
<data android:host="matrix.to" />
</intent-filter>
</activity>
<activity
android:name=".features.roommemberprofile.RoomMemberProfileActivity"
android:parentActivityName=".features.home.HomeActivity">
@ -137,9 +134,9 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".features.home.HomeActivity" />
</activity>
<activity android:name=".features.qrcode.QrCodeScannerActivity" />
<activity android:name=".features.crypto.quads.SharedSecureStorageActivity" />
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait" />
@ -149,25 +146,17 @@
android:theme="@style/AppTheme.AttachmentsPreview" />
<!-- Services -->
<service
android:name=".core.services.CallService"
android:exported="false" />
<service
android:name=".core.services.VectorSyncService"
android:exported="false" />
<!-- Receivers -->
android:exported="false" /> <!-- Receivers -->
<!-- Exported false, should only be accessible from this app!! -->
<receiver
android:name=".features.notifications.NotificationBroadcastReceiver"
android:enabled="true"
android:exported="false" />
<!-- Providers -->
android:exported="false" /> <!-- Providers -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider"

View File

@ -26,6 +26,7 @@ import im.vector.riotx.core.preference.UserAvatarPreference
import im.vector.riotx.features.MainActivity
import im.vector.riotx.features.createdirect.CreateDirectRoomActivity
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupManageActivity
import im.vector.riotx.features.crypto.quads.SharedSecureStorageActivity
import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
import im.vector.riotx.features.debug.DebugMenuActivity
import im.vector.riotx.features.home.HomeActivity
@ -151,6 +152,8 @@ interface ScreenComponent {
fun inject(deviceListBottomSheet: DeviceListBottomSheet)
fun inject(activity: SharedSecureStorageActivity)
@Component.Factory
interface Factory {
fun create(vectorComponent: VectorComponent,

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2020 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 im.vector.riotx.features.crypto.quads
import im.vector.riotx.core.platform.VectorViewEvents
import im.vector.riotx.core.platform.VectorViewModelAction
import im.vector.riotx.core.platform.WaitingViewData
sealed class SharedSecureStorageAction : VectorViewModelAction {
object TogglePasswordVisibility : SharedSecureStorageAction()
object Cancel : SharedSecureStorageAction()
data class SubmitPassphrase(val passphrase: String) : SharedSecureStorageAction()
}
sealed class SharedSecureStorageViewEvent : VectorViewEvents {
object Dismiss : SharedSecureStorageViewEvent()
data class Error(val message: String) : SharedSecureStorageViewEvent()
data class InlineError(val message: String) : SharedSecureStorageViewEvent()
object ShowModalLoading : SharedSecureStorageViewEvent()
object HideModalLoading : SharedSecureStorageViewEvent()
data class UpdateLoadingState(val waitingData: WaitingViewData) : SharedSecureStorageViewEvent()
}

View File

@ -0,0 +1,120 @@
/*
* Copyright (c) 2020 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 im.vector.riotx.features.crypto.quads
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.viewModel
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.error.ErrorFormatter
import im.vector.riotx.core.extensions.addFragment
import im.vector.riotx.core.platform.SimpleFragmentActivity
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity.*
import javax.inject.Inject
class SharedSecureStorageActivity : SimpleFragmentActivity() {
@Parcelize
data class Args(
val keyId: String?,
val requestedSecrets: List<String>,
val resultKeyStoreAlias: String
) : Parcelable
private val uiDisposables = CompositeDisposable()
private fun Disposable.disposeOnDestroyView(): Disposable {
uiDisposables.add(this)
return this
}
private val viewModel: SharedSecureStorageViewModel by viewModel()
@Inject lateinit var viewModelFactory: SharedSecureStorageViewModel.Factory
@Inject lateinit var errorFormatter: ErrorFormatter
override fun injectWith(injector: ScreenComponent) {
super.injectWith(injector)
injector.inject(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
toolbar.visibility = View.GONE
if (isFirstCreation()) {
addFragment(R.id.container, SharedSecuredStoragePassphraseFragment::class.java)
}
viewModel.viewEvents
.observe()
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
observeViewEvents(it)
}
.disposeOnDestroyView()
viewModel.subscribe(this) {
// renderState(it)
}
}
private fun observeViewEvents(it: SharedSecureStorageViewEvent?) {
when (it) {
is SharedSecureStorageViewEvent.Dismiss -> {
setResult(Activity.RESULT_CANCELED)
finish()
}
is SharedSecureStorageViewEvent.ShowModalLoading -> {
showWaitingView()
}
is SharedSecureStorageViewEvent.HideModalLoading -> {
hideWaitingView()
}
is SharedSecureStorageViewEvent.UpdateLoadingState -> {
updateWaitingView(it.waitingData)
}
}
}
// fun renderState(state: SharedSecureStorageViewState) {
// }
companion object {
const val RESULT_KEYSTORE_ALIAS = "SharedSecureStorageActivity"
fun newIntent(context: Context, keyId: String? = null, requestedSecrets: List<String>, resultKeyStoreAlias: String = RESULT_KEYSTORE_ALIAS): Intent {
require(requestedSecrets.isNotEmpty())
return Intent(context, SharedSecureStorageActivity::class.java).also {
it.putExtra(MvRx.KEY_ARG, Args(
keyId,
requestedSecrets,
resultKeyStoreAlias
))
}
}
}
}

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) 2020 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 im.vector.riotx.features.crypto.quads
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.listeners.ProgressListener
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.securestorage.Curve25519AesSha2KeySpec
import im.vector.matrix.android.api.session.securestorage.KeyInfoResult
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.platform.WaitingViewData
import im.vector.riotx.core.resources.StringProvider
data class SharedSecureStorageViewState(
val requestedSecrets: List<String> = emptyList(),
val passphraseVisible: Boolean = false
) : MvRxState
class SharedSecureStorageViewModel @AssistedInject constructor(
@Assisted initialState: SharedSecureStorageViewState,
private val stringProvider: StringProvider,
private val session: Session)
: VectorViewModel<SharedSecureStorageViewState, SharedSecureStorageAction, SharedSecureStorageViewEvent>(initialState) {
@AssistedInject.Factory
interface Factory {
fun create(initialState: SharedSecureStorageViewState): SharedSecureStorageViewModel
}
override fun handle(action: SharedSecureStorageAction) = withState { state ->
when (action) {
is SharedSecureStorageAction.TogglePasswordVisibility -> {
setState {
copy(
passphraseVisible = !passphraseVisible
)
}
}
is SharedSecureStorageAction.Cancel -> {
_viewEvents.post(SharedSecureStorageViewEvent.Dismiss)
}
is SharedSecureStorageAction.SubmitPassphrase -> {
_viewEvents.post(SharedSecureStorageViewEvent.ShowModalLoading)
val passphrase = action.passphrase
val keyInfoResult = session.sharedSecretStorageService.getDefaultKey()
if (!keyInfoResult.isSuccess()) {
_viewEvents.post(SharedSecureStorageViewEvent.HideModalLoading)
_viewEvents.post(SharedSecureStorageViewEvent.Error("Cannot find ssss key"))
return@withState
}
val keyInfo = (keyInfoResult as KeyInfoResult.Success).keyInfo
// TODO
// val decryptedSecretMap = HashMap<String, String>()
// val errors = ArrayList<Throwable>()
_viewEvents.post(SharedSecureStorageViewEvent.UpdateLoadingState(
WaitingViewData(
message = stringProvider.getString(R.string.keys_backup_restoring_computing_key_waiting_message),
isIndeterminate = true
)
))
state.requestedSecrets.forEach {
val keySpec = Curve25519AesSha2KeySpec.fromPassphrase(
passphrase,
keyInfo.content.passphrase?.salt ?: "",
keyInfo.content.passphrase?.iterations ?: 0,
// TODO
object : ProgressListener {
override fun onProgress(progress: Int, total: Int) {
_viewEvents.post(SharedSecureStorageViewEvent.UpdateLoadingState(
WaitingViewData(
message = stringProvider.getString(R.string.keys_backup_restoring_computing_key_waiting_message),
isIndeterminate = false,
progress = progress,
progressTotal = total
)
))
}
}
)
session.sharedSecretStorageService.getSecret(
name = it,
keyId = keyInfo.id,
secretKey = keySpec,
callback = object : MatrixCallback<String> {
override fun onSuccess(data: String) {
_viewEvents.post(SharedSecureStorageViewEvent.HideModalLoading)
}
override fun onFailure(failure: Throwable) {
_viewEvents.post(SharedSecureStorageViewEvent.HideModalLoading)
_viewEvents.post(SharedSecureStorageViewEvent.InlineError(failure.localizedMessage))
}
})
}
}
}
}
companion object : MvRxViewModelFactory<SharedSecureStorageViewModel, SharedSecureStorageViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: SharedSecureStorageViewState): SharedSecureStorageViewModel? {
val activity: SharedSecureStorageActivity = viewModelContext.activity()
val args: SharedSecureStorageActivity.Args = activity.intent.getParcelableExtra(MvRx.KEY_ARG)
return activity.viewModelFactory.create(
SharedSecureStorageViewState(
requestedSecrets = args.requestedSecrets
)
)
}
}
}

View File

@ -0,0 +1,142 @@
/*
* Copyright (c) 2020 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 im.vector.riotx.features.crypto.quads
import android.os.Bundle
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.Button
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import butterknife.BindView
import butterknife.OnClick
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState
import com.google.android.material.textfield.TextInputLayout
import com.jakewharton.rxbinding3.widget.editorActionEvents
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.riotx.R
import im.vector.riotx.core.extensions.showPassword
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.utils.DebouncedClickListener
import me.gujun.android.span.span
import java.util.concurrent.TimeUnit
class SharedSecuredStoragePassphraseFragment : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_ssss_access_from_passphrase
val sharedViewModel: SharedSecureStorageViewModel by activityViewModel()
@BindView(R.id.ssss_restore_with_passphrase_warning_text)
lateinit var warningText: TextView
@BindView(R.id.ssss_restore_with_passphrase_warning_reason)
lateinit var reasonText: TextView
@BindView(R.id.ssss_passphrase_enter_til)
lateinit var mPassphraseInputLayout: TextInputLayout
@BindView(R.id.ssss_passphrase_enter_edittext)
lateinit var mPassphraseTextEdit: EditText
@BindView(R.id.ssss_view_show_password)
lateinit var mPassphraseReveal: ImageView
@BindView(R.id.ssss_passphrase_submit)
lateinit var submitButton: Button
@BindView(R.id.ssss_passphrase_cancel)
lateinit var cancelButton: Button
@OnClick(R.id.ssss_view_show_password)
fun toggleVisibilityMode() {
sharedViewModel.handle(SharedSecureStorageAction.TogglePasswordVisibility)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
warningText.text = span {
span(getString(R.string.enter_secret_storage_passphrase_warning)) {
textStyle = "bold"
}
+" "
+getString(R.string.enter_secret_storage_passphrase_warning_text)
}
reasonText.text = getString(R.string.enter_secret_storage_passphrase_reason_verify)
mPassphraseTextEdit.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.subscribe {
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
submit()
}
}
.disposeOnDestroyView()
mPassphraseTextEdit.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_NULL) {
submit()
return@setOnEditorActionListener true
}
return@setOnEditorActionListener false
}
mPassphraseTextEdit.textChanges()
.subscribe {
mPassphraseInputLayout.error = null
submitButton.isEnabled = it.isNotBlank()
}
.disposeOnDestroyView()
sharedViewModel.observeViewEvents {
when (it) {
is SharedSecureStorageViewEvent.InlineError -> {
mPassphraseInputLayout.error = it.message
}
}
}
submitButton.setOnClickListener(DebouncedClickListener(
View.OnClickListener {
submit()
}
))
cancelButton.setOnClickListener(DebouncedClickListener(
View.OnClickListener {
sharedViewModel.handle(SharedSecureStorageAction.Cancel)
}
))
}
fun submit() {
val text = mPassphraseTextEdit.text.toString()
if (text.isBlank()) return // Should not reach this point as button disabled
submitButton.isEnabled = false
sharedViewModel.handle(SharedSecureStorageAction.SubmitPassphrase(text))
}
override fun invalidate() = withState(sharedViewModel) { state ->
val shouldBeVisible = state.passphraseVisible
mPassphraseTextEdit.showPassword(shouldBeVisible)
mPassphraseReveal.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
}
}

View File

@ -29,4 +29,5 @@ sealed class VerificationAction : VectorViewModelAction {
data class SASDoNotMatchAction(val otherUserId: String, val sasTransactionId: String) : VerificationAction()
object GotItConclusion : VerificationAction()
object SkipVerification : VerificationAction()
object VerifyFromPassphrase : VerificationAction()
}

View File

@ -36,6 +36,7 @@ import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.commitTransactionNow
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.riotx.features.crypto.quads.SharedSecureStorageActivity
import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
import im.vector.riotx.features.crypto.verification.conclusion.VerificationConclusionFragment
import im.vector.riotx.features.crypto.verification.emoji.VerificationEmojiCodeFragment
@ -87,6 +88,9 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
viewModel.observeViewEvents {
when (it) {
is VerificationBottomSheetViewEvents.Dismiss -> dismiss()
is VerificationBottomSheetViewEvents.AccessSecretStore -> {
startActivity(SharedSecureStorageActivity.newIntent(requireContext(),null, listOf("m.cross_signing.user_signing")))
}
}.exhaustive
}
}

View File

@ -23,4 +23,5 @@ import im.vector.riotx.core.platform.VectorViewEvents
*/
sealed class VerificationBottomSheetViewEvents : VectorViewEvents {
object Dismiss : VerificationBottomSheetViewEvents()
object AccessSecretStore : VerificationBottomSheetViewEvents()
}

View File

@ -253,6 +253,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
is VerificationAction.SkipVerification -> {
_viewEvents.post(VerificationBottomSheetViewEvents.Dismiss)
}
is VerificationAction.VerifyFromPassphrase -> {
_viewEvents.post(VerificationBottomSheetViewEvents.AccessSecretStore)
}
}.exhaustive
}

View File

@ -63,6 +63,7 @@ class VerificationRequestFragment @Inject constructor(
}
override fun onClickRecoverFromPassphrase() {
viewModel.handle(VerificationAction.VerifyFromPassphrase)
}
override fun onClickDismiss() {

View File

@ -148,9 +148,22 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
// We need to ask
sharedActionViewModel.hasDisplayedCompleteSecurityPrompt = true
navigator.waitSessionVerification(this)
} else {
// TODO upgrade security -> bootstrap cross signing
PopupAlertManager.postVectorAlert(
PopupAlertManager.VectorAlert(
uid = "completeSecurity",
title = getString(R.string.new_signin),
description = getString(R.string.complete_security),
iconId = R.drawable.ic_shield_warning
).apply {
colorInt = ContextCompat.getColor(this@HomeActivity, R.color.riotx_destructive_accent)
contentAction = Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
it.navigator.waitSessionVerification(it)
}
}
dismissedAction = Runnable {}
}
)
}
}

View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ssss__root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/ssss_shield"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="16dp"
android:src="@drawable/key_big"
android:tint="?riotx_text_primary"
app:layout_constraintBottom_toBottomOf="@+id/ssss_restore_with_passphrase"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_passphrase" />
<TextView
android:id="@+id/ssss_restore_with_passphrase"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="36dp"
android:layout_marginEnd="16dp"
android:text="@string/enter_secret_storage_passphrase"
android:textColor="?riotx_text_primary"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ssss_shield"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/ssss_restore_with_passphrase_warning_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:textColor="?riotx_text_primary"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ssss_restore_with_passphrase"
tools:text="@string/enter_secret_storage_passphrase_warning_text" />
<TextView
android:id="@+id/ssss_restore_with_passphrase_warning_reason"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:textColor="?riotx_text_primary"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ssss_restore_with_passphrase_warning_text"
tools:text="@string/enter_secret_storage_passphrase_reason_verify" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/ssss_passphrase_enter_til"
style="@style/VectorTextInputLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
app:errorEnabled="true"
app:layout_constraintEnd_toStartOf="@id/ssss_view_show_password"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ssss_restore_with_passphrase_warning_reason">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/ssss_passphrase_enter_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/passphrase_enter_passphrase"
android:imeOptions="actionDone"
android:maxLines="3"
android:singleLine="false"
android:textColor="?android:textColorPrimary"
tools:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/ssss_view_show_password"
android:layout_width="@dimen/layout_touch_size"
android:layout_height="@dimen/layout_touch_size"
android:layout_marginTop="8dp"
android:background="?attr/selectableItemBackground"
android:scaleType="center"
android:src="@drawable/ic_eye_black"
android:tint="?colorAccent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/ssss_passphrase_enter_til"
app:layout_constraintTop_toTopOf="@+id/ssss_passphrase_enter_til" />
<com.google.android.material.button.MaterialButton
android:id="@+id/ssss_passphrase_submit"
style="@style/VectorButtonStylePositive"
android:layout_marginTop="@dimen/layout_vertical_margin_big"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:layout_marginBottom="@dimen/layout_vertical_margin_big"
android:minWidth="200dp"
android:text="@string/_continue"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ssss_passphrase_enter_til" />
<com.google.android.material.button.MaterialButton
android:id="@+id/ssss_passphrase_cancel"
style="@style/VectorButtonStyleDestructive"
android:layout_marginTop="8dp"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:layout_marginBottom="@dimen/layout_vertical_margin_big"
android:minWidth="200dp"
android:text="@string/cancel"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ssss_passphrase_submit" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -32,5 +32,6 @@
<!-- Max width for some buttons -->
<dimen name="button_max_width">280dp</dimen>
<dimen name="fab_margin">16dp</dimen>
</resources>

View File

@ -2151,4 +2151,5 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
<string name="qr_code_scanned_by_other_no">No</string>
<string name="no_connectivity_to_the_server_indicator">Connectivity to the server has been lost</string>
<string name="title_activity_share_secure_storage">ShareSecureStorageActivity</string>
</resources>

View File

@ -21,6 +21,15 @@
<string name="verification_cannot_access_other_session">Cant access an existing session?</string>
<string name="verification_use_passphrase">Use your recovery key or passphrase</string>
<string name="new_signin">New Sign In</string>
<string name="enter_secret_storage_passphrase">Enter secret storage passphrase</string>
<string name="enter_secret_storage_passphrase_warning">Warning:</string>
<string name="enter_secret_storage_passphrase_warning_text">You should only access secret storage from a trusted device</string>
<string name="enter_secret_storage_passphrase_reason_verify">Access your secure message history and your cross-signing identity for verifying other sessions by entering your passphrase</string>
<!-- END Strings added by Valere -->

View File

@ -0,0 +1,11 @@
<resources>
<style name="AppTheme.Light.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.Light.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.Light.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>