Skip to content

Commit

Permalink
Can share file with template now
Browse files Browse the repository at this point in the history
  • Loading branch information
T8RIN committed May 25, 2024
1 parent b311268 commit 9b4abd3
Show file tree
Hide file tree
Showing 9 changed files with 277 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,16 @@ import androidx.exifinterface.media.ExifInterface
import dagger.Lazy
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.withContext
import ru.tech.imageresizershrinker.core.data.saving.FileWriteable
import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder
import ru.tech.imageresizershrinker.core.domain.image.ImageCompressor
import ru.tech.imageresizershrinker.core.domain.image.ImageGetter
import ru.tech.imageresizershrinker.core.domain.image.ShareProvider
import ru.tech.imageresizershrinker.core.domain.image.model.ImageInfo
import ru.tech.imageresizershrinker.core.domain.saving.ImageFilenameProvider
import ru.tech.imageresizershrinker.core.domain.saving.Writeable
import ru.tech.imageresizershrinker.core.domain.saving.model.ImageSaveTarget
import ru.tech.imageresizershrinker.core.domain.saving.use
import ru.tech.imageresizershrinker.core.resources.R
import java.io.File
import java.io.FileOutputStream
Expand Down Expand Up @@ -193,4 +196,50 @@ internal class AndroidShareProvider @Inject constructor(
onComplete()
}

override suspend fun cacheData(
writeData: suspend (Writeable) -> Unit,
filename: String
): String? = withContext(ioDispatcher) {
val imagesFolder = File(context.cacheDir, "files")

runCatching {
imagesFolder.mkdirs()
val file = File(imagesFolder, filename)
FileWriteable(file).use {
writeData(it)
}

FileProvider.getUriForFile(context, context.getString(R.string.file_provider), file)
.also { uri ->
runCatching {
context.grantUriPermission(
context.packageName,
uri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
}
}.getOrNull()?.toString()
}

override suspend fun shareData(
writeData: suspend (Writeable) -> Unit,
filename: String,
onComplete: () -> Unit
) = withContext(ioDispatcher) {
cacheData(
writeData = writeData,
filename = filename
)?.let {
shareUri(
uri = it,
type = MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(
imageGetter.getExtension(it)
) ?: "*/*"
)
}
onComplete()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ import ru.tech.imageresizershrinker.core.domain.image.ShareProvider
import ru.tech.imageresizershrinker.core.domain.image.model.MetadataTag
import ru.tech.imageresizershrinker.core.domain.saving.FileController
import ru.tech.imageresizershrinker.core.domain.saving.RandomStringGenerator
import ru.tech.imageresizershrinker.core.domain.saving.Writeable
import ru.tech.imageresizershrinker.core.domain.saving.model.ImageSaveTarget
import ru.tech.imageresizershrinker.core.domain.saving.model.SaveResult
import ru.tech.imageresizershrinker.core.domain.saving.model.SaveTarget
import ru.tech.imageresizershrinker.core.domain.saving.use
import ru.tech.imageresizershrinker.core.domain.utils.readableByteCount
import ru.tech.imageresizershrinker.core.resources.R
import ru.tech.imageresizershrinker.core.settings.domain.SettingsManager
Expand Down Expand Up @@ -499,6 +501,32 @@ internal class AndroidFileController @Inject constructor(
} ?: ByteArray(0)
}

override suspend fun writeBytes(
uri: String,
onError: (Throwable) -> Unit,
block: suspend (Writeable) -> Unit,
) {
context.openWriteableStream(
uri = uri.toUri(),
onError = onError
)?.let { stream ->
StreamWriteable(stream).use { block(it) }
}
}

private fun Context.openWriteableStream(
uri: Uri?,
onError: (Throwable) -> Unit = {}
): OutputStream? = uri?.let {
runCatching {
contentResolver.openOutputStream(uri, "rw")
}.getOrElse {
runCatching {
contentResolver.openOutputStream(uri, "w")
}.onFailure(onError).getOrNull()
}
}

private fun Context.clearCache(onComplete: (cache: String) -> Unit = {}) {
CoroutineScope(defaultDispatcher).launch {
coroutineScope {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* You should have received a copy of the Apache License
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
*/

package ru.tech.imageresizershrinker.core.data.saving

import ru.tech.imageresizershrinker.core.domain.saving.Writeable
import java.io.File
import java.io.FileOutputStream

class FileWriteable(
private val file: File
) : Writeable {

private val stream = FileOutputStream(file)

override fun writeBytes(byteArray: ByteArray) = stream.write(byteArray)

override fun close() = stream.close()

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* You should have received a copy of the Apache License
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
*/

package ru.tech.imageresizershrinker.core.data.saving

import ru.tech.imageresizershrinker.core.domain.saving.Writeable
import java.io.OutputStream

class StreamWriteable(
private val stream: OutputStream
) : Writeable {

override fun writeBytes(byteArray: ByteArray) = stream.write(byteArray)

override fun close() = stream.close()

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package ru.tech.imageresizershrinker.core.domain.image

import ru.tech.imageresizershrinker.core.domain.image.model.ImageInfo
import ru.tech.imageresizershrinker.core.domain.saving.Writeable

interface ShareProvider<I> {

Expand Down Expand Up @@ -49,6 +50,17 @@ interface ShareProvider<I> {
onComplete: () -> Unit = {}
)

suspend fun cacheData(
writeData: suspend (Writeable) -> Unit,
filename: String
): String?

suspend fun shareData(
writeData: suspend (Writeable) -> Unit,
filename: String,
onComplete: () -> Unit = {}
)

suspend fun shareUri(
uri: String,
type: String? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,10 @@ interface FileController {
fun getReadableCacheSize(): String

suspend fun readBytes(uri: String): ByteArray

suspend fun writeBytes(
uri: String,
onError: (Throwable) -> Unit = {},
block: suspend (Writeable) -> Unit
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* You should have received a copy of the Apache License
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
*/

package ru.tech.imageresizershrinker.core.domain.saving

import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

interface Writeable {

fun writeBytes(byteArray: ByteArray)

fun close()

}

@OptIn(ExperimentalContracts::class)
inline fun <T : Writeable?, R> T.use(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
closeFinally(exception)
}
}

@SinceKotlin("1.1")
@PublishedApi
internal fun Writeable?.closeFinally(cause: Throwable?) = when {
this == null -> {}
cause == null -> close()
else ->
try {
close()
} catch (closeException: Throwable) {
cause.addSuppressed(closeException)
}
}
Loading

0 comments on commit 9b4abd3

Please sign in to comment.