Last active
March 3, 2024 17:58
-
-
Save khaledahmedelsayed/aab7595798fb9acea05dca5e95110f4e to your computer and use it in GitHub Desktop.
File Selector Helper
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
/** | |
* 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