mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-25 02:45:53 +03:00
We do not need write storage permission to create a Txt file with the intent Intent.ACTION_CREATE_DOCUMENT
This commit is contained in:
parent
4741169cc7
commit
4387fd3327
9 changed files with 94 additions and 105 deletions
|
@ -25,7 +25,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
|
||||
|
||||
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
|
||||
|
|
|
@ -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,19 +167,19 @@ 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()
|
||||
}
|
||||
}?.let {
|
||||
uri.toString()
|
||||
}
|
||||
?: throw IOException()
|
||||
}
|
||||
.fold(
|
||||
{ throwable ->
|
||||
|
@ -200,11 +204,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) {
|
||||
|
|
|
@ -2526,4 +2526,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…
Reference in a new issue