Last active
April 15, 2025 06:07
-
-
Save mutkuensert/00c5da66c8f741d233bc57b75593fe14 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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