diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..217e5c5
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index d3f8ab2..feb8520 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,48 +1,62 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
- compileSdkVersion 30
- buildToolsVersion "30.0.1"
+ compileSdkVersion 33
defaultConfig {
applicationId "com.alterpat.voicerecorder"
- minSdkVersion 24
- targetSdkVersion 30
+ minSdkVersion 26
+ targetSdkVersion 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
-
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+ kotlinOptions {
+ jvmTarget = '17'
+ }
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
+
+ viewBinding {
+ enabled = true
+ }
+
+ namespace 'com.alterpat.voicerecorder'
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
- implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
- implementation 'androidx.core:core-ktx:1.3.2'
- implementation 'androidx.appcompat:appcompat:1.2.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test.ext:junit:1.1.2'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:1.8.21"
+ implementation 'androidx.core:core-ktx:1.10.0'
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.5'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
- implementation 'com.google.android.material:material:1.4.0-alpha02'
+ implementation 'com.google.android.material:material:1.9.0'
def room_version = "2.2.6"
- implementation "androidx.room:room-runtime:$room_version"
+ implementation "androidx.room:room-runtime:2.5.1"
kapt "androidx.room:room-compiler:$room_version"
// optional - Kotlin Extensions and Coroutines support for Room
- implementation "androidx.room:room-ktx:$room_version"
+ //ROOM
+ implementation "androidx.room:room-runtime:2.5.1"
+ kapt "androidx.room:room-compiler:2.5.1"
+ implementation "androidx.room:room-ktx:2.5.1"
+ implementation "androidx.room:room-paging:2.5.1"
}
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/alterpat/voicerecorder/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/alterpat/voicerecorder/ExampleInstrumentedTest.kt
index 8b818ec..f721d4c 100644
--- a/app/src/androidTest/java/com/alterpat/voicerecorder/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/com/alterpat/voicerecorder/ExampleInstrumentedTest.kt
@@ -1,13 +1,11 @@
package com.alterpat.voicerecorder
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.Assert.*
-
/**
* Instrumented test, which will execute on an Android device.
*
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f32a027..35ad590 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,5 @@
-
+
@@ -11,11 +10,12 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
-
+
+ android:windowSoftInputMode="adjustResize"
+ android:exported="true">
diff --git a/app/src/main/java/com/alterpat/voicerecorder/Adapter.kt b/app/src/main/java/com/alterpat/voicerecorder/Adapter.kt
index ad9f1f5..4a98066 100644
--- a/app/src/main/java/com/alterpat/voicerecorder/Adapter.kt
+++ b/app/src/main/java/com/alterpat/voicerecorder/Adapter.kt
@@ -7,45 +7,52 @@ import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
+import com.alterpat.voicerecorder.databinding.ItemviewLayoutBinding
import com.alterpat.voicerecorder.db.AudioRecord
import java.text.SimpleDateFormat
-import java.util.*
+import java.util.Date
-class Adapter(private var audioRecords: List,
- private val listener: OnItemClickListener) : RecyclerView.Adapter() {
+class Adapter(
+ private var audioRecords: List,
+ private val listener: OnItemClickListener
+) : RecyclerView.Adapter() {
private var editMode = false
+ lateinit var binding : ItemviewLayoutBinding
interface OnItemClickListener {
fun onItemClick(position: Int)
fun onItemLongClick(position: Int)
}
- inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener, View.OnLongClickListener {
- var filename : TextView = itemView.findViewById(R.id.filename)
- var fileMeta : TextView = itemView.findViewById(R.id.file_meta)
- var checkBox : CheckBox = itemView.findViewById(R.id.checkbox)
+ inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
+ View.OnClickListener, View.OnLongClickListener {
+ var filename: TextView = itemView.findViewById(R.id.filename)
+ var fileMeta: TextView = itemView.findViewById(R.id.file_meta)
+ var checkBox: CheckBox = itemView.findViewById(R.id.checkbox)
init {
itemView.setOnClickListener(this)
itemView.setOnLongClickListener(this)
}
+
override fun onClick(p0: View?) {
val position = adapterPosition // property of the recyclerview class
- if(position != RecyclerView.NO_POSITION)
+ if (position != RecyclerView.NO_POSITION)
listener.onItemClick(position)
}
override fun onLongClick(p0: View?): Boolean {
val position = adapterPosition // property of the recyclerview class
- if(position != RecyclerView.NO_POSITION)
+ if (position != RecyclerView.NO_POSITION)
listener.onItemLongClick(position)
return true
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
- val view = LayoutInflater.from(parent.context).inflate(R.layout.itemview_layout, parent, false)
+ binding = ItemviewLayoutBinding.inflate(LayoutInflater.from(parent.context),parent,false)
+ val view = binding.root
return ViewHolder(view)
}
@@ -54,22 +61,22 @@ class Adapter(private var audioRecords: List,
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- if(position != RecyclerView.NO_POSITION){
+ if (position != RecyclerView.NO_POSITION) {
var audioRecord = audioRecords[position]
holder.filename.text = audioRecord.filename
val sdf = SimpleDateFormat("dd/MM/yy")
val netDate = Date(audioRecord.date)
- val date =sdf.format(netDate)
+ val date = sdf.format(netDate)
holder.fileMeta.text = "${audioRecord.duration} $date"
Log.d("ListingTag", audioRecord.isChecked.toString())
- if(editMode) {
+ if (editMode) {
holder.checkBox.visibility = View.VISIBLE
if (audioRecord.isChecked)
holder.checkBox.isChecked = audioRecord.isChecked
- }else {
+ } else {
holder.checkBox.visibility = View.GONE
audioRecord.isChecked = false
holder.checkBox.isChecked = false
@@ -77,21 +84,19 @@ class Adapter(private var audioRecords: List,
}
}
- fun setData(audioRecords: List){
+ fun setData(audioRecords: List) {
this.audioRecords = audioRecords
notifyDataSetChanged()
}
- fun setEditMode(mode: Boolean){
+ fun setEditMode(mode: Boolean) {
editMode = mode
notifyDataSetChanged()
}
- fun isEditMode():Boolean{
+ fun isEditMode(): Boolean {
return editMode
}
-
-
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alterpat/voicerecorder/BottomSheet.kt b/app/src/main/java/com/alterpat/voicerecorder/BottomSheet.kt
index 94803dd..fe13106 100644
--- a/app/src/main/java/com/alterpat/voicerecorder/BottomSheet.kt
+++ b/app/src/main/java/com/alterpat/voicerecorder/BottomSheet.kt
@@ -8,17 +8,16 @@ import android.view.ViewGroup
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.Button
+import com.alterpat.voicerecorder.databinding.ActivityMainBinding
+import com.alterpat.voicerecorder.databinding.BottomSheetBinding
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.textfield.TextInputEditText
import java.io.File
-class BottomSheet: BottomSheetDialogFragment {
+class BottomSheet : BottomSheetDialogFragment {
- // Step 1 - This interface defines the type of messages I want to communicate to my owner
interface OnClickListener {
- // These methods are the different events and
- // need to pass relevant arguments related to the event triggered
fun onCancelClicked()
fun onOkClicked(filePath: String, filename: String)
}
@@ -26,11 +25,12 @@ class BottomSheet: BottomSheetDialogFragment {
// Step 2 - This variable represents the listener passed in by the owning object
// The listener must implement the events interface and passes messages up to the parent.
private lateinit var listener: OnClickListener
+ private lateinit var binding: BottomSheetBinding
private lateinit var filename: String
private lateinit var dirPath: String
- constructor(dirPath: String, filename : String, listener: OnClickListener){
+ constructor(dirPath: String, filename: String, listener: OnClickListener) {
this.dirPath = dirPath
this.filename = filename
this.listener = listener
@@ -41,7 +41,8 @@ class BottomSheet: BottomSheetDialogFragment {
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
- var view = inflater.inflate(R.layout.bottom_sheet, container)
+ binding = BottomSheetBinding.inflate(layoutInflater)
+ var view = binding.root
var editText = view.findViewById(R.id.filenameInput)
@@ -61,9 +62,9 @@ class BottomSheet: BottomSheetDialogFragment {
// update filename if need
val updatedFilename = editText.text.toString()
- if(updatedFilename != filename){
+ if (updatedFilename != filename) {
var newFile = File("$dirPath$updatedFilename.mp3")
- File(dirPath+filename).renameTo(newFile)
+ File(dirPath + filename).renameTo(newFile)
}
// add entry to db
@@ -80,7 +81,7 @@ class BottomSheet: BottomSheetDialogFragment {
// hide keyboard
hideKeyboard(view)
// delete file from storage
- File(dirPath+filename).delete()
+ File(dirPath + filename).delete()
// dismiss dialog
dismiss()
@@ -95,7 +96,8 @@ class BottomSheet: BottomSheetDialogFragment {
private fun showKeyboard(view: View) {
if (view.requestFocus()) {
- val imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
+ val imm =
+ view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
imm?.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
}
}
diff --git a/app/src/main/java/com/alterpat/voicerecorder/ListingActivity.kt b/app/src/main/java/com/alterpat/voicerecorder/ListingActivity.kt
index 6962b10..ed3a4b0 100644
--- a/app/src/main/java/com/alterpat/voicerecorder/ListingActivity.kt
+++ b/app/src/main/java/com/alterpat/voicerecorder/ListingActivity.kt
@@ -6,7 +6,6 @@ import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.Menu
-import android.view.MenuItem
import android.view.View
import android.widget.LinearLayout
import android.widget.Toast
@@ -14,53 +13,56 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.room.Room
+import com.alterpat.voicerecorder.databinding.ActivityListingBinding
import com.alterpat.voicerecorder.db.AppDatabase
import com.alterpat.voicerecorder.db.AudioRecord
import com.google.android.material.bottomsheet.BottomSheetBehavior
-import kotlinx.android.synthetic.main.activity_listing.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class ListingActivity : AppCompatActivity(), Adapter.OnItemClickListener {
- private lateinit var adapter : Adapter
- private lateinit var audioRecords : List
- private lateinit var db : AppDatabase
+ private lateinit var binding: ActivityListingBinding
+ private lateinit var adapter: Adapter
+ private lateinit var audioRecords: List
+ private lateinit var db: AppDatabase
private lateinit var bottomSheetBehavior: BottomSheetBehavior
- private lateinit var menu : Menu
+ private lateinit var menu: Menu
private var allSelected = false
private var nbSelected = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_listing)
+ binding = ActivityListingBinding.inflate(layoutInflater)
+ setContentView(binding.root)
- setSupportActionBar(toolbar)
+ setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
- toolbar.setNavigationOnClickListener {
+ binding.toolbar.setNavigationOnClickListener {
onBackPressed()
}
- bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
+ bottomSheetBehavior = BottomSheetBehavior.from(binding.bottomSheet)
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
audioRecords = emptyList()
adapter = Adapter(audioRecords, this)
- recyclerview.adapter = adapter
- recyclerview.layoutManager = LinearLayoutManager(this)
+ binding.recyclerview.adapter = adapter
+ binding.recyclerview.layoutManager = LinearLayoutManager(this)
db = Room.databaseBuilder(
this,
AppDatabase::class.java,
- "audioRecords")
+ "audioRecords"
+ )
//.fallbackToDestructiveMigration()
.build()
fetchAll()
- searchInput.addTextChangedListener(object : TextWatcher{
+ binding.searchInput.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
@@ -72,7 +74,7 @@ class ListingActivity : AppCompatActivity(), Adapter.OnItemClickListener {
})
- btnSelectAll.setOnClickListener {
+ binding.btnSelectAll.setOnClickListener {
allSelected = !allSelected
Log.d("ListingTag", allSelected.toString())
audioRecords.forEach {
@@ -85,30 +87,30 @@ class ListingActivity : AppCompatActivity(), Adapter.OnItemClickListener {
adapter.notifyDataSetChanged()
}
- btnClose.setOnClickListener {
+ binding.btnClose.setOnClickListener {
closeEditor()
}
- btnDelete.setOnClickListener {
+ binding.btnDelete.setOnClickListener {
closeEditor()
- var toDelete : List = audioRecords.filter { it.isChecked }
+ var toDelete: List = audioRecords.filter { it.isChecked }
audioRecords = audioRecords.filter { !it.isChecked }
GlobalScope.launch {
db.audioRecordDAO().delete(toDelete)
- if(audioRecords.isEmpty())
+ if (audioRecords.isEmpty())
fetchAll()
else
adapter.setData(audioRecords)
}
}
- btnRename.setOnClickListener {
+ binding.btnRename.setOnClickListener {
Toast.makeText(this, "rename clicked", Toast.LENGTH_SHORT).show()
}
}
- private fun closeEditor(){
+ private fun closeEditor() {
allSelected = false
adapter.setEditMode(false)
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
@@ -117,55 +119,63 @@ class ListingActivity : AppCompatActivity(), Adapter.OnItemClickListener {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
// show relative layout
- editorBar.visibility = View.GONE
+ binding.editorBar.visibility = View.GONE
nbSelected = 0
}
- private fun fetchAll(){
+ private fun fetchAll() {
GlobalScope.launch {
audioRecords = db.audioRecordDAO().getAll()
adapter.setData(audioRecords)
}
}
- private fun searchDatabase(query: String){
+ private fun searchDatabase(query: String) {
GlobalScope.launch {
audioRecords = db.audioRecordDAO().searchDatabase(query)
- runOnUiThread{
+ runOnUiThread {
adapter.setData(audioRecords)
}
}
}
- private fun updateBottomSheet(){
- when(nbSelected){
+ private fun updateBottomSheet() {
+ when (nbSelected) {
0 -> {
- btnRename.isClickable = false
- btnRename.background = ResourcesCompat.getDrawable(resources, R.drawable.ic_edit_disabled, theme)
- tvRename.setTextColor(resources.getColor(R.color.colorDisabled, theme))
- btnDelete.isClickable = false
- btnDelete.background = ResourcesCompat.getDrawable(resources, R.drawable.ic_delete_disabled2, theme)
- tvDelete.setTextColor(resources.getColor(R.color.colorDisabled, theme))
+ binding.btnRename.isClickable = false
+ binding.btnRename.background =
+ ResourcesCompat.getDrawable(resources, R.drawable.ic_edit_disabled, theme)
+ binding.tvRename.setTextColor(resources.getColor(R.color.colorDisabled, theme))
+ binding.btnDelete.isClickable = false
+ binding.btnDelete.background =
+ ResourcesCompat.getDrawable(resources, R.drawable.ic_delete_disabled2, theme)
+ binding.tvDelete.setTextColor(resources.getColor(R.color.colorDisabled, theme))
}
+
1 -> {
- btnRename.isClickable = true
- btnRename.background = ResourcesCompat.getDrawable(resources, R.drawable.ic_edit, theme)
- tvRename.setTextColor(resources.getColor(R.color.colorText, theme))
+ binding.btnRename.isClickable = true
+ binding.btnRename.background =
+ ResourcesCompat.getDrawable(resources, R.drawable.ic_edit, theme)
+ binding.tvRename.setTextColor(resources.getColor(R.color.colorText, theme))
- btnDelete.isClickable = true
- btnDelete.background = ResourcesCompat.getDrawable(resources, R.drawable.ic_delete, theme)
- tvDelete.setTextColor(resources.getColor(R.color.colorText, theme))
+ binding.btnDelete.isClickable = true
+ binding.btnDelete.background =
+ ResourcesCompat.getDrawable(resources, R.drawable.ic_delete, theme)
+ binding.tvDelete.setTextColor(resources.getColor(R.color.colorText, theme))
}
+
else -> {
- btnRename.isClickable = false
- btnRename.background = ResourcesCompat.getDrawable(resources, R.drawable.ic_edit_disabled, theme)
- tvRename.setTextColor(resources.getColor(R.color.colorDisabled, theme))
+ binding.btnRename.isClickable = false
+ binding.btnRename.background =
+ ResourcesCompat.getDrawable(resources, R.drawable.ic_edit_disabled, theme)
+ binding.tvRename.setTextColor(resources.getColor(R.color.colorDisabled, theme))
- btnDelete.isClickable = true
- btnDelete.background = ResourcesCompat.getDrawable(resources, R.drawable.ic_delete, theme)
- tvDelete.setTextColor(resources.getColor(R.color.colorText, theme))
+ binding.btnDelete.isClickable = true
+ binding.btnDelete.background =
+ ResourcesCompat.getDrawable(resources, R.drawable.ic_delete, theme)
+ binding.tvDelete.setTextColor(resources.getColor(R.color.colorText, theme))
}
}
@@ -175,15 +185,15 @@ class ListingActivity : AppCompatActivity(), Adapter.OnItemClickListener {
var intent = Intent(this, PlayerActivity::class.java)
var audioRecord = audioRecords[position]
- if(adapter.isEditMode()){
+ if (adapter.isEditMode()) {
Log.d("ITEMCHANGE", audioRecord.isChecked.toString())
audioRecord.isChecked = !audioRecord.isChecked
adapter.notifyItemChanged(position)
- nbSelected = if (audioRecord.isChecked) nbSelected+1 else nbSelected-1
+ nbSelected = if (audioRecord.isChecked) nbSelected + 1 else nbSelected - 1
updateBottomSheet()
- }else{
+ } else {
intent.putExtra("filepath", audioRecord.filePath)
intent.putExtra("filename", audioRecord.filename)
startActivity(intent)
@@ -199,14 +209,14 @@ class ListingActivity : AppCompatActivity(), Adapter.OnItemClickListener {
audioRecord.isChecked = !audioRecord.isChecked
- nbSelected = if (audioRecord.isChecked) nbSelected+1 else nbSelected-1
+ nbSelected = if (audioRecord.isChecked) nbSelected + 1 else nbSelected - 1
updateBottomSheet()
// hide back button
supportActionBar?.setDisplayHomeAsUpEnabled(false)
supportActionBar?.setDisplayShowHomeEnabled(false)
// show relative layout
- editorBar.visibility = View.VISIBLE
+ binding.editorBar.visibility = View.VISIBLE
}
diff --git a/app/src/main/java/com/alterpat/voicerecorder/MainActivity.kt b/app/src/main/java/com/alterpat/voicerecorder/MainActivity.kt
index e5dbc7d..7ba2b3d 100644
--- a/app/src/main/java/com/alterpat/voicerecorder/MainActivity.kt
+++ b/app/src/main/java/com/alterpat/voicerecorder/MainActivity.kt
@@ -4,38 +4,39 @@ import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.media.MediaRecorder
+import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
+import android.view.LayoutInflater
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.room.Room
+import com.alterpat.voicerecorder.databinding.ActivityMainBinding
import com.alterpat.voicerecorder.db.AppDatabase
import com.alterpat.voicerecorder.db.AudioRecord
import com.alterpat.voicerecorder.tools.Timer
-import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.File
import java.io.IOException
-import java.lang.Exception
import java.text.SimpleDateFormat
-import java.util.*
+import java.util.Date
private const val LOG_TAG = "AudioRecordTest"
private const val REQUEST_RECORD_AUDIO_PERMISSION = 200
class MainActivity : AppCompatActivity(), BottomSheet.OnClickListener, Timer.OnTimerUpdateListener {
-
+ private lateinit var binding: ActivityMainBinding
private lateinit var fileName: String
private lateinit var dirPath: String
private var recorder: MediaRecorder? = null
private var recording = false
private var onPause = false
- private var refreshRate : Long = 60
+ private var refreshRate: Long = 60
private lateinit var timer: Timer
private lateinit var handler: Handler
@@ -46,14 +47,15 @@ class MainActivity : AppCompatActivity(), BottomSheet.OnClickListener, Timer.OnT
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
// Record to the external cache directory for visibility
ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION)
handler = Handler(Looper.myLooper()!!)
- recordBtn.setOnClickListener {
+ binding.recordBtn.setOnClickListener {
when {
onPause -> resumeRecording()
@@ -62,21 +64,21 @@ class MainActivity : AppCompatActivity(), BottomSheet.OnClickListener, Timer.OnT
}
}
- doneBtn.setOnClickListener {
+ binding.doneBtn.setOnClickListener {
stopRecording()
showBottomSheet()
}
- listBtn.setOnClickListener {
+ binding.listBtn.setOnClickListener {
startActivity(Intent(this, ListingActivity::class.java))
}
- deleteBtn.setOnClickListener {
+ binding.deleteBtn.setOnClickListener {
stopRecording()
- File(dirPath+fileName).delete()
+ File(dirPath + fileName).delete()
}
- deleteBtn.isClickable = false
+ binding.deleteBtn.isClickable = false
}
override fun onRequestPermissionsResult(
@@ -93,17 +95,17 @@ class MainActivity : AppCompatActivity(), BottomSheet.OnClickListener, Timer.OnT
//if (!permissionToRecordAccepted) finish()
}
- private fun startRecording(){
+ private fun startRecording() {
- if(!permissionToRecordAccepted){
+ if (!permissionToRecordAccepted) {
ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION)
return
}
- listBtn.visibility = View.GONE
- doneBtn.visibility = View.VISIBLE
- deleteBtn.isClickable = true
- deleteBtn.setImageResource(R.drawable.ic_delete_enabled)
+ binding.listBtn.visibility = View.GONE
+ binding.doneBtn.visibility = View.VISIBLE
+ binding.deleteBtn.isClickable = true
+ binding.deleteBtn.setImageResource(R.drawable.ic_delete_enabled)
recording = true
timer = Timer(this)
@@ -117,16 +119,14 @@ class MainActivity : AppCompatActivity(), BottomSheet.OnClickListener, Timer.OnT
dirPath = "${externalCacheDir?.absolutePath}/"
fileName = "voice_record_${date}.mp3"
- recorder = MediaRecorder().apply {
+ recorder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) MediaRecorder(this)
+ else MediaRecorder().apply {
setAudioSource(MediaRecorder.AudioSource.MIC)
- /** START COMMENT
- * These two together enable saving file into mp3 format
- * because android doesn't support mp3 saving explicitly **/
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
- /** END COMMENT **/
-
- setOutputFile(dirPath+fileName)
+ setAudioEncodingBitRate(96000)
+ setAudioSamplingRate(44100)
+ setOutputFile(dirPath + fileName)
try {
prepare()
} catch (e: IOException) {
@@ -136,16 +136,16 @@ class MainActivity : AppCompatActivity(), BottomSheet.OnClickListener, Timer.OnT
start()
}
- recordBtn.setImageResource(R.drawable.ic_pause)
+ binding.recordBtn.setImageResource(R.drawable.ic_pause)
animatePlayerView()
}
- private fun animatePlayerView(){
- if(recording && !onPause){
- var amp = recorder!!.maxAmplitude
- playerView.updateAmps(amp)
+ private fun animatePlayerView() {
+ if (recording && !onPause) {
+ val amp = recorder!!.maxAmplitude
+ binding.playerView.updateAmps(amp)
// write maxmap to a file for visualization in player activity
@@ -157,27 +157,27 @@ class MainActivity : AppCompatActivity(), BottomSheet.OnClickListener, Timer.OnT
}
}
- private fun pauseRecording(){
+ private fun pauseRecording() {
onPause = true
recorder?.apply {
pause()
}
- recordBtn.setImageResource(R.drawable.ic_record)
+ binding.recordBtn.setImageResource(R.drawable.ic_record)
timer.pause()
}
- private fun resumeRecording(){
+ private fun resumeRecording() {
onPause = false
recorder?.apply {
resume()
}
- recordBtn.setImageResource(R.drawable.ic_pause)
+ binding.recordBtn.setImageResource(R.drawable.ic_pause)
animatePlayerView()
timer.start()
}
- private fun stopRecording(){
+ private fun stopRecording() {
recording = false
onPause = false
recorder?.apply {
@@ -185,29 +185,29 @@ class MainActivity : AppCompatActivity(), BottomSheet.OnClickListener, Timer.OnT
release()
}
recorder = null
- recordBtn.setImageResource(R.drawable.ic_record)
+ binding.recordBtn.setImageResource(R.drawable.ic_record)
- listBtn.visibility = View.VISIBLE
- doneBtn.visibility = View.GONE
- deleteBtn.isClickable = false
- deleteBtn.setImageResource(R.drawable.ic_delete_disabled)
+ binding.listBtn.visibility = View.VISIBLE
+ binding.doneBtn.visibility = View.GONE
+ binding.deleteBtn.isClickable = false
+ binding.deleteBtn.setImageResource(R.drawable.ic_delete_disabled)
- playerView.reset()
+ binding.playerView.reset()
try {
timer.stop()
- }catch (e: Exception){}
+ } catch (_: Exception) {
+ }
- timerView.text = "00:00.00"
+ binding.timerView.text = "00:00.00"
}
- private fun showBottomSheet(){
- var bottomSheet = BottomSheet(dirPath, fileName, this)
+ private fun showBottomSheet() {
+ val bottomSheet = BottomSheet(dirPath, fileName, this)
bottomSheet.show(supportFragmentManager, LOG_TAG)
}
-
override fun onCancelClicked() {
Toast.makeText(this, "Audio record deleted", Toast.LENGTH_SHORT).show()
stopRecording()
@@ -215,12 +215,13 @@ class MainActivity : AppCompatActivity(), BottomSheet.OnClickListener, Timer.OnT
override fun onOkClicked(filePath: String, filename: String) {
// add audio record info to database
- var db = Room.databaseBuilder(
+ val db = Room.databaseBuilder(
this,
AppDatabase::class.java,
- "audioRecords").build()
+ "audioRecords"
+ ).build()
- var duration = timer.format().split(".")[0]
+ val duration = timer.format().split(".")[0]
stopRecording()
GlobalScope.launch {
@@ -230,9 +231,9 @@ class MainActivity : AppCompatActivity(), BottomSheet.OnClickListener, Timer.OnT
}
override fun onTimerUpdate(duration: String) {
- runOnUiThread{
- if(recording)
- timerView.text = duration
+ runOnUiThread {
+ if (recording)
+ binding.timerView.text = duration
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alterpat/voicerecorder/PlayerActivity.kt b/app/src/main/java/com/alterpat/voicerecorder/PlayerActivity.kt
index 0faf67f..3cc3942 100644
--- a/app/src/main/java/com/alterpat/voicerecorder/PlayerActivity.kt
+++ b/app/src/main/java/com/alterpat/voicerecorder/PlayerActivity.kt
@@ -2,47 +2,46 @@ package com.alterpat.voicerecorder
import android.media.MediaPlayer
import android.media.PlaybackParams
-import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.SeekBar
+import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
-import kotlinx.android.synthetic.main.activity_listing.*
-import kotlinx.android.synthetic.main.activity_player.*
-import kotlinx.android.synthetic.main.activity_player.toolbar
+import com.alterpat.voicerecorder.databinding.ActivityPlayerBinding
class PlayerActivity : AppCompatActivity() {
-
+ private lateinit var binding: ActivityPlayerBinding
private val delay = 100L
- private lateinit var runnable : Runnable
- private lateinit var handler : Handler
- private lateinit var mediaPlayer : MediaPlayer
- private var playbackSpeed :Float = 1.0f
+ private lateinit var runnable: Runnable
+ private lateinit var handler: Handler
+ private lateinit var mediaPlayer: MediaPlayer
+ private var playbackSpeed: Float = 1.0f
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_player)
-
- setSupportActionBar(toolbar)
+ binding = ActivityPlayerBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
- toolbar.setNavigationOnClickListener {
+ binding.toolbar.setNavigationOnClickListener {
onBackPressed()
}
var filePath = intent.getStringExtra("filepath")
var filename = intent.getStringExtra("filename")
- tvFilename.text = filename
+ binding.tvFilename.text = filename
mediaPlayer = MediaPlayer()
mediaPlayer.apply {
setDataSource(filePath)
prepare()
}
- seekBar.max = mediaPlayer.duration
+ binding.seekBar.max = mediaPlayer.duration
handler = Handler(Looper.getMainLooper())
playPausePlayer()
@@ -51,76 +50,82 @@ class PlayerActivity : AppCompatActivity() {
stopPlayer()
}
- btnPlay.setOnClickListener {
+ binding.btnPlay.setOnClickListener {
playPausePlayer()
}
- btnForward.setOnClickListener {
+ binding.btnForward.setOnClickListener {
mediaPlayer.seekTo(mediaPlayer.currentPosition + 1000)
- seekBar.progress += 1000
+ binding.seekBar.progress += 1000
}
- btnBackward.setOnClickListener {
+ binding.btnBackward.setOnClickListener {
mediaPlayer.seekTo(mediaPlayer.currentPosition - 1000)
- seekBar.progress -= 1000
+ binding.seekBar.progress -= 1000
}
- seekBar.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener{
+ binding.seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {
- if(p2) mediaPlayer.seekTo(p1)
+ if (p2) mediaPlayer.seekTo(p1)
}
+
override fun onStartTrackingTouch(p0: SeekBar?) {}
override fun onStopTrackingTouch(p0: SeekBar?) {}
})
- chip.setOnClickListener {
- when(playbackSpeed){
+ binding.chip.setOnClickListener {
+ when (playbackSpeed) {
0.5f -> playbackSpeed += 0.5f
1.0f -> playbackSpeed += 0.5f
1.5f -> playbackSpeed += 0.5f
2.0f -> playbackSpeed = 0.5f
}
mediaPlayer.playbackParams = PlaybackParams().setSpeed(playbackSpeed)
- chip.text = "x $playbackSpeed"
+ binding.chip.text = "x $playbackSpeed"
}
}
- private fun playPausePlayer(){
- if(!mediaPlayer.isPlaying){
+ private fun playPausePlayer() {
+ if (!mediaPlayer.isPlaying) {
mediaPlayer.start()
- btnPlay.background = ResourcesCompat.getDrawable(resources, R.drawable.ic_pause_circle, theme)
+ binding.btnPlay.background =
+ ResourcesCompat.getDrawable(resources, R.drawable.ic_pause_circle, theme)
runnable = Runnable {
- var progress = mediaPlayer.currentPosition
+ val progress = mediaPlayer.currentPosition
Log.d("progress", progress.toString())
- seekBar.progress = progress
+ binding.seekBar.progress = progress
- var amp = 80 + Math.random()*300
- playerView.updateAmps(amp.toInt())
+ val amp = 80 + Math.random() * 300
+ binding.playerView.updateAmps(amp.toInt())
handler.postDelayed(runnable, delay)
}
handler.postDelayed(runnable, delay)
- }else{
+ } else {
mediaPlayer.pause()
- btnPlay.background = ResourcesCompat.getDrawable(resources, R.drawable.ic_play_circle, theme)
+ binding.btnPlay.background =
+ ResourcesCompat.getDrawable(resources, R.drawable.ic_play_circle, theme)
handler.removeCallbacks(runnable)
}
}
- private fun stopPlayer(){
- btnPlay.background = ResourcesCompat.getDrawable(resources, R.drawable.ic_play_circle, theme)
+ private fun stopPlayer() {
+ binding.btnPlay.background =
+ ResourcesCompat.getDrawable(resources, R.drawable.ic_play_circle, theme)
handler.removeCallbacks(runnable)
}
+ @Deprecated("Deprecated in Java")
override fun onBackPressed() {
super.onBackPressed()
mediaPlayer.stop()
mediaPlayer.release()
handler.removeCallbacks(runnable)
}
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alterpat/voicerecorder/PlayerWaveformView.kt b/app/src/main/java/com/alterpat/voicerecorder/PlayerWaveformView.kt
index 4c7228b..e0bd79e 100644
--- a/app/src/main/java/com/alterpat/voicerecorder/PlayerWaveformView.kt
+++ b/app/src/main/java/com/alterpat/voicerecorder/PlayerWaveformView.kt
@@ -1,37 +1,41 @@
package com.alterpat.voicerecorder
import android.content.Context
-import android.graphics.*
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.RectF
import android.util.AttributeSet
-import android.util.DisplayMetrics
import android.util.Log
import android.view.View
-import androidx.annotation.Nullable
-class PlayerWaveformView: View {
+class PlayerWaveformView : View {
private lateinit var spikes: Array
private lateinit var paintRead: Paint
- private var w : Int = 18
- private var d : Int = 4
- private var sw : Int = 0
- private var maxAmp : Int = 200
+ private var w: Int = 18
+ private var d: Int = 4
+ private var sw: Int = 0
+ private var maxAmp: Int = 200
private var delta = 320
private lateinit var rect: Rect
private var nbSpikes = 30
- constructor(context: Context?) : super(context){
+ constructor(context: Context?) : super(context) {
init(null)
}
- constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs){
+
+ constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init(attrs)
}
+
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
- ){
+ ) {
init(attrs)
}
@@ -40,15 +44,15 @@ class PlayerWaveformView: View {
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
- ) : super(context, attrs, defStyleAttr, defStyleRes){
+ ) : super(context, attrs, defStyleAttr, defStyleRes) {
init(attrs)
}
// this function is to avoid duplicating code in every constructor
// indeed each constructor is called in a specific situation
// and we want the View to de the same thing no matter what
- private fun init(attrs: AttributeSet?){
- spikes = Array(nbSpikes){ RectF() }
+ private fun init(attrs: AttributeSet?) {
+ spikes = Array(nbSpikes) { RectF() }
paintRead = Paint() //Paint.ANTI_ALIAS_FLAG
paintRead.color = Color.rgb(244, 81, 30) // orange
@@ -64,15 +68,15 @@ class PlayerWaveformView: View {
}
- fun updateAmps(amp: Int){
+ fun updateAmps(amp: Int) {
//var norm = Math.min(amp/7, maxAmp) // 100*abs(Math.log10(1.0*amp/(sqrt(amp*1.0)+1)))
var norm = amp
- for(i in spikes.indices){
- var bottom : Float = (Math.random() * norm).toFloat()
+ for (i in spikes.indices) {
+ var bottom: Float = (Math.random() * norm).toFloat()
var top = delta - bottom
- var rectUp = RectF(i*(w+d)*1f, top, i*(w+d) + w*1f, bottom)
+ var rectUp = RectF(i * (w + d) * 1f, top, i * (w + d) + w * 1f, bottom)
spikes[i] = rectUp
}
@@ -86,7 +90,7 @@ class PlayerWaveformView: View {
spikes.forEach {
Log.d("waveform", it.bottom.toString())
- canvas?.drawRoundRect(it,10f, 10f, paintRead)
+ canvas?.drawRoundRect(it, 10f, 10f, paintRead)
}
//Log.d("waveform", rect.bottom.toString())
//canvas?.drawRect(rect, paintRead)
diff --git a/app/src/main/java/com/alterpat/voicerecorder/RecorderWaveformView.kt b/app/src/main/java/com/alterpat/voicerecorder/RecorderWaveformView.kt
index a771a76..cc36431 100644
--- a/app/src/main/java/com/alterpat/voicerecorder/RecorderWaveformView.kt
+++ b/app/src/main/java/com/alterpat/voicerecorder/RecorderWaveformView.kt
@@ -1,34 +1,37 @@
package com.alterpat.voicerecorder
import android.content.Context
-import android.graphics.*
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.RectF
import android.util.AttributeSet
-import android.util.DisplayMetrics
import android.view.View
-import androidx.annotation.Nullable
-class RecorderWaveformView: View {
+class RecorderWaveformView : View {
private lateinit var amplitudes: ArrayList
private lateinit var spikes: ArrayList
private lateinit var paintRead: Paint
- private var w : Float = 9f
- private var d : Float = 4f
- private var sw : Int = 0
- private var maxSpikes : Int = 0
- private var maxAmp : Int = 200
+ private var w: Float = 9f
+ private var d: Float = 4f
+ private var sw: Int = 0
+ private var maxSpikes: Int = 0
+ private var maxAmp: Int = 200
- constructor(context: Context?) : super(context){
+ constructor(context: Context?) : super(context) {
init(null)
}
- constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs){
+
+ constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init(attrs)
}
+
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
- ){
+ ) {
init(attrs)
}
@@ -37,14 +40,14 @@ class RecorderWaveformView: View {
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
- ) : super(context, attrs, defStyleAttr, defStyleRes){
+ ) : super(context, attrs, defStyleAttr, defStyleRes) {
init(attrs)
}
// this function is to avoid duplicating code in every constructor
// indeed each constructor is called in a specific situation
// and we want the View to de the same thing no matter what
- private fun init(attrs: AttributeSet?){
+ private fun init(attrs: AttributeSet?) {
amplitudes = ArrayList()
paintRead = Paint() //Paint.ANTI_ALIAS_FLAG
paintRead.color = Color.rgb(244, 81, 30) // orange
@@ -53,30 +56,30 @@ class RecorderWaveformView: View {
val displayMetrics = resources.displayMetrics
sw = displayMetrics.widthPixels
- maxSpikes = (sw/(w+d)).toInt()
+ maxSpikes = (sw / (w + d)).toInt()
spikes = ArrayList()
}
- fun reset(){
+ fun reset() {
amplitudes.clear()
spikes.clear()
invalidate()
}
- fun updateAmps(amp: Int){
+ fun updateAmps(amp: Int) {
- var norm = Math.min(amp/7, maxAmp) // 100*abs(Math.log10(1.0*amp/(sqrt(amp*1.0)+1)))
+ var norm = Math.min(amp / 7, maxAmp) // 100*abs(Math.log10(1.0*amp/(sqrt(amp*1.0)+1)))
amplitudes.add(norm)
var amps = amplitudes.takeLast(maxSpikes)
spikes.clear()
- for(i in amps.indices){
+ for (i in amps.indices) {
val delta = maxAmp.toFloat()
val top = delta - amps[i]
var bottom = top + amps[i] as Int
- var rectUp = RectF(sw-i*(w+d), top, sw-i*(w+d) - w, bottom)
- var rectDown = RectF(sw-i*(w+d), delta-2, sw-i*(w+d) - w, delta+amps[i])
+ var rectUp = RectF(sw - i * (w + d), top, sw - i * (w + d) - w, bottom)
+ var rectDown = RectF(sw - i * (w + d), delta - 2, sw - i * (w + d) - w, delta + amps[i])
spikes.add(rectUp)
spikes.add(rectDown)
}
@@ -88,7 +91,7 @@ class RecorderWaveformView: View {
// therefore we shouldn't initialize objects here
spikes.forEach {
- canvas?.drawRoundRect(it, 6f, 6f,paintRead)
+ canvas?.drawRoundRect(it, 6f, 6f, paintRead)
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alterpat/voicerecorder/db/AppDatabase.kt b/app/src/main/java/com/alterpat/voicerecorder/db/AppDatabase.kt
index b70a3bf..5e214c3 100644
--- a/app/src/main/java/com/alterpat/voicerecorder/db/AppDatabase.kt
+++ b/app/src/main/java/com/alterpat/voicerecorder/db/AppDatabase.kt
@@ -4,6 +4,6 @@ import androidx.room.Database
import androidx.room.RoomDatabase
@Database(entities = [AudioRecord::class], version = 1)
-abstract class AppDatabase : RoomDatabase(){
+abstract class AppDatabase : RoomDatabase() {
abstract fun audioRecordDAO(): AudioRecordDAO
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alterpat/voicerecorder/db/AudioRecord.kt b/app/src/main/java/com/alterpat/voicerecorder/db/AudioRecord.kt
index b35bb4b..4e07636 100644
--- a/app/src/main/java/com/alterpat/voicerecorder/db/AudioRecord.kt
+++ b/app/src/main/java/com/alterpat/voicerecorder/db/AudioRecord.kt
@@ -3,17 +3,18 @@ package com.alterpat.voicerecorder.db
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
-import java.sql.Timestamp
@Entity(tableName = "audioRecords")
-data class AudioRecord (
+data class AudioRecord(
var filename: String,
var filePath: String,
var date: Long,
- var duration: String){
+ var duration: String
+) {
@PrimaryKey(autoGenerate = true)
var id: Int = 0
+
@Ignore
var isChecked: Boolean = false
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alterpat/voicerecorder/tools/Timer.kt b/app/src/main/java/com/alterpat/voicerecorder/tools/Timer.kt
index f48dfdb..b95ec5a 100644
--- a/app/src/main/java/com/alterpat/voicerecorder/tools/Timer.kt
+++ b/app/src/main/java/com/alterpat/voicerecorder/tools/Timer.kt
@@ -1,30 +1,30 @@
package com.alterpat.voicerecorder.tools
-import java.util.*
import java.util.Timer
+import java.util.TimerTask
class Timer(private var listener: OnTimerUpdateListener) {
- interface OnTimerUpdateListener{
+ interface OnTimerUpdateListener {
fun onTimerUpdate(duration: String)
}
- private var duration : Long = 0
- private var period : Long = 258
- private lateinit var timer : Timer
+ private var duration: Long = 0
+ private var period: Long = 258
+ private lateinit var timer: Timer
- fun start(){
+ fun start() {
timer = Timer()
- timer.scheduleAtFixedRate(object : TimerTask(){
+ timer.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
duration += period
listener.onTimerUpdate(format())
}
- },period, period)
+ }, period, period)
}
- fun pause(){
+ fun pause() {
timer.cancel()
}
@@ -34,19 +34,21 @@ class Timer(private var listener: OnTimerUpdateListener) {
timer.purge()
}
- fun getDuration(): Long{return duration}
+ fun getDuration(): Long {
+ return duration
+ }
- fun format(): String{
+ fun format(): String {
val milli = duration % 1000
- val seconds = (duration / 1000) % 60
+ val seconds = (duration / 1000) % 60
val minutes = (duration / (1000 * 60) % 60)
val hours = (duration / (1000 * 60 * 60) % 24)
- var formatted : String
- formatted = if(hours > 0)
- "%02d:%02d:%02d.%02d".format(hours, minutes, seconds, milli/10)
+ var formatted: String
+ formatted = if (hours > 0)
+ "%02d:%02d:%02d.%02d".format(hours, minutes, seconds, milli / 10)
else
- "%02d:%02d.%02d".format(minutes, seconds, milli/10)
+ "%02d:%02d.%02d".format(minutes, seconds, milli / 10)
return formatted
}
diff --git a/app/src/main/java/com/alterpat/voicerecorder/tools/TimerHandler.kt b/app/src/main/java/com/alterpat/voicerecorder/tools/TimerHandler.kt
index 565028b..053b72f 100644
--- a/app/src/main/java/com/alterpat/voicerecorder/tools/TimerHandler.kt
+++ b/app/src/main/java/com/alterpat/voicerecorder/tools/TimerHandler.kt
@@ -4,13 +4,14 @@ import android.os.Handler
import android.os.Looper
const val delay = 100L
+
class Timer(private val listener: OnTimerUpdateListener) {
- interface OnTimerUpdateListener{
+ interface OnTimerUpdateListener {
fun onTimerTicks(duration: String)
}
- private var handler : Handler = Handler(Looper.getMainLooper())
+ private var handler: Handler = Handler(Looper.getMainLooper())
private lateinit var runnable: Runnable
private var duration = 0L
@@ -23,30 +24,30 @@ class Timer(private val listener: OnTimerUpdateListener) {
}
}
- fun start(){
+ fun start() {
handler.postDelayed(runnable, delay)
}
- fun pause(){
+ fun pause() {
handler.removeCallbacks(runnable)
}
- fun stop(){
+ fun stop() {
handler.removeCallbacks(runnable)
listener.onTimerTicks("00:00.00")
duration = 0L
}
- private fun format(): String{
+ private fun format(): String {
val milli = duration % 1000
val seconds = (duration / 1000) % 60
val minutes = (duration / (1000 * 60)) % 60
val hours = (duration / (1000 * 60 * 60)) % 24
- var formatted = if(hours > 0)
- "%02d:%02d:%02d.%02d".format(hours, minutes, seconds, milli/10)
+ var formatted = if (hours > 0)
+ "%02d:%02d:%02d.%02d".format(hours, minutes, seconds, milli / 10)
else
- "%02d:%02d.%02d".format(minutes, seconds, milli/10)
+ "%02d:%02d.%02d".format(minutes, seconds, milli / 10)
return formatted
}
diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml
index 3ab2d9d..34b7cb8 100644
--- a/app/src/main/res/drawable/ic_back.xml
+++ b/app/src/main/res/drawable/ic_back.xml
@@ -1,10 +1,10 @@
-
+ android:viewportHeight="24">
+
diff --git a/app/src/main/res/drawable/ic_backward.xml b/app/src/main/res/drawable/ic_backward.xml
index 75888e6..a366ef7 100644
--- a/app/src/main/res/drawable/ic_backward.xml
+++ b/app/src/main/res/drawable/ic_backward.xml
@@ -1,8 +1,10 @@
-
-
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
diff --git a/app/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/ic_circle.xml
index 00f3986..a1f57db 100644
--- a/app/src/main/res/drawable/ic_circle.xml
+++ b/app/src/main/res/drawable/ic_circle.xml
@@ -1,5 +1,4 @@
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_circle_pressed.xml b/app/src/main/res/drawable/ic_circle_pressed.xml
index de79604..f3d0e68 100644
--- a/app/src/main/res/drawable/ic_circle_pressed.xml
+++ b/app/src/main/res/drawable/ic_circle_pressed.xml
@@ -1,5 +1,4 @@
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_circle_selectable.xml b/app/src/main/res/drawable/ic_circle_selectable.xml
index a76a3b7..5b7df86 100644
--- a/app/src/main/res/drawable/ic_circle_selectable.xml
+++ b/app/src/main/res/drawable/ic_circle_selectable.xml
@@ -1,8 +1,6 @@
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_circle_selectable_white.xml b/app/src/main/res/drawable/ic_circle_selectable_white.xml
index 09c09ad..e2dd5b3 100644
--- a/app/src/main/res/drawable/ic_circle_selectable_white.xml
+++ b/app/src/main/res/drawable/ic_circle_selectable_white.xml
@@ -1,8 +1,6 @@
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_circle_white.xml b/app/src/main/res/drawable/ic_circle_white.xml
index f095461..507d925 100644
--- a/app/src/main/res/drawable/ic_circle_white.xml
+++ b/app/src/main/res/drawable/ic_circle_white.xml
@@ -1,5 +1,4 @@
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_close.xml b/app/src/main/res/drawable/ic_close.xml
index e82b00a..13a42e5 100644
--- a/app/src/main/res/drawable/ic_close.xml
+++ b/app/src/main/res/drawable/ic_close.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml
index 433896d..accd0d7 100644
--- a/app/src/main/res/drawable/ic_delete.xml
+++ b/app/src/main/res/drawable/ic_delete.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_delete_disabled.xml b/app/src/main/res/drawable/ic_delete_disabled.xml
index 985d12d..7d4a595 100644
--- a/app/src/main/res/drawable/ic_delete_disabled.xml
+++ b/app/src/main/res/drawable/ic_delete_disabled.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_delete_disabled2.xml b/app/src/main/res/drawable/ic_delete_disabled2.xml
index fb25953..da6955b 100644
--- a/app/src/main/res/drawable/ic_delete_disabled2.xml
+++ b/app/src/main/res/drawable/ic_delete_disabled2.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_delete_enabled.xml b/app/src/main/res/drawable/ic_delete_enabled.xml
index 43fe2a1..dc0b983 100644
--- a/app/src/main/res/drawable/ic_delete_enabled.xml
+++ b/app/src/main/res/drawable/ic_delete_enabled.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_done.xml b/app/src/main/res/drawable/ic_done.xml
index dbb58cd..9ae924a 100644
--- a/app/src/main/res/drawable/ic_done.xml
+++ b/app/src/main/res/drawable/ic_done.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_edit.xml b/app/src/main/res/drawable/ic_edit.xml
index 339af96..985adb9 100644
--- a/app/src/main/res/drawable/ic_edit.xml
+++ b/app/src/main/res/drawable/ic_edit.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_edit_disabled.xml b/app/src/main/res/drawable/ic_edit_disabled.xml
index 4316947..489e4c7 100644
--- a/app/src/main/res/drawable/ic_edit_disabled.xml
+++ b/app/src/main/res/drawable/ic_edit_disabled.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_forward.xml b/app/src/main/res/drawable/ic_forward.xml
index 99962de..d846063 100644
--- a/app/src/main/res/drawable/ic_forward.xml
+++ b/app/src/main/res/drawable/ic_forward.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_list.xml b/app/src/main/res/drawable/ic_list.xml
index ddc740c..9d4d39a 100644
--- a/app/src/main/res/drawable/ic_list.xml
+++ b/app/src/main/res/drawable/ic_list.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_list_selected.xml b/app/src/main/res/drawable/ic_list_selected.xml
index 011c6d7..7c66c46 100644
--- a/app/src/main/res/drawable/ic_list_selected.xml
+++ b/app/src/main/res/drawable/ic_list_selected.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_menu.xml b/app/src/main/res/drawable/ic_menu.xml
index 44bf628..ea4b0ea 100644
--- a/app/src/main/res/drawable/ic_menu.xml
+++ b/app/src/main/res/drawable/ic_menu.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_pause.xml b/app/src/main/res/drawable/ic_pause.xml
index 6bfbe82..fbc2524 100644
--- a/app/src/main/res/drawable/ic_pause.xml
+++ b/app/src/main/res/drawable/ic_pause.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_pause_circle.xml b/app/src/main/res/drawable/ic_pause_circle.xml
index f35034b..d66c7bc 100644
--- a/app/src/main/res/drawable/ic_pause_circle.xml
+++ b/app/src/main/res/drawable/ic_pause_circle.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_play.xml b/app/src/main/res/drawable/ic_play.xml
index 399fa5e..469232d 100644
--- a/app/src/main/res/drawable/ic_play.xml
+++ b/app/src/main/res/drawable/ic_play.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_play_circle.xml b/app/src/main/res/drawable/ic_play_circle.xml
index 415c740..5eccf89 100644
--- a/app/src/main/res/drawable/ic_play_circle.xml
+++ b/app/src/main/res/drawable/ic_play_circle.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_record.xml b/app/src/main/res/drawable/ic_record.xml
index 7a3812b..edde0e3 100644
--- a/app/src/main/res/drawable/ic_record.xml
+++ b/app/src/main/res/drawable/ic_record.xml
@@ -1,5 +1,4 @@
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml
index 47bab97..3111e97 100644
--- a/app/src/main/res/drawable/ic_search.xml
+++ b/app/src/main/res/drawable/ic_search.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_stop.xml b/app/src/main/res/drawable/ic_stop.xml
index d57c65a..b4645c5 100644
--- a/app/src/main/res/drawable/ic_stop.xml
+++ b/app/src/main/res/drawable/ic_stop.xml
@@ -1,8 +1,7 @@
-
-
+
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_listing.xml b/app/src/main/res/layout/activity_listing.xml
index 6edae88..e842fea 100644
--- a/app/src/main/res/layout/activity_listing.xml
+++ b/app/src/main/res/layout/activity_listing.xml
@@ -1,75 +1,79 @@
-
+
+ android:outlineSpotShadowColor="@android:color/transparent"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+ app:layout_scrollFlags="scroll|exitUntilCollapsed"
+ app:statusBarScrim="@color/colorBackground">
+ app:title="Recordings">
+
+
+ android:background="@drawable/ic_close" />
+ android:layout_marginEnd="16dp"
+ android:background="@drawable/ic_list" />
-
+
+
-
-
+ android:hint="Search audio record"
+ android:imeOptions="actionSearch"
+ android:inputType="text"
+ android:textColor="@color/colorText"
+ android:textColorHint="@color/colorGrayDark" />
+
+
+ android:layout_height="match_parent" />
+
+ app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
+
+
+ android:background="@drawable/ic_edit_disabled"
+ android:clickable="false" />
+
+ android:text="Rename"
+ android:textColor="@color/colorDisabled" />
+
+ android:background="@drawable/ic_delete_disabled2"
+ android:clickable="false" />
+
+ android:text="Delete"
+ android:textColor="@color/colorDisabled" />
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index f91a090..9e408bc 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,10 +1,10 @@
-
+
+ app:layout_constraintStart_toStartOf="parent" />
+ android:clickable="false"
+ android:src="@drawable/ic_delete_disabled" />
+ android:background="@drawable/ic_record" />
+ android:src="@drawable/ic_menu" />
+ android:src="@drawable/ic_done"
+ android:visibility="gone" />
diff --git a/app/src/main/res/layout/activity_player.xml b/app/src/main/res/layout/activity_player.xml
index d56b9cb..2791f4e 100644
--- a/app/src/main/res/layout/activity_player.xml
+++ b/app/src/main/res/layout/activity_player.xml
@@ -12,14 +12,15 @@
android:layout_height="?actionBarSize"
app:layout_constraintTop_toTopOf="parent"
app:title=" ">
+
+ android:gravity="center"
+ android:text=""
+ android:textSize="20sp" />
-
-
+
+ android:layout_marginTop="40dp"
+ android:text="Save recording?"
+ android:textSize="24sp" />
+ app:boxStrokeColor="@color/colorBox"
+ app:boxStrokeWidth="2dp">
+
+ android:padding="16dp"
+ android:text="12" />
+ android:layout_marginBottom="30dp"
+ android:gravity="center_horizontal">
+
+ android:text="Cancel"
+ android:textColor="@color/colorGrayDark"
+ app:cornerRadius="26dp"
+ app:rippleColor="@color/colorGrayDark" />
+
+ android:text="OK"
+ android:textColor="@android:color/white"
+ app:cornerRadius="26dp"
+ app:rippleColor="@color/colorGrayDark" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/itemview_layout.xml b/app/src/main/res/layout/itemview_layout.xml
index 32d666a..1b58368 100644
--- a/app/src/main/res/layout/itemview_layout.xml
+++ b/app/src/main/res/layout/itemview_layout.xml
@@ -1,40 +1,44 @@
-
+ android:paddingStart="22dp">
+
+ android:background="@drawable/ic_circle"
+ android:src="@drawable/ic_play" />
+
+ android:layout_marginStart="20dp"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ android:textSize="18sp"
+ android:textStyle="bold" />
+
+ android:textSize="14sp" />
+
+ android:layout_height="20dp"
+ android:clickable="false"
+ android:visibility="gone" />
\ No newline at end of file
diff --git a/app/src/test/java/com/alterpat/voicerecorder/ExampleUnitTest.kt b/app/src/test/java/com/alterpat/voicerecorder/ExampleUnitTest.kt
index aea4121..9c01cc2 100644
--- a/app/src/test/java/com/alterpat/voicerecorder/ExampleUnitTest.kt
+++ b/app/src/test/java/com/alterpat/voicerecorder/ExampleUnitTest.kt
@@ -1,9 +1,8 @@
package com.alterpat.voicerecorder
+import org.junit.Assert.assertEquals
import org.junit.Test
-import org.junit.Assert.*
-
/**
* Example local unit test, which will execute on the development machine (host).
*
diff --git a/build.gradle b/build.gradle
index 1241d12..25ce43d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,12 +1,12 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = "1.3.72"
+ ext.kotlin_version = '1.8.21'
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
- classpath "com.android.tools.build:gradle:4.0.1"
+ classpath 'com.android.tools.build:gradle:8.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
@@ -17,7 +17,7 @@ buildscript {
allprojects {
repositories {
google()
- jcenter()
+ mavenCentral()
}
}
diff --git a/gradle.properties b/gradle.properties
index 4d15d01..fde9fa4 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -18,4 +18,7 @@ android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
-kotlin.code.style=official
\ No newline at end of file
+kotlin.code.style=official
+android.defaults.buildfeatures.buildconfig=true
+android.nonTransitiveRClass=false
+android.nonFinalResIds=false
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 8c58e9d..b70a3fc 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip