|
|
@ -2,11 +2,9 @@ package org.thoughtcrime.securesms.loki.activities
|
|
|
|
|
|
|
|
|
|
|
|
import android.app.Activity
|
|
|
|
import android.app.Activity
|
|
|
|
import android.app.Application
|
|
|
|
import android.app.Application
|
|
|
|
import android.content.Context
|
|
|
|
|
|
|
|
import android.content.Intent
|
|
|
|
import android.content.Intent
|
|
|
|
import android.graphics.Typeface
|
|
|
|
import android.graphics.Typeface
|
|
|
|
import android.net.Uri
|
|
|
|
import android.net.Uri
|
|
|
|
import android.os.AsyncTask
|
|
|
|
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.provider.OpenableColumns
|
|
|
|
import android.provider.OpenableColumns
|
|
|
|
import android.text.Spannable
|
|
|
|
import android.text.Spannable
|
|
|
@ -15,13 +13,20 @@ import android.text.method.LinkMovementMethod
|
|
|
|
import android.text.style.ClickableSpan
|
|
|
|
import android.text.style.ClickableSpan
|
|
|
|
import android.text.style.StyleSpan
|
|
|
|
import android.text.style.StyleSpan
|
|
|
|
import android.view.View
|
|
|
|
import android.view.View
|
|
|
|
|
|
|
|
import android.view.inputmethod.InputMethodManager
|
|
|
|
import android.widget.Toast
|
|
|
|
import android.widget.Toast
|
|
|
|
|
|
|
|
import androidx.activity.result.ActivityResult
|
|
|
|
|
|
|
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
|
|
import androidx.activity.viewModels
|
|
|
|
import androidx.activity.viewModels
|
|
|
|
import androidx.core.widget.addTextChangedListener
|
|
|
|
import androidx.core.widget.addTextChangedListener
|
|
|
|
import androidx.databinding.DataBindingUtil
|
|
|
|
import androidx.databinding.DataBindingUtil
|
|
|
|
import androidx.lifecycle.AndroidViewModel
|
|
|
|
import androidx.lifecycle.AndroidViewModel
|
|
|
|
import androidx.lifecycle.MutableLiveData
|
|
|
|
import androidx.lifecycle.MutableLiveData
|
|
|
|
|
|
|
|
import androidx.lifecycle.viewModelScope
|
|
|
|
import com.google.android.gms.common.util.Strings
|
|
|
|
import com.google.android.gms.common.util.Strings
|
|
|
|
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
|
|
|
|
import kotlinx.coroutines.withContext
|
|
|
|
import network.loki.messenger.R
|
|
|
|
import network.loki.messenger.R
|
|
|
|
import network.loki.messenger.databinding.ActivityBackupRestoreBinding
|
|
|
|
import network.loki.messenger.databinding.ActivityBackupRestoreBinding
|
|
|
|
import org.thoughtcrime.securesms.ApplicationContext
|
|
|
|
import org.thoughtcrime.securesms.ApplicationContext
|
|
|
@ -31,94 +36,147 @@ import org.thoughtcrime.securesms.backup.FullBackupImporter.DatabaseDowngradeExc
|
|
|
|
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider
|
|
|
|
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider
|
|
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
|
|
import org.thoughtcrime.securesms.logging.Log
|
|
|
|
import org.thoughtcrime.securesms.logging.Log
|
|
|
|
|
|
|
|
import org.thoughtcrime.securesms.loki.utilities.fadeIn
|
|
|
|
|
|
|
|
import org.thoughtcrime.securesms.loki.utilities.fadeOut
|
|
|
|
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
|
|
|
|
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
|
|
|
|
import org.thoughtcrime.securesms.loki.utilities.show
|
|
|
|
import org.thoughtcrime.securesms.loki.utilities.show
|
|
|
|
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
|
|
|
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
|
|
|
|
|
|
|
import org.thoughtcrime.securesms.util.BackupUtil
|
|
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|
|
|
import java.io.IOException
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BackupRestoreActivity : BaseActionBarActivity() {
|
|
|
|
class BackupRestoreActivity : BaseActionBarActivity() {
|
|
|
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
companion object {
|
|
|
|
private const val TAG = "BackupRestoreActivity"
|
|
|
|
private const val TAG = "BackupRestoreActivity"
|
|
|
|
private const val REQUEST_CODE_BACKUP_FILE = 779955
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private val viewModel by viewModels<BackupRestoreViewModel>()
|
|
|
|
private val viewModel by viewModels<BackupRestoreViewModel>()
|
|
|
|
|
|
|
|
|
|
|
|
// region Lifecycle
|
|
|
|
private val fileSelectionResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()
|
|
|
|
|
|
|
|
) { result: ActivityResult ->
|
|
|
|
|
|
|
|
if (result.resultCode == Activity.RESULT_OK && result.data != null && result.data!!.data != null) {
|
|
|
|
|
|
|
|
viewModel.backupFile.value = result.data!!.data!!
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
setUpActionBarSessionLogo()
|
|
|
|
setUpActionBarSessionLogo()
|
|
|
|
val dataBinding = DataBindingUtil.setContentView<ActivityBackupRestoreBinding>(this, R.layout.activity_backup_restore)
|
|
|
|
|
|
|
|
dataBinding.lifecycleOwner = this
|
|
|
|
|
|
|
|
dataBinding.viewModel = viewModel
|
|
|
|
|
|
|
|
// setContentView(R.layout.activity_backup_restore)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dataBinding.restoreButton.setOnClickListener { restore() }
|
|
|
|
val viewBinding = DataBindingUtil.setContentView<ActivityBackupRestoreBinding>(this, R.layout.activity_backup_restore)
|
|
|
|
|
|
|
|
viewBinding.lifecycleOwner = this
|
|
|
|
|
|
|
|
viewBinding.viewModel = viewModel
|
|
|
|
|
|
|
|
|
|
|
|
dataBinding.buttonSelectFile.setOnClickListener {
|
|
|
|
viewBinding.restoreButton.setOnClickListener { viewModel.tryRestoreBackup() }
|
|
|
|
// Let user pick a file.
|
|
|
|
|
|
|
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
|
|
|
viewBinding.buttonSelectFile.setOnClickListener {
|
|
|
|
|
|
|
|
fileSelectionResultLauncher.launch(Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
|
|
|
|
|
|
|
//FIXME On some old APIs (tested on 21 & 23) the mime type doesn't filter properly
|
|
|
|
|
|
|
|
// and the backup files are unavailable for selection.
|
|
|
|
// type = BackupUtil.BACKUP_FILE_MIME_TYPE
|
|
|
|
// type = BackupUtil.BACKUP_FILE_MIME_TYPE
|
|
|
|
type = "*/*"
|
|
|
|
type = "*/*"
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
startActivityForResult(intent, REQUEST_CODE_BACKUP_FILE)
|
|
|
|
|
|
|
|
|
|
|
|
viewBinding.backupCode.addTextChangedListener { text -> viewModel.backupPassphrase.value = text.toString() }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Focus passphrase text edit when backup file is selected.
|
|
|
|
|
|
|
|
viewModel.backupFile.observe(this, { backupFile ->
|
|
|
|
|
|
|
|
if (backupFile != null) viewBinding.backupCode.post {
|
|
|
|
|
|
|
|
viewBinding.backupCode.requestFocus()
|
|
|
|
|
|
|
|
(getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager)
|
|
|
|
|
|
|
|
.showSoftInput(viewBinding.backupCode, InputMethodManager.SHOW_IMPLICIT)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
dataBinding.backupCode.addTextChangedListener { text -> viewModel.backupPassphrase.value = text.toString() }
|
|
|
|
// React to backup import result.
|
|
|
|
|
|
|
|
viewModel.backupImportResult.observe(this) { result ->
|
|
|
|
|
|
|
|
if (result != null) when (result) {
|
|
|
|
|
|
|
|
BackupRestoreViewModel.BackupImportResult.SUCCESS -> {
|
|
|
|
|
|
|
|
val intent = Intent(this, HomeActivity::class.java)
|
|
|
|
|
|
|
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
|
|
|
|
|
|
this.show(intent)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
BackupRestoreViewModel.BackupImportResult.FAILURE_VERSION_DOWNGRADE ->
|
|
|
|
|
|
|
|
Toast.makeText(this, R.string.RegistrationActivity_backup_failure_downgrade, Toast.LENGTH_LONG).show()
|
|
|
|
|
|
|
|
BackupRestoreViewModel.BackupImportResult.FAILURE_UNKNOWN ->
|
|
|
|
|
|
|
|
Toast.makeText(this, R.string.RegistrationActivity_incorrect_backup_passphrase, Toast.LENGTH_LONG).show()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//region Legal info views
|
|
|
|
//region Legal info views
|
|
|
|
val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy")
|
|
|
|
val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy")
|
|
|
|
termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
|
|
termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
|
|
termsExplanation.setSpan(object : ClickableSpan() {
|
|
|
|
termsExplanation.setSpan(object : ClickableSpan() {
|
|
|
|
|
|
|
|
|
|
|
|
override fun onClick(widget: View) {
|
|
|
|
override fun onClick(widget: View) {
|
|
|
|
openURL("https://getsession.org/terms-of-service/")
|
|
|
|
openURL("https://getsession.org/terms-of-service/")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
|
|
}, 40, 56, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
|
|
termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
|
|
termsExplanation.setSpan(StyleSpan(Typeface.BOLD), 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
|
|
termsExplanation.setSpan(object : ClickableSpan() {
|
|
|
|
termsExplanation.setSpan(object : ClickableSpan() {
|
|
|
|
|
|
|
|
|
|
|
|
override fun onClick(widget: View) {
|
|
|
|
override fun onClick(widget: View) {
|
|
|
|
openURL("https://getsession.org/privacy-policy/")
|
|
|
|
openURL("https://getsession.org/privacy-policy/")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
|
|
}, 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
|
|
dataBinding.termsTextView.movementMethod = LinkMovementMethod.getInstance()
|
|
|
|
viewBinding.termsTextView.movementMethod = LinkMovementMethod.getInstance()
|
|
|
|
dataBinding.termsTextView.text = termsExplanation
|
|
|
|
viewBinding.termsTextView.text = termsExplanation
|
|
|
|
//endregion
|
|
|
|
//endregion
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
|
|
private fun openURL(url: String) {
|
|
|
|
super.onActivityResult(requestCode, resultCode, data)
|
|
|
|
try {
|
|
|
|
|
|
|
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
|
|
|
|
|
|
startActivity(intent)
|
|
|
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
|
|
|
Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
when (requestCode) {
|
|
|
|
class BackupRestoreViewModel(application: Application): AndroidViewModel(application) {
|
|
|
|
REQUEST_CODE_BACKUP_FILE -> {
|
|
|
|
|
|
|
|
if (resultCode == Activity.RESULT_OK && data != null && data.data != null) {
|
|
|
|
|
|
|
|
// // Acquire persistent access permissions for the file selected.
|
|
|
|
|
|
|
|
// val persistentFlags: Int = data.flags and
|
|
|
|
|
|
|
|
// (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
|
|
|
|
|
|
// context.contentResolver.takePersistableUriPermission(data.data!!, persistentFlags)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
viewModel.onBackupFileSelected(data.data!!)
|
|
|
|
companion object {
|
|
|
|
}
|
|
|
|
private const val TAG = "BackupRestoreViewModel"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@JvmStatic
|
|
|
|
|
|
|
|
fun uriToFileName(view: View, fileUri: Uri?): String? {
|
|
|
|
|
|
|
|
fileUri ?: return null
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
view.context.contentResolver.query(fileUri, null, null, null, null).use {
|
|
|
|
|
|
|
|
val nameIndex = it!!.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
|
|
|
|
|
|
|
it.moveToFirst()
|
|
|
|
|
|
|
|
return it.getString(nameIndex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@JvmStatic
|
|
|
|
|
|
|
|
fun validateData(fileUri: Uri?, passphrase: String?): Boolean {
|
|
|
|
|
|
|
|
return fileUri != null &&
|
|
|
|
|
|
|
|
!Strings.isEmptyOrWhitespace(passphrase) &&
|
|
|
|
|
|
|
|
passphrase!!.length == BackupUtil.BACKUP_PASSPHRASE_LENGTH
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// endregion
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val backupFile = MutableLiveData<Uri>(null)
|
|
|
|
|
|
|
|
val backupPassphrase = MutableLiveData<String>(null)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val processingBackupFile = MutableLiveData<Boolean>(false)
|
|
|
|
|
|
|
|
val backupImportResult = MutableLiveData<BackupImportResult>(null)
|
|
|
|
|
|
|
|
|
|
|
|
// region Interaction
|
|
|
|
fun tryRestoreBackup() = viewModelScope.launch {
|
|
|
|
private fun restore() {
|
|
|
|
if (backupImportResult.value == BackupImportResult.SUCCESS) return@launch
|
|
|
|
if (viewModel.backupFile.value == null && Strings.isEmptyOrWhitespace(viewModel.backupPassphrase.value)) return
|
|
|
|
if (!validateData(backupFile.value, backupPassphrase.value)) return@launch
|
|
|
|
|
|
|
|
|
|
|
|
val backupFile = viewModel.backupFile.value!!
|
|
|
|
val context = getApplication<Application>()
|
|
|
|
val passphrase = viewModel.backupPassphrase.value!!.trim()
|
|
|
|
val backupFile = backupFile.value!!
|
|
|
|
|
|
|
|
val passphrase = backupPassphrase.value!!
|
|
|
|
|
|
|
|
|
|
|
|
object : AsyncTask<Void?, Void?, BackupImportResult>() {
|
|
|
|
val result: BackupImportResult
|
|
|
|
override fun doInBackground(vararg params: Void?): BackupImportResult {
|
|
|
|
|
|
|
|
return try {
|
|
|
|
processingBackupFile.value = true
|
|
|
|
val context: Context = this@BackupRestoreActivity
|
|
|
|
|
|
|
|
|
|
|
|
withContext(Dispatchers.IO) {
|
|
|
|
|
|
|
|
result = try {
|
|
|
|
val database = DatabaseFactory.getBackupDatabase(context)
|
|
|
|
val database = DatabaseFactory.getBackupDatabase(context)
|
|
|
|
FullBackupImporter.importFromUri(
|
|
|
|
FullBackupImporter.importFromUri(
|
|
|
|
context,
|
|
|
|
context,
|
|
|
@ -130,21 +188,6 @@ class BackupRestoreActivity : BaseActionBarActivity() {
|
|
|
|
DatabaseFactory.upgradeRestored(context, database)
|
|
|
|
DatabaseFactory.upgradeRestored(context, database)
|
|
|
|
NotificationChannels.restoreContactNotificationChannels(context)
|
|
|
|
NotificationChannels.restoreContactNotificationChannels(context)
|
|
|
|
TextSecurePreferences.setRestorationTime(context, System.currentTimeMillis())
|
|
|
|
TextSecurePreferences.setRestorationTime(context, System.currentTimeMillis())
|
|
|
|
|
|
|
|
|
|
|
|
BackupImportResult.SUCCESS
|
|
|
|
|
|
|
|
} catch (e: DatabaseDowngradeException) {
|
|
|
|
|
|
|
|
Log.w(TAG, "Failed due to the backup being from a newer version of Signal.", e)
|
|
|
|
|
|
|
|
BackupImportResult.FAILURE_VERSION_DOWNGRADE
|
|
|
|
|
|
|
|
} catch (e: IOException) {
|
|
|
|
|
|
|
|
Log.w(TAG, e)
|
|
|
|
|
|
|
|
BackupImportResult.FAILURE_UNKNOWN
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun onPostExecute(result: BackupImportResult) {
|
|
|
|
|
|
|
|
val context = this@BackupRestoreActivity
|
|
|
|
|
|
|
|
when (result) {
|
|
|
|
|
|
|
|
BackupImportResult.SUCCESS -> {
|
|
|
|
|
|
|
|
TextSecurePreferences.setHasViewedSeed(context, true)
|
|
|
|
TextSecurePreferences.setHasViewedSeed(context, true)
|
|
|
|
TextSecurePreferences.setHasSeenWelcomeScreen(context, true)
|
|
|
|
TextSecurePreferences.setHasSeenWelcomeScreen(context, true)
|
|
|
|
TextSecurePreferences.setPromptedPushRegistration(context, true)
|
|
|
|
TextSecurePreferences.setPromptedPushRegistration(context, true)
|
|
|
@ -156,60 +199,22 @@ class BackupRestoreActivity : BaseActionBarActivity() {
|
|
|
|
|
|
|
|
|
|
|
|
HomeActivity.requestResetAllSessionsOnStartup(context)
|
|
|
|
HomeActivity.requestResetAllSessionsOnStartup(context)
|
|
|
|
|
|
|
|
|
|
|
|
val intent = Intent(context, HomeActivity::class.java)
|
|
|
|
BackupImportResult.SUCCESS
|
|
|
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
|
|
} catch (e: DatabaseDowngradeException) {
|
|
|
|
show(intent)
|
|
|
|
Log.w(TAG, "Failed due to the backup being from a newer version of Signal.", e)
|
|
|
|
}
|
|
|
|
BackupImportResult.FAILURE_VERSION_DOWNGRADE
|
|
|
|
BackupImportResult.FAILURE_VERSION_DOWNGRADE ->
|
|
|
|
|
|
|
|
Toast.makeText(context, R.string.RegistrationActivity_backup_failure_downgrade, Toast.LENGTH_LONG).show()
|
|
|
|
|
|
|
|
BackupImportResult.FAILURE_UNKNOWN ->
|
|
|
|
|
|
|
|
Toast.makeText(context, R.string.RegistrationActivity_incorrect_backup_passphrase, Toast.LENGTH_LONG).show()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}.execute()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun openURL(url: String) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
|
|
|
|
|
|
startActivity(intent)
|
|
|
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
} catch (e: Exception) {
|
|
|
|
Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show()
|
|
|
|
Log.w(TAG, e)
|
|
|
|
}
|
|
|
|
BackupImportResult.FAILURE_UNKNOWN
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum class BackupImportResult {
|
|
|
|
|
|
|
|
SUCCESS, FAILURE_VERSION_DOWNGRADE, FAILURE_UNKNOWN
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// endregion
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class BackupRestoreViewModel(application: Application): AndroidViewModel(application) {
|
|
|
|
processingBackupFile.value = false
|
|
|
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|
|
|
|
@JvmStatic
|
|
|
|
|
|
|
|
fun uriToFileName(view: View, fileUri: Uri?): String? {
|
|
|
|
|
|
|
|
fileUri ?: return null
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
view.context.contentResolver.query(fileUri, null, null, null, null).use {
|
|
|
|
backupImportResult.value = result
|
|
|
|
val nameIndex = it!!.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
|
|
|
|
|
|
|
it.moveToFirst()
|
|
|
|
|
|
|
|
return it.getString(nameIndex)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@JvmStatic
|
|
|
|
enum class BackupImportResult {
|
|
|
|
fun validateData(fileUri: Uri?, passphrase: String?): Boolean {
|
|
|
|
SUCCESS, FAILURE_VERSION_DOWNGRADE, FAILURE_UNKNOWN
|
|
|
|
return fileUri != null && !Strings.isEmptyOrWhitespace(passphrase)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val backupFile = MutableLiveData<Uri>()
|
|
|
|
|
|
|
|
val backupPassphrase = MutableLiveData<String>("000000000000000000000000000000")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun onBackupFileSelected(backupFile: Uri) {
|
|
|
|
|
|
|
|
//TODO Check if backup file is correct.
|
|
|
|
|
|
|
|
this.backupFile.value = backupFile
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|