mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
Merge pull request #3436 from vitorpamplona/claude/download-button-blossom-uri-ii41o1
Support Blossom URI scheme for media downloads
This commit is contained in:
@@ -44,6 +44,7 @@ import okio.buffer
|
||||
import okio.sink
|
||||
import okio.source
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
@@ -53,6 +54,7 @@ object MediaSaverToDisk {
|
||||
okHttpClient: (String) -> OkHttpClient,
|
||||
mimeType: String?,
|
||||
localContext: Context,
|
||||
resolveBlossom: suspend (String) -> String? = { null },
|
||||
onSuccess: () -> Any?,
|
||||
onError: (Throwable) -> Any?,
|
||||
) = withContext(Dispatchers.IO) {
|
||||
@@ -77,6 +79,7 @@ object MediaSaverToDisk {
|
||||
mimeType = mimeType,
|
||||
okHttpClient = okHttpClient,
|
||||
context = localContext,
|
||||
resolveBlossom = resolveBlossom,
|
||||
onSuccess = onSuccess,
|
||||
onError = onError,
|
||||
)
|
||||
@@ -87,6 +90,11 @@ object MediaSaverToDisk {
|
||||
/**
|
||||
* Saves the image to the gallery. May require a storage permission.
|
||||
*
|
||||
* `blossom:` (BUD-10) URIs are resolved to a concrete `http(s)` server URL
|
||||
* via [resolveBlossom] before downloading, since OkHttp only speaks
|
||||
* http/https. When resolution fails the save reports an error instead of
|
||||
* crashing with "expected scheme http or https but was blossom".
|
||||
*
|
||||
* @see AMETHYST_SUBDIRECTORY
|
||||
*/
|
||||
suspend fun downloadAndSave(
|
||||
@@ -94,26 +102,35 @@ object MediaSaverToDisk {
|
||||
mimeType: String?,
|
||||
okHttpClient: (String) -> OkHttpClient,
|
||||
context: Context,
|
||||
resolveBlossom: suspend (String) -> String? = { null },
|
||||
onSuccess: () -> Any?,
|
||||
onError: (Throwable) -> Any?,
|
||||
) {
|
||||
try {
|
||||
val client = okHttpClient(url)
|
||||
val downloadUrl =
|
||||
if (url.startsWith(BLOSSOM_SCHEME, ignoreCase = true)) {
|
||||
resolveBlossom(url)
|
||||
?: throw IOException("Could not find a Blossom server that hosts $url")
|
||||
} else {
|
||||
url
|
||||
}
|
||||
|
||||
val client = okHttpClient(downloadUrl)
|
||||
|
||||
val request =
|
||||
Request
|
||||
.Builder()
|
||||
.get()
|
||||
.url(url)
|
||||
.url(downloadUrl)
|
||||
.build()
|
||||
|
||||
client.newCall(request).executeAsync().use { response ->
|
||||
withContext(Dispatchers.IO) {
|
||||
check(response.isSuccessful) {
|
||||
"Failed to download $url: HTTP ${response.code} ${response.message}"
|
||||
"Failed to download $downloadUrl: HTTP ${response.code} ${response.message}"
|
||||
}
|
||||
|
||||
val trimmedUrl = trimInlineMetaData(url)
|
||||
val trimmedUrl = trimInlineMetaData(downloadUrl)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val headerType =
|
||||
response
|
||||
@@ -292,4 +309,5 @@ object MediaSaverToDisk {
|
||||
|
||||
private const val AMETHYST_SUBDIRECTORY = "Amethyst"
|
||||
private const val PDF_MIME_TYPE = "application/pdf"
|
||||
private const val BLOSSOM_SCHEME = "blossom:"
|
||||
}
|
||||
|
||||
+6
@@ -83,6 +83,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
@@ -532,6 +533,11 @@ internal suspend fun saveMediaToGallery(
|
||||
}
|
||||
},
|
||||
localContext,
|
||||
resolveBlossom = {
|
||||
Amethyst.instance.blossomResolver
|
||||
.findServers(it)
|
||||
?.serverUrl
|
||||
},
|
||||
onSuccess = {
|
||||
showToastOnMain(localContext, success)
|
||||
},
|
||||
|
||||
+5
@@ -2254,6 +2254,11 @@ class AccountViewModel(
|
||||
okHttpClient = httpClientBuilder::okHttpClientForVideo,
|
||||
mimeType = mimeType,
|
||||
localContext = localContext,
|
||||
resolveBlossom = {
|
||||
Amethyst.instance.blossomResolver
|
||||
.findServers(it)
|
||||
?.serverUrl
|
||||
},
|
||||
onSuccess = {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
Toast
|
||||
|
||||
@@ -60,7 +60,6 @@ object Hex {
|
||||
* emoji in `p` tags) instead of throwing. ~47ns in debug on the Emulator;
|
||||
* use [isHex64] when the length is known to be 64.
|
||||
*/
|
||||
// 47ns in debug on the Emulator
|
||||
fun isHex(hex: String?): Boolean {
|
||||
if (hex == null) return false
|
||||
if (hex.length and 1 != 0) return false
|
||||
@@ -93,7 +92,6 @@ object Hex {
|
||||
* the length is fixed and the checks are unrolled. Assumes [hex] is at least
|
||||
* 64 chars long; it does not verify the total length.
|
||||
*/
|
||||
// 30% faster than isHex
|
||||
fun isHex64(hex: String): Boolean =
|
||||
try {
|
||||
hexToByte[hex[0].code] >= 0 &&
|
||||
|
||||
Reference in New Issue
Block a user