handle playing media sounds

- add MediaSoundHelper to separate logic for playing media action sounds
- add support for playing media action sounds (shutter, start and stop recording) in CameraXPreview
This commit is contained in:
darthpaul 2022-06-25 16:39:29 +01:00
parent 074351b88f
commit c398370446
5 changed files with 93 additions and 19 deletions

View File

@ -39,6 +39,7 @@ import com.simplemobiletools.commons.helpers.PERMISSION_RECORD_AUDIO
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
import com.simplemobiletools.commons.helpers.REFRESH_PATH import com.simplemobiletools.commons.helpers.REFRESH_PATH
import com.simplemobiletools.commons.models.Release import com.simplemobiletools.commons.models.Release
import java.util.concurrent.TimeUnit
import kotlinx.android.synthetic.main.activity_main.btn_holder import kotlinx.android.synthetic.main.activity_main.btn_holder
import kotlinx.android.synthetic.main.activity_main.capture_black_screen import kotlinx.android.synthetic.main.activity_main.capture_black_screen
import kotlinx.android.synthetic.main.activity_main.change_resolution import kotlinx.android.synthetic.main.activity_main.change_resolution
@ -549,6 +550,24 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
updateFlashlightState(flashMode) updateFlashlightState(flashMode)
} }
override fun onVideoRecordingStarted() {
shutter.setImageResource(R.drawable.ic_video_stop)
toggle_camera.beInvisible()
video_rec_curr_timer.beVisible()
}
override fun onVideoRecordingStopped() {
shutter.setImageResource(R.drawable.ic_video_rec)
video_rec_curr_timer.text = 0.getFormattedDuration()
video_rec_curr_timer.beGone()
toggle_camera.beVisible()
}
override fun onVideoDurationChanged(durationNanos: Long) {
val seconds = TimeUnit.NANOSECONDS.toSeconds(durationNanos).toInt()
video_rec_curr_timer.text = seconds.getFormattedDuration()
}
fun setRecordingState(isRecording: Boolean) { fun setRecordingState(isRecording: Boolean) {
runOnUiThread { runOnUiThread {
if (isRecording) { if (isRecording) {

View File

@ -0,0 +1,25 @@
package com.simplemobiletools.camera.helpers
import android.media.MediaActionSound
class MediaSoundHelper {
private val mediaActionSound = MediaActionSound()
fun loadSounds() {
mediaActionSound.load(MediaActionSound.START_VIDEO_RECORDING)
mediaActionSound.load(MediaActionSound.STOP_VIDEO_RECORDING)
mediaActionSound.load(MediaActionSound.SHUTTER_CLICK)
}
fun playShutterSound() {
mediaActionSound.play(MediaActionSound.SHUTTER_CLICK)
}
fun playStartVideoRecordingSound() {
mediaActionSound.play(MediaActionSound.START_VIDEO_RECORDING)
}
fun playStopVideoRecordingSound() {
mediaActionSound.play(MediaActionSound.STOP_VIDEO_RECORDING)
}
}

View File

@ -37,6 +37,7 @@ import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.extensions.config import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.extensions.toAppFlashMode import com.simplemobiletools.camera.extensions.toAppFlashMode
import com.simplemobiletools.camera.extensions.toCameraXFlashMode import com.simplemobiletools.camera.extensions.toCameraXFlashMode
import com.simplemobiletools.camera.helpers.MediaSoundHelper
import com.simplemobiletools.camera.interfaces.MyPreview import com.simplemobiletools.camera.interfaces.MyPreview
import com.simplemobiletools.commons.extensions.showErrorToast import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.commons.extensions.toast import com.simplemobiletools.commons.extensions.toast
@ -63,6 +64,7 @@ class CameraXPreview(
private val config = activity.config private val config = activity.config
private val contentResolver = activity.contentResolver private val contentResolver = activity.contentResolver
private val mainExecutor = activity.mainExecutor private val mainExecutor = activity.mainExecutor
private val mediaSoundHelper = MediaSoundHelper()
private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate() private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
private val orientationEventListener = object : OrientationEventListener(activity, SensorManager.SENSOR_DELAY_NORMAL) { private val orientationEventListener = object : OrientationEventListener(activity, SensorManager.SENSOR_DELAY_NORMAL) {
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
@ -109,6 +111,7 @@ class CameraXPreview(
init { init {
bindToLifeCycle() bindToLifeCycle()
mediaSoundHelper.loadSounds()
viewFinder.doOnLayout { viewFinder.doOnLayout {
startCamera() startCamera()
} }
@ -315,6 +318,7 @@ class CameraXPreview(
.setMetadata(metadata) .setMetadata(metadata)
.build() .build()
imageCapture.takePicture(outputOptions, mainExecutor, object : OnImageSavedCallback { imageCapture.takePicture(outputOptions, mainExecutor, object : OnImageSavedCallback {
override fun onImageSaved(outputFileResults: OutputFileResults) { override fun onImageSaved(outputFileResults: OutputFileResults) {
listener.toggleBottomButtons(false) listener.toggleBottomButtons(false)
@ -327,6 +331,7 @@ class CameraXPreview(
Log.e(TAG, "Error", exception) Log.e(TAG, "Error", exception)
} }
}) })
playShutterSoundIfEnabled()
} }
override fun initPhotoMode() { override fun initPhotoMode() {
@ -368,15 +373,25 @@ class CameraXPreview(
.withAudioEnabled() .withAudioEnabled()
.start(mainExecutor) { recordEvent -> .start(mainExecutor) { recordEvent ->
Log.d(TAG, "recordEvent=$recordEvent ") Log.d(TAG, "recordEvent=$recordEvent ")
if (recordEvent !is VideoRecordEvent.Status) { recordingState = recordEvent
recordingState = recordEvent when(recordEvent){
} is VideoRecordEvent.Start -> {
playStartVideoRecordingSoundIfEnabled()
listener.onVideoRecordingStarted()
}
if (recordEvent is VideoRecordEvent.Finalize) { is VideoRecordEvent.Status -> {
if (recordEvent.hasError()) { listener.onVideoDurationChanged(recordEvent.recordingStats.recordedDurationNanos)
// TODO: Handle errors }
} else {
listener.onMediaCaptured(recordEvent.outputResults.outputUri) is VideoRecordEvent.Finalize -> {
playStopVideoRecordingSoundIfEnabled()
listener.onVideoRecordingStopped()
if (recordEvent.hasError()) {
// TODO: Handle errors
} else {
listener.onMediaCaptured(recordEvent.outputResults.outputUri)
}
} }
} }
} }
@ -391,4 +406,22 @@ class CameraXPreview(
"VID_$timestamp" "VID_$timestamp"
} }
} }
private fun playShutterSoundIfEnabled(){
if(config.isSoundEnabled){
mediaSoundHelper.playShutterSound()
}
}
private fun playStartVideoRecordingSoundIfEnabled(){
if(config.isSoundEnabled){
mediaSoundHelper.playStartVideoRecordingSound()
}
}
private fun playStopVideoRecordingSoundIfEnabled(){
if(config.isSoundEnabled){
mediaSoundHelper.playStopVideoRecordingSound()
}
}
} }

View File

@ -10,4 +10,7 @@ interface CameraXPreviewListener {
fun toggleBottomButtons(hide:Boolean) fun toggleBottomButtons(hide:Boolean)
fun onMediaCaptured(uri: Uri) fun onMediaCaptured(uri: Uri)
fun onChangeFlashMode(flashMode: Int) fun onChangeFlashMode(flashMode: Int)
fun onVideoRecordingStarted()
fun onVideoRecordingStopped()
fun onVideoDurationChanged(durationNanos: Long)
} }

View File

@ -96,7 +96,7 @@ class CameraPreview : ViewGroup, TextureView.SurfaceTextureListener, MyPreview {
private val mCameraToPreviewMatrix = Matrix() private val mCameraToPreviewMatrix = Matrix()
private val mPreviewToCameraMatrix = Matrix() private val mPreviewToCameraMatrix = Matrix()
private val mCameraOpenCloseLock = Semaphore(1) private val mCameraOpenCloseLock = Semaphore(1)
private val mMediaActionSound = MediaActionSound() private val mediaSoundHelper = MediaSoundHelper()
private var mZoomRect: Rect? = null private var mZoomRect: Rect? = null
constructor(context: Context) : super(context) constructor(context: Context) : super(context)
@ -114,7 +114,7 @@ class CameraPreview : ViewGroup, TextureView.SurfaceTextureListener, MyPreview {
mUseFrontCamera = false mUseFrontCamera = false
mIsInVideoMode = !initPhotoMode mIsInVideoMode = !initPhotoMode
loadSounds() mediaSoundHelper.loadSounds()
val gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() { val gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean { override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
@ -182,12 +182,6 @@ class CameraPreview : ViewGroup, TextureView.SurfaceTextureListener, MyPreview {
} }
} }
private fun loadSounds() {
mMediaActionSound.load(MediaActionSound.START_VIDEO_RECORDING)
mMediaActionSound.load(MediaActionSound.STOP_VIDEO_RECORDING)
mMediaActionSound.load(MediaActionSound.SHUTTER_CLICK)
}
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
private fun openCamera(width: Int, height: Int) { private fun openCamera(width: Int, height: Int) {
try { try {
@ -576,7 +570,7 @@ class CameraPreview : ViewGroup, TextureView.SurfaceTextureListener, MyPreview {
} }
if (mActivity.config.isSoundEnabled) { if (mActivity.config.isSoundEnabled) {
mMediaActionSound.play(MediaActionSound.SHUTTER_CLICK) mediaSoundHelper.playShutterSound()
} }
mCameraState = STATE_PICTURE_TAKEN mCameraState = STATE_PICTURE_TAKEN
@ -824,7 +818,7 @@ class CameraPreview : ViewGroup, TextureView.SurfaceTextureListener, MyPreview {
closeCaptureSession() closeCaptureSession()
setupMediaRecorder() setupMediaRecorder()
if (mActivity.config.isSoundEnabled) { if (mActivity.config.isSoundEnabled) {
mMediaActionSound.play(MediaActionSound.START_VIDEO_RECORDING) mediaSoundHelper.playStartVideoRecordingSound()
} }
try { try {
@ -853,7 +847,7 @@ class CameraPreview : ViewGroup, TextureView.SurfaceTextureListener, MyPreview {
private fun stopRecording() { private fun stopRecording() {
mCameraState = STATE_STOPING_RECORDING mCameraState = STATE_STOPING_RECORDING
if (mActivity.config.isSoundEnabled) { if (mActivity.config.isSoundEnabled) {
mMediaActionSound.play(MediaActionSound.STOP_VIDEO_RECORDING) mediaSoundHelper.playStopVideoRecordingSound()
} }
mIsRecording = false mIsRecording = false