Skip to content

Instantly share code, notes, and snippets.

@khaledahmedelsayed
Last active March 3, 2024 17:58
Show Gist options
  • Save khaledahmedelsayed/aab7595798fb9acea05dca5e95110f4e to your computer and use it in GitHub Desktop.
Save khaledahmedelsayed/aab7595798fb9acea05dca5e95110f4e to your computer and use it in GitHub Desktop.
File Selector Helper
/**
* You can use this helper class in your fragment using composition to select files from gallery/camera
*/
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
open class FileSelectorHelper(
private val fragment: Fragment,
private val fileDataType: FileDataType,
private val maxFileSizeInBytesValidation : Int? = null,
private val onFileSelected: (ByteArray?, Uri?, isInvalidFileSize : Boolean) -> Unit
) {
// Intents
private val galleryIntent: Intent
private val cameraIntent: Intent
// Permission launchers
var galleryPermissionLauncher: ActivityResultLauncher<Array<String>>
private val cameraPermissionLauncher: ActivityResultLauncher<String>
// Intent launchers
private val galleryLauncher: ActivityResultLauncher<Intent>
private val cameraLauncher: ActivityResultLauncher<Intent>
init {
with(fragment) {
galleryIntent = Intent(Intent.ACTION_GET_CONTENT).apply { type = fileDataType.supportedTypesIntents.joinToString(";") }
cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
galleryLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK)
result.data?.data?.let { uri ->
val fileBytes =
uri.let {
requireContext().contentResolver.openInputStream(uri)
?.readBytes()
}
onFileSelected(fileBytes, uri, (fileBytes?.size ?: 0) > (maxFileSizeInBytesValidation ?: Int.MAX_VALUE)
)
}
}
cameraLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK)
result.data?.extras?.get("data")?.let {
val imageBytes = (it as Bitmap).convertToByteArray()
onFileSelected.invoke(imageBytes, null, false)
}
}
galleryPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) {
if (it.all { it.value })
galleryLauncher.launch(galleryIntent)
}
cameraPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted)
cameraLauncher.launch(cameraIntent)
}
}
}
fun checkStoragePermission() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) // -> https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions
(ContextCompat.checkSelfPermission(
fragment.requireContext(),
Manifest.permission.READ_MEDIA_VIDEO
) == PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(
fragment.requireContext(),
Manifest.permission.READ_MEDIA_IMAGES
) == PackageManager.PERMISSION_GRANTED)
else
(ContextCompat.checkSelfPermission(
fragment.requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED)
private fun checkCameraPermission() = (ContextCompat.checkSelfPermission(
fragment.requireContext(),
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED)
open fun openGallery() {
checkStoragePermission().let { hasStoragePermission ->
if (hasStoragePermission)
galleryLauncher.launch(galleryIntent)
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) // -> https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions
galleryPermissionLauncher.launch(arrayOf(Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO))
else
galleryPermissionLauncher.launch(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE))
}
}
fun openCamera() {
checkCameraPermission().let { hasCameraPermission ->
if (hasCameraPermission)
cameraLauncher.launch(cameraIntent)
else
cameraPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
companion object {
enum class FileDataType(val supportedTypesIntents : List<String>) {
IMAGE(SupportedImageTypes.entries.toTypedArray().map { it.intent }),
VIDEO(SupportedVideoTypes.entries.toTypedArray().map { it.intent }),
FILE(SupportedFileTypes.entries.toTypedArray().map { it.intent })
}
enum class SupportedImageTypes(val intent :String) {
SVG("image/svg"),
PNG("image/png"),
JPG("image/jpg"),
JPEG("image/jpeg"),
GIF("image/gif")
}
enum class SupportedVideoTypes(val intent :String) {
MP4("video/mp4")
}
enum class SupportedFileTypes(val intent :String) {
PDF("application/pdf")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment