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) {
|
private fun saveDraftAndFinish(contentText: String, contentWarning: String) {
|
||||||
viewModel.saveDraft(contentText, contentWarning)
|
lifecycleScope.launch {
|
||||||
finishWithoutSlideOutAnimation()
|
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> {
|
override fun search(token: String): List<ComposeAutoCompleteAdapter.AutocompleteResult> {
|
||||||
|
|
|
@ -220,31 +220,36 @@ 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
|
||||||
val mediaUris: MutableList<String> = mutableListOf()
|
return media.value.any { mediaValue ->
|
||||||
val mediaDescriptions: MutableList<String?> = mutableListOf()
|
mediaValue.uri.scheme == "https"
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
* Send status to the server.
|
||||||
* Uses current state plus provided arguments.
|
* Uses current state plus provided arguments.
|
||||||
|
|
|
@ -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 mimeType = contentResolver.getType(this)
|
val fileExtension = if (scheme == "https") {
|
||||||
val map = MimeTypeMap.getSingleton()
|
lastPathSegment?.substringAfterLast('.', "tmp")
|
||||||
val fileExtension = map.getExtensionFromMimeType(mimeType)
|
} 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 filename = String.format("Tusky_Draft_Media_%s.%s", timeStamp, fileExtension)
|
||||||
val file = File(folder, filename)
|
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)
|
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", 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>
|
||||||
|
|
Loading…
Reference in New Issue