Создание собственного Android-диктофона с помощью Kotlin – Devcolibri

Создание собственного Android-диктофона с помощью Kotlin

Создание собственного Android-диктофона с помощью Kotlin

Мультимедийный фреймворк Android поддерживает запись и воспроизведение аудио. В этой статье я покажу, как разработать простое приложение для звукозаписи, которое будет записывать аудио и сохранять его в локальном хранилище Android-устройства с помощью MediaRecorder из Android SDK.

Вы также узнаете, как запросить разрешения у пользователя в режиме реального времени и как работать с локальным хранилищем Android-устройства.

Создание пользовательского интерфейса

Сперва нам нужно создать интерфейс для звукозаписи. Это простой layout с тремя кнопками, которые будут использоваться для запуска, приостановки/возобновления и остановки записи.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent" android:layout_height="match_parent"
        tools:context=".MainActivity">

    <TextView
            android:id="@+id/textview_sound_recorder_heading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Sound Recorder"
            android:layout_centerHorizontal="true"
            android:textSize="32dp"
            android:textStyle="bold"
            android:textColor="#000"
            android:layout_marginTop="32dp"/>

    <Button
            android:id="@+id/button_start_recording"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Start"
            android:layout_alignParentBottom="true"
            android:layout_marginLeft="32dp"
            android:layout_marginBottom="32dp"
            android:layout_centerVertical="true"/>

    <Button
            android:id="@+id/button_pause_recording"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Pause"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:layout_marginBottom="32dp"/>

    <Button
            android:id="@+id/button_stop_recording"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Stop"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="32dp"
            android:layout_marginRight="32dp"/>
</RelativeLayout>

Запрос требуемых разрешений

После создания пользовательского интерфейса мы можем начать использовать MediaRecorder для реализации основной функциональности нашего приложения. Но сначала нам нужно запросить необходимые разрешения для записи аудио и доступа к локальному хранилищу. Cделаем мы это это с помощью нескольких простых строк кода в нашем файле AndroidManifest.xml:

<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Также нужно проверить, одобрил ли пользователь разрешения, прежде чем мы сможем использовать наш MediaRecorder. Сделаем это в Activity MainActivity.kt:

if (ContextCompat.checkSelfPermission(this,
      Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
      Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
  val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE)
  ActivityCompat.requestPermissions(this, permissions,0)
}

Примечание: позже эти строки кода будут перемещены в OnClickListener кнопки начала записи аудио, чтобы мы могли убедиться, что MediaRecorder не будет запущен без необходимых разрешений.

Запись и сохранение аудио

Добавление OnClickListeners

Добавим слушатели к кнопкам, чтобы они реагировали на пользовательские события. Как я упоминал ранее, проверка на наличие необходимых разрешений будет добавлена в OnClickListener кнопки начала записи аудио:

button_start_recording.setOnClickListener {
    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE)
        ActivityCompat.requestPermissions(this, permissions,0)
    } else {
        startRecording()
    }
}

button_stop_recording.setOnClickListener{
    stopRecording()
}

button_pause_recording.setOnClickListener {
    pauseRecording()
}

Настройка MediaRecorder

Далее нам нужно указать путь для сохранения аудио и настроить MediaRecorder.

private var output: String? = null
private var mediaRecorder: MediaRecorder? = null
private var state: Boolean = false
private var recordingStopped: Boolean = false

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    output = Environment.getExternalStorageDirectory().absolutePath + "/recording.mp3"
    mediaRecorder = MediaRecorder()
    
    mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
    mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
    mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
    mediaRecorder?.setOutputFile(output)
}

Мы берём путь к корню нашего внешнего хранилища и добавляем в него имя нашей записи и тип файла. После этого мы создаём объект MediaRecorder и определяем источник звука, аудиокодер, формат и файл для записи.

Запись и сохранение аудио

Код, используемый для запуска MediaRecorder, определяется в OnClickListener кнопки начала записи аудио:

