mirror of https://github.com/readrops/Readrops.git
Improve image share/download in ItemScreen
* 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
This commit is contained in:
parent
640744bf6d
commit
b803058210
|
@ -135,6 +135,12 @@ class ItemScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(state.error) {
|
||||||
|
if (state.error != null) {
|
||||||
|
snackbarHostState.showSnackbar(state.error!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (state.itemWithFeed != null) {
|
if (state.itemWithFeed != null) {
|
||||||
val itemWithFeed = state.itemWithFeed!!
|
val itemWithFeed = state.itemWithFeed!!
|
||||||
val item = itemWithFeed.item
|
val item = itemWithFeed.item
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package com.readrops.app.item
|
package com.readrops.app.item
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import android.media.MediaScannerConnection
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
|
@ -14,6 +16,7 @@ import coil3.imageLoader
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import coil3.request.allowHardware
|
import coil3.request.allowHardware
|
||||||
import coil3.toBitmap
|
import coil3.toBitmap
|
||||||
|
import com.readrops.app.R
|
||||||
import com.readrops.app.repositories.BaseRepository
|
import com.readrops.app.repositories.BaseRepository
|
||||||
import com.readrops.app.util.Preferences
|
import com.readrops.app.util.Preferences
|
||||||
import com.readrops.db.Database
|
import com.readrops.db.Database
|
||||||
|
@ -33,7 +36,7 @@ import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.get
|
import org.koin.core.component.get
|
||||||
import org.koin.core.parameter.parametersOf
|
import org.koin.core.parameter.parametersOf
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.net.URI
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
class ItemScreenModel(
|
class ItemScreenModel(
|
||||||
|
@ -107,6 +110,7 @@ class ItemScreenModel(
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
type = "text/plain"
|
type = "text/plain"
|
||||||
putExtra(Intent.EXTRA_TEXT, item.link)
|
putExtra(Intent.EXTRA_TEXT, item.link)
|
||||||
|
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
}.also {
|
}.also {
|
||||||
context.startActivity(Intent.createChooser(it, null))
|
context.startActivity(Intent.createChooser(it, null))
|
||||||
}
|
}
|
||||||
|
@ -132,16 +136,23 @@ class ItemScreenModel(
|
||||||
screenModelScope.launch(dispatcher) {
|
screenModelScope.launch(dispatcher) {
|
||||||
val bitmap = getImage(url, context)
|
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(
|
val target = File(
|
||||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||||
url.substringAfterLast('/')
|
url.substringAfterLast('/')
|
||||||
)
|
).apply {
|
||||||
FileOutputStream(target).apply {
|
outputStream().apply {
|
||||||
bitmap.compress(Bitmap.CompressFormat.PNG, 90, this)
|
bitmap.compress(Bitmap.CompressFormat.PNG, 90, this)
|
||||||
flush()
|
flush()
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaScannerConnection.scanFile(context, arrayOf(target.absolutePath), null, null)
|
||||||
mutableState.update { it.copy(fileDownloadedEvent = true) }
|
mutableState.update { it.copy(fileDownloadedEvent = true) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,39 +160,52 @@ class ItemScreenModel(
|
||||||
fun shareImage(url: String, context: Context) {
|
fun shareImage(url: String, context: Context) {
|
||||||
screenModelScope.launch(dispatcher) {
|
screenModelScope.launch(dispatcher) {
|
||||||
val bitmap = getImage(url, context)
|
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)
|
val uri = saveImageInCache(bitmap, url, context)
|
||||||
|
|
||||||
Intent().apply {
|
Intent().apply {
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
type = "image/*"
|
|
||||||
|
clipData = ClipData.newRawUri(null, uri)
|
||||||
putExtra(Intent.EXTRA_STREAM, uri)
|
putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
|
|
||||||
|
type = "image/*"
|
||||||
|
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
}.also {
|
}.also {
|
||||||
context.startActivity(Intent.createChooser(it, null))
|
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
|
val downloader = context.imageLoader
|
||||||
|
|
||||||
return (downloader.execute(
|
val image = downloader.execute(
|
||||||
ImageRequest.Builder(context)
|
ImageRequest.Builder(context)
|
||||||
.data(url)
|
.data(url)
|
||||||
.allowHardware(false)
|
.allowHardware(false)
|
||||||
.build()
|
.build()
|
||||||
).image!!.toBitmap())
|
).image
|
||||||
|
|
||||||
|
return image?.toBitmap()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveImageInCache(bitmap: Bitmap, url: String, context: Context): Uri {
|
private fun saveImageInCache(bitmap: Bitmap, url: String, context: Context): Uri {
|
||||||
val imagesFolder = File(context.cacheDir.absolutePath, "images")
|
val imagesFolder = File(context.cacheDir.absolutePath, "images")
|
||||||
if (!imagesFolder.exists()) imagesFolder.mkdirs()
|
if (!imagesFolder.exists()) imagesFolder.mkdirs()
|
||||||
|
|
||||||
val image = File(imagesFolder, url.substringAfterLast('/'))
|
val name = URI.create(url).path.substringAfterLast('/')
|
||||||
FileOutputStream(image).apply {
|
val image = File(imagesFolder, name).apply {
|
||||||
|
outputStream().apply {
|
||||||
bitmap.compress(Bitmap.CompressFormat.PNG, 90, this)
|
bitmap.compress(Bitmap.CompressFormat.PNG, 90, this)
|
||||||
flush()
|
flush()
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return FileProvider.getUriForFile(context, context.packageName, image)
|
return FileProvider.getUriForFile(context, context.packageName, image)
|
||||||
}
|
}
|
||||||
|
@ -194,5 +218,6 @@ data class ItemState(
|
||||||
val imageDialogUrl: String? = null,
|
val imageDialogUrl: String? = null,
|
||||||
val fileDownloadedEvent: Boolean = false,
|
val fileDownloadedEvent: Boolean = false,
|
||||||
val openInExternalBrowser: Boolean = false,
|
val openInExternalBrowser: Boolean = false,
|
||||||
val theme: String? = ""
|
val theme: String? = "",
|
||||||
|
val error: String? = null
|
||||||
)
|
)
|
|
@ -185,4 +185,5 @@
|
||||||
<string name="feed_already_exists">Le flux existe déjà</string>
|
<string name="feed_already_exists">Le flux existe déjà</string>
|
||||||
<string name="invalid_folder">Dossier invalide</string>
|
<string name="invalid_folder">Dossier invalide</string>
|
||||||
<string name="invalid_feed">Flux 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>
|
</resources>
|
|
@ -194,4 +194,5 @@
|
||||||
<string name="feed_already_exists">Feed already exists</string>
|
<string name="feed_already_exists">Feed already exists</string>
|
||||||
<string name="invalid_folder">Invalid folder</string>
|
<string name="invalid_folder">Invalid folder</string>
|
||||||
<string name="invalid_feed">Invalid feed</string>
|
<string name="invalid_feed">Invalid feed</string>
|
||||||
|
<string name="error_image_download">An error occurred while downloading the image</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue