mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-02 12:16:55 +01:00
Merge pull request #1661 from vector-im/feature/create_file_intent
Feature/create file intent
This commit is contained in:
commit
25bbe9c3d6
@ -13,6 +13,7 @@ Improvements 🙌:
|
||||
Bugfix 🐛:
|
||||
- Regression | Share action menu do not work (#1647)
|
||||
- verification issues on transition (#1555)
|
||||
- Fix issue when restoring keys backup using recovery key
|
||||
|
||||
Translations 🗣:
|
||||
-
|
||||
@ -25,6 +26,7 @@ Build 🧱:
|
||||
- Revert to build-tools 3.5.3
|
||||
|
||||
Other changes:
|
||||
- Use Intent.ACTION_CREATE_DOCUMENT to save megolm key or recovery key in a txt file
|
||||
- Use `Context#withStyledAttributes` extension function (#1546)
|
||||
|
||||
Changes in Riot.imX 0.91.4 (2020-07-06)
|
||||
|
@ -16,13 +16,12 @@
|
||||
|
||||
package im.vector.riotx.core.extensions
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.app.Activity
|
||||
import android.os.Parcelable
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.core.utils.toast
|
||||
import im.vector.riotx.core.utils.selectTxtFileToWrite
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
@ -98,27 +97,25 @@ fun Fragment.getAllChildFragments(): List<Fragment> {
|
||||
const val POP_BACK_STACK_EXCLUSIVE = 0
|
||||
|
||||
fun Fragment.queryExportKeys(userId: String, requestCode: Int) {
|
||||
// We need WRITE_EXTERNAL permission
|
||||
// if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES,
|
||||
// this,
|
||||
// PERMISSION_REQUEST_CODE_EXPORT_KEYS,
|
||||
// R.string.permissions_rationale_msg_keys_backup_export)) {
|
||||
// WRITE permissions are not needed
|
||||
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).let {
|
||||
it.format(Date())
|
||||
}
|
||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.type = "text/plain"
|
||||
intent.putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
"riot-megolm-export-$userId-$timestamp.txt"
|
||||
)
|
||||
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
||||
|
||||
try {
|
||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.keys_backup_setup_step1_manual_export)), requestCode)
|
||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||
activity?.toast(R.string.error_no_external_application_found)
|
||||
}
|
||||
// }
|
||||
selectTxtFileToWrite(
|
||||
activity = requireActivity(),
|
||||
fragment = this,
|
||||
defaultFileName = "riot-megolm-export-$userId-$timestamp.txt",
|
||||
chooserHint = getString(R.string.keys_backup_setup_step1_manual_export),
|
||||
requestCode = requestCode
|
||||
)
|
||||
}
|
||||
|
||||
fun Activity.queryExportKeys(userId: String, requestCode: Int) {
|
||||
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
||||
|
||||
selectTxtFileToWrite(
|
||||
activity = this,
|
||||
fragment = null,
|
||||
defaultFileName = "riot-megolm-export-$userId-$timestamp.txt",
|
||||
chooserHint = getString(R.string.keys_backup_setup_step1_manual_export),
|
||||
requestCode = requestCode
|
||||
)
|
||||
}
|
||||
|
@ -424,6 +424,33 @@ fun openPlayStore(activity: Activity, appId: String = BuildConfig.APPLICATION_ID
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the user to select a location and a file name to write in
|
||||
*/
|
||||
fun selectTxtFileToWrite(
|
||||
activity: Activity,
|
||||
fragment: Fragment?,
|
||||
defaultFileName: String,
|
||||
chooserHint: String,
|
||||
requestCode: Int
|
||||
) {
|
||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.type = "text/plain"
|
||||
intent.putExtra(Intent.EXTRA_TITLE, defaultFileName)
|
||||
|
||||
try {
|
||||
val chooserIntent = Intent.createChooser(intent, chooserHint)
|
||||
if (fragment != null) {
|
||||
fragment.startActivityForResult(chooserIntent, requestCode)
|
||||
} else {
|
||||
activity.startActivityForResult(chooserIntent, requestCode)
|
||||
}
|
||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||
activity.toast(R.string.error_no_external_application_found)
|
||||
}
|
||||
}
|
||||
|
||||
// ==============================================================================================================
|
||||
// Media utils
|
||||
// ==============================================================================================================
|
||||
|
@ -63,7 +63,6 @@ const val PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA = 569
|
||||
const val PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA = 570
|
||||
const val PERMISSION_REQUEST_CODE_AUDIO_CALL = 571
|
||||
const val PERMISSION_REQUEST_CODE_VIDEO_CALL = 572
|
||||
const val PERMISSION_REQUEST_CODE_EXPORT_KEYS = 573
|
||||
const val PERMISSION_REQUEST_CODE_CHANGE_AVATAR = 574
|
||||
const val PERMISSION_REQUEST_CODE_DOWNLOAD_FILE = 575
|
||||
const val PERMISSION_REQUEST_CODE_PICK_ATTACHMENT = 576
|
||||
|
@ -49,7 +49,7 @@ class KeysBackupRestoreFromKeyViewModel @Inject constructor(
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val recoveryKey = recoveryCode.value!!
|
||||
try {
|
||||
sharedViewModel.recoverUsingBackupPass(recoveryKey)
|
||||
sharedViewModel.recoverUsingBackupRecoveryKey(recoveryKey)
|
||||
} catch (failure: Throwable) {
|
||||
recoveryCodeErrorText.postValue(stringProvider.getString(R.string.keys_backup_recovery_code_error_decrypt))
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
package im.vector.riotx.features.crypto.keysbackup.setup
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
@ -27,12 +26,9 @@ import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.dialogs.ExportKeysDialog
|
||||
import im.vector.riotx.core.extensions.observeEvent
|
||||
import im.vector.riotx.core.extensions.queryExportKeys
|
||||
import im.vector.riotx.core.extensions.replaceFragment
|
||||
import im.vector.riotx.core.platform.SimpleFragmentActivity
|
||||
import im.vector.riotx.core.utils.PERMISSIONS_FOR_WRITING_FILES
|
||||
import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_EXPORT_KEYS
|
||||
import im.vector.riotx.core.utils.allGranted
|
||||
import im.vector.riotx.core.utils.checkPermissions
|
||||
import im.vector.riotx.core.utils.toast
|
||||
import im.vector.riotx.features.crypto.keys.KeysExporter
|
||||
|
||||
@ -97,7 +93,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
|
||||
.show()
|
||||
}
|
||||
KeysBackupSetupSharedViewModel.NAVIGATE_MANUAL_EXPORT -> {
|
||||
exportKeysManually()
|
||||
queryExportKeys(session.myUserId, REQUEST_CODE_SAVE_MEGOLM_EXPORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,38 +125,6 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
|
||||
})
|
||||
}
|
||||
|
||||
private fun exportKeysManually() {
|
||||
if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES,
|
||||
this,
|
||||
PERMISSION_REQUEST_CODE_EXPORT_KEYS,
|
||||
R.string.permissions_rationale_msg_keys_backup_export)) {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.type = "text/plain"
|
||||
intent.putExtra(Intent.EXTRA_TITLE, "riot-megolm-export-${session.myUserId}-${System.currentTimeMillis()}.txt")
|
||||
|
||||
startActivityForResult(
|
||||
Intent.createChooser(
|
||||
intent,
|
||||
getString(R.string.keys_backup_setup_step1_manual_export)
|
||||
),
|
||||
REQUEST_CODE_SAVE_MEGOLM_EXPORT
|
||||
)
|
||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||
toast(R.string.error_no_external_application_found)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
if (allGranted(grantResults)) {
|
||||
if (requestCode == PERMISSION_REQUEST_CODE_EXPORT_KEYS) {
|
||||
exportKeysManually()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == REQUEST_CODE_SAVE_MEGOLM_EXPORT) {
|
||||
val uri = data?.data
|
||||
|
@ -48,6 +48,9 @@ class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() {
|
||||
|
||||
lateinit var session: Session
|
||||
|
||||
val userId: String
|
||||
get() = session.myUserId
|
||||
|
||||
var showManualExport: MutableLiveData<Boolean> = MutableLiveData()
|
||||
|
||||
var navigateEvent: MutableLiveData<LiveEvent<String>> = MutableLiveData()
|
||||
|
@ -15,8 +15,10 @@
|
||||
*/
|
||||
package im.vector.riotx.features.crypto.keysbackup.setup
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
@ -29,25 +31,27 @@ import butterknife.BindView
|
||||
import butterknife.OnClick
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.files.addEntryToDownloadManager
|
||||
import im.vector.riotx.core.files.writeToFile
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.core.utils.LiveEvent
|
||||
import im.vector.riotx.core.utils.PERMISSIONS_FOR_WRITING_FILES
|
||||
import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_EXPORT_KEYS
|
||||
import im.vector.riotx.core.utils.allGranted
|
||||
import im.vector.riotx.core.utils.checkPermissions
|
||||
import im.vector.riotx.core.utils.copyToClipboard
|
||||
import im.vector.riotx.core.utils.selectTxtFileToWrite
|
||||
import im.vector.riotx.core.utils.startSharePlainTextIntent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() {
|
||||
|
||||
companion object {
|
||||
private const val SAVE_RECOVERY_KEY_REQUEST_CODE = 2754
|
||||
}
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step3
|
||||
|
||||
@BindView(R.id.keys_backup_setup_step3_button)
|
||||
@ -130,15 +134,15 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment()
|
||||
}
|
||||
|
||||
dialog.findViewById<View>(R.id.keys_backup_setup_save)?.setOnClickListener {
|
||||
val permissionsChecked = checkPermissions(
|
||||
PERMISSIONS_FOR_WRITING_FILES,
|
||||
this,
|
||||
PERMISSION_REQUEST_CODE_EXPORT_KEYS,
|
||||
R.string.permissions_rationale_msg_keys_backup_export
|
||||
val userId = viewModel.userId
|
||||
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
||||
selectTxtFileToWrite(
|
||||
activity = requireActivity(),
|
||||
fragment = this,
|
||||
defaultFileName = "recovery-key-$userId-$timestamp.txt",
|
||||
chooserHint = getString(R.string.save_recovery_key_chooser_hint),
|
||||
requestCode = SAVE_RECOVERY_KEY_REQUEST_CODE
|
||||
)
|
||||
if (permissionsChecked) {
|
||||
exportRecoveryKeyToFile(recoveryKey)
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
@ -163,34 +167,33 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment()
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportRecoveryKeyToFile(data: String) {
|
||||
private fun exportRecoveryKeyToFile(uri: Uri, data: String) {
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
Try {
|
||||
withContext(Dispatchers.IO) {
|
||||
val parentDir = context?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
|
||||
val file = File(parentDir, "recovery-key-" + System.currentTimeMillis() + ".txt")
|
||||
|
||||
writeToFile(data, file)
|
||||
|
||||
addEntryToDownloadManager(requireContext(), file, "text/plain")
|
||||
|
||||
file.absolutePath
|
||||
requireContext().contentResolver.openOutputStream(uri)
|
||||
?.use { os ->
|
||||
os.write(data.toByteArray())
|
||||
os.flush()
|
||||
}
|
||||
}
|
||||
?: throw IOException("Unable to write the file")
|
||||
}
|
||||
.fold(
|
||||
{ throwable ->
|
||||
context?.let {
|
||||
activity?.let {
|
||||
AlertDialog.Builder(it)
|
||||
.setTitle(R.string.dialog_title_error)
|
||||
.setMessage(throwable.localizedMessage)
|
||||
.setMessage(errorFormatter.toHumanReadable(throwable))
|
||||
|
||||
}
|
||||
},
|
||||
{ path ->
|
||||
{
|
||||
viewModel.copyHasBeenMade = true
|
||||
|
||||
context?.let {
|
||||
activity?.let {
|
||||
AlertDialog.Builder(it)
|
||||
.setMessage(getString(R.string.recovery_key_export_saved_as_warning, path))
|
||||
.setTitle(R.string.dialog_title_success)
|
||||
.setMessage(R.string.recovery_key_export_saved)
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -200,11 +203,14 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
if (allGranted(grantResults)) {
|
||||
if (requestCode == PERMISSION_REQUEST_CODE_EXPORT_KEYS) {
|
||||
viewModel.recoveryKey.value?.let {
|
||||
exportRecoveryKeyToFile(it)
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
when (requestCode) {
|
||||
SAVE_RECOVERY_KEY_REQUEST_CODE -> {
|
||||
val uri = data?.data
|
||||
if (resultCode == Activity.RESULT_OK && uri != null) {
|
||||
viewModel.recoveryKey.value?.let {
|
||||
exportRecoveryKeyToFile(uri, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,8 +42,6 @@ import im.vector.riotx.core.intent.analyseIntent
|
||||
import im.vector.riotx.core.intent.getFilenameFromUri
|
||||
import im.vector.riotx.core.platform.SimpleTextWatcher
|
||||
import im.vector.riotx.core.preference.VectorPreference
|
||||
import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_EXPORT_KEYS
|
||||
import im.vector.riotx.core.utils.allGranted
|
||||
import im.vector.riotx.core.utils.openFileSelection
|
||||
import im.vector.riotx.core.utils.toast
|
||||
import im.vector.riotx.features.crypto.keys.KeysExporter
|
||||
@ -142,14 +140,6 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
|
||||
mCrossSigningStatePreference.isVisible = true
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
if (allGranted(grantResults)) {
|
||||
if (requestCode == PERMISSION_REQUEST_CODE_EXPORT_KEYS) {
|
||||
queryExportKeys(activeSessionHolder.getSafeActiveSession()?.myUserId ?: "", REQUEST_CODE_SAVE_MEGOLM_EXPORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == REQUEST_CODE_SAVE_MEGOLM_EXPORT) {
|
||||
|
@ -126,6 +126,7 @@
|
||||
<string name="dialog_title_confirmation">Confirmation</string>
|
||||
<string name="dialog_title_warning">Warning</string>
|
||||
<string name="dialog_title_error">Error</string>
|
||||
<string name="dialog_title_success">Success</string>
|
||||
|
||||
<!-- Bottom navigation buttons -->
|
||||
<string name="bottom_action_home">Home</string>
|
||||
@ -1413,6 +1414,7 @@ Why choose Riot.im?
|
||||
<string name="keys_backup_setup_step3_share_recovery_file">Share</string>
|
||||
<string name="keys_backup_setup_step3_save_button_title">Save as File</string>
|
||||
<string name="recovery_key_export_saved_as_warning">The recovery key has been saved to \'%s\'.\n\nWarning: this file may be deleted if the application is uninstalled.</string>
|
||||
<string name="recovery_key_export_saved">The recovery key has been saved.</string>
|
||||
|
||||
<string name="keys_backup_setup_override_backup_prompt_tile">A backup already exist on your HomeServer</string>
|
||||
<string name="keys_backup_setup_override_backup_prompt_description">It looks like you already have setup key backup from another session. Do you want to replace it with the one you’re creating?</string>
|
||||
@ -2526,4 +2528,6 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
|
||||
<string name="crypto_error_withheld_unverified">You cannot access this message because your session is not trusted by the sender</string>
|
||||
<string name="crypto_error_withheld_generic">You cannot access this message because the sender purposely did not send the keys</string>
|
||||
<string name="notice_crypto_unable_to_decrypt_merged">Waiting for encryption history</string>
|
||||
|
||||
<string name="save_recovery_key_chooser_hint">Save recovery key in</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user