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:
parent
b4eda5ea65
commit
beaed6b875
|
@ -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> {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue