WIP grabador de audio
This commit is contained in:
parent
b539f03ef6
commit
96fd70556b
4
.idea/assetWizardSettings.xml
generated
4
.idea/assetWizardSettings.xml
generated
@ -308,7 +308,7 @@
|
|||||||
<PersistentState>
|
<PersistentState>
|
||||||
<option name="values">
|
<option name="values">
|
||||||
<map>
|
<map>
|
||||||
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/password/baseline_password_24.xml" />
|
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/play_arrow/baseline_play_arrow_24.xml" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</PersistentState>
|
</PersistentState>
|
||||||
@ -318,7 +318,7 @@
|
|||||||
</option>
|
</option>
|
||||||
<option name="values">
|
<option name="values">
|
||||||
<map>
|
<map>
|
||||||
<entry key="outputName" value="baseline_password_24" />
|
<entry key="outputName" value="baseline_play_arrow_24" />
|
||||||
<entry key="sourceFile" value="C:\Users\erike\Downloads\face-kiss-wink-heart-solid.svg" />
|
<entry key="sourceFile" value="C:\Users\erike\Downloads\face-kiss-wink-heart-solid.svg" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
18
.idea/navEditor.xml
generated
18
.idea/navEditor.xml
generated
@ -8,25 +8,13 @@
|
|||||||
<LayoutPositions>
|
<LayoutPositions>
|
||||||
<option name="myPositions">
|
<option name="myPositions">
|
||||||
<map>
|
<map>
|
||||||
<entry key="audioDraftsFragment">
|
<entry key="audiosFragment2">
|
||||||
<value>
|
<value>
|
||||||
<LayoutPositions>
|
<LayoutPositions>
|
||||||
<option name="myPosition">
|
<option name="myPosition">
|
||||||
<Point>
|
<Point>
|
||||||
<option name="x" value="130" />
|
<option name="x" value="40" />
|
||||||
<option name="y" value="18" />
|
<option name="y" value="40" />
|
||||||
</Point>
|
|
||||||
</option>
|
|
||||||
</LayoutPositions>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="audioRecorderMainFragment">
|
|
||||||
<value>
|
|
||||||
<LayoutPositions>
|
|
||||||
<option name="myPosition">
|
|
||||||
<Point>
|
|
||||||
<option name="x" value="-74" />
|
|
||||||
<option name="y" value="17" />
|
|
||||||
</Point>
|
</Point>
|
||||||
</option>
|
</option>
|
||||||
</LayoutPositions>
|
</LayoutPositions>
|
||||||
|
|||||||
@ -40,6 +40,7 @@
|
|||||||
<activity android:name=".images.image_maker.ui.ImageMakerActivity" android:theme="@style/Theme.Isolaatti"/>
|
<activity android:name=".images.image_maker.ui.ImageMakerActivity" android:theme="@style/Theme.Isolaatti"/>
|
||||||
<activity android:name=".images.image_chooser.ui.ImageChooserActivity" android:theme="@style/Theme.Isolaatti"/>
|
<activity android:name=".images.image_chooser.ui.ImageChooserActivity" android:theme="@style/Theme.Isolaatti"/>
|
||||||
<activity android:name=".profile.ui.EditProfileActivity" android:theme="@style/Theme.Isolaatti" />
|
<activity android:name=".profile.ui.EditProfileActivity" android:theme="@style/Theme.Isolaatti" />
|
||||||
|
<activity android:name=".audio.recorder.ui.AudioRecorderActivity" android:theme="@style/Theme.Isolaatti" />
|
||||||
<provider
|
<provider
|
||||||
android:authorities="com.isolaatti.provider"
|
android:authorities="com.isolaatti.provider"
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.isolaatti.audio.recorder.data
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "audio_drafts")
|
||||||
|
data class AudioDraftEntity(
|
||||||
|
@PrimaryKey val id: Long,
|
||||||
|
val name: String,
|
||||||
|
val audioLocalPath: String
|
||||||
|
)
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.isolaatti.audio.recorder.data
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface AudiosDraftsDao {
|
||||||
|
@Insert
|
||||||
|
fun insertAudioDraft(audioDraftEntity: AudioDraftEntity): Long
|
||||||
|
|
||||||
|
@Query("SELECT * FROM audio_drafts WHERE id = :draftId")
|
||||||
|
fun getAudioDraftById(draftId: Long): AudioDraftEntity
|
||||||
|
|
||||||
|
@Query("SELECT * FROM audio_drafts WHERE id < :before ORDER BY id DESC LIMIT :count")
|
||||||
|
fun getDrafts(count: Int, before: Long): List<AudioDraftEntity>
|
||||||
|
|
||||||
|
@Query("DELETE FROM audio_drafts WHERE id in (:draftIds)")
|
||||||
|
fun deleteDrafts(draftIds: LongArray)
|
||||||
|
}
|
||||||
@ -1,6 +1,11 @@
|
|||||||
package com.isolaatti.audio.recorder.ui
|
package com.isolaatti.audio.recorder.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
|
||||||
class AudioDraftsFragment : Fragment() {
|
class AudioDraftsFragment : Fragment() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,6 +1,219 @@
|
|||||||
package com.isolaatti.audio.recorder.ui
|
package com.isolaatti.audio.recorder.ui
|
||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
import android.Manifest
|
||||||
|
import android.content.Intent
|
||||||
|
import android.media.MediaRecorder
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.PermissionChecker
|
||||||
|
import androidx.core.content.PermissionChecker.PERMISSION_GRANTED
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.isolaatti.databinding.ActivityAudioRecorderBinding
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class AudioRecorderActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val LOG_TAG = "AudioRecorderActivity"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityAudioRecorderBinding
|
||||||
|
|
||||||
|
private var audioRecorder: MediaRecorder? = null
|
||||||
|
|
||||||
|
private val audioUID = UUID.randomUUID()
|
||||||
|
private lateinit var outputFile: String
|
||||||
|
|
||||||
|
private val requestAudioPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||||
|
if(it) {
|
||||||
|
startRecording()
|
||||||
|
} else {
|
||||||
|
showPermissionRationale()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityAudioRecorderBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
File("${filesDir.absolutePath}/audios/").let {
|
||||||
|
if(!it.isDirectory) {
|
||||||
|
it.mkdir()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputFile = "${filesDir.absolutePath}/audios/${audioUID}.3gp"
|
||||||
|
|
||||||
|
setupListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkRecordAudioPermission(): Boolean {
|
||||||
|
return when {
|
||||||
|
PermissionChecker.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PERMISSION_GRANTED -> true
|
||||||
|
ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO) -> {
|
||||||
|
showPermissionRationale()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
requestAudioPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showPermissionRationale() {
|
||||||
|
MaterialAlertDialogBuilder(this)
|
||||||
|
.setTitle("Record audio permission")
|
||||||
|
.setMessage("We need permission to access your microphone so that you can record your audio. Go to settings.")
|
||||||
|
.setPositiveButton("Go to settings"){_, _ ->
|
||||||
|
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||||
|
intent.data = Uri.fromParts("package", packageName, null)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
.setNegativeButton("No, thanks", null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun setupListeners() {
|
||||||
|
binding.recordButton.setOnClickListener {
|
||||||
|
if(checkRecordAudioPermission()) {
|
||||||
|
Log.d(LOG_TAG, "Starts recording")
|
||||||
|
startRecording()
|
||||||
|
} else {
|
||||||
|
Log.d(LOG_TAG, "Failed to start recording: mic permission not granted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.stopRecording.setOnClickListener {
|
||||||
|
stopRecording()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.cancelButton.setOnClickListener {
|
||||||
|
discardRecording()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.pauseRecording.setOnClickListener {
|
||||||
|
pauseRecording()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.acceptButton.setOnClickListener {
|
||||||
|
acceptRecording()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.playPauseButton.setOnClickListener {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// region timer
|
||||||
|
private var timer: Job? = null
|
||||||
|
private var timerValue = 0
|
||||||
|
|
||||||
|
private fun setDisplayTime(seconds: Int, showTotalTime: Boolean) {
|
||||||
|
val stringBuilder = StringBuilder()
|
||||||
|
|
||||||
|
stringBuilder.append(seconds)
|
||||||
|
|
||||||
|
binding.time.text = stringBuilder.toString()
|
||||||
|
}
|
||||||
|
private fun startTimerRecorder() {
|
||||||
|
|
||||||
|
timer = CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
setDisplayTime(timerValue, false)
|
||||||
|
delay(1000)
|
||||||
|
timerValue++
|
||||||
|
startTimerRecorder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startTimerPlayer() {
|
||||||
|
timer?.cancel()
|
||||||
|
timer = CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
setDisplayTime(timerValue, true)
|
||||||
|
delay(1000)
|
||||||
|
timerValue++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopTimer() {
|
||||||
|
timer?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// end region
|
||||||
|
|
||||||
|
// region record functions
|
||||||
|
private fun startRecording() {
|
||||||
|
audioRecorder = MediaRecorder().apply {
|
||||||
|
setAudioSource(MediaRecorder.AudioSource.MIC)
|
||||||
|
setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
|
||||||
|
setOutputFile(outputFile)
|
||||||
|
setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
|
||||||
|
|
||||||
|
try {
|
||||||
|
prepare()
|
||||||
|
start()
|
||||||
|
timerValue = 0
|
||||||
|
startTimerRecorder()
|
||||||
|
binding.viewAnimator.displayedChild = 1
|
||||||
|
} catch(e: IOException) {
|
||||||
|
Log.e(LOG_TAG, "prepare() failed\n${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopRecording() {
|
||||||
|
audioRecorder?.apply {
|
||||||
|
stop()
|
||||||
|
release()
|
||||||
|
}
|
||||||
|
stopTimer()
|
||||||
|
audioRecorder = null
|
||||||
|
|
||||||
|
// shows third state: audio recorded
|
||||||
|
binding.viewAnimator.displayedChild = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pauseRecording() {
|
||||||
|
audioRecorder?.pause()
|
||||||
|
stopTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun discardRecording(){
|
||||||
|
File(outputFile).apply {
|
||||||
|
try {
|
||||||
|
delete()
|
||||||
|
} catch(e: SecurityException) {
|
||||||
|
Log.e(LOG_TAG, "Could not delete file\n${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.viewAnimator.displayedChild = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// end region
|
||||||
|
|
||||||
|
private fun acceptRecording() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun playPauseRecording() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class AudioRecorderActivity : ComponentActivity() {
|
|
||||||
}
|
}
|
||||||
@ -1,8 +0,0 @@
|
|||||||
package com.isolaatti.audio.recorder.ui
|
|
||||||
|
|
||||||
import android.media.MediaRecorder
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
|
|
||||||
class AudioRecorderMainFragment : Fragment() {
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -2,14 +2,17 @@ package com.isolaatti.database
|
|||||||
|
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
|
import com.isolaatti.audio.recorder.data.AudioDraftEntity
|
||||||
|
import com.isolaatti.audio.recorder.data.AudiosDraftsDao
|
||||||
import com.isolaatti.auth.data.local.UserInfoDao
|
import com.isolaatti.auth.data.local.UserInfoDao
|
||||||
import com.isolaatti.auth.data.local.UserInfoEntity
|
import com.isolaatti.auth.data.local.UserInfoEntity
|
||||||
import com.isolaatti.settings.data.KeyValueDao
|
import com.isolaatti.settings.data.KeyValueDao
|
||||||
import com.isolaatti.settings.data.KeyValueEntity
|
import com.isolaatti.settings.data.KeyValueEntity
|
||||||
|
|
||||||
@Database(entities = [KeyValueEntity::class, UserInfoEntity::class], version = 2)
|
@Database(entities = [KeyValueEntity::class, UserInfoEntity::class, AudioDraftEntity::class], version = 3)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
abstract fun keyValueDao(): KeyValueDao
|
abstract fun keyValueDao(): KeyValueDao
|
||||||
abstract fun userInfoDao(): UserInfoDao
|
abstract fun userInfoDao(): UserInfoDao
|
||||||
|
abstract fun audioDrafts(): AudiosDraftsDao
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.isolaatti.posting.posts.ui
|
package com.isolaatti.posting.posts.ui
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -9,6 +10,7 @@ import androidx.core.widget.doOnTextChanged
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
import com.isolaatti.audio.recorder.ui.AudioRecorderActivity
|
||||||
import com.isolaatti.databinding.FragmentMarkdownEditingBinding
|
import com.isolaatti.databinding.FragmentMarkdownEditingBinding
|
||||||
import com.isolaatti.images.image_chooser.ui.ImageChooserContract
|
import com.isolaatti.images.image_chooser.ui.ImageChooserContract
|
||||||
import com.isolaatti.posting.link_creator.presentation.LinkCreatorViewModel
|
import com.isolaatti.posting.link_creator.presentation.LinkCreatorViewModel
|
||||||
@ -63,6 +65,10 @@ class MarkdownEditingFragment : Fragment(){
|
|||||||
binding.addLinkButton.setOnClickListener {
|
binding.addLinkButton.setOnClickListener {
|
||||||
insertLink()
|
insertLink()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.addAudioButton.setOnClickListener {
|
||||||
|
requireContext().startActivity(Intent(requireContext(), AudioRecorderActivity::class.java))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupObservers(){
|
private fun setupObservers(){
|
||||||
|
|||||||
5
app/src/main/res/drawable/baseline_circle_24.xml
Normal file
5
app/src/main/res/drawable/baseline_circle_24.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2z"/>
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/baseline_pause_24.xml
Normal file
5
app/src/main/res/drawable/baseline_pause_24.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/baseline_play_arrow_24.xml
Normal file
5
app/src/main/res/drawable/baseline_play_arrow_24.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M8,5v14l11,-7z"/>
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/baseline_stop_24.xml
Normal file
5
app/src/main/res/drawable/baseline_stop_24.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M6,6h12v12H6z"/>
|
||||||
|
</vector>
|
||||||
157
app/src/main/res/layout/activity_audio_recorder.xml
Normal file
157
app/src/main/res/layout/activity_audio_recorder.xml
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:navigationIcon="@drawable/baseline_close_24"/>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/time"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||||
|
android:layout_margin="24dp"
|
||||||
|
style="?attr/materialCardViewFilledStyle">
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/fragment_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:navGraph="@navigation/audio_recorder_navigation"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
|
app:defaultNavHost="true"/>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/time"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/card_controls"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textSize="24sp"
|
||||||
|
tools:text="00:00"
|
||||||
|
android:textAlignment="center"/>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card_controls"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
style="?attr/materialCardViewFilledStyle"
|
||||||
|
android:layout_margin="24dp">
|
||||||
|
|
||||||
|
<ViewAnimator
|
||||||
|
android:id="@+id/viewAnimator"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:inAnimation="@anim/nav_default_enter_anim"
|
||||||
|
android:outAnimation="@anim/nav_default_exit_anim">
|
||||||
|
<!-- initial state: show only start record button-->
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp">
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/record_button"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:icon="@drawable/baseline_circle_24"
|
||||||
|
app:iconTint="@color/danger"
|
||||||
|
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<!-- second state: recording-->
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_margin="8dp">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/stop_recording"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:icon="@drawable/baseline_stop_24"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/pause_recording"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/pause_recording"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:icon="@drawable/baseline_pause_24"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/stop_recording"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<!-- third state: recorded-->
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_margin="8dp">
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/cancel_button"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:icon="@drawable/baseline_close_24"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/play_pause_button"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/play_pause_button"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:icon="@drawable/baseline_play_arrow_24"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/accept_button"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/cancel_button"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/accept_button"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:icon="@drawable/baseline_check_24"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/play_pause_button"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</ViewAnimator>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@ -40,5 +40,28 @@
|
|||||||
style="@style/Widget.Material3.Button.IconButton"
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
app:icon="@drawable/baseline_add_link_24" />
|
app:icon="@drawable/baseline_add_link_24" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginVertical="30dp"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
style="?attr/materialCardViewFilledStyle">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_margin="16dp">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Audio attachment"/>
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/add_audio_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Attach audio" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
@ -1,14 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/audio_recorder_navigation"
|
android:id="@+id/audio_recorder_navigation">
|
||||||
app:startDestination="@id/audioRecorderMainFragment">
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/audioRecorderMainFragment"
|
|
||||||
android:name="com.isolaatti.audio.recorder.ui.AudioRecorderMainFragment"
|
|
||||||
android:label="AudioRecorderMainFragment" />
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/audioDraftsFragment"
|
|
||||||
android:name="com.isolaatti.audio.recorder.ui.AudioDraftsFragment"
|
|
||||||
android:label="AudioDraftsFragment" />
|
|
||||||
</navigation>
|
</navigation>
|
||||||
@ -4,4 +4,5 @@
|
|||||||
<color name="purple_lighter">#3F0095</color>
|
<color name="purple_lighter">#3F0095</color>
|
||||||
<color name="surface">#1D1725</color>
|
<color name="surface">#1D1725</color>
|
||||||
<color name="on_surface">#FFFFFF</color>
|
<color name="on_surface">#FFFFFF</color>
|
||||||
|
<color name="danger">#750606</color>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
x
Reference in New Issue
Block a user