├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── drawable │ │ │ │ ├── em_back.jpg │ │ │ │ ├── ic_close_black.xml │ │ │ │ └── ic_android.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ └── layout │ │ │ │ ├── activity_main.xml │ │ │ │ └── test.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── br │ │ │ └── com │ │ │ └── safety │ │ │ └── audio_recorder_button │ │ │ └── MainActivity.java │ ├── test │ │ └── java │ │ │ └── br │ │ │ └── com │ │ │ └── safety │ │ │ └── audio_recorder_button │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── br │ │ └── com │ │ └── safety │ │ └── audio_recorder_button │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── audio-recorder ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ ├── dimens.xml │ │ │ │ └── attrs.xml │ │ │ ├── drawable-hdpi │ │ │ │ └── ic_fileviewer.png │ │ │ ├── drawable-mdpi │ │ │ │ └── ic_fileviewer.png │ │ │ ├── drawable-xhdpi │ │ │ │ └── ic_fileviewer.png │ │ │ ├── drawable-xxhdpi │ │ │ │ └── ic_fileviewer.png │ │ │ ├── drawable-xxxhdpi │ │ │ │ ├── ic_fileviewer.png │ │ │ │ └── shape_event.xml │ │ │ ├── drawable │ │ │ │ ├── shape_circle.xml │ │ │ │ ├── ic_close.xml │ │ │ │ └── mic_shape.xml │ │ │ ├── anim │ │ │ │ ├── fade_out.xml │ │ │ │ └── fade_in.xml │ │ │ └── animator │ │ │ │ └── animation_up.xml │ │ ├── java │ │ │ └── br │ │ │ │ └── com │ │ │ │ └── safety │ │ │ │ └── audio_recorder │ │ │ │ ├── Constants.java │ │ │ │ ├── AudioListener.java │ │ │ │ ├── RecordingItem.java │ │ │ │ ├── AudioRecording.java │ │ │ │ └── AudioRecordButton.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── br │ │ │ └── com │ │ │ └── safety │ │ │ └── audio_recorder │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── br │ │ └── com │ │ └── safety │ │ └── audio_recorder │ │ └── ExampleInstrumentedTest.java ├── build.gradle └── proguard-rules.pro ├── settings.gradle ├── etc └── audio-button.gif ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── .gitignore ├── gradlew.bat ├── README.md └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /audio-recorder/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':audio-recorder' 2 | -------------------------------------------------------------------------------- /etc/audio-button.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/etc/audio-button.gif -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | audio-recorder-button 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/em_back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/app/src/main/res/drawable/em_back.jpg -------------------------------------------------------------------------------- /audio-recorder/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #fff 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /audio-recorder/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | audio-recorder 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /audio-recorder/src/main/res/drawable-hdpi/ic_fileviewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/audio-recorder/src/main/res/drawable-hdpi/ic_fileviewer.png -------------------------------------------------------------------------------- /audio-recorder/src/main/res/drawable-mdpi/ic_fileviewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/audio-recorder/src/main/res/drawable-mdpi/ic_fileviewer.png -------------------------------------------------------------------------------- /audio-recorder/src/main/res/drawable-xhdpi/ic_fileviewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/audio-recorder/src/main/res/drawable-xhdpi/ic_fileviewer.png -------------------------------------------------------------------------------- /audio-recorder/src/main/res/drawable-xxhdpi/ic_fileviewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/audio-recorder/src/main/res/drawable-xxhdpi/ic_fileviewer.png -------------------------------------------------------------------------------- /audio-recorder/src/main/res/drawable-xxxhdpi/ic_fileviewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safetysystemtechnology/audio-recorder-button/HEAD/audio-recorder/src/main/res/drawable-xxxhdpi/ic_fileviewer.png -------------------------------------------------------------------------------- /audio-recorder/src/main/java/br/com/safety/audio_recorder/Constants.java: -------------------------------------------------------------------------------- 1 | package br.com.safety.audio_recorder; 2 | 3 | /** 4 | * @author netodevel 5 | */ 6 | public interface Constants { 7 | 8 | String FORMAT_OUTPUT_DEFAULT = ".ogg"; 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /audio-recorder/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 90dp 4 | 50dp 5 | 10dp 6 | -------------------------------------------------------------------------------- /audio-recorder/src/main/res/drawable/shape_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Oct 01 09:25:21 BRT 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip 7 | -------------------------------------------------------------------------------- /audio-recorder/src/main/res/anim/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /audio-recorder/src/main/res/animator/animation_up.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /audio-recorder/src/main/java/br/com/safety/audio_recorder/AudioListener.java: -------------------------------------------------------------------------------- 1 | package br.com.safety.audio_recorder; 2 | 3 | /** 4 | * @author netodevel 5 | */ 6 | public interface AudioListener { 7 | 8 | void onStop(RecordingItem recordingItem); 9 | 10 | void onCancel(); 11 | 12 | void onError(Exception e); 13 | } 14 | -------------------------------------------------------------------------------- /audio-recorder/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /audio-recorder/src/main/res/anim/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /audio-recorder/src/main/res/drawable-xxxhdpi/shape_event.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close_black.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /audio-recorder/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /audio-recorder/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/test/java/br/com/safety/audio_recorder_button/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package br.com.safety.audio_recorder_button; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /audio-recorder/src/test/java/br/com/safety/audio_recorder/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package br.com.safety.audio_recorder; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | android.enableJetifier=true 13 | android.useAndroidX=true 14 | org.gradle.jvmargs=-Xmx1536m 15 | 16 | # When configured, Gradle will run in incubating parallel mode. 17 | # This option should only be used with decoupled projects. More details, visit 18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 19 | # org.gradle.parallel=true 20 | -------------------------------------------------------------------------------- /audio-recorder/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 29 5 | 6 | defaultConfig { 7 | minSdkVersion 15 8 | targetSdkVersion 29 9 | versionCode 1 10 | versionName "1.0" 11 | 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(dir: 'libs', include: ['*.jar']) 25 | androidTestCompile('androidx.test.espresso:espresso-core:3.1.0', { 26 | exclude group: 'com.android.support', module: 'support-annotations' 27 | }) 28 | compile 'androidx.appcompat:appcompat:1.0.0' 29 | testCompile 'junit:junit:4.12' 30 | } 31 | -------------------------------------------------------------------------------- /audio-recorder/src/androidTest/java/br/com/safety/audio_recorder/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package br.com.safety.audio_recorder; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("br.com.safety.audio_recorder.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/androidTest/java/br/com/safety/audio_recorder_button/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package br.com.safety.audio_recorder_button; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("br.com.safety.audio_recorder_button", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /audio-recorder/src/main/res/drawable/mic_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_android.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/josevieira/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /audio-recorder/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/josevieira/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | defaultConfig { 6 | applicationId "br.com.safety.audio_recorder_button" 7 | minSdkVersion 15 8 | targetSdkVersion 29 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile fileTree(dir: 'libs', include: ['*.jar']) 23 | androidTestCompile('androidx.test.espresso:espresso-core:3.1.0', { 24 | exclude group: 'com.android.support', module: 'support-annotations' 25 | }) 26 | compile 'androidx.appcompat:appcompat:1.0.0' 27 | compile 'androidx.constraintlayout:constraintlayout:1.1.3' 28 | testCompile 'junit:junit:4.12' 29 | compile project(path: ':audio-recorder') 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Android template 3 | # Built application files 4 | *.apk 5 | *.ap_ 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Log Files 29 | *.log 30 | 31 | # Android Studio Navigation editor temp files 32 | .navigation/ 33 | 34 | # Android Studio captures folder 35 | captures/ 36 | 37 | # Intellij 38 | *.iml 39 | .idea/workspace.xml 40 | .idea/tasks.xml 41 | .idea/gradle.xml 42 | .idea/dictionaries 43 | .idea/libraries 44 | 45 | # Keystore files 46 | *.jks 47 | 48 | # External native build folder generated in Android Studio 2.2 and later 49 | .externalNativeBuild 50 | 51 | # Google Services (e.g. APIs or Firebase) 52 | google-services.json 53 | 54 | # Freeline 55 | freeline.py 56 | freeline/ 57 | freeline_project_description.json 58 | 59 | .gitignore 60 | .idea/ 61 | audio-recorder-button_fork.iml 62 | 63 | *.DS_Store 64 | -------------------------------------------------------------------------------- /app/src/main/res/layout/test.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 17 | 18 | 22 | 23 | 24 | 29 | 30 | 36 | 37 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /audio-recorder/src/main/java/br/com/safety/audio_recorder/RecordingItem.java: -------------------------------------------------------------------------------- 1 | package br.com.safety.audio_recorder; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | public class RecordingItem implements Parcelable { 7 | 8 | private String mName; 9 | private String mFilePath; 10 | private int mId; 11 | private int mLength; 12 | private long mTime; 13 | 14 | public RecordingItem() 15 | { 16 | } 17 | 18 | public RecordingItem(Parcel in) { 19 | mName = in.readString(); 20 | mFilePath = in.readString(); 21 | mId = in.readInt(); 22 | mLength = in.readInt(); 23 | mTime = in.readLong(); 24 | } 25 | 26 | public String getFilePath() { 27 | return mFilePath; 28 | } 29 | 30 | public void setFilePath(String filePath) { 31 | mFilePath = filePath; 32 | } 33 | 34 | public int getLength() { 35 | return mLength; 36 | } 37 | 38 | public void setLength(int length) { 39 | mLength = length; 40 | } 41 | 42 | public int getId() { 43 | return mId; 44 | } 45 | 46 | public void setId(int id) { 47 | mId = id; 48 | } 49 | 50 | public String getName() { 51 | return mName; 52 | } 53 | 54 | public void setName(String name) { 55 | mName = name; 56 | } 57 | 58 | public long getTime() { 59 | return mTime; 60 | } 61 | 62 | public void setTime(long time) { 63 | mTime = time; 64 | } 65 | 66 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 67 | public RecordingItem createFromParcel(Parcel in) { 68 | return new RecordingItem(in); 69 | } 70 | 71 | public RecordingItem[] newArray(int size) { 72 | return new RecordingItem[size]; 73 | } 74 | }; 75 | 76 | @Override 77 | public void writeToParcel(Parcel dest, int flags) { 78 | dest.writeInt(mId); 79 | dest.writeInt(mLength); 80 | dest.writeLong(mTime); 81 | dest.writeString(mFilePath); 82 | dest.writeString(mName); 83 | } 84 | 85 | @Override 86 | public int describeContents() { 87 | return 0; 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /app/src/main/java/br/com/safety/audio_recorder_button/MainActivity.java: -------------------------------------------------------------------------------- 1 | package br.com.safety.audio_recorder_button; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.widget.Toast; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.appcompat.app.AppCompatActivity; 11 | import androidx.core.app.ActivityCompat; 12 | 13 | import br.com.safety.audio_recorder.AudioListener; 14 | import br.com.safety.audio_recorder.AudioRecordButton; 15 | import br.com.safety.audio_recorder.AudioRecording; 16 | import br.com.safety.audio_recorder.RecordingItem; 17 | 18 | import static android.Manifest.permission.READ_EXTERNAL_STORAGE; 19 | import static android.Manifest.permission.RECORD_AUDIO; 20 | import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; 21 | 22 | public class MainActivity extends AppCompatActivity { 23 | 24 | private AudioRecordButton mAudioRecordButton; 25 | private AudioRecording audioRecording; 26 | 27 | @TargetApi(Build.VERSION_CODES.M) 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_main); 32 | 33 | audioRecording = new AudioRecording(getBaseContext()); 34 | 35 | initView(); 36 | 37 | ActivityCompat.requestPermissions(this, new String[]{WRITE_EXTERNAL_STORAGE, RECORD_AUDIO, READ_EXTERNAL_STORAGE},0); 38 | 39 | ActivityCompat.requestPermissions(this, new String[]{WRITE_EXTERNAL_STORAGE}, 0); 40 | 41 | this.mAudioRecordButton.setOnAudioListener(new AudioListener() { 42 | @Override 43 | public void onStop(RecordingItem recordingItem) { 44 | Toast.makeText(getBaseContext(), "Audio..", Toast.LENGTH_SHORT).show(); 45 | audioRecording.play(recordingItem); 46 | } 47 | 48 | @Override 49 | public void onCancel() { 50 | Toast.makeText(getBaseContext(), "Cancel", Toast.LENGTH_SHORT).show(); 51 | } 52 | 53 | @Override 54 | public void onError(Exception e) { 55 | Log.d("MainActivity", "Error: " + e.getMessage()); 56 | } 57 | }); 58 | } 59 | 60 | private void initView() { 61 | this.mAudioRecordButton = (AudioRecordButton) findViewById(R.id.audio_record_button); 62 | } 63 | 64 | 65 | @Override 66 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 67 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /audio-recorder/src/main/java/br/com/safety/audio_recorder/AudioRecording.java: -------------------------------------------------------------------------------- 1 | package br.com.safety.audio_recorder; 2 | 3 | import android.content.Context; 4 | import android.media.MediaPlayer; 5 | import android.media.MediaRecorder; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | 10 | /** 11 | * @author netodevel 12 | */ 13 | public class AudioRecording { 14 | 15 | private String mFileName; 16 | private Context mContext; 17 | 18 | private MediaPlayer mMediaPlayer; 19 | private AudioListener audioListener; 20 | private MediaRecorder mRecorder; 21 | private long mStartingTimeMillis = 0; 22 | private long mElapsedMillis = 0; 23 | 24 | public AudioRecording(Context context) { 25 | mRecorder = new MediaRecorder(); 26 | this.mContext = context; 27 | } 28 | 29 | public AudioRecording() { 30 | mRecorder = new MediaRecorder(); 31 | } 32 | 33 | public AudioRecording setNameFile(String nameFile) { 34 | this.mFileName = nameFile; 35 | return this; 36 | } 37 | 38 | public AudioRecording start(AudioListener audioListener) { 39 | this.audioListener = audioListener; 40 | 41 | try { 42 | 43 | mRecorder.reset(); 44 | 45 | mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 46 | mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); 47 | mRecorder.setOutputFile(mContext.getCacheDir() + mFileName); 48 | mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); 49 | 50 | mRecorder.prepare(); 51 | mRecorder.start(); 52 | mStartingTimeMillis = System.currentTimeMillis(); 53 | } catch (IOException e) { 54 | this.audioListener.onError(e); 55 | } 56 | return this; 57 | } 58 | 59 | public void stop(Boolean cancel) { 60 | try { 61 | mRecorder.stop(); 62 | } catch (RuntimeException e) { 63 | deleteOutput(); 64 | } 65 | mRecorder.release(); 66 | mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); 67 | 68 | RecordingItem recordingItem = new RecordingItem(); 69 | recordingItem.setFilePath(mContext.getCacheDir() + mFileName); 70 | recordingItem.setName(mFileName); 71 | recordingItem.setLength((int)mElapsedMillis); 72 | recordingItem.setTime(System.currentTimeMillis()); 73 | 74 | if (cancel == false) { 75 | audioListener.onStop(recordingItem); 76 | } else { 77 | audioListener.onCancel(); 78 | } 79 | } 80 | 81 | private void deleteOutput() { 82 | File file = new File(mContext.getCacheDir() + mFileName); 83 | if (file.exists()) { 84 | file.delete(); 85 | } 86 | } 87 | 88 | public void play(RecordingItem recordingItem) { 89 | try { 90 | this.mMediaPlayer = new MediaPlayer(); 91 | this.mMediaPlayer.setDataSource(recordingItem.getFilePath()); 92 | this.mMediaPlayer.prepare(); 93 | this.mMediaPlayer.start(); 94 | 95 | } catch (IOException e) { 96 | e.printStackTrace(); 97 | } 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://jitpack.io/v/safetysystemtechnology/audio-recorder-button.svg) 2 | 3 | # Safety Audio Recorder Button 4 | Simple audio recorder component for android 5 | 6 | ## Demo 7 |

