├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hwangjr │ │ └── recordhelper │ │ └── app │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── hwangjr │ │ │ └── recordhelper │ │ │ └── app │ │ │ ├── AudioPlaybackActivity.java │ │ │ ├── AudioRecordingActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── SizeAdapter.java │ │ │ ├── VideoPlaybackActivity.java │ │ │ ├── VideoRecordingActivity.java │ │ │ └── utils │ │ │ ├── NotificationUtils.java │ │ │ └── StorageUtils.java │ └── res │ │ ├── drawable-hdpi │ │ ├── ic_launcher.png │ │ └── ic_switch.jpg │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ │ ├── layout │ │ ├── audio_play.xml │ │ ├── audio_rec.xml │ │ ├── main.xml │ │ ├── video_play.xml │ │ └── video_rec.xml │ │ ├── values-sw600dp │ │ └── dimens.xml │ │ ├── values-sw720dp-land │ │ └── dimens.xml │ │ ├── values-v11 │ │ └── styles.xml │ │ ├── values-v14 │ │ └── styles.xml │ │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── hwangjr │ └── recordhelper │ └── app │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jhelper ├── .gitignore ├── README.md ├── build.gradle ├── docs │ ├── Files.md │ ├── IOs.md │ ├── ProjectHome.md │ ├── SimpleTextReader.md │ ├── SimpleTextWriter.md │ └── Strings.md └── src │ ├── main │ └── java │ │ └── com │ │ └── hwangjr │ │ └── jhelper │ │ ├── Bytes.java │ │ ├── Chars.java │ │ ├── CountingSet.java │ │ ├── Doubles.java │ │ ├── Dumper.java │ │ ├── Files.java │ │ ├── Filter.java │ │ ├── IOs.java │ │ ├── IterableLineReader.java │ │ ├── KeyValueReader.java │ │ ├── LineIterator.java │ │ ├── MultiFileLineIterator.java │ │ ├── Preconditions.java │ │ ├── SimpleTextReader.java │ │ ├── SimpleTextWriter.java │ │ ├── StringFilters.java │ │ ├── Strings.java │ │ ├── Systems.java │ │ └── Words.java │ └── test │ └── java │ └── com │ └── hwangjr │ └── jhelper │ ├── CountingSetTest.java │ ├── FilesTest.java │ ├── IOTest.java │ ├── KeyValueReaderTest.java │ ├── SimpleTextReaderTest.java │ ├── SimpleTextWriterTest.java │ ├── StringsTest.java │ ├── TestBytes.java │ ├── TestSystems.java │ ├── TestUtil.java │ └── WordsTest.java ├── recordhelper ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hwangjr │ │ └── recordhepler │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── hwangjr │ │ └── recordhelper │ │ ├── audio │ │ ├── AudioPlaybackManager.java │ │ ├── AudioRecordingHandler.java │ │ └── AudioRecordingThread.java │ │ ├── fft │ │ ├── Complex.java │ │ └── FFT.java │ │ ├── video │ │ ├── AdaptiveSurfaceView.java │ │ ├── CameraHelper.java │ │ ├── CameraManager.java │ │ ├── MediaPlayerManager.java │ │ ├── MediaRecorderManager.java │ │ ├── PlaybackHandler.java │ │ ├── VideoPlaybackManager.java │ │ ├── VideoRecordingHandler.java │ │ └── VideoRecordingManager.java │ │ └── visualizer │ │ ├── AudioData.java │ │ ├── FFTData.java │ │ ├── VisualizerView.java │ │ └── renderer │ │ ├── BarGraphRenderer.java │ │ └── Renderer.java │ └── test │ └── java │ └── com │ └── hwangjr │ └── recordhepler │ └── ExampleUnitTest.java ├── settings.gradle └── soundhelper ├── .gitignore ├── README.md ├── build.gradle └── src ├── main └── java │ └── com │ └── hwangjr │ └── soundhelper │ ├── dsp │ ├── Complex.java │ ├── DoubleVector.java │ ├── DoubleVectorFrameSource.java │ ├── DoubleVectorProcessingPipeline.java │ ├── DoubleVectorProcessor.java │ ├── MutableComplex.java │ ├── NormalizedFrameIterator.java │ └── WindowerFactory.java │ ├── pcm │ ├── JavaAudioFormatBuilder.java │ ├── MonoWavFileReader.java │ ├── PcmAudioFormat.java │ ├── PcmAudioHelper.java │ ├── PcmMonoInputStream.java │ ├── PcmMonoOutputStream.java │ ├── RiffHeaderData.java │ ├── WavAudioFormat.java │ └── WavFileWriter.java │ └── system │ └── WavPlayer.java └── test └── java └── com └── hwangjr └── soundhelper └── pcm ├── IterableFrameReaderTest.java ├── PcmAudioFormatTest.java ├── PcmAudioInputStreamTests.java └── WavAuidoFormatTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | /*/build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | *.iml 30 | /.idea 31 | /.idea/workspace.xml 32 | /.idea/libraries 33 | .DS_Store 34 | /captures 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RecordHelper 2 | ============================= 3 | This is a simple record library from [steelkiwi/AndroidRecording](https://github.com/steelkiwi/AndroidRecording), it seems that she don't contribute to the repo anymore. 4 | 5 | This project is build on android studio ide and explore all dependence for modules, enjoy it. 6 | 7 | Content below is the source readme, i have modify the package. if u have some issues, welcome to pull to me. 8 | 9 | Download 10 | -------- 11 | ``` 12 | allprojects { 13 | repositories { 14 | maven { 15 | url "https://jitpack.io" 16 | } 17 | } 18 | } 19 | ``` 20 | ``` 21 | dependencies { 22 | compile 'com.github.steelkiwi:AndroidRecording:master' 23 | } 24 | ``` 25 | 26 | Android Recording Library 27 | ======================================= 28 | 29 | Android Recording library offers convenient tools for audio/video recording and playback. 30 | 31 | For audio it uses: 32 | * AudioRecord to capture and save audio signal from microphone 33 | * MediaPlayer with MediaController to play recorded audio 34 | * custom Visualizer (like bar chart) to represent audio signal on screen while recording and during playback 35 | 36 | For video it uses: 37 | * Camera and MediaRecorder to record a video of specified resolution 38 | * MediaPlayer with MediaController to play recorded video 39 | * custom SurfaceView with adjustable size to properly display Camera preview and recorded video (in portrait and landscape modes) 40 | 41 | Record audio: how to use 42 | ------------------------ 43 | 44 | 1. Setup VisualizerView 45 | 46 | ```xml 47 | 51 | 52 | ``` 53 | 54 | ```java 55 | visualizerView = (VisualizerView) findViewById(R.id.visualizerView); 56 | setupVisualizer(); 57 | 58 | ... 59 | 60 | private void setupVisualizer() { 61 | Paint paint = new Paint(); 62 | paint.setStrokeWidth(5f); //set bar width 63 | paint.setAntiAlias(true); 64 | paint.setColor(Color.argb(200, 227, 69, 53)); //set bar color 65 | BarGraphRenderer barGraphRendererBottom = new BarGraphRenderer(2, paint, false); 66 | visualizerView.addRenderer(barGraphRendererBottom); 67 | } 68 | ``` 69 | 70 | 2. Start recording thread 71 | 72 | ```java 73 | private void startRecording() { 74 | recordingThread = new AudioRecordingThread(fileName, new AudioRecordingHandler() { //pass file name where to store the recorded audio 75 | @Override 76 | public void onFftDataCapture(final byte[] bytes) { 77 | runOnUiThread(new Runnable() { 78 | public void run() { 79 | if (visualizerView != null) { 80 | visualizerView.updateVisualizerFFT(bytes); //update VisualizerView with new audio portion 81 | } 82 | } 83 | }); 84 | } 85 | 86 | @Override 87 | public void onRecordSuccess() {} 88 | 89 | @Override 90 | public void onRecordingError() {} 91 | 92 | @Override 93 | public void onRecordSaveError() {} 94 | }); 95 | recordingThread.start(); 96 | } 97 | ``` 98 | 99 | 3. When done, stop recording 100 | 101 | ```java 102 | private void stopRecording() { 103 | if (recordingThread != null) { 104 | recordingThread.stopRecording(); 105 | recordingThread = null; 106 | } 107 | } 108 | ``` 109 | 110 | Play audio: how to use 111 | ------------------------ 112 | 113 | 1. Setup VisualizerView 114 | 115 | 2. Setup AudioPlaybackManager. It will attach MediaPlayer to a VisualizerView 116 | 117 | ```java 118 | playbackManager = new AudioPlaybackManager(this, visualizerView, playbackHandler); 119 | playbackManager.setupPlayback(fileName); //pass file name of the recorded audio 120 | ``` 121 | 122 | 3. Use onscreen MediaController to play/pause/stop/rewind audio 123 | 124 | 125 | Record video: how to use 126 | ------------------------ 127 | 128 | 1. Setup custom SurfaceView (AdaptiveSurfaceView) 129 | 130 | ```xml 131 | 135 | ``` 136 | 137 | 2. Setup RecordingManager 138 | 139 | ```java 140 | videoView = (AdaptiveSurfaceView) findViewById(R.id.videoView); 141 | recordingManager = new VideoRecordingManager(videoView, recordingHandler); //pass reference to custom SurfaceView 142 | ``` 143 | 144 | 3. Choose desired video resolution and pass it to RecordingManager, it will adjust size of AdaptiveSurfaceView to properly display Camera output 145 | 146 | ```java 147 | recordingManager.setPreviewSize(videoSize); 148 | ``` 149 | 150 | 4. To start recording use 151 | 152 | ```java 153 | recordingManager.startRecording(fileName, videoSize) 154 | ``` 155 | 156 | 5. To stop recording use 157 | 158 | ```java 159 | recordingManager.stopRecording() 160 | ``` 161 | 162 | Play video: how to use 163 | ------------------------ 164 | 165 | 1. Setup custom SurfaceView (AdaptiveSurfaceView) 166 | 167 | 2. Setup VideoPlaybackManager. It will attach MediaPlayer to a VisualizerView 168 | 169 | ```java 170 | playbackManager = new VideoPlaybackManager(this, videoView, playbackHandler); 171 | playbackManager.setupPlayback(fileName); //pass file name of the recorded video 172 | ``` 173 | 174 | 3. Use onscreen MediaController to play/pause/stop/rewind video 175 | 176 | 177 | ==================================== 178 | 179 | For more details, please, see the demo project. 180 | 181 | License 182 | -------- 183 | 184 | Copyright 2015 HwangJR, Inc. 185 | 186 | Licensed under the Apache License, Version 2.0 (the "License"); 187 | you may not use this file except in compliance with the License. 188 | You may obtain a copy of the License at 189 | 190 | http://www.apache.org/licenses/LICENSE-2.0 191 | 192 | Unless required by applicable law or agreed to in writing, software 193 | distributed under the License is distributed on an "AS IS" BASIS, 194 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 195 | See the License for the specific language governing permissions and 196 | limitations under the License. 197 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.hwangjr.recordhelper.app" 9 | minSdkVersion 9 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:23.1.1' 25 | compile project(':recordhelper') 26 | 27 | // test 28 | testCompile 'junit:junit:4.12' 29 | } 30 | -------------------------------------------------------------------------------- /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 /usr/local/android-sdk-linux/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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/hwangjr/recordhelper/app/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.recordhelper.app; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 19 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 39 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/hwangjr/recordhelper/app/AudioPlaybackActivity.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.recordhelper.app; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.os.Bundle; 8 | import android.view.MotionEvent; 9 | 10 | import com.hwangjr.recordhelper.audio.AudioPlaybackManager; 11 | import com.hwangjr.recordhelper.video.PlaybackHandler; 12 | import com.hwangjr.recordhelper.visualizer.VisualizerView; 13 | import com.hwangjr.recordhelper.visualizer.renderer.BarGraphRenderer; 14 | 15 | public class AudioPlaybackActivity extends Activity { 16 | public static String FileNameArg = "arg_filename"; 17 | 18 | private static String fileName = null; 19 | 20 | private VisualizerView visualizerView; 21 | 22 | private AudioPlaybackManager playbackManager; 23 | 24 | private PlaybackHandler playbackHandler = new PlaybackHandler() { 25 | @Override 26 | public void onPreparePlayback() { 27 | runOnUiThread(new Runnable() { 28 | public void run() { 29 | playbackManager.showMediaController(); 30 | } 31 | }); 32 | } 33 | }; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.audio_play); 39 | 40 | Intent i = getIntent(); 41 | if ((i != null) && (i.getExtras() != null)) { 42 | fileName = i.getExtras().getString(FileNameArg); 43 | } 44 | 45 | visualizerView = (VisualizerView) findViewById(R.id.visualizerView); 46 | setupVisualizer(); 47 | 48 | playbackManager = new AudioPlaybackManager(this, visualizerView, playbackHandler); 49 | playbackManager.setupPlayback(fileName); 50 | } 51 | 52 | @Override 53 | public boolean onTouchEvent(MotionEvent event) { 54 | playbackManager.showMediaController(); 55 | return false; 56 | } 57 | 58 | @Override 59 | protected void onPause() { 60 | super.onPause(); 61 | 62 | playbackManager.pause(); 63 | playbackManager.hideMediaController(); 64 | } 65 | 66 | @Override 67 | protected void onDestroy() { 68 | playbackManager.dispose(); 69 | playbackHandler = null; 70 | 71 | super.onDestroy(); 72 | } 73 | 74 | private void setupVisualizer() { 75 | Paint paint = new Paint(); 76 | paint.setStrokeWidth(5f); 77 | paint.setAntiAlias(true); 78 | paint.setColor(Color.argb(200, 227, 69, 53)); 79 | BarGraphRenderer barGraphRendererBottom = new BarGraphRenderer(2, paint, false); 80 | visualizerView.addRenderer(barGraphRendererBottom); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/hwangjr/recordhelper/app/AudioRecordingActivity.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.recordhelper.app; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.view.View.OnClickListener; 10 | import android.widget.Button; 11 | 12 | import com.hwangjr.recordhelper.app.utils.NotificationUtils; 13 | import com.hwangjr.recordhelper.app.utils.StorageUtils; 14 | import com.hwangjr.recordhelper.audio.AudioRecordingHandler; 15 | import com.hwangjr.recordhelper.audio.AudioRecordingThread; 16 | import com.hwangjr.recordhelper.visualizer.VisualizerView; 17 | import com.hwangjr.recordhelper.visualizer.renderer.BarGraphRenderer; 18 | 19 | public class AudioRecordingActivity extends Activity { 20 | private static String fileName = null; 21 | 22 | private Button recordBtn, playBtn; 23 | private VisualizerView visualizerView; 24 | 25 | private AudioRecordingThread recordingThread; 26 | private boolean startRecording = true; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.audio_rec); 32 | 33 | if (!StorageUtils.checkExternalStorageAvailable()) { 34 | NotificationUtils.showInfoDialog(this, getString(R.string.noExtStorageAvailable)); 35 | return; 36 | } 37 | fileName = StorageUtils.getFileName(true); 38 | 39 | recordBtn = (Button) findViewById(R.id.recordBtn); 40 | recordBtn.setOnClickListener(new OnClickListener() { 41 | @Override 42 | public void onClick(View v) { 43 | record(); 44 | } 45 | }); 46 | 47 | playBtn = (Button) findViewById(R.id.playBtn); 48 | playBtn.setOnClickListener(new OnClickListener() { 49 | @Override 50 | public void onClick(View v) { 51 | play(); 52 | } 53 | }); 54 | 55 | visualizerView = (VisualizerView) findViewById(R.id.visualizerView); 56 | setupVisualizer(); 57 | } 58 | 59 | @Override 60 | protected void onPause() { 61 | super.onPause(); 62 | 63 | recordStop(); 64 | } 65 | 66 | @Override 67 | protected void onDestroy() { 68 | recordStop(); 69 | releaseVisualizer(); 70 | 71 | super.onDestroy(); 72 | } 73 | 74 | private void setupVisualizer() { 75 | Paint paint = new Paint(); 76 | paint.setStrokeWidth(5f); 77 | paint.setAntiAlias(true); 78 | paint.setColor(Color.argb(200, 227, 69, 53)); 79 | BarGraphRenderer barGraphRendererBottom = new BarGraphRenderer(2, paint, false); 80 | visualizerView.addRenderer(barGraphRendererBottom); 81 | } 82 | 83 | private void releaseVisualizer() { 84 | visualizerView.release(); 85 | visualizerView = null; 86 | } 87 | 88 | private void record() { 89 | if (startRecording) { 90 | recordStart(); 91 | } else { 92 | recordStop(); 93 | } 94 | } 95 | 96 | private void recordStart() { 97 | startRecording(); 98 | startRecording = false; 99 | recordBtn.setText(R.string.stopRecordBtn); 100 | playBtn.setEnabled(false); 101 | } 102 | 103 | private void recordStop() { 104 | stopRecording(); 105 | startRecording = true; 106 | recordBtn.setText(R.string.recordBtn); 107 | playBtn.setEnabled(true); 108 | } 109 | 110 | private void startRecording() { 111 | recordingThread = new AudioRecordingThread(fileName, new AudioRecordingHandler() { 112 | @Override 113 | public void onFftDataCapture(final byte[] bytes) { 114 | runOnUiThread(new Runnable() { 115 | public void run() { 116 | if (visualizerView != null) { 117 | visualizerView.updateVisualizerFFT(bytes); 118 | } 119 | } 120 | }); 121 | } 122 | 123 | @Override 124 | public void onRecordSuccess() { 125 | } 126 | 127 | @Override 128 | public void onRecordingError() { 129 | runOnUiThread(new Runnable() { 130 | public void run() { 131 | recordStop(); 132 | NotificationUtils.showInfoDialog(AudioRecordingActivity.this, getString(R.string.recordingError)); 133 | } 134 | }); 135 | } 136 | 137 | @Override 138 | public void onRecordSaveError() { 139 | runOnUiThread(new Runnable() { 140 | public void run() { 141 | recordStop(); 142 | NotificationUtils.showInfoDialog(AudioRecordingActivity.this, getString(R.string.saveRecordError)); 143 | } 144 | }); 145 | } 146 | }); 147 | recordingThread.start(); 148 | } 149 | 150 | private void stopRecording() { 151 | if (recordingThread != null) { 152 | recordingThread.stopRecording(); 153 | recordingThread = null; 154 | } 155 | } 156 | 157 | private void play() { 158 | Intent i = new Intent(AudioRecordingActivity.this, AudioPlaybackActivity.class); 159 | i.putExtra(VideoPlaybackActivity.FileNameArg, fileName); 160 | startActivityForResult(i, 0); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /app/src/main/java/com/hwangjr/recordhelper/app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.recordhelper.app; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.AdapterView; 8 | import android.widget.AdapterView.OnItemClickListener; 9 | import android.widget.ArrayAdapter; 10 | import android.widget.ListView; 11 | 12 | public class MainActivity extends Activity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.main); 18 | 19 | ListView list = (ListView) findViewById(android.R.id.list); 20 | ArrayAdapter listAdapter = new ArrayAdapter(getApplicationContext(), 21 | android.R.layout.simple_list_item_1, 22 | getResources().getStringArray(R.array.activities)); 23 | list.setAdapter(listAdapter); 24 | list.setOnItemClickListener(new OnItemClickListener() { 25 | @Override 26 | public void onItemClick(AdapterView arg0, View arg1, int arg2, long arg3) { 27 | launchDemo(arg2); 28 | } 29 | }); 30 | } 31 | 32 | private void launchDemo(int position) { 33 | switch (position) { 34 | case 0: default: { 35 | Intent i = new Intent(MainActivity.this, AudioRecordingActivity.class); 36 | startActivity(i); 37 | break; 38 | } 39 | case 1: { 40 | Intent i = new Intent(MainActivity.this, VideoRecordingActivity.class); 41 | startActivity(i); 42 | break; 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/hwangjr/recordhelper/app/SizeAdapter.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.recordhelper.app; 2 | 3 | import java.util.List; 4 | 5 | import android.hardware.Camera.Size; 6 | import android.text.TextUtils.TruncateAt; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.BaseAdapter; 10 | import android.widget.TextView; 11 | 12 | public class SizeAdapter extends BaseAdapter { 13 | 14 | private List sizes; 15 | 16 | public SizeAdapter(List sizes) { 17 | this.sizes = sizes; 18 | } 19 | 20 | public void set(List sizes) { 21 | this.sizes.clear(); 22 | this.sizes = sizes; 23 | notifyDataSetChanged(); 24 | } 25 | 26 | @Override 27 | public int getCount() { 28 | return sizes != null ? sizes.size() : 0; 29 | } 30 | 31 | @Override 32 | public Size getItem(int position) { 33 | return sizes.get(position); 34 | } 35 | 36 | @Override 37 | public long getItemId(int position) { 38 | return position; 39 | } 40 | 41 | @Override 42 | public View getView(int position, View convertView, ViewGroup parent) { 43 | TextView view = new TextView(parent.getContext()); 44 | view.setText(String.format("%s: %sx%s", "Video", sizes.get(position).width, sizes.get(position).height)); 45 | view.setEllipsize(TruncateAt.END); 46 | view.setPadding(16, 16, 16, 16); 47 | return view; 48 | } 49 | 50 | @Override 51 | public View getDropDownView(int position, View convertView, ViewGroup parent) { 52 | TextView view = new TextView(parent.getContext()); 53 | view.setText(String.format("%s: %sx%s", "Video", sizes.get(position).width, sizes.get(position).height)); 54 | view.setPadding(16, 16, 16, 16); 55 | return view; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/hwangjr/recordhelper/app/VideoPlaybackActivity.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.recordhelper.app; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.MotionEvent; 7 | 8 | import com.hwangjr.recordhelper.video.AdaptiveSurfaceView; 9 | import com.hwangjr.recordhelper.video.PlaybackHandler; 10 | import com.hwangjr.recordhelper.video.VideoPlaybackManager; 11 | 12 | public class VideoPlaybackActivity extends Activity { 13 | public static String FileNameArg = "arg_filename"; 14 | 15 | private static String fileName = null; 16 | 17 | private AdaptiveSurfaceView videoView; 18 | 19 | private VideoPlaybackManager playbackManager; 20 | 21 | private PlaybackHandler playbackHandler = new PlaybackHandler() { 22 | @Override 23 | public void onPreparePlayback() { 24 | runOnUiThread (new Runnable() { 25 | public void run() { 26 | playbackManager.showMediaController(); 27 | } 28 | }); 29 | } 30 | }; 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.video_play); 36 | 37 | Intent i = getIntent(); 38 | if ((i != null) && (i.getExtras() != null)) { 39 | fileName = i.getExtras().getString(FileNameArg); 40 | } 41 | 42 | videoView = (AdaptiveSurfaceView) findViewById(R.id.videoView); 43 | 44 | playbackManager = new VideoPlaybackManager(this, videoView, playbackHandler); 45 | playbackManager.setupPlayback(fileName); 46 | } 47 | 48 | @Override 49 | public boolean onTouchEvent(MotionEvent event) { 50 | playbackManager.showMediaController(); 51 | return false; 52 | } 53 | 54 | @Override 55 | protected void onPause() { 56 | super.onPause(); 57 | 58 | playbackManager.pause(); 59 | playbackManager.hideMediaController(); 60 | } 61 | 62 | @Override 63 | protected void onDestroy() { 64 | playbackManager.dispose(); 65 | playbackHandler = null; 66 | 67 | super.onDestroy(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/hwangjr/recordhelper/app/VideoRecordingActivity.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.recordhelper.app; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Activity; 5 | import android.content.Intent; 6 | import android.hardware.Camera.Size; 7 | import android.os.Build; 8 | import android.os.Bundle; 9 | import android.view.View; 10 | import android.view.View.OnClickListener; 11 | import android.widget.AdapterView; 12 | import android.widget.AdapterView.OnItemSelectedListener; 13 | import android.widget.Button; 14 | import android.widget.ImageButton; 15 | import android.widget.Spinner; 16 | import android.widget.Toast; 17 | 18 | import com.hwangjr.recordhelper.app.utils.NotificationUtils; 19 | import com.hwangjr.recordhelper.app.utils.StorageUtils; 20 | import com.hwangjr.recordhelper.video.AdaptiveSurfaceView; 21 | import com.hwangjr.recordhelper.video.CameraHelper; 22 | import com.hwangjr.recordhelper.video.VideoRecordingHandler; 23 | import com.hwangjr.recordhelper.video.VideoRecordingManager; 24 | 25 | import java.util.List; 26 | 27 | public class VideoRecordingActivity extends Activity { 28 | private static String fileName = null; 29 | 30 | private Button recordBtn, playBtn; 31 | private ImageButton switchBtn; 32 | private Spinner videoSizeSpinner; 33 | 34 | private Size videoSize = null; 35 | private VideoRecordingManager recordingManager; 36 | 37 | private VideoRecordingHandler recordingHandler = new VideoRecordingHandler() { 38 | @Override 39 | public boolean onPrepareRecording() { 40 | if (videoSizeSpinner == null) { 41 | initVideoSizeSpinner(); 42 | return true; 43 | } 44 | return false; 45 | } 46 | 47 | @Override 48 | public Size getVideoSize() { 49 | return videoSize; 50 | } 51 | 52 | @Override 53 | public int getDisplayRotation() { 54 | return VideoRecordingActivity.this.getWindowManager().getDefaultDisplay().getRotation(); 55 | } 56 | }; 57 | 58 | @Override 59 | protected void onCreate(Bundle savedInstanceState) { 60 | super.onCreate(savedInstanceState); 61 | setContentView(R.layout.video_rec); 62 | 63 | if (!StorageUtils.checkExternalStorageAvailable()) { 64 | NotificationUtils.showInfoDialog(this, getString(R.string.noExtStorageAvailable)); 65 | return; 66 | } 67 | fileName = StorageUtils.getFileName(false); 68 | 69 | AdaptiveSurfaceView videoView = (AdaptiveSurfaceView) findViewById(R.id.videoView); 70 | recordingManager = new VideoRecordingManager(videoView, recordingHandler); 71 | 72 | recordBtn = (Button) findViewById(R.id.recordBtn); 73 | recordBtn.setOnClickListener(new OnClickListener() { 74 | @Override 75 | public void onClick(View v) { 76 | record(); 77 | } 78 | }); 79 | 80 | switchBtn = (ImageButton) findViewById(R.id.switchBtn); 81 | if (recordingManager.getCameraManager().hasMultipleCameras()) { 82 | switchBtn.setOnClickListener(new OnClickListener() { 83 | @Override 84 | public void onClick(View v) { 85 | switchCamera(); 86 | } 87 | }); 88 | } else { 89 | switchBtn.setVisibility(View.GONE); 90 | } 91 | 92 | playBtn = (Button) findViewById(R.id.playBtn); 93 | playBtn.setOnClickListener(new OnClickListener() { 94 | @Override 95 | public void onClick(View v) { 96 | play(); 97 | } 98 | }); 99 | } 100 | 101 | @Override 102 | protected void onDestroy() { 103 | recordingManager.dispose(); 104 | recordingHandler = null; 105 | 106 | super.onDestroy(); 107 | } 108 | 109 | @SuppressLint("NewApi") 110 | private void initVideoSizeSpinner() { 111 | videoSizeSpinner = (Spinner) findViewById(R.id.videoSizeSpinner); 112 | if (Build.VERSION.SDK_INT >= 11) { 113 | List sizes = CameraHelper.getCameraSupportedVideoSizes(recordingManager.getCameraManager().getCamera()); 114 | videoSizeSpinner.setAdapter(new SizeAdapter(sizes)); 115 | videoSizeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { 116 | @Override 117 | public void onItemSelected(AdapterView arg0, View arg1, int arg2, long arg3) { 118 | videoSize = (Size) arg0.getItemAtPosition(arg2); 119 | recordingManager.setPreviewSize(videoSize); 120 | } 121 | 122 | @Override 123 | public void onNothingSelected(AdapterView arg0) { 124 | } 125 | }); 126 | videoSize = (Size) videoSizeSpinner.getItemAtPosition(0); 127 | } else { 128 | videoSizeSpinner.setVisibility(View.GONE); 129 | } 130 | } 131 | 132 | @SuppressLint("NewApi") 133 | private void updateVideoSizes() { 134 | if (Build.VERSION.SDK_INT >= 11) { 135 | ((SizeAdapter) videoSizeSpinner.getAdapter()).set(CameraHelper.getCameraSupportedVideoSizes(recordingManager.getCameraManager().getCamera())); 136 | videoSizeSpinner.setSelection(0); 137 | videoSize = (Size) videoSizeSpinner.getItemAtPosition(0); 138 | recordingManager.setPreviewSize(videoSize); 139 | } 140 | } 141 | 142 | private void switchCamera() { 143 | recordingManager.getCameraManager().switchCamera(); 144 | updateVideoSizes(); 145 | } 146 | 147 | private void record() { 148 | if (recordingManager.stopRecording()) { 149 | recordBtn.setText(R.string.recordBtn); 150 | switchBtn.setEnabled(true); 151 | playBtn.setEnabled(true); 152 | videoSizeSpinner.setEnabled(true); 153 | } else { 154 | startRecording(); 155 | } 156 | } 157 | 158 | private void startRecording() { 159 | if (recordingManager.startRecording(fileName, videoSize)) { 160 | recordBtn.setText(R.string.stopRecordBtn); 161 | switchBtn.setEnabled(false); 162 | playBtn.setEnabled(false); 163 | videoSizeSpinner.setEnabled(false); 164 | return; 165 | } 166 | Toast.makeText(this, getString(R.string.videoRecordingError), Toast.LENGTH_LONG).show(); 167 | } 168 | 169 | private void play() { 170 | Intent i = new Intent(VideoRecordingActivity.this, VideoPlaybackActivity.class); 171 | i.putExtra(VideoPlaybackActivity.FileNameArg, fileName); 172 | startActivityForResult(i, 0); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /app/src/main/java/com/hwangjr/recordhelper/app/utils/NotificationUtils.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.recordhelper.app.utils; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.Context; 5 | import android.content.DialogInterface; 6 | import android.content.DialogInterface.OnClickListener; 7 | 8 | public class NotificationUtils { 9 | public static void showInfoDialog(Context ctx, String msg) { 10 | AlertDialog.Builder builder = new AlertDialog.Builder(ctx); 11 | builder.setMessage(msg); 12 | builder.setPositiveButton(ctx.getString(android.R.string.ok), new OnClickListener() { 13 | public void onClick(DialogInterface dialog, int arg1) { 14 | dialog.cancel(); 15 | } 16 | }); 17 | AlertDialog alert = builder.create(); 18 | alert.show(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/hwangjr/recordhelper/app/utils/StorageUtils.java: -------------------------------------------------------------------------------- 1 | package com.hwangjr.recordhelper.app.utils; 2 | 3 | import android.os.Environment; 4 | 5 | public class StorageUtils { 6 | private static final String AUDIO_FILE_NAME = "audiorecordtest.wav"; 7 | private static final String VIDEO_FILE_NAME = "videorecordtest.3gp"; 8 | 9 | public static boolean checkExternalStorageAvailable() { 10 | String state = Environment.getExternalStorageState(); 11 | if (Environment.MEDIA_MOUNTED.equals(state)) { 12 | return true; 13 | } 14 | else { 15 | return false; 16 | } 17 | } 18 | 19 | public static String getFileName(boolean isAudio) { 20 | String storageDir = Environment.getExternalStorageDirectory().getAbsolutePath(); 21 | return String.format("%s/%s", storageDir, (isAudio) ? AUDIO_FILE_NAME : VIDEO_FILE_NAME); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidKnife/RecordHelper/e10b213fc03b082cdd92eed0386e01e81cd84d3d/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_switch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidKnife/RecordHelper/e10b213fc03b082cdd92eed0386e01e81cd84d3d/app/src/main/res/drawable-hdpi/ic_switch.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidKnife/RecordHelper/e10b213fc03b082cdd92eed0386e01e81cd84d3d/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidKnife/RecordHelper/e10b213fc03b082cdd92eed0386e01e81cd84d3d/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/audio_play.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/audio_rec.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | 14 |