Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
main.com.sceyt.chatuikit.shared.helpers.picker.FilePickerHelper.kt Maven / Gradle / Ivy
package com.sceyt.chatuikit.shared.helpers.picker
import android.Manifest
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.provider.MediaStore
import android.provider.Settings
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.withResumed
import com.sceyt.chatuikit.R
import com.sceyt.chatuikit.extensions.TAG
import com.sceyt.chatuikit.extensions.asFragmentActivity
import com.sceyt.chatuikit.extensions.checkAndAskPermissions
import com.sceyt.chatuikit.extensions.copyFile
import com.sceyt.chatuikit.extensions.getFileUriWithProvider
import com.sceyt.chatuikit.extensions.getPermissionsForMangeStorage
import com.sceyt.chatuikit.extensions.initAttachmentLauncher
import com.sceyt.chatuikit.extensions.initCameraLauncher
import com.sceyt.chatuikit.extensions.initPermissionLauncher
import com.sceyt.chatuikit.extensions.initVideoCameraLauncher
import com.sceyt.chatuikit.extensions.oneOfPermissionsIgnored
import com.sceyt.chatuikit.extensions.permissionIgnored
import com.sceyt.chatuikit.logger.SceytLog
import com.sceyt.chatuikit.presentation.common.SceytDialog
import com.sceyt.chatuikit.presentation.common.SceytLoader
import com.sceyt.chatuikit.presentation.components.picker.BottomSheetMediaPicker
import com.sceyt.chatuikit.presentation.components.picker.BottomSheetMediaPicker.Companion.MAX_SELECT_MEDIA_COUNT
import com.sceyt.chatuikit.presentation.common.DebounceHelper
import com.sceyt.chatuikit.shared.utils.FileUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileInputStream
import java.util.UUID
class FilePickerHelper {
private lateinit var context: Context
private var requestCameraPermissionLauncher: ActivityResultLauncher? = null
private var requestVideoCameraPermissionLauncher: ActivityResultLauncher? = null
private var requestSceytGalleryPermissionLauncher: ActivityResultLauncher? = null
private var takePhotoLauncher: ActivityResultLauncher? = null
private var takeVideoLauncher: ActivityResultLauncher? = null
private var addAttachmentLauncher: ActivityResultLauncher? = null
private var allowMultiple: Boolean = true
private var onlyImages: Boolean = true
private var sceytGalleryFilter = BottomSheetMediaPicker.PickerFilterType.All
private var sceytGalleryMaxSelectCount: Int = MAX_SELECT_MEDIA_COUNT
private var chooseFilesCb: ((List) -> Unit)? = null
private var takePictureCb: ((String) -> Unit)? = null
private var takeVideoCb: ((String) -> Unit)? = null
private var scope: CoroutineScope
private var placeToSavePathsList: MutableSet = mutableSetOf()
constructor(activity: ComponentActivity) {
with(activity) {
[email protected] = activity
scope = activity.lifecycleScope
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED))
return
requestCameraPermissionLauncher = initPermissionLauncher {
onCameraPermissionResult(it)
}
requestVideoCameraPermissionLauncher = initPermissionLauncher {
onVideoCameraPermissionResult(it)
}
requestSceytGalleryPermissionLauncher = initPermissionLauncher {
onSceytGalleryPermissionResult(it)
}
takePhotoLauncher = initCameraLauncher {
onTakePhotoResult(it)
}
takeVideoLauncher = initVideoCameraLauncher {
onTakeVideoResult(it)
}
addAttachmentLauncher = initAttachmentLauncher {
onChooseFileResult(it)
}
}
}
constructor(fragment: Fragment) {
with(fragment) {
scope = fragment.lifecycleScope
lifecycleScope.launch {
lifecycle.withResumed {
[email protected] = requireContext()
}
}
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED))
return
requestCameraPermissionLauncher = initPermissionLauncher {
onCameraPermissionResult(it)
}
requestVideoCameraPermissionLauncher = initPermissionLauncher {
onVideoCameraPermissionResult(it)
}
requestSceytGalleryPermissionLauncher = initPermissionLauncher {
onSceytGalleryPermissionResult(it)
}
takePhotoLauncher = initCameraLauncher {
onTakePhotoResult(it)
}
takeVideoLauncher = initVideoCameraLauncher {
onTakeVideoResult(it)
}
addAttachmentLauncher = initAttachmentLauncher {
onChooseFileResult(it)
}
}
}
fun takePicture(result: (uri: String) -> Unit) {
takePictureCb = result
if (context.checkAndAskPermissions(requestCameraPermissionLauncher,
Manifest.permission.CAMERA)) {
takePhotoLauncher?.launch(getPhotoFileUri())
}
}
fun takeVideo(result: (uri: String) -> Unit) {
takeVideoCb = result
if (context.checkAndAskPermissions(requestVideoCameraPermissionLauncher, Manifest.permission.CAMERA)) {
takeVideoLauncher?.launch(getVideoFileUri())
}
}
fun chooseFromGallery(allowMultiple: Boolean, onlyImages: Boolean, result: (uris: List) -> Unit) {
chooseFilesCb = result
this.onlyImages = onlyImages
this.allowMultiple = allowMultiple
openGallery()
}
fun chooseMultipleFiles(allowMultiple: Boolean, result: (uris: List) -> Unit) {
chooseFilesCb = result
this.allowMultiple = allowMultiple
pickFile()
}
fun openMediaPicker(pickerListener: BottomSheetMediaPicker.PickerListener,
filter: BottomSheetMediaPicker.PickerFilterType = sceytGalleryFilter,
maxSelectCount: Int = sceytGalleryMaxSelectCount,
vararg selections: String) {
val permissions = getPermissionsForMangeStorage()
sceytGalleryFilter = filter
sceytGalleryMaxSelectCount = maxSelectCount
if (context.checkAndAskPermissions(requestSceytGalleryPermissionLauncher, *permissions)) {
openSceytGalleryPicker(pickerListener, filter = filter, maxSelectCount = maxSelectCount, *selections)
} else BottomSheetMediaPicker.pickerListener = pickerListener
}
private fun onTakePhotoResult(success: Boolean) {
if (success) {
placeToSavePathsList.lastOrNull()?.let { path ->
takePictureCb?.invoke(path)
}
}
}
private fun onTakeVideoResult(success: Boolean) {
if (success) {
placeToSavePathsList.lastOrNull()?.let { path ->
takeVideoCb?.invoke(path)
}
}
}
private fun onChooseFileResult(result: ActivityResult) {
if (result.resultCode == AppCompatActivity.RESULT_OK) {
val data = result.data
if (data?.clipData != null) {
val uris = mutableListOf()
for (i in 0 until (data.clipData?.itemCount ?: 0)) {
data.clipData?.getItemAt(i)?.uri?.let { uri ->
uris.add(uri)
}
}
scope.launch(Dispatchers.IO) {
val paths = getPathFromFile(*uris.toTypedArray())
if (paths.isNotEmpty()) {
withContext(Dispatchers.Main) {
placeToSavePathsList.addAll(paths)
chooseFilesCb?.invoke(paths)
}
} else withContext(Dispatchers.Main) {
Toast.makeText(context, context.getString(R.string.sceyt_could_not_open_file), Toast.LENGTH_SHORT).show()
}
}
} else {
scope.launch(Dispatchers.IO) {
val paths = getPathFromFile(data?.data)
if (paths.isNotEmpty()) {
withContext(Dispatchers.Main) {
placeToSavePathsList.addAll(paths)
chooseFilesCb?.invoke(paths)
}
} else withContext(Dispatchers.Main) {
Toast.makeText(context, context.getString(R.string.sceyt_could_not_open_file), Toast.LENGTH_SHORT).show()
}
}
}
}
}
private suspend fun getPathFromFile(vararg uris: Uri?): List {
val paths = mutableListOf()
val filteredUris = uris.filterNotNull()
if (filteredUris.isEmpty()) return emptyList()
val debounceHelper by lazy { DebounceHelper(300L, scope) }
filteredUris.forEach { uri ->
try {
debounceHelper.submitSuspendable {
withContext(Dispatchers.Main) { SceytLoader.showLoading(context) }
}
var realFile: File? = null
try {
val path = FileUtil(context).getPath(uri)
FileInputStream(File(path))
realFile = File(path)
} catch (ex: Exception) {
SceytLog.e(TAG, "error to get path with reason ${ex.message}")
} finally {
if (realFile != null && realFile.exists()) {
paths.add(realFile.path)
} else {
val name = DocumentFile.fromSingleUri(context, uri)?.name
if (name != null) {
val copiedFile = copyFile(context, uri.toString(), name)
paths.add(copiedFile.path)
}
}
}
} catch (e: Exception) {
SceytLog.e(TAG, "error to copy file with reason ${e.message}")
}
}
debounceHelper.cancelLastDebounce()
withContext(Dispatchers.Main) { SceytLoader.hideLoading() }
return paths
}
private fun onCameraPermissionResult(isGranted: Boolean) {
if (isGranted) {
takePhotoLauncher?.launch(getPhotoFileUri())
} else if (context.permissionIgnored(Manifest.permission.CAMERA))
showPermissionDeniedDialog(R.string.sceyt_camera_permission_disabled_title, R.string.sceyt_camera_permission_disabled_desc)
}
private fun onVideoCameraPermissionResult(isGranted: Boolean) {
if (isGranted) {
takeVideoLauncher?.launch(getVideoFileUri())
} else if (context.permissionIgnored(Manifest.permission.CAMERA))
showPermissionDeniedDialog(R.string.sceyt_camera_permission_disabled_title, R.string.sceyt_camera_permission_disabled_desc)
}
private fun onSceytGalleryPermissionResult(isGranted: Boolean) {
if (isGranted) {
openSceytGalleryPicker()
} else if (context.oneOfPermissionsIgnored(*getPermissionsForMangeStorage()))
showPermissionDeniedDialog(R.string.sceyt_media_permission_disabled_title, R.string.sceyt_media_permission_disabled_desc)
}
private fun openGallery() {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple)
if (onlyImages)
intent.type = "image/*"
addAttachmentLauncher?.launch(intent)
}
private fun openSceytGalleryPicker(pickerListener: BottomSheetMediaPicker.PickerListener? = BottomSheetMediaPicker.pickerListener,
filter: BottomSheetMediaPicker.PickerFilterType = sceytGalleryFilter,
maxSelectCount: Int = sceytGalleryMaxSelectCount,
vararg selections: String) {
BottomSheetMediaPicker.instance(
selections = selections,
fileFilter = filter,
maxSelectCount = maxSelectCount).apply {
BottomSheetMediaPicker.pickerListener = pickerListener
}.show(context.asFragmentActivity().supportFragmentManager, BottomSheetMediaPicker.TAG)
}
private fun pickFile() {
val mimetypes = arrayOf(
"application/*",
"audio/*",
"font/*",
"message/*",
"model/*",
"multipart/*",
"text/*")
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes)
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple)
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, false)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addAttachmentLauncher?.launch(intent)
}
fun setSaveUrlsPlace(savePathsTo: MutableSet) {
placeToSavePathsList = savePathsTo
}
private fun showPermissionDeniedDialog(titleId: Int, descId: Int) {
SceytDialog.showDialog(context,
titleId = titleId,
descId = descId,
positiveBtnTitleId = R.string.sceyt_settings,
replaceLastDialog = false,
positiveCb = {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", context.packageName, null)
intent.data = uri
context.startActivity(intent)
})
}
private fun getPhotoFileUri(): Uri {
val directory = File(context.filesDir, "Photos")
if (!directory.exists()) directory.mkdir()
val file = File.createTempFile("Photo_${UUID.randomUUID()}", ".jpg", directory)
return context.getFileUriWithProvider(file).also { placeToSavePathsList.add(file.path) }
}
private fun getVideoFileUri(): Uri {
val directory = File(context.filesDir, "Videos")
if (!directory.exists()) directory.mkdir()
val file = File.createTempFile("Video_${UUID.randomUUID()}", ".mp4", directory)
return context.getFileUriWithProvider(file).also { placeToSavePathsList.add(file.path) }
}
}