mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
Convert KeysExporter to coroutines
This commit is contained in:
parent
f99600f115
commit
3be95ca442
@ -84,7 +84,7 @@ interface CryptoService {
|
||||
|
||||
fun importRoomKeys(roomKeysAsArray: ByteArray, password: String, progressListener: ProgressListener?, callback: MatrixCallback<ImportRoomKeysResult>)
|
||||
|
||||
fun exportRoomKeys(password: String, callback: MatrixCallback<ByteArray>)
|
||||
suspend fun exportRoomKeys(password: String): ByteArray
|
||||
|
||||
fun setRoomBlacklistUnverifiedDevices(roomId: String)
|
||||
|
||||
|
@ -928,14 +928,10 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
* Export the crypto keys
|
||||
*
|
||||
* @param password the password
|
||||
* @param callback the exported keys
|
||||
* @return the exported keys
|
||||
*/
|
||||
override fun exportRoomKeys(password: String, callback: MatrixCallback<ByteArray>) {
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||
runCatching {
|
||||
exportRoomKeys(password, MXMegolmExportEncryption.DEFAULT_ITERATION_COUNT)
|
||||
}.foldToCallback(callback)
|
||||
}
|
||||
override suspend fun exportRoomKeys(password: String): ByteArray {
|
||||
return exportRoomKeys(password, MXMegolmExportEncryption.DEFAULT_ITERATION_COUNT)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,6 +32,7 @@ import im.vector.app.features.call.conference.VectorJitsiActivity
|
||||
import im.vector.app.features.call.transfer.CallTransferActivity
|
||||
import im.vector.app.features.createdirect.CreateDirectRoomActivity
|
||||
import im.vector.app.features.crypto.keysbackup.settings.KeysBackupManageActivity
|
||||
import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity
|
||||
import im.vector.app.features.crypto.quads.SharedSecureStorageActivity
|
||||
import im.vector.app.features.crypto.recover.BootstrapBottomSheet
|
||||
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
||||
@ -138,6 +139,7 @@ interface ScreenComponent {
|
||||
fun inject(activity: LinkHandlerActivity)
|
||||
fun inject(activity: MainActivity)
|
||||
fun inject(activity: RoomDirectoryActivity)
|
||||
fun inject(activity: KeysBackupSetupActivity)
|
||||
fun inject(activity: BugReportActivity)
|
||||
fun inject(activity: FilteredRoomsActivity)
|
||||
fun inject(activity: CreateRoomActivity)
|
||||
|
@ -18,35 +18,24 @@ package im.vector.app.features.crypto.keys
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import im.vector.app.features.session.coroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.internal.extensions.foldToCallback
|
||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||
|
||||
class KeysExporter(private val session: Session) {
|
||||
import javax.inject.Inject
|
||||
|
||||
class KeysExporter @Inject constructor(
|
||||
private val session: Session,
|
||||
private val context: Context
|
||||
) {
|
||||
/**
|
||||
* Export keys and return the file path with the callback
|
||||
* Export keys and write them to the provided uri
|
||||
*/
|
||||
fun export(context: Context, password: String, uri: Uri, callback: MatrixCallback<Boolean>) {
|
||||
session.coroutineScope.launch(Dispatchers.Main) {
|
||||
runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
val data = awaitCallback<ByteArray> { session.cryptoService().exportRoomKeys(password, it) }
|
||||
val os = context.contentResolver?.openOutputStream(uri)
|
||||
if (os == null) {
|
||||
false
|
||||
} else {
|
||||
os.write(data)
|
||||
os.flush()
|
||||
true
|
||||
}
|
||||
}
|
||||
}.foldToCallback(callback)
|
||||
suspend fun export(password: String, uri: Uri) {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val data = session.cryptoService().exportRoomKeys(password)
|
||||
context.contentResolver.openOutputStream(uri)
|
||||
?.use { it.write(data) }
|
||||
?: throw IllegalStateException("Unable to open file for writting")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,13 @@ package im.vector.app.features.crypto.keysbackup.setup
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ScreenComponent
|
||||
import im.vector.app.core.dialogs.ExportKeysDialog
|
||||
import im.vector.app.core.extensions.observeEvent
|
||||
import im.vector.app.core.extensions.queryExportKeys
|
||||
@ -30,7 +33,8 @@ import im.vector.app.core.extensions.replaceFragment
|
||||
import im.vector.app.core.platform.SimpleFragmentActivity
|
||||
import im.vector.app.core.utils.toast
|
||||
import im.vector.app.features.crypto.keys.KeysExporter
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class KeysBackupSetupActivity : SimpleFragmentActivity() {
|
||||
|
||||
@ -38,6 +42,13 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
|
||||
|
||||
private lateinit var viewModel: KeysBackupSetupSharedViewModel
|
||||
|
||||
@Inject lateinit var keysExporter: KeysExporter
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
super.injectWith(injector)
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun initUiAndData() {
|
||||
super.initUiAndData()
|
||||
if (isFirstCreation()) {
|
||||
@ -132,30 +143,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
|
||||
ExportKeysDialog().show(this, object : ExportKeysDialog.ExportKeyDialogListener {
|
||||
override fun onPassphrase(passphrase: String) {
|
||||
showWaitingView()
|
||||
|
||||
KeysExporter(session)
|
||||
.export(this@KeysBackupSetupActivity,
|
||||
passphrase,
|
||||
uri,
|
||||
object : MatrixCallback<Boolean> {
|
||||
override fun onSuccess(data: Boolean) {
|
||||
if (data) {
|
||||
toast(getString(R.string.encryption_exported_successfully))
|
||||
Intent().apply {
|
||||
putExtra(MANUAL_EXPORT, true)
|
||||
}.let {
|
||||
setResult(Activity.RESULT_OK, it)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
hideWaitingView()
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
toast(failure.localizedMessage ?: getString(R.string.unexpected_error))
|
||||
hideWaitingView()
|
||||
}
|
||||
})
|
||||
export(passphrase, uri)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@ -165,6 +153,20 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun export(passphrase: String, uri: Uri) {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
keysExporter.export(passphrase, uri)
|
||||
toast(getString(R.string.encryption_exported_successfully))
|
||||
setResult(Activity.RESULT_OK, Intent().apply { putExtra(MANUAL_EXPORT, true) })
|
||||
finish()
|
||||
} catch (failure: Throwable) {
|
||||
toast(failure.localizedMessage ?: getString(R.string.unexpected_error))
|
||||
}
|
||||
hideWaitingView()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (viewModel.shouldPromptOnBack) {
|
||||
if (waitingView?.isVisible == true) {
|
||||
|
@ -20,6 +20,7 @@ package im.vector.app.features.settings
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
@ -60,6 +61,7 @@ import im.vector.app.features.raw.wellknown.isE2EByDefault
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import kotlinx.coroutines.launch
|
||||
import me.gujun.android.span.span
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.extensions.getFingerprintHumanReadable
|
||||
@ -75,6 +77,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val pinCodeStore: PinCodeStore,
|
||||
private val keysExporter: KeysExporter,
|
||||
private val navigator: Navigator
|
||||
) : VectorSettingsBaseFragment() {
|
||||
|
||||
@ -320,29 +323,24 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
|
||||
override fun onPassphrase(passphrase: String) {
|
||||
displayLoadingView()
|
||||
|
||||
KeysExporter(session)
|
||||
.export(requireContext(),
|
||||
passphrase,
|
||||
uri,
|
||||
object : MatrixCallback<Boolean> {
|
||||
override fun onSuccess(data: Boolean) {
|
||||
if (data) {
|
||||
requireActivity().toast(getString(R.string.encryption_exported_successfully))
|
||||
} else {
|
||||
requireActivity().toast(getString(R.string.unexpected_error))
|
||||
}
|
||||
hideLoadingView()
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
onCommonDone(failure.localizedMessage)
|
||||
}
|
||||
})
|
||||
export(passphrase, uri)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun export(passphrase: String, uri: Uri) {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
keysExporter.export(passphrase, uri)
|
||||
requireActivity().toast(getString(R.string.encryption_exported_successfully))
|
||||
} catch (failure: Throwable) {
|
||||
requireActivity().toast(errorFormatter.toHumanReadable(failure))
|
||||
}
|
||||
hideLoadingView()
|
||||
}
|
||||
}
|
||||
|
||||
private val pinActivityResultLauncher = registerStartForActivityResult {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
doOpenPinCodePreferenceScreen()
|
||||
|
@ -42,9 +42,7 @@ import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity
|
||||
import im.vector.app.features.crypto.recover.BootstrapBottomSheet
|
||||
import im.vector.app.features.crypto.recover.SetupMode
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO this needs to be refactored to current standard and remove legacy
|
||||
@ -113,31 +111,6 @@ class SignOutBottomSheetDialogFragment :
|
||||
views.setupMegolmBackupButton.action = {
|
||||
setupBackupActivityResultLauncher.launch(KeysBackupSetupActivity.intent(requireContext(), true))
|
||||
}
|
||||
|
||||
viewModel.observeViewEvents {
|
||||
when (it) {
|
||||
is SignoutCheckViewModel.ViewEvents.ExportKeys -> {
|
||||
it.exporter
|
||||
.export(requireContext(),
|
||||
it.passphrase,
|
||||
it.uri,
|
||||
object : MatrixCallback<Boolean> {
|
||||
override fun onSuccess(data: Boolean) {
|
||||
if (data) {
|
||||
viewModel.handle(SignoutCheckViewModel.Actions.KeySuccessfullyManuallyExported)
|
||||
} else {
|
||||
viewModel.handle(SignoutCheckViewModel.Actions.KeyExportFailed)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e("## Failed to export manually keys ${failure.localizedMessage}")
|
||||
viewModel.handle(SignoutCheckViewModel.Actions.KeyExportFailed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
|
@ -17,6 +17,7 @@
|
||||
package im.vector.app.features.workers.signout
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.airbnb.mvrx.ActivityViewModelContext
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
@ -27,13 +28,14 @@ import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.VectorViewEvents
|
||||
import im.vector.app.core.platform.EmptyViewEvents
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
import im.vector.app.features.crypto.keys.KeysExporter
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||
@ -41,6 +43,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_S
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener
|
||||
import org.matrix.android.sdk.rx.rx
|
||||
import timber.log.Timber
|
||||
|
||||
data class SignoutCheckViewState(
|
||||
val userId: String = "",
|
||||
@ -50,18 +53,15 @@ data class SignoutCheckViewState(
|
||||
val hasBeenExportedToFile: Async<Boolean> = Uninitialized
|
||||
) : MvRxState
|
||||
|
||||
class SignoutCheckViewModel @AssistedInject constructor(@Assisted initialState: SignoutCheckViewState,
|
||||
private val session: Session)
|
||||
: VectorViewModel<SignoutCheckViewState, SignoutCheckViewModel.Actions, SignoutCheckViewModel.ViewEvents>(initialState), KeysBackupStateListener {
|
||||
class SignoutCheckViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: SignoutCheckViewState,
|
||||
private val session: Session,
|
||||
private val keysExporter: KeysExporter
|
||||
) : VectorViewModel<SignoutCheckViewState, SignoutCheckViewModel.Actions, EmptyViewEvents>(initialState), KeysBackupStateListener {
|
||||
|
||||
sealed class Actions : VectorViewModelAction {
|
||||
data class ExportKeys(val passphrase: String, val uri: Uri) : Actions()
|
||||
object KeySuccessfullyManuallyExported : Actions()
|
||||
object KeyExportFailed : Actions()
|
||||
}
|
||||
|
||||
sealed class ViewEvents : VectorViewEvents {
|
||||
data class ExportKeys(val exporter: KeysExporter, val passphrase: String, val uri: Uri) : ViewEvents()
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
@ -128,22 +128,32 @@ class SignoutCheckViewModel @AssistedInject constructor(@Assisted initialState:
|
||||
|
||||
override fun handle(action: Actions) {
|
||||
when (action) {
|
||||
is Actions.ExportKeys -> {
|
||||
setState {
|
||||
copy(hasBeenExportedToFile = Loading())
|
||||
}
|
||||
_viewEvents.post(ViewEvents.ExportKeys(KeysExporter(session), action.passphrase, action.uri))
|
||||
}
|
||||
is Actions.ExportKeys -> handleExportKeys(action)
|
||||
Actions.KeySuccessfullyManuallyExported -> {
|
||||
setState {
|
||||
copy(hasBeenExportedToFile = Success(true))
|
||||
}
|
||||
}
|
||||
Actions.KeyExportFailed -> {
|
||||
setState {
|
||||
copy(hasBeenExportedToFile = Uninitialized)
|
||||
}
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleExportKeys(action: Actions.ExportKeys) {
|
||||
setState {
|
||||
copy(hasBeenExportedToFile = Loading())
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
val newState = try {
|
||||
keysExporter.export(action.passphrase, action.uri)
|
||||
Success(true)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e("## Failed to export manually keys ${failure.localizedMessage}")
|
||||
Uninitialized
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(hasBeenExportedToFile = newState)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user