ساخت اپلیکیشن ضبط صدا با کاتلین (Kotlin) — به زبان ساده
فریمورک مالتیمدیا در اندروید از ضبط و پخش صدا نیز پشتیبانی میکند. در این مطلب مراحل ساخت یک اپلیکیشن ضبط صدا را بررسی میکنیم که قابلیت ضبط کردن صدا و ذخیرهسازی آن در حافظه داخلی دستگاه اندرویدی را دارد. این کار با استفاده از MediaRecorder که از سوی SDK اندروید ارائه شده است صورت میگیرد. همچنین با روش تقاضای آنی «دسترسیهای کاربر» (User Permissions) و شیوه کار با حافظه داخلی یک دستگاه اندرویدی آشنا خواهیم شد.
ایجاد رابط کاربری (UI)
ابتدا باید رابط کاربری اپلیکیشن ضبط صدای خودمان را بسازیم.
این رابط از یک طرحبندی ساده با 3 دکمه تشکیل یافته است که برای آغاز، مکث/ ازسرگیری و توقف ضبط صدا استفاده میشوند.
1<?xml version="1.0" encoding="utf-8"?>
2<RelativeLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:layout_width="match_parent" android:layout_height="match_parent"
6 tools:context=".MainActivity">
7
8 <TextView
9 android:id="@+id/textview_sound_recorder_heading"
10 android:layout_width="wrap_content"
11 android:layout_height="wrap_content"
12 android:text="Sound Recorder"
13 android:layout_centerHorizontal="true"
14 android:textSize="32dp"
15 android:textStyle="bold"
16 android:textColor="#000"
17 android:layout_marginTop="32dp"
18 />
19
20 <Button
21 android:id="@+id/button_start_recording"
22 android:layout_width="wrap_content"
23 android:layout_height="wrap_content"
24 android:text="Start"
25 android:layout_alignParentBottom="true"
26 android:layout_marginLeft="32dp"
27 android:layout_marginBottom="32dp"
28 android:layout_centerVertical="true"/>
29
30 <Button
31 android:id="@+id/button_pause_recording"
32 android:layout_width="wrap_content"
33 android:layout_height="wrap_content"
34 android:text="Pause"
35 android:layout_alignParentBottom="true"
36 android:layout_centerHorizontal="true"
37 android:layout_marginBottom="32dp"/>
38
39 <Button
40 android:id="@+id/button_stop_recording"
41 android:layout_width="wrap_content"
42 android:layout_height="wrap_content"
43 android:text="Stop"
44 android:layout_alignParentBottom="true"
45 android:layout_alignParentRight="true"
46 android:layout_marginBottom="32dp"
47 android:layout_marginRight="32dp"/>
48</RelativeLayout>
تقاضای مجوزهای مورد نیاز
پس از ایجاد رابط کاربری میتوانیم با استفاده از MediaRecorder شروع به ساخت اپلیکیشن خود بکنیم. اما قبل از آن باید مجوز دسترسیهای مورد نیاز خود برای ضبط صدا و استفاده از حافظه داخلی را از کاربر بخواهیم. این کار با چند خط کد ساده در فایل AndroidManifest.xml ممکن است:
1<uses-permission android:name="android.permission.RECORD_AUDIO"/>
2<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
همچنین پیش از استفاده عملی از MediaRecorder لازم است که بررسی کنیم آیا کاربر واقعاً مجوز دسترسیها را ارائه کرده است یا نه؛ این کار را در فایل MainActivity.kt انجام میدهیم:
1if (ContextCompat.checkSelfPermission(this,
2 Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
3 Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
4 val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE)
5 ActivityCompat.requestPermissions(this, permissions,0)
6}
نکته: این خطوط کد در ادامه به فراخوانی OnClickListener در دکمه start_recording انتقال خواهند یافت تا بتوانیم مطمئن باشیم که MediaRecorder بدون در اختیار داشتن دسترسیهای لازم شروع به کار نمیکند.
ضبط و ذخیرهسازی صدا
پس از تهیه مقدمات کار اینک نوبت آن رسیده است که به صورت عملی اقدام به ضبط و ذخیرهسازی صدا بکنیم.
افزودن OnClickListeners
ابتدا باید یک OnClickListeners به دکمههای خود اضافه کنیم تا مطمئن شویم که به رویدادهای اجرا شده از سوی کاربر پاسخ میدهند. همان طور که پیشتر گفتیم وجود مجوز دسترسیهای صحیح را پیش از افزودن فراخوانی OnClickListener به دکمه start_recording بررسی میکنیم.
1button_start_recording.setOnClickListener {
2 if (ContextCompat.checkSelfPermission(this,
3 Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
4 Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
5 val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE)
6 ActivityCompat.requestPermissions(this, permissions,0)
7 } else {
8 startRecording()
9 }
10}
11
12button_stop_recording.setOnClickListener{
13 stopRecording()
14}
15
16button_pause_recording.setOnClickListener {
17 pauseRecording()
18}
پیکربندی MediaRecorder
سپس باید یک مسیر برای خروجی تعریف کرده و شروع به پیکربندی MediaRecorder خودمان کنیم.
1private var output: String? = null
2private var mediaRecorder: MediaRecorder? = null
3private var state: Boolean = false
4private var recordingStopped: Boolean = false
5
6override fun onCreate(savedInstanceState: Bundle?) {
7 super.onCreate(savedInstanceState)
8 setContentView(R.layout.activity_main)
9
10 output = Environment.getExternalStorageDirectory().absolutePath + "/recording.mp3"
11 mediaRecorder = MediaRecorder()
12
13 mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
14 mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
15 mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
16 mediaRecorder?.setOutputFile(output)
17}
در کد فوق مسیر به سوی ریشه حافظه دستگاه را یافته و نام فایل ضبط شده و نوع فایل را به آن اضافه میکنیم. پس از آن شیء MediaRecorder را ایجاد کرده و منبع صدا، انکودر صدا، قالب خروجی و فایل خروجی را تعریف میکنیم.
ضبط و ذخیرهسازی صوت
کد مورد استفاده برای آغاز به کار MediaRecorder در رویداد OnClickListener برای دکمه start_recording قرار میگیرد:
1private fun startRecording() {
2 try {
3 mediaRecorder?.prepare()
4 mediaRecorder?.start()
5 state = true
6 Toast.makeText(this, "Recording started!", Toast.LENGTH_SHORT).show()
7 } catch (e: IllegalStateException) {
8 e.printStackTrace()
9 } catch (e: IOException) {
10 e.printStackTrace()
11 }
12}
همان طور که مشاهده میکنید ما باید تابع prepare را پیش از آن که اقدام به ضبط صدا بکنیم، فراخوانی کرده باشیم. همچنین آن را درون بلوک try-catch جاسازی کردهایم تا مطمئن شویم که اپلیکیشن هنگامی که تابع prepares ناموفق باشد، از کار نمیافتد.
متد OnClickListeners دکمه Stop کاملاً شبیه به آن متدی است که در بخش قبل تعریف کردیم.
1private fun stopRecording(){
2 if(state){
3 mediaRecorder?.stop()
4 mediaRecorder?.release()
5 state = false
6 }else{
7 Toast.makeText(this, "You are not recording right now!", Toast.LENGTH_SHORT).show()
8 }
9}
در کد فوق بررسی میکنیم که آیا MediaRecorder در حال حاضر و پیش از توقف عملی recording در حال اجرا است یا نه، چون اپلیکیشن در زمانی که متد stop هنگام عدم ضبط صدا فراخوانی شود، ممکن است موجب از کار افتادن برنامه شود. پس از آن متغیر state را به حالت false تغییر میدهیم تا مانع این بشویم که کاربر مجدداً روی دکمه stop بزند.
سپس باید OnClickListener را برای دکمه pause/resume تعریف کنیم.
1@SuppressLint("RestrictedApi", "SetTextI18n")
2@TargetApi(Build.VERSION_CODES.N)
3private fun pauseRecording() {
4 if(state) {
5 if(!recordingStopped){
6 Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show()
7 mediaRecorder?.pause()
8 recordingStopped = true
9 button_pause_recording.text = "Resume"
10 }else{
11 resumeRecording()
12 }
13 }
14}
15
16@SuppressLint("RestrictedApi", "SetTextI18n")
17@TargetApi(Build.VERSION_CODES.N)
18private fun resumeRecording() {
19 Toast.makeText(this,"Resume!", Toast.LENGTH_SHORT).show()
20 mediaRecorder?.resume()
21 button_pause_recording.text = "Pause"
22 recordingStopped = false
23}
ما در این دو متد بررسی میکنیم که آیا MediaRecorder در حال اجرا است یا نه. اگر چنین باشد فرایند ضبط صدا را متوقف میکنیم و متن دکمه را به resume تغییر میدهیم. اگر دکمه دوباره کلیک شود فرایند ضبط صدا را از نقطهای که باقی مانده بود از سر میگیریم.
در نهایت ما میتوانیم ضبط صدا را آغاز کرده و با باز کردن فایل recording.mp3 که در حافظه داخلی دستگاه ذخیره شده است به آن گوش دهیم.
کد کامل اپلیکیشن ضبط صدا در کاتلین: MainActivity.kt
در ادامه کد منبع کامل اپلیکیشن ابتدایی ضبط صدا را مشاهده میکنید:
1package com.example.android.soundrecorder
2
3import android.Manifest
4import android.annotation.SuppressLint
5import android.annotation.TargetApi
6import android.content.pm.PackageManager
7import android.media.MediaRecorder
8import android.os.Build
9import android.support.v7.app.AppCompatActivity
10import android.os.Bundle
11import android.os.Environment
12import android.support.v4.app.ActivityCompat
13import android.support.v4.content.ContextCompat
14import android.widget.Toast
15import kotlinx.android.synthetic.main.activity_main.*
16import java.io.IOException
17
18class MainActivity : AppCompatActivity() {
19
20 private var output: String? = null
21 private var mediaRecorder: MediaRecorder? = null
22 private var state: Boolean = false
23 private var recordingStopped: Boolean = false
24
25 override fun onCreate(savedInstanceState: Bundle?) {
26 super.onCreate(savedInstanceState)
27 setContentView(R.layout.activity_main)
28
29 mediaRecorder = MediaRecorder()
30 output = Environment.getExternalStorageDirectory().absolutePath + "/recording.mp3"
31
32 mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
33 mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
34 mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
35 mediaRecorder?.setOutputFile(output)
36
37 button_start_recording.setOnClickListener {
38 if (ContextCompat.checkSelfPermission(this,
39 Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
40 Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
41 val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE)
42 ActivityCompat.requestPermissions(this, permissions,0)
43 } else {
44 startRecording()
45 }
46 }
47
48 button_stop_recording.setOnClickListener{
49 stopRecording()
50 }
51
52 button_pause_recording.setOnClickListener {
53 pauseRecording()
54 }
55 }
56
57 private fun startRecording() {
58 try {
59 mediaRecorder?.prepare()
60 mediaRecorder?.start()
61 state = true
62 Toast.makeText(this, "Recording started!", Toast.LENGTH_SHORT).show()
63 } catch (e: IllegalStateException) {
64 e.printStackTrace()
65 } catch (e: IOException) {
66 e.printStackTrace()
67 }
68 }
69
70 @SuppressLint("RestrictedApi", "SetTextI18n")
71 @TargetApi(Build.VERSION_CODES.N)
72 private fun pauseRecording() {
73 if(state) {
74 if(!recordingStopped){
75 Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show()
76 mediaRecorder?.pause()
77 recordingStopped = true
78 button_pause_recording.text = "Resume"
79 }else{
80 resumeRecording()
81 }
82 }
83 }
84
85 @SuppressLint("RestrictedApi", "SetTextI18n")
86 @TargetApi(Build.VERSION_CODES.N)
87 private fun resumeRecording() {
88 Toast.makeText(this,"Resume!", Toast.LENGTH_SHORT).show()
89 mediaRecorder?.resume()
90 button_pause_recording.text = "Pause"
91 recordingStopped = false
92 }
93
94 private fun stopRecording(){
95 if(state){
96 mediaRecorder?.stop()
97 mediaRecorder?.release()
98 state = false
99 }else{
100 Toast.makeText(this, "You are not recording right now!", Toast.LENGTH_SHORT).show()
101 }
102 }
103}
بررسی فایل صدا
پس از ضبط کردن صدا باید به حافظه داخلی دستگاه اندرویدی خود بروید تا بتوانید به فایل ضبط شده گوش بدهید. اگر در مورد مسیر این کار مطمئن نیستید، مراحل کار را در ادامه توضیح دادهایم:
- اپلیکیشن file را روی دستگاه اندرویدی خود باز کنید.
- به بخش local بروید.
- به دنبال فایلی با نام recording.mp3 بگردید.
سخن پایانی
بدین ترتیب به پایان این راهنما با موضوع اپلیکیشن مقدماتی ضبط صدا در کاتلین رسیدیم. اینک شما با طرز کار MediaRecorder آشنا شدهاید و میدانید که چگونه میتوانید به صورت همزمان تقاضای دسترسی بکنید و چرا این کار مهم است. همچنین نکاتی را در مورد حافظه داخلی دستگاه اندرویدی و شیوه ذخیرهسازی دادهها در آن آموختید. نسخه پیشرفتهتری از این اپلیکیشن که مجهز به امکانات بیشتری مانند پخش صدای ضبط شده با استفاده از MediaPlayer است را میتوانید در این ریپوی گیتهاب (+) مشاهده کنید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی اندروید
- آموزش برنامه نویسی اندروید (Android) – تکمیلی
- مجموعه آموزشهای پروژهمحور برنامهنویسی اندروید
- برنامه نویسی Kotlin — مقدمهای بر برنامهنویسی اندروید با زبان کاتلین
- برنامه نویسی Kotlin — ایجاد الگوهای طراحی اندروید با کاتلین
- ۷ زبان برنامه نویسی برای نوشتن اپلیکیشنهای اندروید — راهنمای صفر تا صد
==
اجرا شد ولی فایلاش کجا میره هرچی گشتم پیدا نکردم
سلام خسته نباشید
@SuppressLint(“RestrictedApi”, “SetTextI18n”)
@TargetApi(Build.VERSION_CODES.N)
این دو کد برای چیه؟
button_pause_recording.text = “Pause”
و این کد به خطا بر برخوردم
Unresolved reference: button_pause_recording