8 | progress image view 9 |

10 | 11 | 12 | ### Add permissions in your androidmanifest.xml 13 | ```xml 14 | 15 | 16 | 17 | ``` 18 | 19 | ## Install 20 | Add the dependecy 21 | 22 | ```gradle 23 | allprojects { 24 | repositories { 25 | ... 26 | maven { url 'https://jitpack.io' } 27 | } 28 | } 29 | 30 | dependencies { 31 | compile 'com.github.safetysystemtechnology:audio-recorder-button:v1.6' 32 | } 33 | 34 | ``` 35 | ## Usage 36 | 37 | ### XML 38 | 39 | ```xml 40 | 45 | 46 | ``` 47 | ### Configure XML 48 | 49 | * recorder_image_size: size to image micro voice 50 | * remove_image_size: size to image cancel audio 51 | * recorder_image: drawable to image voice 52 | * remove_image: drawable to image voice 53 | * remove_position: left or right (Todo) 54 | 55 | ### Java 56 | 57 | ```java 58 | 59 | private AudioRecordButton audioRecordButton; 60 | 61 | audioRecordButton = (AudioRecordButton) findViewById(R.id.audio_record_button); 62 | ``` 63 | 64 | Starting audio record 65 | 66 | ```java 67 | audioRecordButton.setOnAudioListener(new AudioListener() { 68 | @Override 69 | public void onStop(RecordingItem recordingItem) { 70 | Toast.makeText(getBaseContext(), "Audio...", Toast.LENGTH_SHORT).show(); 71 | } 72 | 73 | @Override 74 | public void onCancel() { 75 | Toast.makeText(getBaseContext(), "Cancel", Toast.LENGTH_SHORT).show(); 76 | } 77 | 78 | @Override 79 | public void onError(Exception e) { 80 | Log.d("MainActivity", "Error: " + e.getMessage()); 81 | } 82 | }); 83 | ``` 84 | 85 | If you prefer to execute the sound after the audio capture, just call the `play()` method inside `onStop()` 86 | 87 | ```java 88 | @Override 89 | public void onStop(RecordingItem recordingItem) { 90 | Toast.makeText(getBaseContext(), "Audio...", Toast.LENGTH_SHORT).show(); 91 | new AudioRecording(getBaseContext()).play(recordingItem); 92 | } 93 | ``` 94 | 95 | ## License 96 | The MIT License (MIT) 97 | 98 | Copyright (c) Safety System Technology 99 | 100 | Permission is hereby granted, free of charge, to any person obtaining a 101 | copy of this software and associated documentation files (the "Software"), 102 | to deal in the Software without restriction, including without limitation 103 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 104 | and/or sell copies of the Software, and to permit persons to whom the Software is 105 | furnished to do so, subject to the following conditions: 106 | 107 | The above copyright notice and this permission notice shall be included 108 | in all copies or substantial portions of the Software. 109 | 110 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 111 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 112 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 113 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 114 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 115 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /audio-recorder/src/main/java/br/com/safety/audio_recorder/AudioRecordButton.java: -------------------------------------------------------------------------------- 1 | package br.com.safety.audio_recorder; 2 | 3 | import android.animation.LayoutTransition; 4 | import android.animation.ValueAnimator; 5 | import android.annotation.TargetApi; 6 | import android.content.Context; 7 | import android.content.res.TypedArray; 8 | import android.graphics.Color; 9 | import android.graphics.drawable.Drawable; 10 | import android.os.Build; 11 | import android.os.Handler; 12 | import android.os.SystemClock; 13 | import android.util.AttributeSet; 14 | import android.view.MotionEvent; 15 | import android.view.ViewGroup; 16 | import android.view.WindowManager; 17 | import android.view.animation.AccelerateDecelerateInterpolator; 18 | import android.widget.Chronometer; 19 | import android.widget.ImageButton; 20 | import android.widget.ImageView; 21 | import android.widget.RelativeLayout; 22 | 23 | import androidx.core.content.ContextCompat; 24 | 25 | import java.util.UUID; 26 | 27 | public class AudioRecordButton extends RelativeLayout { 28 | 29 | private final int DEFAULT_ICON_SIZE = Math.round(getResources().getDimension(R.dimen.default_icon_size)); 30 | private final int DEFAULT_REMOVE_ICON_SIZE = Math.round(getResources().getDimension(R.dimen.default_icon_remove_size)); 31 | 32 | private Context mContext; 33 | private RelativeLayout mLayoutTimer; 34 | private RelativeLayout mLayoutVoice; 35 | 36 | private Chronometer mChronometer; 37 | private ImageView mImageView; 38 | private ImageButton mImageButton; 39 | private AudioListener mAudioListener; 40 | private AudioRecording mAudioRecording; 41 | 42 | private float initialX = 0; 43 | private float initialXImageButton; 44 | private float initialTouchX; 45 | 46 | private int recorderImageWidth = 0; 47 | private int recorderImageHeight = 0; 48 | private int removeImageWidth = 0; 49 | private int removeImageHeight = 0; 50 | private Drawable drawableMicVoice; 51 | private Drawable drawableRemoveButton; 52 | private boolean isPlaying = false; 53 | private boolean isPausing = false; 54 | 55 | 56 | private WindowManager.LayoutParams params; 57 | 58 | public AudioRecordButton(Context context) { 59 | super(context); 60 | setupLayout(context, null, -1, -1); 61 | } 62 | 63 | public AudioRecordButton(Context context, AttributeSet attrs) { 64 | super(context, attrs); 65 | setupLayout(context, attrs, -1, -1); 66 | } 67 | 68 | public AudioRecordButton(Context context, AttributeSet attrs, int defStyleAttr) { 69 | super(context, attrs, defStyleAttr); 70 | setupLayout(context, attrs, defStyleAttr, -1); 71 | } 72 | 73 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 74 | public AudioRecordButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 75 | super(context, attrs, defStyleAttr, defStyleRes); 76 | 77 | setupLayout(context, attrs, defStyleAttr, defStyleRes); 78 | } 79 | 80 | WindowManager.LayoutParams getViewParams() { 81 | return this.params; 82 | } 83 | 84 | @Override 85 | public boolean onTouchEvent(MotionEvent event) { 86 | switch (event.getAction()) { 87 | case MotionEvent.ACTION_DOWN: 88 | if (!isPlaying && !isPausing) { 89 | isPlaying = true; 90 | initialTouchX = event.getRawX(); 91 | changeImageView(); 92 | 93 | if (this.initialX == 0) { 94 | this.initialX = this.mImageView.getX(); 95 | } 96 | 97 | mLayoutTimer.setVisibility(VISIBLE); 98 | mImageButton.setVisibility(VISIBLE); 99 | startRecord(); 100 | return true; 101 | } 102 | break; 103 | case MotionEvent.ACTION_MOVE: 104 | 105 | if (isPlaying && !isPausing) { 106 | 107 | this.mImageView.setX(event.getX() - this.mImageView.getWidth() / 2); 108 | 109 | if (this.mImageView.getX() < DEFAULT_REMOVE_ICON_SIZE - 20) { 110 | this.mImageView.setX(0); 111 | this.changeSizeToRemove(); 112 | } else if (this.mImageView.getX() > DEFAULT_REMOVE_ICON_SIZE + DEFAULT_REMOVE_ICON_SIZE / 2) { 113 | this.unRevealSizeToRemove(); 114 | } 115 | 116 | if (this.mImageView.getX() <= 0) { 117 | this.mImageButton.setX(0); 118 | } 119 | 120 | if (this.mImageView.getX() > this.initialX) { 121 | this.mImageView.setX(this.initialX); 122 | } 123 | } 124 | 125 | break; 126 | case MotionEvent.ACTION_UP: 127 | if (isPlaying && !isPausing) { 128 | isPausing = true; 129 | moveImageToBack(); 130 | 131 | mLayoutTimer.setVisibility(INVISIBLE); 132 | mImageButton.setVisibility(INVISIBLE); 133 | 134 | if (this.mImageView.getX() < DEFAULT_REMOVE_ICON_SIZE - 10) { 135 | stopRecord(true); 136 | } else { 137 | stopRecord(false); 138 | } 139 | } 140 | break; 141 | default: 142 | return false; 143 | } 144 | return true; 145 | } 146 | 147 | private void moveImageToBack() { 148 | this.mImageButton.setAlpha(0.5f); 149 | final ValueAnimator positionAnimator = 150 | ValueAnimator.ofFloat(this.mImageView.getX(), this.initialX); 151 | 152 | positionAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); 153 | positionAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 154 | @Override 155 | public void onAnimationUpdate(ValueAnimator animation) { 156 | float x = (Float) animation.getAnimatedValue(); 157 | mImageView.setX(x); 158 | if (mImageView.getX() > DEFAULT_REMOVE_ICON_SIZE){ 159 | unRevealSizeToRemove(); 160 | } 161 | } 162 | }); 163 | 164 | positionAnimator.setDuration(200); 165 | positionAnimator.start(); 166 | } 167 | 168 | private void startRecord() { 169 | if (mAudioListener != null) { 170 | AudioListener audioListener = new AudioListener() { 171 | @Override 172 | public void onStop(RecordingItem recordingItem) { 173 | mAudioListener.onStop(recordingItem); 174 | } 175 | 176 | @Override 177 | public void onCancel() { 178 | mAudioListener.onCancel(); 179 | } 180 | 181 | @Override 182 | public void onError(Exception e) { 183 | mAudioListener.onError(e); 184 | } 185 | }; 186 | 187 | this.mAudioRecording = 188 | new AudioRecording(this.mContext) 189 | .setNameFile("/" + UUID.randomUUID() + "-audio.ogg") 190 | .start(audioListener); 191 | } 192 | } 193 | 194 | private void stopRecord(final Boolean cancel) { 195 | if (mAudioListener != null) { 196 | final Handler handler = new Handler(); 197 | handler.postDelayed(new Runnable() { 198 | @Override 199 | public void run() { 200 | mAudioRecording.stop(cancel); 201 | unRevealImageView(); 202 | isPlaying = false; 203 | isPausing = false; 204 | } 205 | }, 300); 206 | } 207 | } 208 | 209 | public void setOnAudioListener(AudioListener audioListener) { 210 | this.mAudioListener = audioListener; 211 | } 212 | 213 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 214 | public void setupLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 215 | mContext = context; 216 | 217 | /** 218 | * Component Attributes 219 | */ 220 | if (attrs != null && defStyleAttr == -1 && defStyleRes == -1) { 221 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AudioButton, 222 | defStyleAttr, defStyleRes); 223 | 224 | recorderImageWidth = (int) typedArray.getDimension(R.styleable.AudioButton_recorder_image_size, 225 | ViewGroup.LayoutParams.WRAP_CONTENT); 226 | recorderImageHeight = (int) typedArray.getDimension(R.styleable.AudioButton_recorder_image_size, 227 | ViewGroup.LayoutParams.WRAP_CONTENT); 228 | 229 | removeImageWidth = (int) typedArray.getDimension(R.styleable.AudioButton_remove_image_size, 230 | ViewGroup.LayoutParams.WRAP_CONTENT); 231 | removeImageHeight = (int) typedArray.getDimension(R.styleable.AudioButton_remove_image_size, 232 | ViewGroup.LayoutParams.WRAP_CONTENT); 233 | 234 | drawableMicVoice = typedArray.getDrawable(R.styleable.AudioButton_recorder_image); 235 | drawableRemoveButton = typedArray.getDrawable(R.styleable.AudioButton_remove_image); 236 | } 237 | 238 | /** 239 | * layout to chronometer 240 | */ 241 | mLayoutTimer = new RelativeLayout(context); 242 | mLayoutTimer.setId(9 + 1); 243 | mLayoutTimer.setVisibility(INVISIBLE); 244 | mLayoutTimer.setBackground(ContextCompat.getDrawable(context, R.drawable.shape_event)); 245 | 246 | RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( 247 | ViewGroup.LayoutParams.WRAP_CONTENT, 248 | ViewGroup.LayoutParams.WRAP_CONTENT); 249 | 250 | layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE); 251 | layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE); 252 | Integer margin = Math.round(getResources().getDimension(R.dimen.chronometer_margin)); 253 | layoutParams.setMargins(margin, margin, margin, margin); 254 | addView(mLayoutTimer, layoutParams); 255 | 256 | /** 257 | * chronometer 258 | */ 259 | this.mChronometer = new Chronometer(context); 260 | this.mChronometer.setTextColor(Color.WHITE); 261 | 262 | RelativeLayout.LayoutParams layoutParamsChronometer = new RelativeLayout.LayoutParams( 263 | ViewGroup.LayoutParams.WRAP_CONTENT, 264 | ViewGroup.LayoutParams.WRAP_CONTENT); 265 | 266 | layoutParamsChronometer.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); 267 | mLayoutTimer.addView(this.mChronometer, layoutParamsChronometer); 268 | 269 | /** 270 | * Layout to voice and cancel audio 271 | */ 272 | mLayoutVoice = new RelativeLayout(context); 273 | RelativeLayout.LayoutParams layoutVoiceParams = new RelativeLayout.LayoutParams( 274 | ViewGroup.LayoutParams.MATCH_PARENT, 275 | ViewGroup.LayoutParams.WRAP_CONTENT 276 | ); 277 | layoutVoiceParams.addRule(RelativeLayout.BELOW, 9 + 1); 278 | addView(this.mLayoutVoice, layoutVoiceParams); 279 | 280 | /** 281 | * Image voice 282 | */ 283 | this.mImageView = new ImageView(context); 284 | this.mImageView.setBackground(drawableMicVoice != null ? drawableMicVoice : ContextCompat.getDrawable(context, R.drawable.mic_shape)); 285 | LayoutParams layoutParamImage = new LayoutParams( 286 | recorderImageWidth > 0 ? recorderImageWidth : DEFAULT_ICON_SIZE, 287 | recorderImageHeight > 0 ? recorderImageHeight : DEFAULT_ICON_SIZE); 288 | layoutParamImage.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); 289 | this.mLayoutVoice.addView(this.mImageView, layoutParamImage); 290 | 291 | /** 292 | * Image Button 293 | */ 294 | this.mImageButton = new ImageButton(context); 295 | this.mImageButton.setVisibility(INVISIBLE); 296 | this.mImageButton.setAlpha(0.5f); 297 | this.mImageButton.setImageDrawable(drawableRemoveButton != null ? drawableRemoveButton : ContextCompat.getDrawable(context, R.drawable.ic_close)); 298 | this.mImageButton.setBackground(ContextCompat.getDrawable(context, R.drawable.shape_circle)); 299 | this.mImageButton.setColorFilter(Color.WHITE); 300 | 301 | RelativeLayout.LayoutParams layoutParamImageButton = new RelativeLayout.LayoutParams( 302 | ((removeImageWidth > 0) && (removeImageWidth < DEFAULT_REMOVE_ICON_SIZE)) ? removeImageWidth : DEFAULT_REMOVE_ICON_SIZE, 303 | ((removeImageHeight > 0) && (removeImageHeight < DEFAULT_REMOVE_ICON_SIZE)) ? removeImageHeight : DEFAULT_REMOVE_ICON_SIZE 304 | ); 305 | layoutParamImageButton.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE); 306 | 307 | this.mLayoutVoice.addView(this.mImageButton, layoutParamImageButton); 308 | 309 | this.initialXImageButton = this.mImageButton.getX(); 310 | 311 | } 312 | 313 | public void changeImageView() { 314 | LayoutTransition transition = new LayoutTransition(); 315 | transition.setDuration(600); 316 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 317 | transition.enableTransitionType(LayoutTransition.CHANGING); 318 | } 319 | this.mLayoutTimer.setLayoutTransition(transition); 320 | this.setLayoutTransition(transition); 321 | 322 | this.mChronometer.setBase(SystemClock.elapsedRealtime()); 323 | this.mChronometer.start(); 324 | 325 | this.mImageView.setScaleX(0.8f); 326 | this.mImageView.setScaleY(0.8f); 327 | this.requestLayout(); 328 | } 329 | 330 | public void changeSizeToRemove() { 331 | if (this.mImageButton.getLayoutParams().width != this.mImageView.getWidth()) { 332 | this.mImageButton.getLayoutParams().width = this.mImageView.getWidth(); 333 | this.mImageButton.getLayoutParams().height = this.mImageView.getHeight(); 334 | this.mImageButton.requestLayout(); 335 | this.mImageButton.setX(0); 336 | } 337 | } 338 | 339 | public void unRevealSizeToRemove() { 340 | this.mImageButton.getLayoutParams().width = ((removeImageWidth > 0) && (removeImageWidth < DEFAULT_REMOVE_ICON_SIZE)) ? removeImageWidth : DEFAULT_REMOVE_ICON_SIZE; 341 | this.mImageButton.getLayoutParams().height = ((removeImageHeight > 0) && (removeImageHeight < DEFAULT_REMOVE_ICON_SIZE)) ? removeImageHeight : DEFAULT_REMOVE_ICON_SIZE; 342 | this.mImageButton.requestLayout(); 343 | } 344 | 345 | public void unRevealImageView() { 346 | this.mChronometer.stop(); 347 | 348 | this.mImageView.setScaleX(1f); 349 | this.mImageView.setScaleY(1f); 350 | this.requestLayout(); 351 | } 352 | 353 | } --------------------------------------------------------------------------------