moving the actual recording into the service

This commit is contained in:
tibbi 2020-03-30 15:16:44 +02:00
parent 8255def4cb
commit 9cea12a1e2
3 changed files with 133 additions and 126 deletions

View File

@ -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'
}

View File

@ -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 {

View File

@ -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))
}
}