moving the actual recording into the service
This commit is contained in:
parent
8255def4cb
commit
9cea12a1e2
|
@ -38,6 +38,6 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.simplemobiletools:commons:5.24.3'
|
||||
implementation 'com.simplemobiletools:commons:5.24.4'
|
||||
implementation 'org.greenrobot:eventbus:3.2.0'
|
||||
}
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
package com.simplemobiletools.voicerecorder.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentValues
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.media.MediaRecorder
|
||||
import android.media.MediaScannerConnection
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.widget.Toast
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_RECORD_AUDIO
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.helpers.isQPlus
|
||||
import com.simplemobiletools.commons.models.FAQItem
|
||||
import com.simplemobiletools.voicerecorder.BuildConfig
|
||||
|
@ -25,17 +18,11 @@ import com.simplemobiletools.voicerecorder.services.RecorderService
|
|||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class MainActivity : SimpleActivity() {
|
||||
private var isRecording = false
|
||||
private var recorder: MediaRecorder? = null
|
||||
private var bus: EventBus? = null
|
||||
private var currFilePath = ""
|
||||
private var duration = 0
|
||||
private var timer = Timer()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -63,12 +50,6 @@ class MainActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
recorder?.release()
|
||||
recorder = null
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
bus?.unregister(this)
|
||||
|
@ -106,8 +87,7 @@ class MainActivity : SimpleActivity() {
|
|||
bus = EventBus.getDefault()
|
||||
bus!!.register(this)
|
||||
|
||||
duration = 0
|
||||
updateRecordingDuration()
|
||||
updateRecordingDuration(0)
|
||||
toggle_recording_button.setOnClickListener {
|
||||
toggleRecording()
|
||||
}
|
||||
|
@ -129,116 +109,25 @@ class MainActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateRecordingDuration() {
|
||||
private fun updateRecordingDuration(duration: Int) {
|
||||
recording_duration.text = duration.getFormattedDuration()
|
||||
}
|
||||
|
||||
// mp4 output format with aac encoding should produce good enough mp3 files according to https://stackoverflow.com/a/33054794/1967672
|
||||
private fun startRecording() {
|
||||
val baseFolder = if (isQPlus()) {
|
||||
cacheDir
|
||||
} else {
|
||||
val defaultFolder = File("$internalStoragePath/${getString(R.string.app_name)}")
|
||||
if (!defaultFolder.exists()) {
|
||||
defaultFolder.mkdir()
|
||||
}
|
||||
defaultFolder.absolutePath
|
||||
}
|
||||
|
||||
currFilePath = "$baseFolder/${getCurrentFormattedDateTime()}.mp3"
|
||||
recorder = MediaRecorder().apply {
|
||||
setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
|
||||
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
|
||||
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
|
||||
setOutputFile(currFilePath)
|
||||
|
||||
try {
|
||||
prepare()
|
||||
start()
|
||||
duration = 0
|
||||
timer = Timer()
|
||||
timer.scheduleAtFixedRate(getTimerTask(), 1000, 1000)
|
||||
|
||||
Intent(this@MainActivity, RecorderService::class.java).apply {
|
||||
startService(this)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
Intent(this@MainActivity, RecorderService::class.java).apply {
|
||||
startService(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopRecording() {
|
||||
timer.cancel()
|
||||
|
||||
Intent(this@MainActivity, RecorderService::class.java).apply {
|
||||
stopService(this)
|
||||
}
|
||||
|
||||
recorder?.apply {
|
||||
stop()
|
||||
release()
|
||||
|
||||
ensureBackgroundThread {
|
||||
if (isQPlus()) {
|
||||
addFileInNewMediaStore()
|
||||
} else {
|
||||
addFileInLegacyMediaStore()
|
||||
}
|
||||
}
|
||||
}
|
||||
recorder = null
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun gotDurationEvent(event: Events.RecordingDuration) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private fun addFileInNewMediaStore() {
|
||||
val audioCollection = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||
|
||||
val storeFilename = currFilePath.getFilenameFromPath()
|
||||
val newSongDetails = ContentValues().apply {
|
||||
put(MediaStore.Audio.Media.DISPLAY_NAME, storeFilename)
|
||||
put(MediaStore.Audio.Media.TITLE, storeFilename)
|
||||
put(MediaStore.Audio.Media.MIME_TYPE, storeFilename.getMimeType())
|
||||
}
|
||||
|
||||
val newUri = contentResolver.insert(audioCollection, newSongDetails)
|
||||
if (newUri == null) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
return
|
||||
}
|
||||
|
||||
val outputStream = contentResolver.openOutputStream(newUri)
|
||||
val inputStream = getFileInputStreamSync(currFilePath)
|
||||
inputStream!!.copyTo(outputStream!!, DEFAULT_BUFFER_SIZE)
|
||||
recordingSavedSuccessfully(true)
|
||||
}
|
||||
|
||||
private fun addFileInLegacyMediaStore() {
|
||||
MediaScannerConnection.scanFile(
|
||||
this,
|
||||
arrayOf(currFilePath),
|
||||
arrayOf(currFilePath.getMimeType())
|
||||
) { _, _ -> recordingSavedSuccessfully(false) }
|
||||
}
|
||||
|
||||
private fun recordingSavedSuccessfully(showFilenameOnly: Boolean) {
|
||||
val title = if (showFilenameOnly) currFilePath.getFilenameFromPath() else currFilePath
|
||||
val msg = String.format(getString(R.string.recording_saved_successfully), title)
|
||||
toast(msg, Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
private fun getTimerTask() = object : TimerTask() {
|
||||
override fun run() {
|
||||
duration++
|
||||
runOnUiThread {
|
||||
updateRecordingDuration()
|
||||
}
|
||||
}
|
||||
updateRecordingDuration(event.duration)
|
||||
}
|
||||
|
||||
private fun getToggleButtonIcon(): Drawable {
|
||||
|
|
|
@ -1,37 +1,155 @@
|
|||
package com.simplemobiletools.voicerecorder.services
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.annotation.TargetApi
|
||||
import android.app.*
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.MediaRecorder
|
||||
import android.media.MediaScannerConnection
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.provider.MediaStore
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.simplemobiletools.commons.extensions.getLaunchIntent
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.helpers.isOreoPlus
|
||||
import com.simplemobiletools.commons.helpers.isQPlus
|
||||
import com.simplemobiletools.voicerecorder.R
|
||||
import com.simplemobiletools.voicerecorder.activities.SplashActivity
|
||||
import com.simplemobiletools.voicerecorder.helpers.GET_DURATION
|
||||
import com.simplemobiletools.voicerecorder.helpers.RECORDER_RUNNING_NOTIF_ID
|
||||
import com.simplemobiletools.voicerecorder.models.Events
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class RecorderService : Service() {
|
||||
private var currFilePath = ""
|
||||
private var duration = 0
|
||||
private var timer = Timer()
|
||||
private var recorder: MediaRecorder? = null
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? = null
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
super.onStartCommand(intent, flags, startId)
|
||||
|
||||
val action = intent.action
|
||||
if (action == GET_DURATION) {
|
||||
broadcastDuration()
|
||||
} else {
|
||||
startForeground(RECORDER_RUNNING_NOTIF_ID, showNotification())
|
||||
when (intent.action) {
|
||||
GET_DURATION -> broadcastDuration()
|
||||
else -> startRecording()
|
||||
}
|
||||
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
stopRecording()
|
||||
recorder?.release()
|
||||
recorder = null
|
||||
}
|
||||
|
||||
// mp4 output format with aac encoding should produce good enough mp3 files according to https://stackoverflow.com/a/33054794/1967672
|
||||
private fun startRecording() {
|
||||
startForeground(RECORDER_RUNNING_NOTIF_ID, showNotification())
|
||||
duration = 0
|
||||
broadcastDuration()
|
||||
|
||||
val baseFolder = if (isQPlus()) {
|
||||
cacheDir
|
||||
} else {
|
||||
val defaultFolder = File("$internalStoragePath/${getString(R.string.app_name)}")
|
||||
if (!defaultFolder.exists()) {
|
||||
defaultFolder.mkdir()
|
||||
}
|
||||
defaultFolder.absolutePath
|
||||
}
|
||||
|
||||
currFilePath = "$baseFolder/${getCurrentFormattedDateTime()}.mp3"
|
||||
recorder = MediaRecorder().apply {
|
||||
setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
|
||||
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
|
||||
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
|
||||
setOutputFile(currFilePath)
|
||||
|
||||
try {
|
||||
prepare()
|
||||
start()
|
||||
duration = 0
|
||||
timer = Timer()
|
||||
timer.scheduleAtFixedRate(getTimerTask(), 1000, 1000)
|
||||
} catch (e: IOException) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopRecording() {
|
||||
timer.cancel()
|
||||
|
||||
recorder?.apply {
|
||||
stop()
|
||||
release()
|
||||
|
||||
ensureBackgroundThread {
|
||||
if (isQPlus()) {
|
||||
addFileInNewMediaStore()
|
||||
} else {
|
||||
addFileInLegacyMediaStore()
|
||||
}
|
||||
}
|
||||
}
|
||||
recorder = null
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private fun addFileInNewMediaStore() {
|
||||
val audioCollection = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||
|
||||
val storeFilename = currFilePath.getFilenameFromPath()
|
||||
val newSongDetails = ContentValues().apply {
|
||||
put(MediaStore.Audio.Media.DISPLAY_NAME, storeFilename)
|
||||
put(MediaStore.Audio.Media.TITLE, storeFilename)
|
||||
put(MediaStore.Audio.Media.MIME_TYPE, storeFilename.getMimeType())
|
||||
}
|
||||
|
||||
val newUri = contentResolver.insert(audioCollection, newSongDetails)
|
||||
if (newUri == null) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
return
|
||||
}
|
||||
|
||||
val outputStream = contentResolver.openOutputStream(newUri)
|
||||
val inputStream = getFileInputStreamSync(currFilePath)
|
||||
inputStream!!.copyTo(outputStream!!, DEFAULT_BUFFER_SIZE)
|
||||
recordingSavedSuccessfully(true)
|
||||
}
|
||||
|
||||
private fun addFileInLegacyMediaStore() {
|
||||
MediaScannerConnection.scanFile(
|
||||
this,
|
||||
arrayOf(currFilePath),
|
||||
arrayOf(currFilePath.getMimeType())
|
||||
) { _, _ -> recordingSavedSuccessfully(false) }
|
||||
}
|
||||
|
||||
private fun recordingSavedSuccessfully(showFilenameOnly: Boolean) {
|
||||
val title = if (showFilenameOnly) currFilePath.getFilenameFromPath() else currFilePath
|
||||
val msg = String.format(getString(R.string.recording_saved_successfully), title)
|
||||
toast(msg, Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
private fun getTimerTask() = object : TimerTask() {
|
||||
override fun run() {
|
||||
duration++
|
||||
broadcastDuration()
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
private fun showNotification(): Notification {
|
||||
val channelId = "simple_recorder"
|
||||
|
@ -66,6 +184,6 @@ class RecorderService : Service() {
|
|||
}
|
||||
|
||||
private fun broadcastDuration() {
|
||||
EventBus.getDefault().post(Events.RecordingDuration(3))
|
||||
EventBus.getDefault().post(Events.RecordingDuration(duration))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue