Skip to content

Commit

Permalink
Improve image share/download in ItemScreen
Browse files Browse the repository at this point in the history
* Downloaded image now appears in media gallery (fixes #226)

* Fix some shared images which could be corrupted

* Shared image preview now appears in share dialog

* Fix crash when no bitmap is fetched due to various errors
  • Loading branch information
Shinokuni committed Nov 23, 2024
1 parent 640744b commit b803058
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 16 deletions.
6 changes: 6 additions & 0 deletions app/src/main/java/com/readrops/app/item/ItemScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ class ItemScreen(
}
}

LaunchedEffect(state.error) {
if (state.error != null) {
snackbarHostState.showSnackbar(state.error!!)
}
}

if (state.itemWithFeed != null) {
val itemWithFeed = state.itemWithFeed!!
val item = itemWithFeed.item
Expand Down
57 changes: 41 additions & 16 deletions app/src/main/java/com/readrops/app/item/ItemScreenModel.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.readrops.app.item

import android.content.ClipData
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Environment
import androidx.compose.runtime.Stable
Expand All @@ -14,6 +16,7 @@ import coil3.imageLoader
import coil3.request.ImageRequest
import coil3.request.allowHardware
import coil3.toBitmap
import com.readrops.app.R
import com.readrops.app.repositories.BaseRepository
import com.readrops.app.util.Preferences
import com.readrops.db.Database
Expand All @@ -33,7 +36,7 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.get
import org.koin.core.parameter.parametersOf
import java.io.File
import java.io.FileOutputStream
import java.net.URI

@OptIn(ExperimentalCoroutinesApi::class)
class ItemScreenModel(
Expand Down Expand Up @@ -107,6 +110,7 @@ class ItemScreenModel(
action = Intent.ACTION_SEND
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, item.link)
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
}.also {
context.startActivity(Intent.createChooser(it, null))
}
Expand All @@ -132,55 +136,75 @@ class ItemScreenModel(
screenModelScope.launch(dispatcher) {
val bitmap = getImage(url, context)

if (bitmap == null) {
mutableState.update { it.copy(error = context.getString(R.string.error_image_download)) }
return@launch
}

val target = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
url.substringAfterLast('/')
)
FileOutputStream(target).apply {
bitmap.compress(Bitmap.CompressFormat.PNG, 90, this)
flush()
close()
).apply {
outputStream().apply {
bitmap.compress(Bitmap.CompressFormat.PNG, 90, this)
flush()
close()
}
}

MediaScannerConnection.scanFile(context, arrayOf(target.absolutePath), null, null)
mutableState.update { it.copy(fileDownloadedEvent = true) }
}
}

fun shareImage(url: String, context: Context) {
screenModelScope.launch(dispatcher) {
val bitmap = getImage(url, context)
if (bitmap == null) {
mutableState.update { it.copy(error = context.getString(R.string.error_image_download)) }
return@launch
}

val uri = saveImageInCache(bitmap, url, context)

Intent().apply {
action = Intent.ACTION_SEND
type = "image/*"

clipData = ClipData.newRawUri(null, uri)
putExtra(Intent.EXTRA_STREAM, uri)

type = "image/*"
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
}.also {
context.startActivity(Intent.createChooser(it, null))
}
}
}

private suspend fun getImage(url: String, context: Context): Bitmap {
private suspend fun getImage(url: String, context: Context): Bitmap? {
val downloader = context.imageLoader

return (downloader.execute(
val image = downloader.execute(
ImageRequest.Builder(context)
.data(url)
.allowHardware(false)
.build()
).image!!.toBitmap())
).image

return image?.toBitmap()
}

private fun saveImageInCache(bitmap: Bitmap, url: String, context: Context): Uri {
val imagesFolder = File(context.cacheDir.absolutePath, "images")
if (!imagesFolder.exists()) imagesFolder.mkdirs()

val image = File(imagesFolder, url.substringAfterLast('/'))
FileOutputStream(image).apply {
bitmap.compress(Bitmap.CompressFormat.PNG, 90, this)
flush()
close()
val name = URI.create(url).path.substringAfterLast('/')
val image = File(imagesFolder, name).apply {
outputStream().apply {
bitmap.compress(Bitmap.CompressFormat.PNG, 90, this)
flush()
close()
}
}

return FileProvider.getUriForFile(context, context.packageName, image)
Expand All @@ -194,5 +218,6 @@ data class ItemState(
val imageDialogUrl: String? = null,
val fileDownloadedEvent: Boolean = false,
val openInExternalBrowser: Boolean = false,
val theme: String? = ""
val theme: String? = "",
val error: String? = null
)
1 change: 1 addition & 0 deletions app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,5 @@
<string name="feed_already_exists">Le flux existe déjà</string>
<string name="invalid_folder">Dossier invalide</string>
<string name="invalid_feed">Flux invalide</string>
<string name="error_image_download">Une erreur s\'est produite lors du téléchargement de l\'image</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,5 @@
<string name="feed_already_exists">Feed already exists</string>
<string name="invalid_folder">Invalid folder</string>
<string name="invalid_feed">Invalid feed</string>
<string name="error_image_download">An error occurred while downloading the image</string>
</resources>

0 comments on commit b803058

Please sign in to comment.