Bootstrap bottomsheet
This commit is contained in:
parent
a995615f87
commit
a40dd31543
@ -26,6 +26,8 @@ import im.vector.riotx.features.attachments.preview.AttachmentsPreviewFragment
|
||||
import im.vector.riotx.features.createdirect.CreateDirectRoomDirectoryUsersFragment
|
||||
import im.vector.riotx.features.createdirect.CreateDirectRoomKnownUsersFragment
|
||||
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
|
||||
import im.vector.riotx.features.crypto.recover.BootstrapConfirmPassphraseFragment
|
||||
import im.vector.riotx.features.crypto.recover.BootstrapEnterPassphraseFragment
|
||||
import im.vector.riotx.features.crypto.verification.cancel.VerificationCancelFragment
|
||||
import im.vector.riotx.features.crypto.verification.cancel.VerificationNotMeFragment
|
||||
import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
|
||||
@ -78,8 +80,8 @@ import im.vector.riotx.features.settings.devices.VectorSettingsDevicesFragment
|
||||
import im.vector.riotx.features.settings.devtools.AccountDataFragment
|
||||
import im.vector.riotx.features.settings.devtools.GossipingEventsPaperTrailFragment
|
||||
import im.vector.riotx.features.settings.devtools.IncomingKeyRequestListFragment
|
||||
import im.vector.riotx.features.settings.devtools.OutgoingKeyRequestListFragment
|
||||
import im.vector.riotx.features.settings.devtools.KeyRequestsFragment
|
||||
import im.vector.riotx.features.settings.devtools.OutgoingKeyRequestListFragment
|
||||
import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment
|
||||
import im.vector.riotx.features.settings.push.PushGatewaysFragment
|
||||
import im.vector.riotx.features.share.IncomingShareFragment
|
||||
@ -402,4 +404,14 @@ interface FragmentModule {
|
||||
@IntoMap
|
||||
@FragmentKey(GossipingEventsPaperTrailFragment::class)
|
||||
fun bindGossipingEventsPaperTrailFragment(fragment: GossipingEventsPaperTrailFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(BootstrapEnterPassphraseFragment::class)
|
||||
fun bindBootstrapEnterPassphraseFragment(fragment: BootstrapEnterPassphraseFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(BootstrapConfirmPassphraseFragment::class)
|
||||
fun bindBootstrapConfirmPassphraseFragment(fragment: BootstrapConfirmPassphraseFragment): Fragment
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ 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.recover.BootstrapBottomSheet
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
|
||||
import im.vector.riotx.features.debug.DebugMenuActivity
|
||||
import im.vector.riotx.features.home.HomeActivity
|
||||
@ -128,6 +129,7 @@ interface ScreenComponent {
|
||||
fun inject(bottomSheet: VerificationBottomSheet)
|
||||
fun inject(bottomSheet: DeviceVerificationInfoBottomSheet)
|
||||
fun inject(bottomSheet: DeviceListBottomSheet)
|
||||
fun inject(bottomSheet: BootstrapBottomSheet)
|
||||
|
||||
/* ==========================================================================================
|
||||
* Others
|
||||
|
@ -39,6 +39,7 @@ import im.vector.riotx.features.home.AvatarRenderer
|
||||
import im.vector.riotx.features.home.HomeRoomListDataSource
|
||||
import im.vector.riotx.features.html.EventHtmlRenderer
|
||||
import im.vector.riotx.features.html.VectorHtmlCompressor
|
||||
import im.vector.riotx.features.login.ReAuthHelper
|
||||
import im.vector.riotx.features.navigation.Navigator
|
||||
import im.vector.riotx.features.notifications.NotifiableEventResolver
|
||||
import im.vector.riotx.features.notifications.NotificationBroadcastReceiver
|
||||
@ -131,6 +132,8 @@ interface VectorComponent {
|
||||
|
||||
fun alertManager() : PopupAlertManager
|
||||
|
||||
fun reAuthHelper() : ReAuthHelper
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
fun create(@BindsInstance context: Context): VectorComponent
|
||||
|
@ -195,7 +195,8 @@ class MainActivity : VectorBaseActivity() {
|
||||
// We have a session.
|
||||
// Check it can be opened
|
||||
if (sessionHolder.getActiveSession().isOpenable) {
|
||||
HomeActivity.newIntent(this)
|
||||
// DO NOT COMMIT
|
||||
HomeActivity.newIntent(this, accountCreation = true)
|
||||
} else {
|
||||
// The token is still invalid
|
||||
SoftLogoutActivity.newIntent(this)
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.recover
|
||||
|
||||
import im.vector.riotx.core.platform.VectorViewModelAction
|
||||
|
||||
sealed class BootstrapActions : VectorViewModelAction {
|
||||
|
||||
object GoBack : BootstrapActions()
|
||||
data class GoToConfirmPassphrase(val passphrase: String) : BootstrapActions()
|
||||
object TogglePasswordVisibility : BootstrapActions()
|
||||
data class UpdateCandidatePassphrase(val pass: String) : BootstrapActions()
|
||||
data class UpdateConfirmCandidatePassphrase(val pass: String) : BootstrapActions()
|
||||
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.recover
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.ScreenComponent
|
||||
import im.vector.riotx.core.extensions.commitTransaction
|
||||
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_bootstrap.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||
|
||||
override val showExpanded = true
|
||||
|
||||
@Inject
|
||||
lateinit var bootstrapViewModelFactory: BootstrapSharedViewModel.Factory
|
||||
|
||||
private val viewModel by fragmentViewModel(BootstrapSharedViewModel::class)
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_bootstrap
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewModel.observeViewEvents {
|
||||
when (it) {
|
||||
is BootstrapViewEvents.Dismiss -> dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val rootView = super.onCreateView(inflater, container, savedInstanceState)
|
||||
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
||||
return rootView
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return super.onCreateDialog(savedInstanceState).apply {
|
||||
setOnKeyListener { _, keyCode, keyEvent ->
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == KeyEvent.ACTION_UP) {
|
||||
viewModel.handle(BootstrapActions.GoBack)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
|
||||
when (state.step) {
|
||||
is BootstrapStep.SetupPassphrase -> {
|
||||
bootstrapTitleText.text = getString(R.string.recovery_passphrase)
|
||||
showFragment(BootstrapEnterPassphraseFragment::class, Bundle())
|
||||
}
|
||||
is BootstrapStep.ConfirmPassphrase -> {
|
||||
bootstrapTitleText.text = getString(R.string.passphrase_confirm_passphrase)
|
||||
showFragment(BootstrapConfirmPassphraseFragment::class, Bundle())
|
||||
}
|
||||
is BootstrapStep.Initializing -> TODO()
|
||||
}
|
||||
super.invalidate()
|
||||
}
|
||||
|
||||
private fun showFragment(fragmentClass: KClass<out Fragment>, bundle: Bundle) {
|
||||
if (childFragmentManager.findFragmentByTag(fragmentClass.simpleName) == null) {
|
||||
childFragmentManager.commitTransaction {
|
||||
replace(R.id.bottomSheetFragmentContainer,
|
||||
fragmentClass.java,
|
||||
bundle,
|
||||
fragmentClass.simpleName
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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.recover
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import androidx.core.text.toSpannable
|
||||
import androidx.core.view.isGone
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.jakewharton.rxbinding3.view.clicks
|
||||
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.resources.ColorProvider
|
||||
import im.vector.riotx.core.utils.colorizeMatchingText
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
class BootstrapConfirmPassphraseFragment @Inject constructor(
|
||||
private val colorProvider: ColorProvider
|
||||
) : VectorBaseFragment() {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_bootstrap_enter_passphrase
|
||||
|
||||
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
ssss_passphrase_security_progress.isGone = true
|
||||
|
||||
val recPassPhrase = getString(R.string.recovery_passphrase)
|
||||
bootstrapDescriptionText.text = getString(R.string.bootstrap_info_confirm_text, recPassPhrase)
|
||||
.toSpannable()
|
||||
.colorizeMatchingText(recPassPhrase, colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
|
||||
|
||||
ssss_passphrase_enter_edittext.hint = getString(R.string.passphrase_confirm_passphrase)
|
||||
|
||||
ssss_passphrase_enter_edittext.editorActionEvents()
|
||||
.debounce(300, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
submit()
|
||||
}
|
||||
}
|
||||
.disposeOnDestroyView()
|
||||
|
||||
ssss_passphrase_enter_edittext.textChanges()
|
||||
.subscribe {
|
||||
// ssss_passphrase_enter_til.error = null
|
||||
sharedViewModel.handle(BootstrapActions.UpdateConfirmCandidatePassphrase(it?.toString() ?: ""))
|
||||
// ssss_passphrase_submit.isEnabled = it.isNotBlank()
|
||||
}
|
||||
.disposeOnDestroyView()
|
||||
|
||||
sharedViewModel.observeViewEvents {
|
||||
// when (it) {
|
||||
// is SharedSecureStorageViewEvent.InlineError -> {
|
||||
// ssss_passphrase_enter_til.error = it.message
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
ssss_view_show_password.clicks()
|
||||
.debounce(300, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility)
|
||||
}
|
||||
.disposeOnDestroyView()
|
||||
}
|
||||
|
||||
private fun submit() = withState(sharedViewModel) { state ->
|
||||
if (state.step !is BootstrapStep.ConfirmPassphrase) {
|
||||
return@withState
|
||||
}
|
||||
|
||||
// val score = state.passphraseStrength.invoke()?.score
|
||||
// val passphrase = ssss_passphrase_enter_edittext.text?.toString()
|
||||
// if (passphrase.isNullOrBlank()) {
|
||||
// ssss_passphrase_enter_til.error = getString(R.string.passphrase_empty_error_message)
|
||||
// } else if (score != 4) {
|
||||
// ssss_passphrase_enter_til.error = getString(R.string.passphrase_passphrase_too_weak)
|
||||
// } else {
|
||||
// sharedViewModel.handle(BootstrapActions.GoToConfirmPassphrase(passphrase))
|
||||
// }
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(sharedViewModel) { state ->
|
||||
super.invalidate()
|
||||
|
||||
if (state.step is BootstrapStep.ConfirmPassphrase) {
|
||||
val isPasswordVisible = state.step.isPasswordVisible
|
||||
ssss_passphrase_enter_edittext.showPassword(isPasswordVisible)
|
||||
ssss_view_show_password.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.recover
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import androidx.core.text.toSpannable
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.jakewharton.rxbinding3.view.clicks
|
||||
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.resources.ColorProvider
|
||||
import im.vector.riotx.core.utils.colorizeMatchingText
|
||||
import im.vector.riotx.features.settings.VectorLocale
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.*
|
||||
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.ssss_passphrase_enter_edittext
|
||||
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.ssss_passphrase_enter_til
|
||||
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.ssss_view_show_password
|
||||
import kotlinx.android.synthetic.main.fragment_ssss_access_from_passphrase.*
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
class BootstrapEnterPassphraseFragment @Inject constructor(
|
||||
private val colorProvider: ColorProvider
|
||||
) : VectorBaseFragment() {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_bootstrap_enter_passphrase
|
||||
|
||||
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
||||
val recPassPhrase = getString(R.string.recovery_passphrase)
|
||||
bootstrapDescriptionText.text = getString(R.string.bootstrap_info_text, recPassPhrase)
|
||||
.toSpannable()
|
||||
.colorizeMatchingText(recPassPhrase, colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
|
||||
|
||||
ssss_passphrase_enter_edittext.hint = getString(R.string.passphrase_enter_passphrase)
|
||||
withState(sharedViewModel) {
|
||||
// set initial value (usefull when coming back)
|
||||
ssss_passphrase_enter_edittext.setText(it.passphrase ?: "")
|
||||
}
|
||||
ssss_passphrase_enter_edittext.editorActionEvents()
|
||||
.debounce(300, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
submit()
|
||||
}
|
||||
}
|
||||
.disposeOnDestroyView()
|
||||
|
||||
ssss_passphrase_enter_edittext.textChanges()
|
||||
.subscribe {
|
||||
// ssss_passphrase_enter_til.error = null
|
||||
sharedViewModel.handle(BootstrapActions.UpdateCandidatePassphrase(it?.toString() ?: ""))
|
||||
// ssss_passphrase_submit.isEnabled = it.isNotBlank()
|
||||
}
|
||||
.disposeOnDestroyView()
|
||||
|
||||
sharedViewModel.observeViewEvents {
|
||||
// when (it) {
|
||||
// is SharedSecureStorageViewEvent.InlineError -> {
|
||||
// ssss_passphrase_enter_til.error = it.message
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// ssss_passphrase_submit.clicks()
|
||||
// .debounce(300, TimeUnit.MILLISECONDS)
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe {
|
||||
// submit()
|
||||
// }
|
||||
// .disposeOnDestroyView()
|
||||
|
||||
// ssss_passphrase_cancel.clicks()
|
||||
// .debounce(300, TimeUnit.MILLISECONDS)
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe {
|
||||
// sharedViewModel.handle(SharedSecureStorageAction.Cancel)
|
||||
// }
|
||||
// .disposeOnDestroyView()
|
||||
|
||||
ssss_view_show_password.clicks()
|
||||
.debounce(300, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility)
|
||||
}
|
||||
.disposeOnDestroyView()
|
||||
}
|
||||
|
||||
private fun submit() = withState(sharedViewModel) { state ->
|
||||
if (state.step !is BootstrapStep.SetupPassphrase) {
|
||||
return@withState
|
||||
}
|
||||
|
||||
val score = state.passphraseStrength.invoke()?.score
|
||||
val passphrase = ssss_passphrase_enter_edittext.text?.toString()
|
||||
if (passphrase.isNullOrBlank()) {
|
||||
ssss_passphrase_enter_til.error = getString(R.string.passphrase_empty_error_message)
|
||||
} else if (score != 4) {
|
||||
ssss_passphrase_enter_til.error = getString(R.string.passphrase_passphrase_too_weak)
|
||||
} else {
|
||||
sharedViewModel.handle(BootstrapActions.GoToConfirmPassphrase(passphrase))
|
||||
}
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(sharedViewModel) { state ->
|
||||
super.invalidate()
|
||||
|
||||
if (state.step is BootstrapStep.SetupPassphrase) {
|
||||
val isPasswordVisible = state.step.isPasswordVisible
|
||||
ssss_passphrase_enter_edittext.showPassword(isPasswordVisible)
|
||||
ssss_view_show_password.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
|
||||
|
||||
state.passphraseStrength.invoke()?.let { strength ->
|
||||
val score = strength.score
|
||||
ssss_passphrase_security_progress.strength = score
|
||||
|
||||
Timber.e("## Strength info: $strength")
|
||||
Timber.e("## Strength info score: $score")
|
||||
Timber.e("## Strength info getWarning: ${strength.feedback?.getWarning(VectorLocale.applicationLocale)}")
|
||||
Timber.e("## Strength info getSuggestions: ${strength.feedback?.getSuggestions(VectorLocale.applicationLocale)}")
|
||||
Timber.e("## Strength info getFirstSuggestions: ${strength.feedback?.getSuggestions(VectorLocale.applicationLocale)?.firstOrNull()}")
|
||||
if (score in 1..3) {
|
||||
val hint =
|
||||
strength.feedback?.getWarning(VectorLocale.applicationLocale)?.takeIf { it.isNotBlank() }
|
||||
?: strength.feedback?.getSuggestions(VectorLocale.applicationLocale)?.firstOrNull()
|
||||
Timber.e("## Strength info: $hint")
|
||||
Timber.e("## Strength currentValue : ${ssss_passphrase_enter_til.error}")
|
||||
if (hint != null && hint != ssss_passphrase_enter_til.error.toString()) {
|
||||
ssss_passphrase_enter_til.error = hint
|
||||
}
|
||||
} else {
|
||||
ssss_passphrase_enter_til.error = null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.recover
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.nulabinc.zxcvbn.Strength
|
||||
import com.nulabinc.zxcvbn.Zxcvbn
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotx.core.extensions.exhaustive
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import im.vector.riotx.features.login.ReAuthHelper
|
||||
|
||||
data class BootstrapViewState(
|
||||
val step: BootstrapStep = BootstrapStep.SetupPassphrase(false),
|
||||
val passphrase: String? = null,
|
||||
val crossSigningInitialization: Async<Unit> = Uninitialized,
|
||||
val passphraseStrength: Async<Strength> = Uninitialized
|
||||
) : MvRxState
|
||||
|
||||
sealed class BootstrapStep {
|
||||
data class SetupPassphrase(val isPasswordVisible: Boolean) : BootstrapStep()
|
||||
data class ConfirmPassphrase(val isPasswordVisible: Boolean) : BootstrapStep()
|
||||
object Initializing : BootstrapStep()
|
||||
}
|
||||
|
||||
class BootstrapSharedViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: BootstrapViewState,
|
||||
private val session: Session,
|
||||
private val reAuthHelper: ReAuthHelper
|
||||
) : VectorViewModel<BootstrapViewState, BootstrapActions, BootstrapViewEvents>(initialState) {
|
||||
|
||||
private val zxcvbn = Zxcvbn()
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: BootstrapViewState): BootstrapSharedViewModel
|
||||
}
|
||||
|
||||
override fun handle(action: BootstrapActions) = withState { state ->
|
||||
when (action) {
|
||||
is BootstrapActions.GoBack -> queryBack()
|
||||
BootstrapActions.TogglePasswordVisibility -> {
|
||||
when (state.step) {
|
||||
is BootstrapStep.SetupPassphrase -> {
|
||||
setState {
|
||||
copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible))
|
||||
}
|
||||
}
|
||||
is BootstrapStep.ConfirmPassphrase -> {
|
||||
setState {
|
||||
copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
is BootstrapActions.UpdateCandidatePassphrase -> {
|
||||
val strength = zxcvbn.measure(action.pass)
|
||||
setState {
|
||||
copy(
|
||||
passphrase = action.pass,
|
||||
passphraseStrength = Success(strength)
|
||||
)
|
||||
}
|
||||
}
|
||||
is BootstrapActions.GoToConfirmPassphrase -> {
|
||||
setState {
|
||||
copy(
|
||||
passphrase = action.passphrase,
|
||||
step = BootstrapStep.ConfirmPassphrase(
|
||||
isPasswordVisible = (state.step as? BootstrapStep.SetupPassphrase)?.isPasswordVisible ?: false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
is BootstrapActions.UpdateConfirmCandidatePassphrase -> {
|
||||
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
// =======================================
|
||||
// Fragment interaction
|
||||
// =======================================
|
||||
|
||||
private fun queryBack() = withState { state ->
|
||||
when (state.step) {
|
||||
is BootstrapStep.SetupPassphrase -> {
|
||||
|
||||
}
|
||||
is BootstrapStep.ConfirmPassphrase -> {
|
||||
setState {
|
||||
copy(
|
||||
step = BootstrapStep.SetupPassphrase(
|
||||
isPasswordVisible = (state.step as? BootstrapStep.ConfirmPassphrase)?.isPasswordVisible ?: false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================
|
||||
// Companion, view model assisted creation
|
||||
// ======================================
|
||||
|
||||
companion object : MvRxViewModelFactory<BootstrapSharedViewModel, BootstrapViewState> {
|
||||
|
||||
override fun create(viewModelContext: ViewModelContext, state: BootstrapViewState): BootstrapSharedViewModel? {
|
||||
val fragment: BootstrapBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.bootstrapViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.recover
|
||||
|
||||
import im.vector.riotx.core.platform.VectorViewEvents
|
||||
|
||||
sealed class BootstrapViewEvents : VectorViewEvents {
|
||||
object Dismiss : BootstrapViewEvents()
|
||||
// data class Failure(val throwable: Throwable) : DevicesViewEvents()
|
||||
//
|
||||
// object RequestPassword : DevicesViewEvents()
|
||||
//
|
||||
// data class PromptRenameDevice(val deviceInfo: DeviceInfo) : DevicesViewEvents()
|
||||
//
|
||||
// data class ShowVerifyDevice(
|
||||
// val userId: String,
|
||||
// val transactionId: String?
|
||||
// ) : DevicesViewEvents()
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return super.onCreateDialog(savedInstanceState).apply {
|
||||
setOnKeyListener { _, keyCode, keyEvent ->
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == KeyEvent.ACTION_UP) {
|
||||
|
@ -40,7 +40,9 @@ import im.vector.riotx.core.extensions.replaceFragment
|
||||
import im.vector.riotx.core.platform.ToolbarConfigurable
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.core.pushers.PushersManager
|
||||
import im.vector.riotx.features.crypto.recover.BootstrapBottomSheet
|
||||
import im.vector.riotx.features.disclaimer.showDisclaimerDialog
|
||||
import im.vector.riotx.features.login.LoginAction
|
||||
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
||||
import im.vector.riotx.features.popup.PopupAlertManager
|
||||
import im.vector.riotx.features.popup.VerificationVectorAlert
|
||||
@ -95,6 +97,9 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||
drawerLayout.closeDrawer(GravityCompat.START)
|
||||
replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java)
|
||||
}
|
||||
is HomeActivitySharedAction.PromptForSecurityBootstrap -> {
|
||||
BootstrapBottomSheet().apply { isCancelable = false }.show(supportFragmentManager, "BootstrapBottomSheet")
|
||||
}
|
||||
}
|
||||
}
|
||||
.disposeOnDestroy()
|
||||
@ -103,6 +108,10 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||
notificationDrawerManager.clearAllEvents()
|
||||
intent.removeExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)
|
||||
}
|
||||
if (intent.getBooleanExtra(EXTRA_ACCOUNT_CREATION, false)) {
|
||||
sharedActionViewModel.post(HomeActivitySharedAction.PromptForSecurityBootstrap)
|
||||
intent.removeExtra(EXTRA_ACCOUNT_CREATION)
|
||||
}
|
||||
|
||||
activeSessionHolder.getSafeActiveSession()?.getInitialSyncProgressStatus()?.observe(this, Observer { status ->
|
||||
if (status == null) {
|
||||
@ -246,11 +255,13 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_CLEAR_EXISTING_NOTIFICATION = "EXTRA_CLEAR_EXISTING_NOTIFICATION"
|
||||
private const val EXTRA_ACCOUNT_CREATION = "EXTRA_ACCOUNT_CREATION"
|
||||
|
||||
fun newIntent(context: Context, clearNotification: Boolean = false): Intent {
|
||||
fun newIntent(context: Context, clearNotification: Boolean = false, accountCreation: Boolean = false): Intent {
|
||||
return Intent(context, HomeActivity::class.java)
|
||||
.apply {
|
||||
putExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION, clearNotification)
|
||||
putExtra(EXTRA_ACCOUNT_CREATION, accountCreation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,4 +24,5 @@ import im.vector.riotx.core.platform.VectorSharedAction
|
||||
sealed class HomeActivitySharedAction : VectorSharedAction {
|
||||
object OpenDrawer : HomeActivitySharedAction()
|
||||
object OpenGroup : HomeActivitySharedAction()
|
||||
object PromptForSecurityBootstrap : HomeActivitySharedAction()
|
||||
}
|
||||
|
@ -217,7 +217,10 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||
|
||||
private fun updateWithState(loginViewState: LoginViewState) {
|
||||
if (loginViewState.isUserLogged()) {
|
||||
val intent = HomeActivity.newIntent(this)
|
||||
val intent = HomeActivity.newIntent(
|
||||
this,
|
||||
accountCreation = true //loginViewState.signMode == SignMode.SignUp
|
||||
)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
return
|
||||
|
@ -38,6 +38,7 @@ import im.vector.matrix.android.api.auth.registration.Stage
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
|
||||
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||
import im.vector.riotx.core.extensions.configureAndStart
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
@ -56,7 +57,8 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||
private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val pushRuleTriggerListener: PushRuleTriggerListener,
|
||||
private val homeServerConnectionConfigFactory: HomeServerConnectionConfigFactory,
|
||||
private val sessionListener: SessionListener)
|
||||
private val sessionListener: SessionListener,
|
||||
private val reAuthHelper: ReAuthHelper)
|
||||
: VectorViewModel<LoginViewState, LoginAction, LoginViewEvents>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
@ -240,6 +242,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||
|
||||
private fun handleRegisterWith(action: LoginAction.LoginOrRegister) {
|
||||
setState { copy(asyncRegistration = Loading()) }
|
||||
reAuthHelper.rememberAuth(UserPasswordAuth(user = action.username, password = action.password))
|
||||
currentTask = registrationWizard?.createAccount(
|
||||
action.username,
|
||||
action.password,
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.login
|
||||
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
|
||||
import java.util.Timer
|
||||
import java.util.TimerTask
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
const val THREE_MINUTES = 3 * 60_000L
|
||||
|
||||
@Singleton
|
||||
class ReAuthHelper @Inject constructor() {
|
||||
|
||||
private var timer: Timer? = null
|
||||
|
||||
private var rememberedInfo: UserPasswordAuth? = null
|
||||
|
||||
private var clearTask = object : TimerTask() {
|
||||
override fun run() {
|
||||
rememberedInfo = null
|
||||
}
|
||||
}
|
||||
|
||||
fun rememberAuth(password: UserPasswordAuth?) {
|
||||
timer?.cancel()
|
||||
rememberedInfo = password
|
||||
timer = Timer().also {
|
||||
it.schedule(clearTask, THREE_MINUTES)
|
||||
}
|
||||
}
|
||||
}
|
115
vector/src/main/res/layout/bottom_sheet_bootstrap.xml
Normal file
115
vector/src/main/res/layout/bottom_sheet_bootstrap.xml
Normal file
@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.core.widget.NestedScrollView 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/bottomSheetScrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbars="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/bootstrapIcon"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:contentDescription="@string/avatar"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_shield_black"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bootstrapTitleText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/bootstrapIcon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/bootstrapIcon"
|
||||
app:layout_constraintTop_toTopOf="@+id/bootstrapIcon"
|
||||
tools:text="@string/recovery_passphrase" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/bottomSheetFragmentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/bootstrapTitleText" />
|
||||
|
||||
<!-- <TextView-->
|
||||
<!-- android:id="@+id/bootstrapDescriptionText"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginTop="24dp"-->
|
||||
<!-- android:text="@string/bootstrap_info_text"-->
|
||||
<!-- android:textColor="?riotx_text_primary"-->
|
||||
<!-- android:textSize="14sp"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@+id/bootstrapTitleText" />-->
|
||||
|
||||
<!-- <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_marginTop="16dp"-->
|
||||
<!-- app:errorEnabled="true"-->
|
||||
<!-- app:layout_constraintEnd_toStartOf="@id/ssss_view_show_password"-->
|
||||
<!-- app:layout_constraintStart_toStartOf="parent"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@id/bootstrapDescriptionText">-->
|
||||
|
||||
<!-- <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" />-->
|
||||
|
||||
|
||||
<!-- <TextView-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginTop="8dp"-->
|
||||
<!-- android:drawableStart="@drawable/e2e_warning"-->
|
||||
<!-- android:drawablePadding="4dp"-->
|
||||
<!-- android:text="@string/bootstrap_dont_reuse_pwd"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@id/ssss_passphrase_enter_til" />-->
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bootstrapDescriptionText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/bootstrap_info_text"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/ssss_passphrase_enter_til"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<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_marginTop="16dp"
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ssss_view_show_password"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/bootstrapDescriptionText">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/ssss_passphrase_enter_edittext"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:hint="@string/passphrase_enter_passphrase"
|
||||
android:imeOptions="actionDone"
|
||||
android:maxLines="3"
|
||||
android:singleLine="false"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
tools:inputType="textPassword" />
|
||||
|
||||
<!-- This is inside the TIL, if not the keyboard will hide it when in bottomsheet -->
|
||||
|
||||
<im.vector.riotx.core.ui.views.PasswordStrengthBar
|
||||
android:id="@+id/ssss_passphrase_security_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_height="4dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bootstrapWarningInfo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:textSize="12sp"
|
||||
android:gravity="center_vertical"
|
||||
android:drawableStart="@drawable/ic_alert_triangle"
|
||||
android:drawableTint="@color/riotx_destructive_accent"
|
||||
android:drawablePadding="4dp"
|
||||
android:text="@string/bootstrap_dont_reuse_pwd" />
|
||||
|
||||
</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" />
|
||||
|
||||
|
||||
<!-- <TextView-->
|
||||
<!-- android:id="@+id/bootstrapWarningInfo"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginTop="8dp"-->
|
||||
<!-- android:drawableStart="@drawable/e2e_warning"-->
|
||||
<!-- android:drawablePadding="4dp"-->
|
||||
<!-- android:text="@string/bootstrap_dont_reuse_pwd"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@id/ssss_passphrase_enter_til" />-->
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -32,6 +32,13 @@
|
||||
<string name="verify_cancelled_notice">Verify your devices from Settings.</string>
|
||||
<string name="verification_cancelled">Verification Cancelled</string>
|
||||
|
||||
<string name="recovery_passphrase">Recovery Passphrase</string>
|
||||
|
||||
<!-- %s will be replaced by recovery_passphrase -->
|
||||
<string name="bootstrap_info_text">Secure & unlock information with a %s so only you can access encrypted messages and secure information.</string>
|
||||
<!-- %s will be replaced by recovery_passphrase -->
|
||||
<string name="bootstrap_info_confirm_text">Enter your %s again to confirm it.</string>
|
||||
<string name="bootstrap_dont_reuse_pwd">Don’t re-use your account password.</string>
|
||||
<!-- END Strings added by Valere -->
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user