Fix crash when saving redrafted media to drafts (#2502)

* fix crash when saving draft from redraft

* fix crash when saving draft from redraft

* replace ... with …
This commit is contained in:
Konrad Pozniak 2022-05-09 19:39:43 +02:00 committed by GitHub
parent b4eda5ea65
commit beaed6b875
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 87 additions and 41 deletions

View file

@ -967,8 +967,19 @@ class ComposeActivity :
}
private fun saveDraftAndFinish(contentText: String, contentWarning: String) {
viewModel.saveDraft(contentText, contentWarning)
finishWithoutSlideOutAnimation()
lifecycleScope.launch {
val dialog = if (viewModel.shouldShowSaveDraftDialog()) {
ProgressDialog.show(
this@ComposeActivity, null,
getString(R.string.saving_draft), true, false
)
} else {
null
}
viewModel.saveDraft(contentText, contentWarning)
dialog?.cancel()
finishWithoutSlideOutAnimation()
}
}
override fun search(token: String): List<ComposeAutoCompleteAdapter.AutocompleteResult> {

View file

@ -220,31 +220,36 @@ class ComposeViewModel @Inject constructor(
}
}
fun saveDraft(content: String, contentWarning: String) {
viewModelScope.launch {
val mediaUris: MutableList<String> = mutableListOf()
val mediaDescriptions: MutableList<String?> = mutableListOf()
media.value.forEach { item ->
mediaUris.add(item.uri.toString())
mediaDescriptions.add(item.description)
}
draftHelper.saveDraft(
draftId = draftId,
accountId = accountManager.activeAccount?.id!!,
inReplyToId = inReplyToId,
content = content,
contentWarning = contentWarning,
sensitive = markMediaAsSensitive.value!!,
visibility = statusVisibility.value!!,
mediaUris = mediaUris,
mediaDescriptions = mediaDescriptions,
poll = poll.value,
failedToSend = false
)
fun shouldShowSaveDraftDialog(): Boolean {
// if any of the media files need to be downloaded first it could take a while, so show a loading dialog
return media.value.any { mediaValue ->
mediaValue.uri.scheme == "https"
}
}
suspend fun saveDraft(content: String, contentWarning: String) {
val mediaUris: MutableList<String> = mutableListOf()
val mediaDescriptions: MutableList<String?> = mutableListOf()
media.value.forEach { item ->
mediaUris.add(item.uri.toString())
mediaDescriptions.add(item.description)
}
draftHelper.saveDraft(
draftId = draftId,
accountId = accountManager.activeAccount?.id!!,
inReplyToId = inReplyToId,
content = content,
contentWarning = contentWarning,
sensitive = markMediaAsSensitive.value!!,
visibility = statusVisibility.value!!,
mediaUris = mediaUris,
mediaDescriptions = mediaDescriptions,
poll = poll.value,
failedToSend = false
)
}
/**
* Send status to the server.
* Uses current state plus provided arguments.

View file

@ -30,7 +30,12 @@ import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.util.IOUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.buffer
import okio.sink
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@ -38,6 +43,7 @@ import javax.inject.Inject
class DraftHelper @Inject constructor(
val context: Context,
val okHttpClient: OkHttpClient,
db: AppDatabase
) {
@ -71,11 +77,11 @@ class DraftHelper @Inject constructor(
val uris = mediaUris.map { uriString ->
uriString.toUri()
}.map { uri ->
if (uri.isNotInFolder(draftDirectory)) {
uri.copyToFolder(draftDirectory)
} else {
}.mapNotNull { uri ->
if (uri.isInFolder(draftDirectory)) {
uri
} else {
uri.copyToFolder(draftDirectory)
}
}
@ -114,6 +120,7 @@ class DraftHelper @Inject constructor(
)
draftDao.insertOrReplace(draft)
Log.d("DraftHelper", "saved draft to db")
}
suspend fun deleteDraftAndAttachments(draftId: Int) {
@ -133,33 +140,55 @@ class DraftHelper @Inject constructor(
}
}
suspend fun deleteAttachments(draft: DraftEntity) {
withContext(Dispatchers.IO) {
draft.attachments.forEach { attachment ->
if (context.contentResolver.delete(attachment.uri, null, null) == 0) {
Log.e("DraftHelper", "Did not delete file ${attachment.uriString}")
}
suspend fun deleteAttachments(draft: DraftEntity) = withContext(Dispatchers.IO) {
draft.attachments.forEach { attachment ->
if (context.contentResolver.delete(attachment.uri, null, null) == 0) {
Log.e("DraftHelper", "Did not delete file ${attachment.uriString}")
}
}
}
private fun Uri.isNotInFolder(folder: File): Boolean {
private fun Uri.isInFolder(folder: File): Boolean {
val filePath = path ?: return true
return File(filePath).parentFile == folder
}
private fun Uri.copyToFolder(folder: File): Uri {
private fun Uri.copyToFolder(folder: File): Uri? {
val contentResolver = context.contentResolver
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
val mimeType = contentResolver.getType(this)
val map = MimeTypeMap.getSingleton()
val fileExtension = map.getExtensionFromMimeType(mimeType)
val fileExtension = if (scheme == "https") {
lastPathSegment?.substringAfterLast('.', "tmp")
} else {
val mimeType = contentResolver.getType(this)
val map = MimeTypeMap.getSingleton()
map.getExtensionFromMimeType(mimeType)
}
val filename = String.format("Tusky_Draft_Media_%s.%s", timeStamp, fileExtension)
val file = File(folder, filename)
IOUtils.copyToFile(contentResolver, this, file)
if (scheme == "https") {
// saving redrafted media
try {
val request = Request.Builder().url(toString()).build()
val response = okHttpClient.newCall(request).execute()
val sink = file.sink().buffer()
response.body?.source()?.use { input ->
sink.use { output ->
output.writeAll(input)
}
}
} catch (ex: IOException) {
Log.w("DraftHelper", "failed to save media", ex)
return null
}
} else {
IOUtils.copyToFile(contentResolver, this, file)
}
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", file)
}
}

View file

@ -640,5 +640,6 @@
<string name="action_unsubscribe_account">Unsubscribe</string>
<string name="tusky_compose_post_quicksetting_label">Compose Post</string>
<string name="saving_draft">Saving draft…</string>
</resources>