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,9 +967,20 @@ class ComposeActivity :
} }
private fun saveDraftAndFinish(contentText: String, contentWarning: String) { private fun saveDraftAndFinish(contentText: String, contentWarning: String) {
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) viewModel.saveDraft(contentText, contentWarning)
dialog?.cancel()
finishWithoutSlideOutAnimation() finishWithoutSlideOutAnimation()
} }
}
override fun search(token: String): List<ComposeAutoCompleteAdapter.AutocompleteResult> { override fun search(token: String): List<ComposeAutoCompleteAdapter.AutocompleteResult> {
return viewModel.searchAutocompleteSuggestions(token) return viewModel.searchAutocompleteSuggestions(token)

View file

@ -220,8 +220,14 @@ class ComposeViewModel @Inject constructor(
} }
} }
fun saveDraft(content: String, contentWarning: String) { fun shouldShowSaveDraftDialog(): Boolean {
viewModelScope.launch { // 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 mediaUris: MutableList<String> = mutableListOf()
val mediaDescriptions: MutableList<String?> = mutableListOf() val mediaDescriptions: MutableList<String?> = mutableListOf()
media.value.forEach { item -> media.value.forEach { item ->
@ -243,7 +249,6 @@ class ComposeViewModel @Inject constructor(
failedToSend = false failedToSend = false
) )
} }
}
/** /**
* Send status to the server. * Send status to the server.

View file

@ -30,7 +30,12 @@ import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.util.IOUtils import com.keylesspalace.tusky.util.IOUtils
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.buffer
import okio.sink
import java.io.File import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@ -38,6 +43,7 @@ import javax.inject.Inject
class DraftHelper @Inject constructor( class DraftHelper @Inject constructor(
val context: Context, val context: Context,
val okHttpClient: OkHttpClient,
db: AppDatabase db: AppDatabase
) { ) {
@ -71,11 +77,11 @@ class DraftHelper @Inject constructor(
val uris = mediaUris.map { uriString -> val uris = mediaUris.map { uriString ->
uriString.toUri() uriString.toUri()
}.map { uri -> }.mapNotNull { uri ->
if (uri.isNotInFolder(draftDirectory)) { if (uri.isInFolder(draftDirectory)) {
uri.copyToFolder(draftDirectory)
} else {
uri uri
} else {
uri.copyToFolder(draftDirectory)
} }
} }
@ -114,6 +120,7 @@ class DraftHelper @Inject constructor(
) )
draftDao.insertOrReplace(draft) draftDao.insertOrReplace(draft)
Log.d("DraftHelper", "saved draft to db")
} }
suspend fun deleteDraftAndAttachments(draftId: Int) { suspend fun deleteDraftAndAttachments(draftId: Int) {
@ -133,33 +140,55 @@ class DraftHelper @Inject constructor(
} }
} }
suspend fun deleteAttachments(draft: DraftEntity) { suspend fun deleteAttachments(draft: DraftEntity) = withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) {
draft.attachments.forEach { attachment -> draft.attachments.forEach { attachment ->
if (context.contentResolver.delete(attachment.uri, null, null) == 0) { if (context.contentResolver.delete(attachment.uri, null, null) == 0) {
Log.e("DraftHelper", "Did not delete file ${attachment.uriString}") 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 val filePath = path ?: return true
return File(filePath).parentFile == folder return File(filePath).parentFile == folder
} }
private fun Uri.copyToFolder(folder: File): Uri { private fun Uri.copyToFolder(folder: File): Uri? {
val contentResolver = context.contentResolver val contentResolver = context.contentResolver
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date()) val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
val fileExtension = if (scheme == "https") {
lastPathSegment?.substringAfterLast('.', "tmp")
} else {
val mimeType = contentResolver.getType(this) val mimeType = contentResolver.getType(this)
val map = MimeTypeMap.getSingleton() val map = MimeTypeMap.getSingleton()
val fileExtension = map.getExtensionFromMimeType(mimeType) map.getExtensionFromMimeType(mimeType)
}
val filename = String.format("Tusky_Draft_Media_%s.%s", timeStamp, fileExtension) val filename = String.format("Tusky_Draft_Media_%s.%s", timeStamp, fileExtension)
val file = File(folder, filename) val file = File(folder, filename)
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) IOUtils.copyToFile(contentResolver, this, file)
}
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", 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="action_unsubscribe_account">Unsubscribe</string>
<string name="tusky_compose_post_quicksetting_label">Compose Post</string> <string name="tusky_compose_post_quicksetting_label">Compose Post</string>
<string name="saving_draft">Saving draft…</string>
</resources> </resources>