Fix crash: show an error message with a Retry button when there is no network when displaying the BootstrapBottomSheet.
This commit is contained in:
parent
0573915a0a
commit
2a5df54ae4
|
@ -20,6 +20,7 @@ import im.vector.app.core.platform.VectorViewModelAction
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
sealed class BootstrapActions : VectorViewModelAction {
|
sealed class BootstrapActions : VectorViewModelAction {
|
||||||
|
object Retry : BootstrapActions()
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
||||||
|
|
|
@ -212,6 +212,11 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetBoot
|
||||||
views.bootstrapTitleText.text = getString(R.string.upgrade_security)
|
views.bootstrapTitleText.text = getString(R.string.upgrade_security)
|
||||||
showFragment(BootstrapMigrateBackupFragment::class)
|
showFragment(BootstrapMigrateBackupFragment::class)
|
||||||
}
|
}
|
||||||
|
is BootstrapStep.Error -> {
|
||||||
|
views.bootstrapIcon.isVisible = true
|
||||||
|
views.bootstrapTitleText.text = getString(R.string.bottom_sheet_setup_secure_backup_title)
|
||||||
|
showFragment(BootstrapErrorFragment::class)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
super.invalidate()
|
super.invalidate()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.crypto.recover
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.airbnb.mvrx.parentFragmentViewModel
|
||||||
|
import com.airbnb.mvrx.withState
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.onClick
|
||||||
|
import im.vector.app.core.extensions.setTextOrHide
|
||||||
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
|
import im.vector.app.databinding.FragmentBootstrapErrorBinding
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class BootstrapErrorFragment :
|
||||||
|
VectorBaseFragment<FragmentBootstrapErrorBinding>() {
|
||||||
|
|
||||||
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapErrorBinding {
|
||||||
|
return FragmentBootstrapErrorBinding.inflate(inflater, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
|
||||||
|
|
||||||
|
override fun invalidate() = withState(sharedViewModel) { state ->
|
||||||
|
when (state.step) {
|
||||||
|
is BootstrapStep.Error -> {
|
||||||
|
views.bootstrapDescriptionText.setTextOrHide(errorFormatter.toHumanReadable(state.step.error))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// Should not happen, show a generic error
|
||||||
|
views.bootstrapDescriptionText.setTextOrHide(getString(R.string.unknown_error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
views.bootstrapRetryButton.onClick {
|
||||||
|
sharedViewModel.handle(BootstrapActions.Retry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,6 +54,7 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.extractCurveKeyFromR
|
||||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult
|
||||||
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec
|
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec
|
||||||
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
||||||
|
import timber.log.Timber
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resumeWithException
|
import kotlin.coroutines.resumeWithException
|
||||||
|
@ -118,6 +119,12 @@ class BootstrapSharedViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetupMode.NORMAL -> {
|
SetupMode.NORMAL -> {
|
||||||
|
checkMigration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkMigration() {
|
||||||
// need to check if user have an existing keybackup
|
// need to check if user have an existing keybackup
|
||||||
setState {
|
setState {
|
||||||
copy(step = BootstrapStep.CheckingMigration)
|
copy(step = BootstrapStep.CheckingMigration)
|
||||||
|
@ -125,6 +132,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
// We need to check if there is an existing backup
|
// We need to check if there is an existing backup
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
val version = tryOrNull { session.cryptoService().keysBackupService().getCurrentVersion() }?.toKeysVersionResult()
|
val version = tryOrNull { session.cryptoService().keysBackupService().getCurrentVersion() }?.toKeysVersionResult()
|
||||||
if (version == null) {
|
if (version == null) {
|
||||||
// we just resume plain bootstrap
|
// we just resume plain bootstrap
|
||||||
|
@ -148,6 +156,10 @@ class BootstrapSharedViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
Timber.e(failure, "Error while checking key backup")
|
||||||
|
setState {
|
||||||
|
copy(step = BootstrapStep.Error(failure))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,6 +280,9 @@ class BootstrapSharedViewModel @AssistedInject constructor(
|
||||||
copy(step = BootstrapStep.AccountReAuth(stringProvider.getString(R.string.authentication_error)))
|
copy(step = BootstrapStep.AccountReAuth(stringProvider.getString(R.string.authentication_error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BootstrapActions.Retry -> {
|
||||||
|
checkMigration()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,6 +583,12 @@ class BootstrapSharedViewModel @AssistedInject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is BootstrapStep.Error -> {
|
||||||
|
// do we let you cancel from here?
|
||||||
|
if (state.canLeave) {
|
||||||
|
_viewEvents.post(BootstrapViewEvents.SkipBootstrap(state.passphrase != null))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,8 @@ sealed class BootstrapStep {
|
||||||
object Initializing : BootstrapStep()
|
object Initializing : BootstrapStep()
|
||||||
data class SaveRecoveryKey(val isSaved: Boolean) : BootstrapStep()
|
data class SaveRecoveryKey(val isSaved: Boolean) : BootstrapStep()
|
||||||
object DoneSuccess : BootstrapStep()
|
object DoneSuccess : BootstrapStep()
|
||||||
|
|
||||||
|
data class Error(val error: Throwable) : BootstrapStep()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun BootstrapStep.GetBackupSecretForMigration.useKey(): Boolean {
|
fun BootstrapStep.GetBackupSecretForMigration.useKey(): Boolean {
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?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"
|
||||||
|
android:minHeight="200dp"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bootstrapDescriptionText"
|
||||||
|
style="@style/Widget.Vector.TextView.Body"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/error_check_network"
|
||||||
|
android:textColor="?vctr_content_primary"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/bootstrapRetryButton"
|
||||||
|
style="@style/Widget.Vector.Button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||||
|
android:text="@string/global_retry"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/bootstrapDescriptionText"
|
||||||
|
tools:ignore="MissingConstraints" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue