Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: Start video recording on camera icon clicked when "OnlyVideo" is enabled #411

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ interface ImagePickerComponents {
val imageLoader: ImageLoader
val imageFileLoader: ImageFileLoader
val cameraModule: CameraModule
val videoModule: CameraModule
esafirm marked this conversation as resolved.
Show resolved Hide resolved
esafirm marked this conversation as resolved.
Show resolved Hide resolved
}

open class DefaultImagePickerComponents(context: Context) : ImagePickerComponents {
override val appContext: Context = context.applicationContext
override val imageLoader: ImageLoader by lazy { DefaultImageLoader() }
override val imageFileLoader: ImageFileLoader by lazy { DefaultImageFileLoader(context.applicationContext) }
override val cameraModule: CameraModule by lazy { DefaultCameraModule() }
override val videoModule: CameraModule by lazy { DefaultCameraModule() }
}

object ImagePickerComponentsHolder : ImagePickerComponents {
Expand All @@ -38,6 +40,9 @@ object ImagePickerComponentsHolder : ImagePickerComponents {
override val cameraModule: CameraModule
get() = internalComponents.cameraModule

override val videoModule: CameraModule
get() = internalComponents.videoModule

fun setInternalComponent(components: ImagePickerComponents) {
internalComponents = components
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class ImagePickerConfig(
override var savePath: ImagePickerSavePath = ImagePickerSavePath.DEFAULT,
override var returnMode: ReturnMode = ReturnMode.NONE,
override var isSaveImage: Boolean = true,
override var isSaveVideo: Boolean = true,
var showDoneButtonAlways: Boolean = false
) : BaseConfig(), Parcelable {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,11 @@ class ImagePickerFragment : Fragment() {
if (!checkCameraAvailability(requireActivity())) {
return
}
presenter.captureImage(this, config, RC_CAPTURE)
if (config.isOnlyVideo) {
presenter.captureVideo(this, config, RC_CAPTURE)
} else{
esafirm marked this conversation as resolved.
Show resolved Hide resolved
presenter.captureImage(this, config, RC_CAPTURE)
}
}

override fun onDestroy() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ internal class ImagePickerPresenter(
) : ImagePickerAction {

private val cameraModule: CameraModule = ImagePickerComponentsHolder.cameraModule

private val videoModule: CameraModule = ImagePickerComponentsHolder.videoModule
private val stateObs = LiveDataObservableState(
ImagePickerState(isLoading = true),
usePostValue = true
Expand Down Expand Up @@ -95,6 +95,20 @@ internal class ImagePickerPresenter(
fragment.startActivityForResult(intent, requestCode)
}

fun captureVideo(fragment: Fragment, config: BaseConfig, requestCode: Int) {
val context = fragment.requireContext().applicationContext
val intent = videoModule.getVideoIntent(fragment.requireContext(), config)
esafirm marked this conversation as resolved.
Show resolved Hide resolved
if (intent == null) {
Toast.makeText(
context,
context.getString(R.string.ef_error_create_image_file),
esafirm marked this conversation as resolved.
Show resolved Hide resolved
Toast.LENGTH_LONG
).show()
return
}
fragment.startActivityForResult(intent, requestCode)
}

fun finishCaptureImage(context: Context, data: Intent?, config: BaseConfig?) {
cameraModule.getImage(context, data) { images ->
if (ConfigUtils.shouldReturn(config!!, true)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ typealias OnImageReadyListener = (List<Image>?) -> Unit

interface CameraModule {
fun getCameraIntent(context: Context, config: BaseConfig): Intent?
fun getVideoIntent(context: Context, config: BaseConfig): Intent?
esafirm marked this conversation as resolved.
Show resolved Hide resolved
fun getImage(context: Context, intent: Intent?, imageReadyListener: OnImageReadyListener)
fun removeImage(context: Context)
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@ class DefaultCameraModule : CameraModule {
return intent
}

override fun getVideoIntent(context: Context, config: BaseConfig): Intent? {
prepareForNewIntent()

val intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)
val imageFile = ImagePickerUtils.createVideoFile(config.savePath, context)

if (config.isSaveVideo && imageFile != null) {
val appContext = context.applicationContext
val uri = createVideoUri(appContext, imageFile)
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
ImagePickerUtils.grantAppPermission(context, intent, uri)
currentUri = uri.toString()
}
return intent
}

private fun prepareForNewIntent() {
currentImagePath = null
currentUri = null
Expand All @@ -55,6 +71,21 @@ class DefaultCameraModule : CameraModule {
return UriUtils.uriForFile(appContext, imageFile)
}

private fun createVideoUri(appContext: Context, videoFile: File): Uri? {
currentImagePath = "file:" + videoFile.absolutePath
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {

val values = ContentValues().apply {
put(MediaStore.Video.Media.DISPLAY_NAME, videoFile.name)
put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
}
val collection =
MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
return appContext.contentResolver.insert(collection, values)
}
return UriUtils.uriForFile(appContext, videoFile)
}

override fun getImage(
context: Context,
intent: Intent?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ import kotlinx.android.parcel.Parcelize
class CameraOnlyConfig(
override var savePath: ImagePickerSavePath = ImagePickerSavePath.DEFAULT,
override var returnMode: ReturnMode = ReturnMode.ALL,
override var isSaveImage: Boolean = true
override var isSaveImage: Boolean = true,
override var isSaveVideo: Boolean = true
) : BaseConfig(), Parcelable
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ abstract class BaseConfig {
abstract var savePath: ImagePickerSavePath
abstract var returnMode: ReturnMode
abstract var isSaveImage: Boolean
abstract var isSaveVideo: Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,27 @@ object ImagePickerUtils {
return mediaStorageDir
}

private fun createFileInDirectoryVideo(savePath: ImagePickerSavePath, context: Context): File? {
val path = savePath.path
val mediaStorageDir: File = if (savePath.isRelative) {
val parent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
context.getExternalFilesDir(Environment.DIRECTORY_MOVIES)
} else {
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)
}
File(parent, path)
} else {
File(path)
}
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
d("Oops! Failed create $path")
return null
}
}
return mediaStorageDir
}

fun createImageFile(savePath: ImagePickerSavePath, context: Context): File? {
val mediaStorageDir = createFileInDirectory(savePath, context) ?: return null

Expand All @@ -62,6 +83,18 @@ object ImagePickerUtils {
return result
}

fun createVideoFile(savePath: ImagePickerSavePath, context: Context): File? {
val mediaStorageDir = createFileInDirectoryVideo(savePath, context) ?: return null
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss_SSS", Locale.getDefault()).format(Date())
var result = File(mediaStorageDir, "VID_$timeStamp.mp4")
var counter = 0
while (result.exists()) {
counter++
result = File(mediaStorageDir, "VID_$timeStamp($counter).mp4")
}
return result
}

fun getNameFromFilePath(path: String): String {
return if (path.contains(File.separator)) {
path.substring(path.lastIndexOf(File.separator) + 1)
Expand Down