private fun startRecording() {
    try {
        mediaRecorder?.prepare()
        mediaRecorder?.start()
        state = true
        Toast.makeText(this, "Recording started!", Toast.LENGTH_SHORT).show()
    } catch (e: IllegalStateException) {
        e.printStackTrace()
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

Как видите, нужно вызвать функцию prepare, прежде чем мы сможем начать запись. Мы также встраиваем вызов в блок try-catch, чтобы приложение не сломалось при сбое функции prepare.

OnClickListeners кнопки остановки записи очень похож на код выше.

private fun stopRecording() {
    if (state) {
        mediaRecorder?.stop()
        mediaRecorder?.release()
        state = false
    } else {
        Toast.makeText(this, "You are not recording right now!", Toast.LENGTH_SHORT).show()
    }
}

Здесь мы проверяем, работает ли в данный момент MediaRecorder, прежде чем мы остановим запись, потому что наше приложение сломается, если метод stop будет вызван, в то время как MediaRecorder не будет запущен. После этого мы меняем переменную состояния на false, чтобы пользователь не мог снова нажать кнопку остановки.

Нам осталось определить OnClickListener для кнопки приостановки/возобновления.

@SuppressLint("RestrictedApi", "SetTextI18n")
@TargetApi(Build.VERSION_CODES.N)
private fun pauseRecording() {
    if (state) {
        if (!recordingStopped) {
            Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show()
            mediaRecorder?.pause()
            recordingStopped = true
            button_pause_recording.text = "Resume"
        } else {
            resumeRecording()
        }
    }
}

@SuppressLint("RestrictedApi", "SetTextI18n")
@TargetApi(Build.VERSION_CODES.N)
private fun resumeRecording() {
    Toast.makeText(this,"Resume!", Toast.LENGTH_SHORT).show()
    mediaRecorder?.resume()
    button_pause_recording.text = "Pause"
    recordingStopped = false
}

В этих двух методах мы проверяем, работает ли MediaRecorder. Если работает, мы приостановим запись и изменим текст кнопки для возобновления. При повторном нажатии запись возобновится.

Наконец, мы можем записать аудио и прослушать его, открыв файл recording.mp3, который будет сохранён в нашем локальном хранилище. Просто откройте проводник файлов и сделайте поиск по имени файла recording.mp3.

Исходный код

Вот полный исходный код нашего приложения:

package com.example.android.soundrecorder

import android.Manifest
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.content.pm.PackageManager
import android.media.MediaRecorder
import android.os.Build
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
import java.io.IOException

class MainActivity : AppCompatActivity() {

    private var output: String? = null
    private var mediaRecorder: MediaRecorder? = null
    private var state: Boolean = false
    private var recordingStopped: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mediaRecorder = MediaRecorder()
        output = Environment.getExternalStorageDirectory().absolutePath + "/recording.mp3"

        mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
        mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
        mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
        mediaRecorder?.setOutputFile(output)

        button_start_recording.setOnClickListener {
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE)
                ActivityCompat.requestPermissions(this, permissions,0)
            } else {
                startRecording()
            }
        }

        button_stop_recording.setOnClickListener{
            stopRecording()
        }

        button_pause_recording.setOnClickListener {
            pauseRecording()
        }
    }

    private fun startRecording() {
        try {
            mediaRecorder?.prepare()
            mediaRecorder?.start()
            state = true
            Toast.makeText(this, "Recording started!", Toast.LENGTH_SHORT).show()
        } catch (e: IllegalStateException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    @SuppressLint("RestrictedApi", "SetTextI18n")
    @TargetApi(Build.VERSION_CODES.N)
    private fun pauseRecording() {
        if (state) {
            if (!recordingStopped) {
                Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show()
                mediaRecorder?.pause()
                recordingStopped = true
                button_pause_recording.text = "Resume"
            } else {
                resumeRecording()
            }
        }
    }

    @SuppressLint("RestrictedApi", "SetTextI18n")
    @TargetApi(Build.VERSION_CODES.N)
    private fun resumeRecording() {
        Toast.makeText(this,"Resume!", Toast.LENGTH_SHORT).show()
        mediaRecorder?.resume()
        button_pause_recording.text = "Pause"
        recordingStopped = false
    }

    private fun stopRecording(){
        if (state) {
            mediaRecorder?.stop()
            mediaRecorder?.release()
            state = false
        } else {
            Toast.makeText(this, "You are not recording right now!", Toast.LENGTH_SHORT).show()
        }
    }
}

Заключение

Теперь вы знаете, как работает MediaRecorder, как запрашивать разрешения в режиме реального времени и почему это важно делать. Вы также узнали о локальном хранилище вашего устройства Android и о том, как хранить в нём данные.

Более сложная версия этого приложения, в которой есть некоторые дополнительные функции, такие как воспроизведение ваших записей с помощью MediaPlayer, доступна на Github.

Перевод статьи «Create an Android Sound Recorder using Kotlin»

ПОХОЖИЕ ПУБЛИКАЦИИ

1517
21/03/2019

1 комментариев к статье "Создание собственного Android-диктофона с помощью Kotlin"

  1. Можно ли как-то убрать звуковой сигнал во время выполнения этих команд? (по-моему одинарный при старте и двойной при стопе)
    mediaRecorder?.start() и mediaRecorder?.stop()

Добавить комментарий

Сайт использует cookie-файлы для того, чтобы вам было удобнее им пользоваться. Для продолжения работы с сайтом, вам необходимо принять использование cookie-файлов.

Я ознакомлен(а)