Skip to content

Instantly share code, notes, and snippets.

@mutkuensert
Last active April 15, 2025 06:07
Show Gist options
  • Save mutkuensert/00c5da66c8f741d233bc57b75593fe14 to your computer and use it in GitHub Desktop.
Save mutkuensert/00c5da66c8f741d233bc57b75593fe14 to your computer and use it in GitHub Desktop.
import android.content.Intent
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.os.ParcelFileDescriptor
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import timber.log.Timber
import java.io.ByteArrayInputStream
import java.io.FileOutputStream
import java.io.InputStream
/**
* Should be initialized before activity lifecycle is STARTED.
*/
class PermissionlessFileCreator(val activity: ComponentActivity) {
lateinit var fileStream: ByteArrayInputStream
private set
lateinit var name: String
private set
lateinit var type: String
private set
var listener: ResultListener? = null
private val resultLauncher = getResultLauncher()
fun create(
fileStream: ByteArrayInputStream,
name: String,
type: String
) {
this.fileStream = fileStream
this.name = name
this.type = type
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.setType("application/$type")
intent.putExtra(Intent.EXTRA_TITLE, "$name.$type")
resultLauncher.launch(intent)
}
private fun getResultLauncher(): ActivityResultLauncher<Intent> {
return activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val uri = result.data?.data
if (uri != null) {
writeToUri(uri)
} else {
Timber.w("Uri is null.")
}
}
}
private fun writeToUri(uri: Uri) {
Thread {
try {
val parcelFileDescriptor: ParcelFileDescriptor? =
activity.contentResolver.openFileDescriptor(uri, "wt")
if (parcelFileDescriptor == null) {
Timber.w("parcelFileDescriptor is null. Can't write file.")
return@Thread
}
val fileOutputStream = FileOutputStream(parcelFileDescriptor.fileDescriptor)
val buff = ByteArray(1024)
var read: Int
var bytesCopied: Long = 0
val stream: InputStream = fileStream
while ((stream.read(buff, 0, buff.size).also { read = it }) > -1) {
fileOutputStream.write(buff, 0, read)
bytesCopied += read.toLong()
}
fileOutputStream.close()
Handler(Looper.getMainLooper()).post { listener?.onSuccess(uri) }
parcelFileDescriptor.close()
} catch (exception: Exception) {
Timber.e(exception)
Handler(Looper.getMainLooper()).post { listener?.onFailure() }
}
}.start()
}
interface ResultListener {
fun onSuccess(uri: Uri)
fun onFailure()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment